OpenCV的轮廓查找和填充
OpenCV的輪廓查找有C版本和C++版本,當輪廓比較復雜的時候,例如嵌入多層輪廓,如果方法不當那么很容易會漏處理一些輪廓。本文介紹了復雜輪廓場景下的幾種主要的查找輪廓和顏色填充方法。
1:cvFindContours函數介紹
int?cvFindContours( CvArr* image, CvMemStorage* storage, CvSeq** first_contour, int header_size=sizeof(CvContour), int
? ? ? ? mode=CV_RETR_LIST, ?int method=CV_CHAIN_APPROX_SIMPLE, CvPoint offset=cvPoint(0,0) );
image:?8比特單通道的源二值圖像。非零像素作為1處理,0像素保存不變。從一個灰度圖像得到二值圖像的函數有:cvThreshold,cvAdaptiveThreshold和cvCanny。
storage:?返回輪廓的容器。
first_contour:?輸出參數,用于存儲指向第一個外接輪廓。
header_size:?header序列的尺寸.如果選擇method = CV_CHAIN_CODE, 則header_size >= sizeof(CvChain);其他,則 header_size >= sizeof(CvContour)。
mode:
CV_RETR_EXTERNAL:只檢索最外面的輪廓;
CV_RETR_LIST:檢索所有的輪廓,并將其放入list中;
CV_RETR_CCOMP:檢索所有的輪廓,并將他們組織為兩層:頂層是各部分的外部邊界,第二層是空洞的邊界;
CV_RETR_TREE:檢索所有的輪廓,并重構嵌套輪廓的整個層次。
method:?邊緣近似方法(除了CV_RETR_RUNS使用內置的近似,其他模式均使用此設定的近似算法)。可取值如下:
CV_CHAIN_CODE:以Freeman鏈碼的方式輸出輪廓,所有其他方法輸出多邊形(頂點的序列)。
CV_CHAIN_APPROX_NONE:將所有的連碼點,轉換成點。
CV_CHAIN_APPROX_SIMPLE:壓縮水平的、垂直的和斜的部分,也就是,函數只保留他們的終點部分。
CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS:使用the flavors of Teh-Chin chain近似算法的一種。
CV_LINK_RUNS:通過連接水平段的1,使用完全不同的邊緣提取算法。使用CV_RETR_LIST檢索模式能使用此方法。
offset:偏移量,用于移動所有輪廓點。當輪廓是從圖像的ROI提取的,并且需要在整個圖像中分析時,這個參數將很有用。
討論部分cvDrawContours中的案例顯示了任何使用輪廓檢測連通區域。輪廓可以用于形狀分析和目標識別——可以參考文件夾OpenCV sample中的squares.c
2:找輪廓及顏色填充
方法1:樹形遍歷
? ? ? ? 由contours的h_next和 v_next指針組成一個龐大的樹形結構,如下面的圖示,其中藍色表示v_next,綠色表示h_next。如果不了解contours結構的話,很容易僅僅遍歷v_next或者h_next,這樣實際上會漏掉一些需要處理的輪廓。要全部遍歷所有輪廓需要編寫不少代碼,這個還沒搜索到直接用這種方式遍歷輪廓的代碼。需要注意的是,很多博文介紹的利用h_next或者v_next的方法都是會缺失不少輪廓的,輪廓的復雜會導致整個樹也變的很復雜,所以建議還是不要用這種方式來遍歷輪廓。我一開始用的就是這種方法,應該說是琢磨了好長時間,才知道為什么這種方法的輪廓數總是偏少。
?
?
方法2:cvFindNextContour方法
本方法是利用了輪廓掃描器CvContourScanner,使用cvStartFindContours、cvFindNextContour、cvEndFindContours函數獲取從外到內的輪廓,獲取一層輪廓進行一次顏色的填充。具體后面使用的原始輪廓圖如下所示:
#include “opencv/cv.h”
#include “opencv/highgui.h”
int main(int argc, char **argv)
{
IplImage * pSrcImg= cvLoadImage(“E:\\11.jpg”);
IplImage * pGrayImg =cvCreateImage(cvSize(pSrcImg->width,pSrcImg->height),IPL_DEPTH_8U,1);
IplImage * pThresholdImg =cvCreateImage(cvSize(pSrcImg->width,pSrcImg->height),IPL_DEPTH_8U,1);
IplImage * pDstImg =cvCreateImage(cvSize(pSrcImg->width,pSrcImg->height),IPL_DEPTH_8U,3);
cvSetZero(pDstImg);
srand((int)time(0)); ??
CvSeq * contours = 0;
CvMemStorage * storage=cvCreateMemStorage(0);
CvScalar color=cvScalar( rand()&255, rand()&255, rand()&255 );
cvCvtColor(pSrcImg,pGrayImg,CV_BGR2GRAY);
cvNot(pGrayImg,pGrayImg);
cvThreshold(pGrayImg,pThresholdImg,100,255,CV_THRESH_BINARY);
cvSet(pDstImg,color);
CvContourScanner scanner = cvStartFindContours(pThresholdImg, storage,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sizeof(CvContour),CV_RETR_TREE ,CV_CHAIN_APPROX_NONE);
while (contours=cvFindNextContour(scanner))
{
color=cvScalar( rand()&255, rand()&255, rand()&255 );
cvDrawContours(pDstImg, contours, color,color, 0,CV_FILLED);
};
contours= cvEndFindContours(&scanner);
cvSaveImage(“dst.jpg”,pDstImg);
cvReleaseImage(&pDstImg);
cvReleaseImage(&pGrayImg);
cvReleaseImage(&pThresholdImg);
cvReleaseMemStorage(&storage);
return 0;
}
?方法3:cvNextTreeNode方法
本方法是利用cvFindContours,先找到所有輪廓,再使用樹節點的迭代器CvTreeNodeIterator的函數cvNextTreeNode,獲取從外到內的輪廓,并進行顏色的填充。
#include “opencv/cv.h”
#include “opencv/highgui.h”
int main(int argc, char **argv)
{
IplImage * pSrcImg= cvLoadImage(“E:\\11.jpg”);
IplImage * pGrayImg =cvCreateImage(cvSize(pSrcImg->width,pSrcImg->height),IPL_DEPTH_8U,1);
IplImage * pThresholdImg =cvCreateImage(cvSize(pSrcImg->width,pSrcImg->height),IPL_DEPTH_8U,1);
IplImage * pDstImg =cvCreateImage(cvSize(pSrcImg->width,pSrcImg->height),IPL_DEPTH_8U,3);
cvSetZero(pDstImg);
srand((int)time(0));
CvSeq * contours = 0;
CvMemStorage * storage=cvCreateMemStorage(0);
CvScalar color=cvScalar( rand()&255, rand()&255, rand()&255 );
cvCvtColor(pSrcImg,pGrayImg,CV_BGR2GRAY);
cvNot(pGrayImg,pGrayImg);
cvThreshold(pGrayImg,pThresholdImg,100,255,CV_THRESH_BINARY);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?cvFindContours(pThresholdImg,storage,&contours,sizeof(CvContour),
CV_RETR_TREE ,CV_CHAIN_APPROX_NONE);
cvSet(pDstImg,color);
CvTreeNodeIterator iterator;?
cvInitTreeNodeIterator(&iterator,contours,3);?
while( 0 != (contours = (CvSeq*)cvNextTreeNode(&iterator)) )?
{
color=cvScalar( rand()&255, rand()&255, rand()&255 );
cvDrawContours(pDstImg, contours, color,color, 0,CV_FILLED);
}
cvSaveImage(“dst.jpg”,pDstImg);
cvReleaseImage(&pDstImg);
cvReleaseImage(&pGrayImg);
cvReleaseImage(&pThresholdImg);
cvReleaseMemStorage(&storage);?
return 0;
}
方法4:?C++的iterator方法
本方法是利用OpenCV的C++函數findContours,獲取從外到內的輪廓,并利用const_iterator 來逐個找到所有的輪廓。從編碼的效果來看,最開始的輪廓是最外延的輪廓,然后逐漸找內部的輪廓,所以進行顏色填充的時候,不會出現外面的大輪廓覆蓋掉內部的小輪廓的問題。
#include “opencv/cv.h”
#include “opencv/highgui.h”
using namespace cv;
int main( int argc, char** argv )
{
vector<Vec4i> hierarchy;
vector<vector<Point> > contours;
vector<vector<Point>>::const_iterator itContours;
srand((int)time(0));
Mat src = imread(“E:\\11.jpg”,0);
Mat dst = Mat::zeros(src.rows, src.cols, CV_8UC3);
CvScalar color=cvScalar( rand()&255, rand()&255, rand()&255 );
src = src > 100;
findContours( src, contours, hierarchy, CV_RETR_TREE,CV_CHAIN_APPROX_NONE );
itContours=contours.begin();
int i=0;
for(;itContours!=contours.end();++itContours)
{
color=cvScalar( rand()&255, rand()&255, rand()&255 );
drawContours( dst,contours ,i, color, CV_FILLED );
i++;
}
imwrite(“dst.jpg”,dst);
}
必須采用for(;itContours!=contours.end();++itContours)的循環方式。
如果采用for(?;?idx?>=?0;?idx?=?hierarchy[idx][0]?)的循環方式,會導致大的輪廓顏色覆蓋掉其內部小的輪廓顏色。
?結果
如下為兩次運行的效果圖,其中C語言版本不會將整個圖片的外延作為一個輪廓,所以這部分顏色要額外先填充。
從編碼效率來看,C++版本的編碼效率要高于C版本,因為不需要考慮很多的內存的釋放等問題。
?
?
?
?
?
?
?
?
參考資料:
http://baike.baidu.com/view/4111188.htm
http://blog.csdn.net/augusdi/article/details/9000276
http://blog.csdn.net/augusdi/article/details/9000893
http://blog.csdn.net/timidsmile/article/details/8519751
聲明:
如果轉載了本文,也請注明轉載出處:http://www.cvrobot.net/opencv-find-and-draw-contours/
總結
以上是生活随笔為你收集整理的OpenCV的轮廓查找和填充的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 模式识别Pattern Recognit
- 下一篇: 实时SLAM的未来及与深度学习的比较Th