連通域處理函數的原型:
void ConnectedComponents(Mat &mask_process, int poly1_hull0, float perimScale, int number = 0, Rect &bounding_box = Rect(), Point &contour_centers = Point(-1, -1));
參數解析:
參數mask_process表示的是需要進行連通域處理二值圖像。
參數poly1_hull0表示輪廓邊緣是否採用多邊形擬合,如果該參數爲1,則表示採用多邊形擬合,否則採用凸包擬合。
參數perimScale是用來將那些小的輪廓去掉,那些小的輪廓時指它的周長小於(mask長+寬)/perimScale。當然你在其內部代碼也可以該爲面積來判斷。
參數number 表示實際需要處理最多的輪廓的個數(如果輸入的mask有多個輪廓的話),這裏的處理是指計算出這些輪廓的外接矩形和中心點。默認值爲0,表示函數內部不需要處理這些外接矩形和中心點。
參數bounding_box 表示的是處理完後對應輪廓的外接矩形,默認值爲Rect(),表示不需要返回這些外接矩形。
參數contour_centers 表示處理完後對應輪廓的中心點座標,默認值爲Point(-1, -1),表示不需要返回這些中心點
實現代碼:
- #include <iostream>
- #include <opencv.hpp>
- using namespace cv;
- using namespace std;
- //Just some convienience macros
- #define CV_CVX_WHITE CV_RGB(0xff,0xff,0xff)
- #define CV_CVX_BLACK CV_RGB(0x00,0x00,0x00)
- void ConnectedComponents(Mat &mask_process, int poly1_hull0, float perimScale, int number = 0,
- Rect &bounding_box = Rect(), Point &contour_centers = Point(-1, -1))
- {
- /*下面4句代碼是爲了兼容原函數接口,即內部使用的是c風格,但是其接口是c++風格的*/
- IplImage *mask = &mask_process.operator IplImage();
- int *num = &number;
- CvRect *bbs = &bounding_box.operator CvRect();
- CvPoint *centers = &contour_centers.operator CvPoint();
- static CvMemStorage* mem_storage = NULL;
- static CvSeq* contours = NULL;
- //CLEAN UP RAW MASK
- //開運算作用:平滑輪廓,去掉細節,斷開缺口
- cvMorphologyEx( mask, mask, NULL, NULL, CV_MOP_OPEN, 1 );//對輸入mask進行開操作,CVCLOSE_ITR爲開操作的次數,輸出爲mask圖像
- //閉運算作用:平滑輪廓,連接缺口
- cvMorphologyEx( mask, mask, NULL, NULL, CV_MOP_CLOSE, 1 );//對輸入mask進行閉操作,CVCLOSE_ITR爲閉操作的次數,輸出爲mask圖像
- //FIND CONTOURS AROUND ONLY BIGGER REGIONS
- if( mem_storage==NULL ) mem_storage = cvCreateMemStorage(0);
- else cvClearMemStorage(mem_storage);
- //CV_RETR_EXTERNAL=0是在types_c.h中定義的,CV_CHAIN_APPROX_SIMPLE=2也是在該文件中定義的
- CvContourScanner scanner = cvStartFindContours(mask,mem_storage,sizeof(CvContour),CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE);
- CvSeq* c;
- int numCont = 0;
- //該while內部只針對比較大的輪廓曲線進行替換處理
- while( (c = cvFindNextContour( scanner )) != NULL )
- {
- double len = cvContourPerimeter( c );
- double q = (mask->height + mask->width) /perimScale; //calculate perimeter len threshold
- if( len < q ) //Get rid of blob if it's perimeter is too small
- {
- cvSubstituteContour( scanner, NULL ); //用NULL代替原來的那個輪廓
- }
- else //Smooth it's edges if it's large enough
- {
- CvSeq* c_new;
- if(poly1_hull0) //Polygonal approximation of the segmentation
- c_new = cvApproxPoly(c,sizeof(CvContour),mem_storage,CV_POLY_APPROX_DP, 2,0);
- else //Convex Hull of the segmentation
- c_new = cvConvexHull2(c,mem_storage,CV_CLOCKWISE,1);
- cvSubstituteContour( scanner, c_new ); //最開始的輪廓用凸包或者多項式擬合曲線替換
- numCont++;
- }
- }
- contours = cvEndFindContours( &scanner ); //結束輪廓查找操作
- // PAINT THE FOUND REGIONS BACK INTO THE IMAGE
- cvZero( mask );
- IplImage *maskTemp;
- //CALC CENTER OF MASS AND OR BOUNDING RECTANGLES
- if(*num != 0)
- {
- int N = *num, numFilled = 0, i=0;
- CvMoments moments;
- double M00, M01, M10;
- maskTemp = cvCloneImage(mask);
- for(i=0, c=contours; c != NULL; c = c->h_next,i++ ) //h_next爲輪廓序列中的下一個輪廓
- {
- if(i < N) //Only process up to *num of them
- {
- //CV_CVX_WHITE在本程序中是白色的意思
- cvDrawContours(maskTemp,c,CV_CVX_WHITE, CV_CVX_WHITE,-1,CV_FILLED,8);
- //Find the center of each contour
- if(centers != &cvPoint(-1, -1))
- {
- cvMoments(maskTemp,&moments,1); //計算mask圖像的最高達3階的矩
- M00 = cvGetSpatialMoment(&moments,0,0); //提取x的0次和y的0次矩
- M10 = cvGetSpatialMoment(&moments,1,0); //提取x的1次和y的0次矩
- M01 = cvGetSpatialMoment(&moments,0,1); //提取x的0次和y的1次矩
- centers[i].x = (int)(M10/M00); //利用矩的結果求出輪廓的中心點座標
- centers[i].y = (int)(M01/M00);
- }
- //Bounding rectangles around blobs
- if(bbs != &CvRect())
- {
- bbs[i] = cvBoundingRect(c); //算出輪廓c的外接矩形
- }
- cvZero(maskTemp);
- numFilled++;
- }
- //Draw filled contours into mask
- cvDrawContours(mask,c,CV_CVX_WHITE,CV_CVX_WHITE,-1,CV_FILLED,8); //draw to central mask
- } //end looping over contours
- *num = numFilled;
- cvReleaseImage( &maskTemp);
- }
- //ELSE JUST DRAW PROCESSED CONTOURS INTO THE MASK
- else
- {
- for( c=contours; c != NULL; c = c->h_next )
- {
- cvDrawContours(mask,c,CV_CVX_WHITE, CV_CVX_BLACK,-1,CV_FILLED,8);
- }
- }
- }
- int main()
- {
- Mat src, mask;
- src = imread("test.png", 0); //以灰度圖像讀入
- imshow("src", src);
- mask = src > 0; //轉換爲二值圖像
- imshow("mask", mask);
- ConnectedComponents(mask, 1, 8.0, 1, Rect(), Point(-1, -1)); //採用多邊形擬合處理
- imshow("out1", mask);
- ConnectedComponents(mask, 0, 8.0, 1, Rect(), Point(-1, -1)); //c採用凸包進行處理
- imshow("out2", mask);
- waitKey(0);
- return 0;
- }
所需處理原始圖像的灰度圖:
其對應的mask圖像:
使用多項式擬合的連通域處理後圖像:
使用凸包集擬合的連通域處理後的圖像: