OpenCV中一個連通域處理函數


作者:tornadomeet 出處: http://www.cnblogs.com/tornadomeet 歡迎轉載或分享,但請務必聲明文章出處。
連通域處理函數的原型 

前言

  在圖像處理過程中,經常會遇到這樣一部分圖像,圖像的整體部分如果人來看的話一眼就能看出,但是它的內部由於有各種小缺口,導致斷開了,這樣在 計算機“眼”里就被認為是斷開的,為了使圖像達到適應人眼的感覺,需要將這些缺口和斷開的口給連接上去,這就需要用到計算機圖形學中的連通域處理技術。本 文給出一個簡單的連通域處理函數,當然這個函數是來自OpenCV著名教程Learning OpenCV中,只不過它的接口是基於c版本的OpenCV,而到目前為止,基於C++接口的OpenCV已經是主流,所以我將其接口改成了c++版的, 但是其內部一些代碼基本沒有動它。

  開發環境:OpenCV2.4.3+QtCreator2.5.1

 

  實驗基礎

  首先來看這個連通域處理函數的形式:

  void ConnectedComponents(Mat &mask_process,  int poly1_hull0,  float perimScale,  int number = 0,  Rect &bounding_box = Rect(),  Point &contour_centers = Point(-1, -1));

  參數mask表示的是需要進行連通域處理二值圖像。

  參數poly1_hull0表示輪廓邊緣是否采用多邊形擬合,如果該參數為1,則表示采用多邊形擬合,否則采用凸包擬合。

  參數perimScale是用來將那些小的輪廓去掉,那些小的輪廓時指它的周長小於(mask長+寬)/perimScale。當然你在其內部代碼也可以該為面積來判斷。

  參數num表示實際需要處理最多的輪廓的個數(如果輸入的mask有多個輪廓的話),這里的處理是指計算出這些輪廓的外接矩形和中心點。默認值為0,表示函數內部不需要處理這些外接矩形和中心點。

  參數bbs表示的是處理完后對應輪廓的外接矩形,默認值為Rect(),表示不需要返回這些外接矩形。

  參數centers表示處理完后對應輪廓的中心點坐標,默認值為Point(-1, -1),表示不需要返回這些中心點。

 

  C/C++知識點總結:

  如果一些函數需要默認值的話,可以直接在函數定義的時候指定,該指定並不一定是具體的某個值,也可以是空值等等。另外在函數內部實現時,有時候要注意默認值的特殊性。

 

  實驗結果

  所需處理原始圖像的灰度圖:

  

 

  其對應的mask圖像:

  

 

  使用多項式擬合的連通域處理后圖像:

  

 

  使用凸包集擬合的連通域處理后的圖像:

  

 

  實驗代碼及注釋(附錄有工程code下載地址):

  main.cpp:

  

#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; }

 

  實驗總結: 通過該連通域處理,能夠達到一定效果。

 

  參考資料:

  Bradski, G. and A. Kaehler (2008). Learning OpenCV: Computer vision with the OpenCV library, O'Reilly Media.

 

  附錄:實驗工程code下載地址


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM