函數原型
findContours(InputOutputArray image, OutputArrayOfArrays contours,
OutputArray hierarchy, int mode, int method, Point offset = Point());
參數1:單通道圖像矩陣,可以是灰度圖,但更常用的是二值圖像,一般是經過Canny、拉普拉斯等邊緣檢測算子處理過的二值圖像;
參數2:contours定義為“vector<vector<Point>> contours”,是一個雙重向量(向量內每個元素保存了一組由連續的Point構成的點的集合的向量),每一組點集就是一個輪廓,有多少輪廓,contours就有多少元素;
參數3:hierarchy定義為“vector<Vec4i> hierarchy”,Vec4i的定義:typedef Vec<int, 4> Vec4i;(向量內每個元素都包含了4個int型變量),所以從定義上看,hierarchy是一個向量,向量內每個元素都是一個包含4個int型的數組。向量hierarchy內的元素和輪廓向量contours內的元素是一一對應的,向量的容量相同。hierarchy內每個元素的4個int型變量是hierarchy[i][0] ~ hierarchy[i][3],分別表示當前輪廓 i 的后一個輪廓、前一個輪廓、父輪廓和內嵌輪廓的編號索引。如果當前輪廓沒有對應的后一個輪廓、前一個輪廓、父輪廓和內嵌輪廓,則相應的hierarchy[i][*]被置為-1。
參數4:定義輪廓的檢索模式,取值如下:
CV_RETR_EXTERNAL:只檢測最外圍輪廓,包含在外圍輪廓內的內圍輪廓被忽略;
CV_RETR_LIST:檢測所有的輪廓,包括內圍、外圍輪廓,但是檢測到的輪廓不建立等級關系,彼此之間獨立,沒有等級關系,這就意味着這個檢索模式下不存在父輪廓或內嵌輪廓,所以hierarchy向量內所有元素的第3、第4個分量都會被置為-1,具體下文會講到;
CV_RETR_CCOMP: 檢測所有的輪廓,但所有輪廓只建立兩個等級關系,外圍為頂層,若外圍內的內圍輪廓還包含了其他的輪廓信息,則內圍內的所有輪廓均歸屬於頂層;
CV_RETR_TREE: 檢測所有輪廓,所有輪廓建立一個等級樹結構。外層輪廓包含內層輪廓,內層輪廓還可以繼續包含內嵌輪廓。
參數5:定義輪廓的近似方法,取值如下:
CV_CHAIN_APPROX_NONE:保存物體邊界上所有連續的輪廓點到contours向量內;
CV_CHAIN_APPROX_SIMPLE:僅保存輪廓的拐點信息,把所有輪廓拐點處的點保存入contours向量內,拐點與拐點之間直線段上的信息點不予保留;
CV_CHAIN_APPROX_TC89_L1:使用teh-Chinl chain 近似算法;
CV_CHAIN_APPROX_TC89_KCOS:使用teh-Chinl chain 近似算法。
參數6:Point偏移量,所有的輪廓信息相對於原始圖像對應點的偏移量,相當於在每一個檢測出的輪廓點上加上該偏移量,並且Point還可以是負值!
下邊用效果圖對比一下findContours函數中參數4和參數5取不同值時,向量contours和hierarchy的內容如何變化,有何異同。
一、mode取值“CV_RETR_EXTRERNAL”,method取值“CV_CHAIN_APPROX_NONE”,即只檢測最外層輪廓,並且保存輪廓上所有點:
只有最外層輪廓,內層輪廓被忽略
Contours向量內所有點集:保存了所有輪廓上的所有點,圖像表現跟輪廓一致
hierarchy向量
本次參數配置下,hierarchy向量內有3個元素,分別對應於3個輪廓。以第2個輪廓(對應向量內第1個元素)為例,內容為[2,0,-1,-1], “2”表示當前輪廓的后一個輪廓的編號為2,“0”表示當前輪廓的前一個輪廓編號為0,其后2個“-1”表示為空,因為只有最外層輪廓這一個等級,所以不存在父輪廓和內嵌輪廓。
二、mode取值“CV_RETR_LIST”,method取值“CV_CHAIN_APPROX_SIMPLE”,即檢測所有輪廓,但各輪廓彼此獨立,不建立等級關系,並且只保存輪廓上的拐點信息:
內外層輪廓都被檢測到
Contours向量內所有點集:拐點信息得到保留,但拐點與拐點之間的直線段的部分省略
hierarchy向量
本次參數配置下,檢測出了較多輪廓。由於本次配置mode取值“RETR_LIST”,各輪廓間各自獨立,不建立等級關系,所以第3、第4個整形參數值為-1。
三、mode取值“CV_RETR_TREE”,method取值“CV_CHAIN_APPROX_NONE”,即檢測所有輪廓,各輪廓彼之間建立內外層的等級關系,並且只保存輪廓上所有點:
內外層輪廓都被檢測到
Contours向量內所有點集:保存所有輪廓點
hierarchy向量
本次參數配置要求檢測所有輪廓,並且每個輪廓都建立等級關系,所以多個輪廓具有不為-1的第3、第4個整形參數,分別指向當前輪廓的父輪廓、內嵌輪廓索引編號。
四、Point() 偏移量設置
設置偏移量Point(45,15)
輪廓圖像整體向右下角有一個偏轉,偏轉量就是設置的(45,15)。這個偏移量的設置不能過大或過小(負方向上的過小),若圖像上任一點加上該偏移量后超出圖像邊界,程序會內存溢出報錯。
另外,值得關注一下的是繪制輪廓的函數drawContours()中最后一個參數是一個Point類型的offset,這個offset跟findContours()函數中的Point()含義一致,設置之后所繪制的輪廓是原始輪廓上所有像素點加上該偏移量后的效果。當所分析圖像是另外一個圖像的ROI的時候,這個偏移量就可以大顯身手了。通過加減這個偏移量,就可以把ROI圖像的檢測結果投影到原始圖像對應位置上。
示例程序如下:
#include <iostream> #include <opencv2\core\core.hpp> #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> using namespace std; using namespace cv; int main(int argc, char* argv[]) { Mat imageSource = imread("findContours.jpg", 0); imshow("原始圖像", imageSource); Mat image; GaussianBlur(imageSource, image, Size(3, 3), 0); Canny(imageSource, image, 100, 250); vector<vector<Point>> contours; vector<Vec4i> hierarchy; findContours(image, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE, Point()); Mat imageContours = Mat::zeros(image.size(), CV_8UC1); Mat Contours = Mat::zeros(image.size(), CV_8UC1); //繪制 for (int i = 0; i < contours.size(); i++) { //contours[i]代表的是第i個輪廓,contours[i].size()代表的是第i個輪廓上所有的像素點數 for (int j = 0; j < contours[i].size(); j++) { //繪制出contours向量所有的像素點 Point P = Point(contours[i][j].x, contours[i][j].y); Contours.at<uchar>(P) = 255; } //輸出hierarchy向量內容 char ch[256]; sprintf(ch, "%d", i); string str = ch; cout << "向量hierarchy的第" << str << "個元素內容為:" << hierarchy[i] << endl << endl; //繪制輪廓 drawContours(imageContours, contours, i, Scalar(255), 1, 8, hierarchy); } imshow("Contours Image CV_TRET_TREE", imageContours); //輪廓 imshow("Point of Contours CV_CHAIN_APPROX_NONE", Contours); //向量contours內保存的所有輪廓點集 waitKey(0); return 0; }
參考文章:https://blog.csdn.net/dcrmg/article/details/51987348