OpenCV利用findContours找到映像中的輪廓,根據這些輪廓的特徵進行篩選有利於進一步逼近最終的興趣地區,減少其他演算法的時間,提高代碼的運行效率,而對輪廓的繪圖則可以直觀的看到篩選結果。
其實繪圖的函數在下面的部落格中就已經總結過,不過這次換了一個思路重新溫習下這些函數,並把他們應用到輪廓的繪圖中。
http://blog.csdn.net/chaipp0607/article/details/56277479
輪廓繪製
drawContours(Image, contours, k, Scalar(255,0,0), CV_FILLED);
drawContours()函數用於繪製輪廓,Image為靶心圖表像,Contours為找到的輪廓的合集,k為第幾個輪廓(如果為負值則繪製全部輪廓),Scalar(255,0,0)決定了繪製的顏色,需要注意的地方是,如果Scalar為三個顏色的話,那麼靶心圖表像需要是三通道才可以,最後一個參數為線條寬度,如果為CV_FILLED的話則填充輪廓。
以上並不是drawContours()函數的所有參數,但是剩下的參數一般都有預設值或者不常用到,所以在這裡不介紹。
應用:
SrcImage = imread("22.png",1);imshow("原圖",SrcImage);cvtColor(SrcImage,grayImage,CV_BGR2GRAY);threshold(grayImage,thresholdImage, 0, 255, CV_THRESH_OTSU+CV_THRESH_BINARY);vector<vector<Point> > contours;vector<Vec4i>hierarchy;findContours(thresholdImage,contours,hierarchy,CV_RETR_TREE,CV_CHAIN_APPROX_NONE); drawContours(SrcImage, contours,-1,Scalar(255,0,0), CV_FILLED);imshow("填充",SrcImage);
以上代碼用於填充所有找到的輪廓,結果如下:
輪廓尺寸
尺寸這個詞並不準確,其實是輪廓的size,也就是把輪廓圍起來的點的個數,我們都知道OpenCV中的一個輪廓其實是點的合集,所以如果一個輪廓在映像上的相對大,那麼他的點的個數也就相應的會變多,這可以作為篩選的簡單標準之一。
vector<vector<Point> > contours; vector<Vec4i>hierarchy; findContours(thresholdImage,contours,hierarchy,CV_RETR_TREE,CV_CHAIN_APPROX_NONE); for(int k = 0; k < (int)contours.size(); k++) { if (contours.at(k).size()>400) drawContours(SrcImage, contours,k,Scalar(255,0,0), CV_FILLED); } imshow("填充",SrcImage);
其中(int)contours.size()為第幾個輪廓,而contours.at(k).size()則為輪廓的點的個數,為了讓點的個數與輪廓的大小成正比,在findContours()時將參數設定為CV_CHAIN_APPROX_NONE,具體可見:
http://blog.csdn.net/chaipp0607/article/details/61431922
以上代碼用於填充尺寸大於400的輪廓,結果如下:
輪廓面積
根據矩的定義,函數的零階原點矩為品質,對於輪廓而言即為輪廓的面積,所以零階原點矩的數值可以更好的表徵出輪廓的面積。
具體可見:http://blog.csdn.net/chaipp0607/article/details/70256892
vector<vector<Point> > contours; vector<Vec4i>hierarchy; findContours(thresholdImage,contours,hierarchy,CV_RETR_TREE,CV_CHAIN_APPROX_NONE); vector<Moments> mu(contours.size() ); for(int k = 0; k < (int)contours.size(); k++) { mu[k] = moments( contours[k], false ); if (mu[k].m00>1000) drawContours(SrcImage, contours,k,Scalar(255,0,0), CV_FILLED); } imshow("填充",SrcImage);
以上代碼用於填充面積大於1000的輪廓,結果如下:
輪廓質心
同樣,我們可以根據輪廓的零階原點矩與一階原點矩求得輪廓的質心。
vector<vector<Point> > contours; vector<Vec4i>hierarchy; findContours(thresholdImage,contours,hierarchy,CV_RETR_TREE,CV_CHAIN_APPROX_NONE); vector<Moments> mu(contours.size() ); vector<Point2f> mc( contours.size() ); for(int k = 0; k < (int)contours.size(); k++) { mu[k] = moments( contours[k], false ); mc[k] = Point2d( mu[k].m10/mu[k].m00 , mu[k].m01/mu[k].m00 ); if (mc[k].x>SrcImage.cols/2) drawContours(SrcImage, contours,k,Scalar(255,0,0), CV_FILLED); } imshow("填充",SrcImage);
以上代碼用於填充質心在映像中線右側的輪廓,結果如下:
輪廓的外接橢圓
輪廓的外接橢圓能夠提供長短軸與角度的資訊,而長短軸的比值使輪廓具有尺度不變的特性。
OpenCV利用fitEllipse()函數建立輪廓(二維點集)的外接橢圓擬合,該函數的定義:
CV_EXPORTS_W RotatedRect fitEllipse( InputArray points );
可見,它的參數只有二維的點集做輸入,而函數的類型為RotatedRect ,RotatedRect 為OpenCV中的一個常用資料類型——可旋轉的2D矩形。所以我們可以利用這個類型定義的對象用來接fitEllipse()函數的返回結果。而橢圓的長短軸其實本質上是RotatedRect 類中的寬度和高度成員。
vector<vector<Point> > contours; vector<Vec4i>hierarchy; findContours(thresholdImage,contours,hierarchy,CV_RETR_TREE,CV_CHAIN_APPROX_NONE); for(int k = 0; k < (int)contours.size(); k++) { if (contours.at(k).size()>6) { RotatedRect rRect = fitEllipse(contours.at(k)); double majorAxis = rRect.size.height > rRect.size.width ? rRect.size.height : rRect.size.width; double minorAxis = rRect.size.height > rRect.size.width ? rRect.size.width : rRect.size.height; double angle = rRect.angle; if (majorAxis>70) { drawContours(SrcImage, contours,k,Scalar(255,0,0), CV_FILLED); ellipse(SrcImage,rRect,Scalar(0,0,255)); } } } imshow("填充",SrcImage);
以上代碼用於填充長軸在70以上的輪廓並利用ellipse()函數畫出這些輪廓的外接橢圓,結果如下:
除此之外,我們還可以畫出外接橢圓的外接矩形,由於RotatedRect類提供了一個叫做boundingRect()的成員函數(該函式宣告在了RotatedRect類中,其實他是個Rect類型的函數,也可以單獨使用,下面會具體提到這個函數),用於返回包含旋轉矩形的最小直正矩形,所以我們簡單修改代碼後,即可同時畫出橢圓的外接矩形:
rectangle(SrcImage,rRect.boundingRect(),Scalar(0,255,0));
輪廓的外接矩形
外接矩形和外接橢圓的用法差不多:
OpenCV利用boundingRect()函數建立輪廓(二維點集)的外接矩形,該函數的定義:
CV_EXPORTS_W Rect boundingRect( InputArray points );
可見,它的參數只有二維的點集做輸入,而函數的類型為Rect ,Rect 為OpenCV中的一個另一個常用資料類型——2D直正矩形類。所以我們可以利用這個類型定義的對象用來接boundingRect()函數的返回結果。而矩形的位置和寬高資訊其實本質上是Rect 類中的位置,寬度和高度成員。
vector<vector<Point> > contours; vector<Vec4i>hierarchy; findContours(thresholdImage,contours,hierarchy,CV_RETR_TREE,CV_CHAIN_APPROX_NONE); for(int k = 0; k < (int)contours.size(); k++) { if (contours.at(k).size()>6) { Rect position = boundingRect(contours.at(k)); if (position.x<SrcImage.cols/2) { drawContours(SrcImage, contours,k,Scalar(255,0,0), CV_FILLED); rectangle(SrcImage,position,Scalar(0,0,255)); } } } imshow("填充",SrcImage);
以上代碼用於填充外接矩形的橫座標(左上方點)在映像中線左側的輪廓並利用rectangle()函數畫出這些輪廓的外接橢圓,結果如下: