opencv學習系列:連通域參考處理


OpenCV里提取目標輪廓的函數是findContours,它的輸入圖像是一幅二值圖像,輸出的是每一個連通區域的輪廓點的集合:vector<vector<Point>>。
外層vector的size代表了圖像中輪廓的個數,里面vector的size代表了輪廓上點的個數。

hiararchy參數和輪廓個數相同,每個輪廓contours[ i ]對應4個hierarchy元素hierarchy[ i ][ 0 ] ~hierarchy[ i ][ 3 ],分別表示后一個輪廓、前一個輪廓、父輪廓、內嵌輪廓的索引編號,如果沒有對應項,該值設置為負數。
第六個參數傳入CV_CHAIN_CODE時,要設置成sizeof(CvChain),其它情況統一設置成sizeof(CvContour)
CV_CHAIN_CODE:以Freeman鏈碼的方式輸出輪廓,所有其他方法輸出多邊形(頂點的序列)。
CV_CHAIN_APPROX_SIMPLE:壓縮水平的、垂直的和斜的部分,也就是,函數只保留他們的終點部分。例如一個矩形輪廓只需4個點來保存輪廓信息
CV_RETR_EXTERNAL:只檢索最外面的輪廓;
CV_RETR_LIST:檢測的輪廓不建立等級關系,檢索所有的輪廓,並將其保存到一條鏈表當中;
CV_RETR_CCOMP:建立兩個等級的輪廓,上面的一層為外邊界,里面的一層為內孔的邊界信息。檢索所有的輪廓,並將他們組織為兩層:頂層是各部分的外部邊界,第二層是空洞的邊界;
CV_RETR_TREE:檢索所有的輪廓,並重構嵌套輪廓的整個層次,可以參見下圖。
加滾動條確定閾值化的合適閾值!:http://blog.csdn.net/augusdi/article/details/9021467
****************************************************************************************
//做一下膨脹,x與y方向都做,但系數不同
            var kernal = Cv.CreateStructuringElementEx(5, 2, 1, 1, ElementShape.Rect);
            Cv.Erode(gray, gray, kernal, 2);

            //二值化
            Cv.Threshold(gray, gray, 0, 255, ThresholdType.BinaryInv | ThresholdType.Otsu);

            //檢測連通域,每一個連通域以一系列的點表示,FindContours方法只能得到第一個域
            var storage = Cv.CreateMemStorage();
            CvSeq<CvPoint> contour = null;
            Cv.FindContours(gray, storage, out contour, CvContour.SizeOf, ContourRetrieval.CComp, ContourChain.ApproxSimple);
            var color = new CvScalar(0, 0, 255);

            //開始遍歷
            while (contour != null)
            {
                //得到這個連通區域的外接矩形
                var rect = Cv.BoundingRect(contour);

                //如果高度不足,或者長寬比太小,認為是無效數據,否則把矩形畫到原圖上
                if(rect.Height > 10 && (rect.Width * 1.0 / rect.Height) > 0.2)
                    Cv.DrawRect(src, rect, color);

                //取下一個連通域
                contour = contour.HNext;
            }

***************************************************************






***********************************************************************************************************
// 移除過小或過大的輪廓  
void getSizeContours(vector<vector<Point>> &contours)  
{  
    int cmin = 100;   // 最小輪廓長度  
    int cmax = 1000;   // 最大輪廓長度  
    vector<vector<Point>>::const_iterator itc = contours.begin();  
    while(itc != contours.end())  
    {  
        if((itc->size()) < cmin || (itc->size()) > cmax)  
        {  
            itc = contours.erase(itc);  
        }  
        else ++ itc;  
    }  
}  
****************************************************************************
while(contour) {
  /*area = cvContourArea(contour, CV_WHOLE_SEQ);*/
  area = fabs(cvContourArea( contour, CV_WHOLE_SEQ )); //獲取當前輪廓面積
  printf("area == %lf\n", area);
  //畫輪廓
  //畫外接矩形
  CvRect r = ((CvContour*)contour)->rect;
  if (r.height * r.width > size)
  {
   cvRectangle(pimg, cvPoint(r.x, r.y), cvPoint(r.x + r.width, r.y + r.height),CV_RGB(255, 0, 0), 1, CV_AA, 0);


  }
  contour = contour->h_next;

 }

*********************************************************************************************************
// Get the contours of the connected components  
    std::vector<std::vector<cv::Point>> contours;  

    cv::findContours(gray,   
        contours, // a vector of contours   
        CV_RETR_EXTERNAL , // retrieve the external contours  
        CV_CHAIN_APPROX_NONE); // retrieve all pixels of each contours  

    // Print contours' length  
    std::cout << "Contours: " << contours.size() << std::endl;  
    std::vector<std::vector<cv::Point>>::const_iterator itContours= contours.begin();  
    for ( ; itContours!=contours.end(); ++itContours)   
    {  

        std::cout << "Size: " << itContours->size() << std::endl;  
    }  

    // draw black contours on white image  
    cv::Mat result(image.size(),CV_8U,cv::Scalar(255));  
    cv::drawContours(result,contours,  
        -1, // draw all contours  
        cv::Scalar(0), // in black  
        2); // with a thickness of 2  

************************************************************************
 double maxarea = 0;  
    double minarea = 100;  
    int m = 0;  
    for( ; contour != 0; contour = contour->h_next )    
    {    

        double tmparea = fabs(cvContourArea(contour));  
        if(tmparea < minarea)     
        {    
            cvSeqRemove(contour, 0); // 刪除面積小於設定值的輪廓  
            continue;  
        }    
        CvRect aRect = cvBoundingRect( contour, 0 );   
        if ((aRect.width/aRect.height)<1)    
        {    
            cvSeqRemove(contour, 0); //刪除寬高比例小於設定值的輪廓  
            continue;  
        }    
        if(tmparea > maxarea)    
        {    
            maxarea = tmparea;  
        }    
        m++;  
        // 創建一個色彩值  
        CvScalar color = CV_RGB( 0, 255, 255 );  

        //max_level 繪制輪廓的最大等級。如果等級為0,繪制單獨的輪廓。如果為1,繪制輪廓及在其后的相同的級別下輪廓  
        //如果值為2,所有的輪廓。如果等級為2,繪制所有同級輪廓及所有低一級輪廓,諸此種種  
        //如果值為負數,函數不繪制同級輪廓,但會升序繪制直到級別為abs(max_level)-1的子輪廓  
        cvDrawContours(dst, contour, color, color, -1, 1, 8);   //繪制外部和內部的輪廓  
    }    
    contour = _contour;  
    int count = 0;  
    for(; contour != 0; contour = contour->h_next)  
    {    
        count++;  
        double tmparea = fabs(cvContourArea(contour));  
        if (tmparea == maxarea)    
        {    
            CvScalar color = CV_RGB( 255, 0, 0);  
            cvDrawContours(dst, contour, color, color, -1, 1, 8);  
        }    
    }    
*************************************************************************************
在提取之前還可以調用一個函數:   
         contour = cvApproxPoly( contour, sizeof(CvContour), storage, CV_POLY_APPROX_DP, 3, 1 );   
         可能是擬合,有這一句找出的輪廓線更直。   contour里面包含了很多個輪廓,每個輪廓是單獨存放的.  

輸出輪廓位置!
 printf(" %d elements:\n", c->total );  
     for( int i=0; i<c->total; ++i ) {  
     CvPoint* p = CV_GET_SEQ_ELEM( CvPoint, c, i );  
        printf("    (%d,%d)\n", p->x, p->y );  
     }  
輸出輪廓面積!
for( ; contour; contour = contour->h_next)  
    {  
        area = fabs(cvContourArea(contour, CV_WHOLE_SEQ)); //獲取當前輪廓面積  
        printf("area == %lf\n", area);  
        if(area > maxArea)  
        {  
            contmax = contour;  
            maxArea = area;  
        }  
    }  
***********************************************************************************************
內輪廓填充   
// 參數:   
// 1. pBinary: 輸入二值圖像,單通道,位深IPL_DEPTH_8U。  
// 2. dAreaThre: 面積閾值,當內輪廓面積小於等於dAreaThre時,進行填充。   
void FillInternalContours(IplImage *pBinary, double dAreaThre)   
{   
    double dConArea;   
    CvSeq *pContour = NULL;   
    CvSeq *pConInner = NULL;   
    CvMemStorage *pStorage = NULL;   
    // 執行條件   
    if (pBinary)   
    {   
        // 查找所有輪廓   
        pStorage = cvCreateMemStorage(0);   
        cvFindContours(pBinary, pStorage, &pContour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);   
        // 填充所有輪廓   
        cvDrawContours(pBinary, pContour, CV_RGB(255, 255, 255), CV_RGB(255, 255, 255), 2, CV_FILLED, 8, cvPoint(0, 0));  
        // 外輪廓循環   
        for (; pContour != NULL; pContour = pContour->h_next)   
        {   
            // 內輪廓循環   
            for (pConInner = pContour->v_next; pConInner != NULL; pConInner = pConInner->h_next)   
            {   
                // 內輪廓面積   
                dConArea = fabs(cvContourArea(pConInner, CV_WHOLE_SEQ));   
                if (dConArea <= dAreaThre)   
                {   
                    cvDrawContours(pBinary, pConInner, CV_RGB(255, 255, 255), CV_RGB(255, 255, 255), 0, CV_FILLED, 8, cvPoint(0, 0));  
                }   
            }   
        }   
        cvReleaseMemStorage(&pStorage);   
        pStorage = NULL;   
    }   
}  

******************************************************************* *********************
// Get the contours of the connected components
    std::vector<std::vector<cv::Point>> contours;
    cv::findContours(image, 
        contours, // a vector of contours 
        CV_RETR_EXTERNAL, // retrieve the external contours
        CV_CHAIN_APPROX_NONE); // retrieve all pixels of each contours

    // Print contours' length
    std::cout << "Contours: " << contours.size() << std::endl;
    std::vector<std::vector<cv::Point>>::const_iterator itContours= contours.begin();
    for ( ; itContours!=contours.end(); ++itContours) 
    {

        std::cout << "Size: " << itContours->size() << std::endl;
    }

***************************************************************************************
// Eliminate too short or too long contours
    int cmin= 100;  // minimum contour length
    int cmax= 1000; // maximum contour length
    std::vector<std::vector<cv::Point>>::const_iterator itc= contours.begin();
    while (itc!=contours.end()) {

        if (itc->size() < cmin || itc->size() > cmax)
            itc= contours.erase(itc);
        else 
            ++itc;
    }

輸出所有輪廓的旋轉角度
CvBox2D     End_Rage2D;

    CvMemStorage *storage = cvCreateMemStorage(0);  //開辟內存空間


    CvSeq*      contour = NULL;     //CvSeq類型 存放檢測到的圖像輪廓邊緣所有的像素值,坐標值特征的結構體以鏈表形式

    cvFindContours( pSrcImage, storage, &contour, sizeof(CvContour),CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE);//這函數可選參數還有不少
 for(; contour; contour = contour->h_next)   //如果contour不為空,表示找到一個以上輪廓,這樣寫法只顯示一個輪廓
        //如改為for(; contour; contour = contour->h_next) 就可以同時顯示多個輪廓
    {  

        End_Rage2D = cvMinAreaRect2(contour);  
        //代入cvMinAreaRect2這個函數得到最小包圍矩形  這里已得出被測物體的角度,寬度,高度,和中點坐標點存放在CvBox2D類型的結構體中,
        //主要工作基本結束。

    std::cout <<" angle:\n"<<(float)End_Rage2D.angle << std::endl;      //被測物體旋轉角度 

    }
//函數形式畫輪廓
void DrawRec(IplImage* pImgFrame,IplImage* pImgProcessed,int MaxArea)  
{  
    //pImgFrame:初始未處理的幀,用於最后標出檢測結果的輸出;  
    //pImgProcessed:處理完的幀,用於找運動物體的輪廓  

    stor = cvCreateMemStorage(0);  //創建動態結構和序列  
    cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint) , stor);  

    // 找到所有輪廓  
    cvFindContours( pImgProcessed, stor, &cont, sizeof(CvContour),   
                    CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));  

    // 直接使用CONTOUR中的矩形來畫輪廓  
    for(;cont;cont = cont->h_next)  
    {  
              CvRect r = ((CvContour*)cont)->rect;  
              if(r.height * r.width > MaxArea) // 面積小的方形拋棄掉  
              {  
                  cvRectangle( pImgFrame, cvPoint(r.x,r.y),   
                          cvPoint(r.x + r.width, r.y + r.height),  
                          CV_RGB(255,0,0), 1, CV_AA,0);  
              }  
    }  
    cvShowImage("video", pImgFrame);  
}  

***********************************************************************************************

紹opencv 的基於面積區域過濾方法,這個對圖像處理時去除小區域雜點是很有幫助的。基於區域寬度,高度等其他方式的過濾也可以根據這個方法類推。

# 圖片中找到我們需要的目標 一般是最大連通區域
#獲取當前輪廓面積
area = abs(cv.cvContourArea( contour ))
# 獲取最大區域矩形塊
aRect = cv.cvBoundingRect( contmax, 0 )
#原始區域的不加邊框
#rcenter = cv.cvPoint2D32f(aRect.x + aRect.width/2.0, aRect.y + aRect.height/2.0)

*****************************************************************************************
//移除過長或過短的輪廓  
    int cmin = 100; //最小輪廓長度  
    int cmax = 1000;    //最大輪廓  
    vector<vector<Point>>::const_iterator itc = contours.begin();  
    while (itc!=contours.end())  
    {  
        if (itc->size() < cmin || itc->size() > cmax)  
            itc = contours.erase(itc);  
        else  
            ++itc;  
    }  

    //在白色圖像上繪制黑色輪廓  
    Mat result_erase(binaryImage.size(), CV_8U, Scalar(255));  
    drawContours(result_erase, contours,  
        -1, //繪制所有輪廓  
        Scalar(0),  //顏色為黑色  
        2); //輪廓線的繪制寬度為2  

 Rect r0 = boundingRect(Mat(contours[0]));  
    rectangle(result_erase, r0, Scalar(128), 2);  
    Rect r1 = boundingRect(Mat(contours[1]));  
    rectangle(result_erase, r1, Scalar(128), 2);  

*****************************************************************************************************************
 //對前景先進行中值濾波,再進行形態學膨脹操作,以去除偽目標和連接斷開的小目標
 69         medianBlur(mask, mask, 5);
 70         //morphologyEx(mask, mask, MORPH_DILATE, getStructuringElement(MORPH_RECT, Size(5, 5)));
 71 
 72         //測試:先開運算再閉運算
 73         morphologyEx(mask, mask, MORPH_CLOSE, getStructuringElement(MORPH_RECT, Size(5, 5)));
 74         morphologyEx(mask, mask, MORPH_OPEN, getStructuringElement(MORPH_RECT, Size(5, 5)));


 //外接矩陣
 93         Rect rct;
 94 
 95         //對輪廓進行外接矩陣之前先對輪廓按面積降序排序,目的為了去除小目標(偽目標)
 96         sort(contours.begin(), contours.end(), descSort);
 97 
 98         for (int i = 0; i < contours.size(); i++)
 99         {
100             //當第i個連通分量的外接矩陣面積小於最大面積的1/6,則認為是偽目標
101             if (contourArea(contours[i]) < contourArea(contours[0]) / 5)
102                 break;
103             //包含輪廓的最小矩陣
104             rct = boundingRect(contours[i]);
105             rectangle(result, rct, Scalar(0, 255, 0), 2);
106 
107         }

**************************************************************************************************************
 Mat element(5,5,CV_8U,Scalar(1));  
 cvMorphologyEx(green, green, NULL, element, CV_MOP_OPEN);       // 開運算,去除比結構元素小的亮點  

cvThreshold(green, green, 0.0, 255.0, CV_THRESH_BINARY | CV_THRESH_OTSU);   // OTSU法二值化  

一般用:findContours(image,contours,CV_RETR_EXTERNAL,CV_CHAIN_APPROX_NONE);  
int cmin=100;  

int cmax=1000;  

vector<std::vector<cv::Point> >::iterator itc = contours.begin();  

while(itc!=contours.end())  

{  

    if(itc->size()<cmin||itc->size()>cmax)  

        itc =contours.erase(itc);  

    else  

        itc++;  

}   
Mat image2=imread("E:\\group.jpg");   
drawContours(image2,contours,-1,Scalar(255,255,255),2);  
imshow("image",image2);  

  


免責聲明!

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



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