前面在圖像轉換的時候學到canny算子,可以檢測出圖像的輪廓信息,但是,該算子檢測到的輪廓信息還需要我們手動的用眼睛去識別,而實際工程應用中,我們需要得到輪廓的具體數學信息,這就涉及到今天的主題,圖像輪廓檢測.
???????? 一.圖像輪廓檢測
???????? 在opencv中,輪廓對應著一系列的點的集合,opencv提供了一個函數,用來獲得這些點的集合
???????? API:void finContours(輸入圖像,輸出輪廓點集,輸出向量,int 輪廓檢索模式,int 輪廓近似方法,Point 輪廓點的可選偏移量)
???????? 注:1.輸入圖像,是單通道八位閾值化圖像,也就是對應canny檢測之后的圖像,如果不是閾值化圖像,算法會將圖像中不為0的點當成1,0點當成0處理,我們可以通過canny threshold adaptiveThreshold來處理,
???????? ?? 2.該函數在檢測輪廓的時候會修改源圖像的數據,所以最好不要拿源圖像直接檢測輪廓,最好是用拷貝
???????? ?? 3.輸出點集的形式為vector<vector<Point>>contours,每個向量包含有多個向量,包含的向量一個就是一條輪廓,而包含的向量是由一個個點構成的.
????????????????? 4.輸出向量包含了圖像輪廓的拓撲信息,比如這個輪廓的前一個輪廓編號,后一個輪廓編號,父輪廓編號以及子輪廓編號,形式為vector<vec4i>h,h[0]為后一個輪廓編號 h[1]后一個輪廓編號 h[2]父輪廓編號 h[3]內嵌輪廓編號
????????????????? 5.輪廓的檢索模式包含有如下幾種選擇RETR_EXTERNAL只檢測最外圍的輪廓? RETR_LIST提取所有的輪廓,不建立上下等級關系,只有兄弟等級關系? RETR_CCOMP提取所有輪廓,建立為雙層結構 RETR_TREE提取所有輪廓,建立網狀結構
????????????????? 6.輪廓的近似方法有以下取值 CHAIN_APPROX_NONE獲取輪廓的每一個像素,像素的最大間距不超過1 CHAIN_APPROX_SIMPLE壓縮水平垂直對角線的元素,只保留該方向的終點坐標(也就是說一條中垂線a-b,中間的點被忽略了) CHAIN_APPROX_TC89_LI使用TEH_CHAIN逼近算法中的LI算法? CHAIN_APPROX_TC89_KCOS使用TEH_CHAIN逼近算法中的KCOS算法
????????????????? 7.可選偏移量,對ROI區域中獲得的輪廓要在整個圖像中分析的時候,該參數可以派上用場
???????? 二.圖像輪廓繪制
???????? 該函數可以很方便的繪制出我們找到的輪廓點集和拓撲結構構成的輪廓圖.
???????? API:void drawContours(源圖像,輪廓點集,int 繪制指示,scalar 輪廓顏色,int 輪廓粗細,int 輪廓線形,inputarray 輪廓的拓撲結構信息,int 輪廓的最大繪制等級,int 可選的輪廓偏移參數)
???????? 注:1.如果繪制指示為負值,則繪制所有輪廓,輪廓粗細默認值為1,為負值的化,繪制在輪廓的內部進行,使用filled,填充輪廓,線條類型默認為8,LINE_AA將繪制抗鋸齒的線型,輪廓繪制等級,默認值是INT_MAX,指示最多可以繪制幾層輪廓,=.
???????? 輪廓檢測的基本步驟為1.圖像轉換為灰度圖像,2.圖像的濾波,降噪,3.圖像的canny二值化或者其他二值化方式,4.尋找輪廓findContours
代碼例子如下
//邊à?緣|ì輪?廓¤a查¨|找¨° //先¨¨進?行D邊à?緣|ì濾?波?§ 然¨?后¨?用??canny算?法¤?§得ì?到ì?二t值|ì化?¥圖a?像? //濾?波?§算?法¤?§選?擇?均¨′值|ì濾?波?§ 可¨|調ì??的ì?是o?孔?á徑?尺?寸?? //canny算?法¤?§可¨|調ì??的ì?上|?下?限T和¨asobel算?子á¨?孔?á徑?3 5 7 Mat srcImage,blurImage,grayBlurImage,cannyImage,contourImage; vector<vector<Point>>g_vContours; vector<Vec4i>g_vHierarchy; RNG g_rng(12345);const int g_BlurMax = 100; int g_BlurValue; void onBlurTrackBar(int pos,void* userData);const int g_sobelSizeMax = 2;//sobel孔?á徑? int g_sobelValue;const int g_lowThresholdMax = 80;//邊à?緣|ì檢¨?測a低ì¨a閾D值|ì int g_lowThresholdValue; int g_upThresholdValue;void onTrackBarSobelSize(int pos,void* userData); void onTrackBarLowThresholdSize(int pos,void* userData);int main(int argc,char* argv[]) {srcImage = imread("F:\\opencv\\OpenCVImage\\findContours.jpg");g_BlurValue = 4;g_sobelValue = 1;g_lowThresholdValue = 80;g_upThresholdValue = 240;namedWindow("canny Image");namedWindow("contours Image");createTrackbar("blur size value ", "canny Image", &g_BlurValue, g_BlurMax,onBlurTrackBar,0);createTrackbar("sobel size", "canny Image", &g_sobelValue, g_sobelSizeMax,onTrackBarSobelSize,0);createTrackbar("low threshold", "canny Image", &g_lowThresholdValue, g_lowThresholdMax,onTrackBarLowThresholdSize,0);onBlurTrackBar(g_BlurValue, 0);imshow("src image", srcImage);moveWindow("src image", 0, 0);moveWindow("canny Image", srcImage.cols, 0);moveWindow("contour image", srcImage.cols*2, 0);waitKey(0);return 0; }//修T改?了¢?濾?波?§參?數oy void onBlurTrackBar(int pos,void* userData) {int blurSize = g_BlurValue*2+1;blur(srcImage, blurImage, Size(blurSize,blurSize));int sobelValue = g_sobelValue*2 +3;if (g_lowThresholdValue == 0) {g_lowThresholdValue = 1;}int lowThresholdValue = g_lowThresholdValue;int upThresholdValue = lowThresholdValue*3;cvtColor(blurImage, grayBlurImage, CV_RGB2GRAY);//計?算?canny Canny(grayBlurImage, cannyImage, lowThresholdValue, upThresholdValue,sobelValue);contourImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);findContours(cannyImage, g_vContours, g_vHierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));for(int i = 0;i<g_vHierarchy.size();i++){Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));drawContours(contourImage, g_vContours, i, color,2,8,g_vHierarchy,0,Point(0,0));}imshow("contour image", contourImage);imshow("canny Image",cannyImage); }//修T改?了¢?sobel孔?á徑?參?數oy void onTrackBarSobelSize(int pos,void* userData) {int blurSize = g_BlurValue*2+1;blur(srcImage, blurImage, Size(blurSize,blurSize));int sobelValue = g_sobelValue*2 +3;if (g_lowThresholdValue == 0) {g_lowThresholdValue = 1;}int lowThresholdValue = g_lowThresholdValue;int upThresholdValue = lowThresholdValue*3;cvtColor(blurImage, grayBlurImage, CV_RGB2GRAY);//計?算?canny Canny(grayBlurImage, cannyImage, lowThresholdValue, upThresholdValue,sobelValue);contourImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);findContours(cannyImage, g_vContours, g_vHierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));for(int i = 0;i<g_vHierarchy.size();i++){Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));drawContours(contourImage, g_vContours, i, color,2,8,g_vHierarchy,0,Point(0,0));}imshow("contour image", contourImage);imshow("canny Image",cannyImage); }//修T改?了¢?閾D值|ì參?數oy void onTrackBarLowThresholdSize(int pos,void* userData) {int blurSize = g_BlurValue*2+1;blur(srcImage, blurImage, Size(blurSize,blurSize));int sobelValue = g_sobelValue*2 +3;if (g_lowThresholdValue == 0) {g_lowThresholdValue = 1;}int lowThresholdValue = g_lowThresholdValue;int upThresholdValue = lowThresholdValue*3;cvtColor(blurImage, grayBlurImage, CV_RGB2GRAY);//計?算?canny Canny(grayBlurImage, cannyImage, lowThresholdValue, upThresholdValue,sobelValue);contourImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);findContours(cannyImage, g_vContours, g_vHierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));for(int i = 0;i<g_vHierarchy.size();i++){Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));drawContours(contourImage, g_vContours, i, color,2,8,g_vHierarchy,0,Point(0,0));}imshow("contour image", contourImage);imshow("canny Image",cannyImage); }
二.凸包
???????? 凸包是指,給定一個二維平面上的點集,凸包就是將這個點集最外層的點連接起來的構成的凸多邊形
???????? 計算一個物體的凸包,然后計算凸包的凹缺陷,是理解物體輪廓與形狀的有效方式,可以用于典型的相似物體查找中.
???????? API:void convertHull(輸入二維點集,輸出凸包,bool 操作方向標識符,bool 返回點類型)
???????? 注:1.操作方向標識符,是指在笛卡爾坐標中,當為true的時候,起始點到結束點順時針,否則,逆時針.
????????????????? 2返回點類型為真時,返回凸包的各個定點,否則,返回凸包各點的指數,當輸出為vector<point>時,這個參數被忽略.
????????????????? 3.返回的二維點集形態類是vector<point>,得到的凸包類型,也是vector<point>hull,hull.size()是凸包的點的個數.
???????? 一般尋找凸包,主要是先對圖像二值化,后尋找輪廓,然后尋找一條指定輪廓的凸包.
示例代碼如下
//對?一°?張?圖a?片?進?行D輪?廓¤a查¨|找¨°,并?é對?每?一°?個?查¨|找¨°出?來¤??的ì?輪?廓¤a運?行D凸a1包?¨1查¨|找¨°程¨?序¨°,生|¨2成¨|最á?終?顯?示o?圖a?片? //邊à?緣|ì輪?廓¤a查¨|找¨° //先¨¨進?行D邊à?緣|ì濾?波?§ 然¨?后¨?用??canny算?法¤?§得ì?到ì?二t值|ì化?¥圖a?像? //濾?波?§算?法¤?§選?擇?均¨′值|ì濾?波?§ 可¨|調ì??的ì?是o?孔?á徑?尺?寸?? //canny算?法¤?§可¨|調ì??的ì?上|?下?限T和¨asobel算?子á¨?孔?á徑?3 5 7 Mat srcImage,blurImage,grayBlurImage,cannyImage,contourImage,hullImage; vector<vector<Point>>g_vContours; vector<Vec4i>g_vHierarchy; RNG g_rng(12345);const int g_BlurMax = 100; int g_BlurValue; void onBlurTrackBar(int pos,void* userData);const int g_sobelSizeMax = 2;//sobel孔?á徑? int g_sobelValue;const int g_lowThresholdMax = 80;//邊à?緣|ì檢¨?測a低ì¨a閾D值|ì int g_lowThresholdValue; int g_upThresholdValue;void onTrackBarSobelSize(int pos,void* userData); void onTrackBarLowThresholdSize(int pos,void* userData);int main(int argc,char* argv[]) {srcImage = imread("F:\\opencv\\OpenCVImage\\convexHull.jpg");g_BlurValue = 4;g_sobelValue = 1;g_lowThresholdValue = 80;g_upThresholdValue = 240;namedWindow("canny Image");namedWindow("contours Image");namedWindow("hull image");createTrackbar("blur size value ", "canny Image", &g_BlurValue, g_BlurMax,onBlurTrackBar,0);createTrackbar("sobel size", "canny Image", &g_sobelValue, g_sobelSizeMax,onTrackBarSobelSize,0);createTrackbar("low threshold", "canny Image", &g_lowThresholdValue, g_lowThresholdMax,onTrackBarLowThresholdSize,0);onBlurTrackBar(g_BlurValue, 0);moveWindow("src image", 0, 0);moveWindow("canny Image", srcImage.cols, srcImage.rows);moveWindow("contour image", srcImage.cols*2, 0);moveWindow("hull image", srcImage.cols*2, srcImage.rows);waitKey(0);return 0; }//修T改?了¢?濾?波?§參?數oy void onBlurTrackBar(int pos,void* userData) {imshow("src image", srcImage);int blurSize = g_BlurValue*2+1;blur(srcImage, blurImage, Size(blurSize,blurSize));int sobelValue = g_sobelValue*2 +3;if (g_lowThresholdValue == 0) {g_lowThresholdValue = 1;}int lowThresholdValue = g_lowThresholdValue;int upThresholdValue = lowThresholdValue*3;cvtColor(blurImage, grayBlurImage, CV_RGB2GRAY);//計?算?canny Canny(grayBlurImage, cannyImage, lowThresholdValue, upThresholdValue,sobelValue);contourImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);findContours(cannyImage, g_vContours, g_vHierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));for(int i = 0;i<g_vHierarchy.size();i++){Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));drawContours(contourImage, g_vContours, i, color,2,8,g_vHierarchy,0,Point(0,0));}vector<vector<Point>>hull(g_vContours.size());hullImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);for(int i = 0; i < g_vContours.size(); i++){convexHull(Mat(g_vContours[i]), hull[i],false);Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));drawContours(hullImage, hull, i, color,1,8,vector<Vec4i>(),0,Point(0,0));}imshow("hull image", hullImage);imshow("contour image", contourImage);imshow("canny Image",cannyImage); }//修T改?了¢?sobel孔?á徑?參?數oy void onTrackBarSobelSize(int pos,void* userData) {imshow("src image", srcImage);int blurSize = g_BlurValue*2+1;blur(srcImage, blurImage, Size(blurSize,blurSize));int sobelValue = g_sobelValue*2 +3;if (g_lowThresholdValue == 0) {g_lowThresholdValue = 1;}int lowThresholdValue = g_lowThresholdValue;int upThresholdValue = lowThresholdValue*3;cvtColor(blurImage, grayBlurImage, CV_RGB2GRAY);//計?算?canny Canny(grayBlurImage, cannyImage, lowThresholdValue, upThresholdValue,sobelValue);contourImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);findContours(cannyImage, g_vContours, g_vHierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));for(int i = 0;i<g_vHierarchy.size();i++){Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));drawContours(contourImage, g_vContours, i, color,2,8,g_vHierarchy,0,Point(0,0));}vector<vector<Point>>hull(g_vContours.size());hullImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);for(int i = 0; i < g_vContours.size(); i++){convexHull(Mat(g_vContours[i]), hull[i],false);Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));drawContours(hullImage, hull, i, color,1,8,vector<Vec4i>(),0,Point(0,0));}imshow("hull image", hullImage);imshow("contour image", contourImage);imshow("canny Image",cannyImage); }//修T改?了¢?閾D值|ì參?數oy void onTrackBarLowThresholdSize(int pos,void* userData) {imshow("src image", srcImage);int blurSize = g_BlurValue*2+1;blur(srcImage, blurImage, Size(blurSize,blurSize));int sobelValue = g_sobelValue*2 +3;if (g_lowThresholdValue == 0) {g_lowThresholdValue = 1;}int lowThresholdValue = g_lowThresholdValue;int upThresholdValue = lowThresholdValue*3;cvtColor(blurImage, grayBlurImage, CV_RGB2GRAY);//計?算?canny Canny(grayBlurImage, cannyImage, lowThresholdValue, upThresholdValue,sobelValue);contourImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);findContours(cannyImage, g_vContours, g_vHierarchy, RETR_TREE,CHAIN_APPROX_SIMPLE,Point(0,0));for(int i = 0;i<g_vHierarchy.size();i++){Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));drawContours(contourImage, g_vContours, i, color,2,8,g_vHierarchy,0,Point(0,0));}vector<vector<Point>>hull(g_vContours.size());hullImage = Mat::zeros(cannyImage.rows, cannyImage.cols, CV_8UC3);for(int i = 0; i < g_vContours.size(); i++){convexHull(Mat(g_vContours[i]), hull[i],false);Scalar color = Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255));drawContours(hullImage, hull, i, color,1,8,vector<Vec4i>(),0,Point(0,0));}imshow("hull image", hullImage);imshow("contour image", contourImage);imshow("canny Image",cannyImage); }
三.對于輪廓代表的二維點集的其他處理方式
???????? 獲取點集的外圍矩形邊界
???????? API:Rect boundingRect(輸入點集)
???????? 返回點集的最小包圍矩形
???????? API:RotatedRect minAreaRect(輸入點集)
???????? 尋找點集的最小包圍圓心
???????? API:void minEnclosingCircle(輸入點集,Point2f& 圓心,float& 半徑)
???????? 橢圓擬合二維點集
???????? API:RatatedRect fitEllipse(輸入點集)
???????? 逼近多邊形曲線
???????? API:void approxPolyDp(輸入二維點集,輸出多邊形逼近結果,double epsilon,bool close是否封閉)
???????? 注:epsilon為原始曲線和近似曲線之間的最大值
???????? ?? closed為真,則封閉,為假,得到的多邊形不封閉
???????? 以上各個API使用例程如下
//首o?á先¨¨查¨|找¨°輪?廓¤a,再¨′用??多¨¤邊à?形?逼à?近¨1輪?廓¤a //然¨?后¨?依°¨¤靠?多¨¤邊à?形?輪?廓¤a獲?得ì?包?¨1圍?ì多¨¤邊à?形?輪?廓¤a的ì?圓2形? 最á?小?矩?形? 矩?形?邊à?界? //需¨¨要°a變à?更¨1的ì?只?有?D二t值|ì化?¥操¨′作á??時o?à的ì?閾D值|ì //閾D值|ì最á?大?¨?值|ì 255 最á?小?值|ì可¨|變à? Mat srcImage,srcCopyImage,srcGrayImage,srcThresholdImage,DstImage; vector<vector<Point>>contours; vector<Vec4i> hierarchys; const int g_lowThresholdMax = 254; int g_lowThresholdValue; int g_upThresholdValue; void onTrackBarLowThreshold(int pos,void* userData); RNG g_rng(12345); int main(int argc,char* argv[]) {srcImage = imread("F:\\opencv\\OpenCVImage\\contour.jpg");srcCopyImage = srcImage.clone();//轉áa化?¥RGB為a灰¨°度¨¨圖a?像?if(srcImage.channels() == 3){cvtColor(srcImage, srcGrayImage, CV_RGB2GRAY);}else{srcGrayImage = srcImage.clone();}blur(srcGrayImage, srcGrayImage,Size(3,3));namedWindow("src image");namedWindow("threshold image");namedWindow("dst image");g_lowThresholdValue = 80;g_upThresholdValue = 255;createTrackbar("low threshold value", "threshold image", &g_lowThresholdValue, g_lowThresholdMax,onTrackBarLowThreshold,0);onTrackBarLowThreshold(g_lowThresholdValue, 0);imshow("src image", srcImage);moveWindow("src image", 0, 0);moveWindow("threshold image", srcImage.cols, 0);moveWindow("dst image", srcImage.cols*2, 0);waitKey(0);return 0; }void onTrackBarLowThreshold(int pos,void* userData) {if(g_lowThresholdValue == 0)g_lowThresholdValue = 1;threshold(srcGrayImage, srcThresholdImage, g_lowThresholdValue, g_upThresholdValue, THRESH_BINARY);//二t值|ì化?¥完a¨o成¨|,尋??找¨°輪?廓¤afindContours(srcThresholdImage, contours, hierarchys, RETR_TREE, CHAIN_APPROX_SIMPLE,Point(0,0));//多¨¤邊à?形?畢à?竟1輪?廓¤a,先¨¨生|¨2成¨|變à?量¢?vector<vector<Point>>contours_polys(contours.size());//多¨¤邊à?形?vector<Rect>boundRect(contours.size());//輪?廓¤a最á?外aa層?矩?形?邊à?界?vector<Point2f>center(contours.size());//最á?小?面?積y包?¨1圍?ì圓2vector<float>radius(contours.size());DstImage = srcCopyImage.clone();for(int i = 0; i < contours.size();i++){//逼à?近¨1多¨¤邊à?形?approxPolyDP(Mat(contours[i]), contours_polys[i], 3, true);//逼à?近¨1精?度¨¨3且¨°封¤a閉à?//從?¨?逼à?近¨1到ì?的ì?多¨¤邊à?形?得ì?到ì?最á?外aa層?矩?形?boundRect[i] = boundingRect(Mat(contours_polys[i]));//從?¨?逼à?近¨1的ì?多¨¤邊à?形?得ì?到ì?最á?小?圓2形? minEnclosingCircle(Mat(contours_polys[i]), center[i], radius[i]);}//先¨¨繪?制?輪?廓¤adrawContours(DstImage, contours, -1, Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255)));//依°¨¤次??在¨2輪?廓¤a上|?繪?制?矩?形?和¨a圓2形?for(int i = 0; i < contours.size(); i++){rectangle(DstImage, boundRect[i].tl(), boundRect[i].br(), Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255)),2,8,0);circle(DstImage, center[i], (int)radius[i], Scalar(g_rng.uniform(0, 255),g_rng.uniform(0, 255),g_rng.uniform(0, 255)),2,8,0);}imshow("threshold image", srcThresholdImage);imshow("dst image", DstImage); }
四.圖像的矩
???????? 圖像說到底還是一個矩陣,而矩陣在不同的空間大小的情況下,進行分析的時候就需要用到矩陣的矩,也就是圖像的矩,矩函數在圖像分析中具有重要的作用,模式識別,目標分類,目標識別方位估計,圖像編碼重構等都需要用到圖像的矩.
???????? 圖像的一階矩與圖像的形狀相關,二階矩顯示圖像中曲線圍繞直線平均值的擴展程度,三階矩是關于平均值的對稱性的測量,由二階矩和三階矩可以到處七個不變矩,也即是7Hu不變矩.
???????? 不變矩是圖像的統計特征,滿足平移,旋轉,伸縮均不變的不變性,可以用于圖像識別.
???????? 另外,通過中心矩也可以計算圖像的輪廓長度以及面積.
???????? API:Moments moments(源圖像,bool 非0像素是否全看做1);
???????? 注:源可以是二維數組或者單通道,八位或浮點型
????????????????? 第二個參數默認為false,非零像素不全部看作為1
???????? 計算輪廓面積
???????? API:double contourArea(輸入,bool 面向區域標識符)
???????? 輸入輸入為向量或者是二維點,也可以說Mat,一般是查找到的圖像的輪廓點集或者是根據輪廓點集擬合出的矩形,橢圓,圓,多邊形,也可以使圖像的凸包vector<poit2f>,返回圖像的面積,默認為false,表示返回圖像的面積是絕對值,不帶符號.
???????? 計算輪廓長度
???????? API:double arcLength(輸入點集,bool 指示曲線是否封閉)
???????? 注:輸入點集類型與上一個api一致,默認曲線是封閉的.
測試使用圖像的中心矩和opencv提供的算法,來計算圖像輪廓面積,代碼如下
//圖a?像?的ì?矩? //canny算?法¤?§獲?得ì?二t值|ì圖a?像? 二t值|ì圖a?像?獲?得ì?邊à?緣|ì 邊à?緣|ì獲?得ì?邊à?緣|ì矩? 邊à?緣|ì矩?獲?得ì?中D心?距¨¤ //通a?§過y中D心?距¨¤計?算?圖a?像?的ì?輪?廓¤a的ì?面?積y和¨a距¨¤離¤? Mat srcImage,srcGrayImage,srcBlurImage,srcThresholdImage,srcCopyImage;vector<vector<Point>>contours; vector<Vec4i> hierarchys;const int g_lowThresholdMax = 85; int g_lowThresholdValue; int g_upThresholdValue; void onTrackBarLowThreshold(int pos,void* userData);int main(int argc,char* argv[]) {srcImage = imread("F:\\opencv\\OpenCVImage\\mement.jpg");if(srcImage.channels() == 3){cvtColor(srcImage, srcGrayImage, CV_RGB2GRAY);}else{srcGrayImage = srcImage.clone();}blur(srcGrayImage,srcBlurImage,Size(3,3));namedWindow("canny image");g_lowThresholdValue = 80;g_upThresholdValue = g_lowThresholdValue*3;createTrackbar("low threshold value", "canny image", &g_lowThresholdValue, g_lowThresholdMax,onTrackBarLowThreshold,0);onTrackBarLowThreshold(g_lowThresholdValue, 0);imshow("src image", srcImage);moveWindow("src image", 0, 0);moveWindow("canny image", srcBlurImage.cols, 0);moveWindow("dst image", srcBlurImage.cols*2, 0);waitKey(0);return 0; }void onTrackBarLowThreshold(int pos,void* userData) {Canny(srcBlurImage, srcThresholdImage, g_lowThresholdValue, g_upThresholdValue,3);//二t進?制?圖a?像?獲?取¨?輪?廓¤afindContours(srcThresholdImage, contours, hierarchys, RETR_TREE, CHAIN_APPROX_SIMPLE,Point(0,0));//計?算?矩?vector<Moments>mu(contours.size());for(int i = 0; i < contours.size(); i++){mu[i] = moments(contours[i],false);}//計?算?中D心?矩?vector<Point2f>mc(contours.size());for(int i = 0; i < contours.size(); i++){mc[i] = Point2f(static_cast<float>(mu[i].m10/mu[i].m00),static_cast<float>(mu[i].m01/mu[i].m00));}//繪?制?輪?廓¤a//srcCopyImage = srcImage.clone();srcCopyImage = Mat(srcImage.rows,srcImage.cols,CV_8UC1,Scalar::all(0));drawContours(srcCopyImage, contours, -1, Scalar(255));for(int i = 0; i < contours.size(); i++){circle(srcCopyImage, mc[i], 4, Scalar(255),-1,8,0);}imshow("canny image", srcThresholdImage);imshow("dst image", srcCopyImage);//開a始o?計?算?輪?廓¤a并?é且¨°輸o?出?值|ì,通a?§過y矩?和¨aopencv函?¥數oy計?算?出?來¤??的ì?面?積y對?比ਨfor(int i = 0; i < contours.size();i++){printf("計?算?輪?廓¤a面?積y以°?及??長?è度¨¨,第ì¨2%d個?輪?廓¤a的ì?面?積y為a(矩?計?算?得ì?出?):%.2f\n通a?§過yopencv函?¥數oy計?算?出?來¤??的ì?面?積y為a%.2f\t長?è度¨¨為a%.2f\n",i,mu[i].m00,contourArea(contours[i],false),arcLength(contours[i],true));} }
五.分水嶺算法
???????? 分水嶺算法的主要意義在于分割圖像,從背景圖像中獲得有用信息,比如在一張圖像中,前景和背景的像素差異總是很大,此時需要將前景背景分離開來,就需要分水嶺算法了.
???????? 分水嶺算法市一中基于標記的分割算法,表示的是輸入圖像的極大值點,在把圖像傳遞給函數之前,需要大致勾畫出圖像中需要分割的區域,這些標記的值可以使用輪廓查找算法和輪廓繪制算法在圖像中標記.
???????? 最終形成的,是由極值點構成的一個一個的區域,如果圖像中目標是連在一起的,分割起來有困難,可以使用該算法將黏著在一起的目標分開
???????? 直接用邊界來進行分水嶺算法的效果不佳,一般來說,先對前后景進行標記,在應用分水嶺算法,每個對象內部都是相連的,背景里面的每個像素都不屬于任何目標,在應用分水嶺算法就會取得較好的效果.
???????? void waterShed(輸入圖像,圖像掩碼);
???????? 注:輸入圖像必須為八位三通道彩色圖像,掩碼是運算結果,32位單通道圖像,和源圖有一樣的尺寸和類型,為啥為32位呢,因為一張圖像完全可能被分成不止255個區域,那八位就不夠用了呀.
???????? 具體效果看如下代碼例程
//分水嶺算法waterShed
//在源圖像中繪制區域線條,同時把繪制的區域線條保存在mask中,然后對mask進行
//輪廓查找算法,找到輪廓以后,在新的掩碼中按照不同的輪廓繪制不同的灰度值
//調用分水嶺算法,根據結果的不同灰度進行著色,最終的圖像和源圖像混合生成最終圖像
//這就是分水嶺算法的意義
//分水嶺算法配合膨脹 腐蝕等形態學運算,效果應該很好
Mat srcImage,srcImageCopy; Mat maskImage,maskImageCopy; bool draw; Point2i prevPoint;//記?錄?前??一°?個?鼠o¨?標à¨o事o?件t點ì?的ì?位?置? RNG g_rng(12345); void onMouseEvent(int event,int x,int y,int flag,void* userData); int main(int argc,char* argv[]) {srcImage = imread("F:\\opencv\\OpenCVImage\\waterShed.jpg");imshow("src image", srcImage);prevPoint = Point2i(-1,-1);srcImageCopy = srcImage.clone();maskImage = Mat(srcImage.rows,srcImage.cols,CV_32SC1,Scalar::all(0));maskImageCopy = maskImage.clone();setMouseCallback("src image", onMouseEvent);imshow("dst image", maskImage);moveWindow("src image", 0, 0);moveWindow("dst image", srcImage.cols, 0);draw = false;int keyValue = 0;do{keyValue = waitKey(30);if(keyValue == '1')//清?除y圖a?像? {draw = false;srcImageCopy = srcImage.clone();maskImageCopy = maskImage.clone();prevPoint = Point2i(-1,-1);imshow("src image", srcImageCopy);imshow("dst image", maskImageCopy);}else if(keyValue == '2'){//開a始o?計?算?if(draw == true)//有?D輪?廓¤a {vector<vector<Point2i>>contours;vector<Vec4i>hierarchy;findContours(maskImageCopy, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);//檢¨?查¨|輪?廓¤aif(contours.size() == 0)continue;//找¨°出?輪?廓¤a以°?后¨?將?輪?廓¤a繪?制?在¨2源??mask上|?//對?不?同a?的ì?輪?廓¤a,用??不?同a?的ì?值|ì進?行D區?分¤?Mat maskDstImage = Mat(maskImageCopy.size(),CV_32SC1,Scalar::all(0));int c_comp = 0;for(int i = 0 ; i >= 0;i = hierarchy[i][0],c_comp++){//hierarchy[i][0]對?應?|后¨?一°?個?輪?廓¤a//繪?制?輪?廓¤a,輪?廓¤a的ì?值|ì依°¨¤次??為a1 2 3 4 5drawContours(maskDstImage, contours, i, Scalar::all(c_comp+1),-1,8,hierarchy,INT_MAX);}//此??時o?à,一°?共2有?Dc_comp個?輪?廓¤a點ì?,生|¨2成¨|scalar數oy組á¨|,用??于?¨2后¨?期¨2著á?色|?vector<Vec3b>colorTab;for (int i = 0; i < c_comp; i++) {int r = g_rng.uniform(0, 255);int g = g_rng.uniform(0, 255);int b = g_rng.uniform(0, 255);colorTab.push_back(Vec3b((uchar)r,(uchar)g,(uchar)b));}//進?行D分¤?水?嶺¢?處?|理¤¨a算?法¤?§double startTime = (double)getTickCount();watershed(srcImageCopy, maskDstImage);double endTime = (double)getTickCount();printf("算?法¤?§使o1用??時o?à間?為a%.2f \r\n",((endTime-startTime)*1000)/getTickFrequency());//得ì?到ì?了¢?分¤?水?嶺¢?圖a?像?以°?后¨?,按???照?colortab的ì?內¨2容¨Y,對?分¤?水?嶺¢?圖a?像?進?行D區?域?¨°著á?色|?//分¤?水?嶺¢?算?法¤?§的ì?處?|理¤¨a結¨¢果?存??放¤?在¨2maskdstImage中D Mat dstImage(maskDstImage.size(),CV_8UC3);for(int i = 0 ; i< maskDstImage.rows;i++){for (int j = 0; j < maskDstImage.cols; j++) {int index = maskDstImage.at<int>(i,j);if(index == -1){dstImage.at<Vec3b>(i,j) = Vec3b(255,255,255);}else if(index < 0 || index > c_comp){dstImage.at<Vec3b>(i,j) = Vec3b(0,0,0);}else{dstImage.at<Vec3b>(i,j) = colorTab[index-1];}}}//再¨′把??源??圖a?像?和¨a得ì?到ì?的ì?掩¨2碼?圖a?像?混¨?合?顯?示o?新?的ì?圖a?片?addWeighted(srcImage, 0.5, dstImage, 0.5, 0.0, dstImage);//這a里¤?就¨a已°?經-得ì?到ì?最á?終?的ì?maskdst,顯?示o?看??看??imshow("dst image", dstImage);}else{continue;}}}while(keyValue != 27);return 0; }void onMouseEvent(int event,int x,int y,int flag,void* userData) {if(x < 0||y< 0||x>=srcImage.cols||y>=srcImage.rows)return;if(event == EVENT_LBUTTONDOWN){prevPoint = Point2i(x,y);}else if(event == EVENT_LBUTTONUP){prevPoint = Point2i(-1,-1);}else if(event == EVENT_MOUSEMOVE){if(flag&EVENT_FLAG_LBUTTON){//鼠o¨?標à¨o左á¨?鍵¨1滑?動?¥draw = true;line(srcImageCopy, prevPoint,Point2i(x,y), Scalar::all(255),4,LINE_AA);line(maskImageCopy,prevPoint,Point2i(x,y), Scalar::all(INT_MAX),4,LINE_AA);prevPoint = Point2i(x,y);imshow("dst image", maskImageCopy);imshow("src image", srcImageCopy);}} }
六.圖像修復
??????? 圖像修復是指利用那些已經被破壞區域的邊沿,即邊緣的顏色和結構,通過對它的繁殖和混合,填充的被損壞區域中去,已達到圖像修補的目的,注意,邊緣損壞過多的圖像也是難以修復的,對于小塊破損比較有效
???????? API:void inPaint(原圖片,修復區域掩碼,輸出圖片,double inpaintRadius修復算法參考半徑,int 修復算法標識符)
???????? 注:1.原圖片必須為八位單通道或者是三通道圖片,掩碼是八位單通道圖片,其中那個非零的那些像素點表示需要修復的區域,輸出圖像必須和源圖像有一樣的尺寸和類型,最小修復半徑修復的每個店所參考的周圍區域顏色的半徑.
????????????????? 2.修復算法標識符表示用什么修復算法進行計算,取值INPAINT_NS基于nerier_stokes方法 ALEXANDRU_TELEA另一種方法.
???????? 修復圖片的例子見面的代碼
//圖a?像?修T補1 //需¨¨要°a原-圖a? 掩¨2碼?圖a? 目?標à¨o圖a? //修T復??算?法¤?§參?考?半??徑?為a3 修T復??方¤?法¤?§為a INPAINT_TELEA Mat srcImage,srcImageCopy; Mat maskImage,maskImageCopy; bool draw;Point2i prevPoint;//記?錄?前??一°?個?鼠o¨?標à¨o事o?件t點ì?的ì?位?置? void onMouseEvent(int event,int x,int y,int flag,void* userData);int main(int argc,char* argv[]) {srcImage = imread("F:\\opencv\\OpenCVImage\\inpaint.jpg");imshow("src image", srcImage);prevPoint = Point2i(-1,-1);srcImageCopy = srcImage.clone();maskImage = Mat(srcImage.rows,srcImage.cols,CV_8UC1,Scalar::all(0));maskImageCopy = maskImage.clone();setMouseCallback("src image", onMouseEvent);imshow("dst image", maskImage);moveWindow("src image", 0, 0);moveWindow("dst image", srcImage.cols, 0);draw = false;int keyValue = 0;do {keyValue = waitKey(30);if(keyValue == '1'){//清?除ydraw = false;srcImageCopy = srcImage.clone();maskImageCopy = maskImage.clone();prevPoint = Point2i(-1,-1);imshow("src image", srcImageCopy);imshow("dst image", maskImageCopy);}else if(keyValue == '2'){//修T復??if(draw == true){Mat srcImageInpaint = srcImageCopy.clone();Mat dstImage = Mat(srcImageInpaint.size(),CV_8UC3,Scalar::all(0));inpaint(srcImageInpaint, maskImageCopy, dstImage, 3, INPAINT_TELEA);srcImageCopy = srcImage.clone();maskImageCopy = maskImage.clone();prevPoint = Point2i(-1,-1);imshow("dst image", dstImage);}}} while (keyValue != 27);return 0; }void onMouseEvent(int event,int x,int y,int flag,void* userData) {if(x < 0||y< 0||x>=srcImage.cols||y>=srcImage.rows)return;if(event == EVENT_LBUTTONDOWN){prevPoint = Point2i(x,y);}else if(event == EVENT_LBUTTONUP){prevPoint = Point2i(-1,-1);}else if(event == EVENT_MOUSEMOVE){if(flag&EVENT_FLAG_LBUTTON){//鼠o¨?標à¨o左á¨?鍵¨1滑?動?¥draw = true;line(srcImageCopy, prevPoint,Point2i(x,y), Scalar::all(255),4,LINE_AA);line(maskImageCopy,prevPoint,Point2i(x,y), Scalar::all(INT_MAX),4,LINE_AA);prevPoint = Point2i(x,y);imshow("dst image", maskImageCopy);imshow("src image", srcImageCopy);}} }
?