OpenCV 輪廓提取findContours和drawContours


最近用OPENCV的輪廓提取函數,總結一下。

1 void findContours//提取輪廓,用於提取圖像的輪廓
2 ( 3 InputOutputArray image,//輸入圖像,必須是8位單通道圖像,並且應該轉化成二值的
4 OutputArrayOfArrays contours,//檢測到的輪廓,每個輪廓被表示成一個point向量
5 OutputArray hierarchy,//可選的輸出向量,包含圖像的拓撲信息。其中元素的個數和檢測到的輪廓的數量相等
6 int mode,//說明需要的輪廓類型和希望的返回值方式
7 int method,//輪廓近似方法
8 Point offset = Point() 9 )
 1 void drawContours//繪制輪廓,用於繪制找到的圖像輪廓
 2 (  3  InputOutputArray image,//要繪制輪廓的圖像
 4  InputArrayOfArrays contours,//所有輸入的輪廓,每個輪廓被保存成一個point向量
 5  int contourIdx,//指定要繪制輪廓的編號,如果是負數,則繪制所有的輪廓
 6  const Scalar& color,//繪制輪廓所用的顏色
 7  int thickness = 1, //繪制輪廓的線的粗細,如果是負數,則輪廓內部被填充
 8  int lineType = 8, /繪制輪廓的線的連通性  9  InputArray hierarchy = noArray(),//關於層級的可選參數,只有繪制部分輪廓時才會用到
10  int maxLevel = INT_MAX,//繪制輪廓的最高級別,這個參數只有hierarchy有效的時候才有效 11                                           //maxLevel=0,繪制與輸入輪廓屬於同一等級的所有輪廓即輸入輪廓和與其相鄰的輪廓 12                                           //maxLevel=1, 繪制與輸入輪廓同一等級的所有輪廓與其子節點。 13                                           //maxLevel=2,繪制與輸入輪廓同一等級的所有輪廓與其子節點以及子節點的子節點
14  Point offset = Point() 15 )

注意:findContours()運行的時候,這個圖像會被直接塗改,因此如果是將來還有用的圖像,應該復制之后再傳給findContours()。

接下來總結一下我在書上看到的解釋和自己實驗的結果。

一《學習opencv》中給的例子。

圖1 輸入的測試圖(上圖)和得到的輪廓圖(下圖)

傳遞給findContours()測試圖(上圖),得到輪廓圖(下圖)。得到的輪廓只有兩種,外部輪廓(橙色虛線)或者孔(藍色點線)。

1.參數mode的意義

mode的值決定把找到的輪廓如何掛到輪廓樹節點變量(h_prev, h_next, v_prev, v_next)上。圖2展示了四種可能的mode值所得到的結果的拓撲結構。

圖2 輪廓連接方法

每種情況下,結構都可以看成是被橫向連接(h_prev, h_next)聯系和被縱向連接(v_prev, v_next)不同層次。

CV_RETR_EXTERNAL 只檢測出最外輪廓即c0。圖2中第一個輪廓指向最外的序列,除此之外沒有別的連接。

CV_RETR_LIST 檢測出所有的輪廓並將他們保存到表(list)中,圖2中描繪了這個表,被找到的9條輪廓相互之間由h_prev和h_next連接。這里並沒有表達出縱向的連接關系,沒有使用v_prev和v_next.

CV_RETR_COMP 檢測出所有的輪廓並將他們組織成雙層的結構,第一層是外部輪廓邊界,第二層邊界是孔的邊界。從圖2可以看到5個輪廓的邊界,其中3個包含孔。最外層邊界c0有兩個孔,c0之間的所有孔相互間由h_prev和h_next指針連接。

CV_RETR_TREE 檢測出所有輪廓並且重新建立網狀的輪廓結構。圖2中,根節點是最外層的邊界c0,c0之下是孔h00,在同一層中與另一個孔h01相連接。同理,每個孔都有子節點(相對於c000和c010),這些子節點和父節點被垂直連接起來。這個步驟一直持續到圖像最內層的輪廓,這些輪廓會成為樹葉節點。

2. method的五個值

CV_CHAIN_CODE 用freeman鏈碼輸出輪廓,其他方法輸出多邊形(頂點的序列)。

CV_CHAIN_APPROX_NONE將鏈碼編碼中的所有點轉換為點。

CV_CHAIN_APPROX_SIMPLE壓縮水平,垂直或斜的部分,只保存最后一個點。

CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_QPPROX_TC89_KCOS使用Teh-Chin鏈逼近算法中的一個。

CV_LINK_RUNS與上述的算法完全不同,連接所有的水平層次的輪廓。

二 實驗部分

我用跟上面的例子結構一樣的圖作為測試圖片(這是我照着上面的圖自己畫的)。

圖3 原圖

測試代碼如下:

 1 #include "opencv/highgui.h"
 2 #include "opencv/cv.h"
 3 using namespace cv;  4 void main()  5 {undefined  6  Mat image = imread("test.png");  7  Mat gray;  8  cvtColor(image, gray, CV_BGR2GRAY);  9  GaussianBlur(gray, gray, Size(3, 3), 3, 3); 10  Mat img; 11  threshold(gray, img, 0, 255, THRESH_OTSU); 12  Mat img1; 13  img.copyTo(img1); 14  vector<vector<Point>> contours; 15  vector<Vec4i> hierarchy; 16  findContours(img, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE); 17  Mat resultImage = Mat ::zeros(img.size(),CV_8U); 18  drawContours(resultImage, contours, -1, Scalar(255, 0, 255)); 19  return; 20 }

1. mode的四種方法

    下圖是分別用mode的四種方法得到的輪廓的結果

圖4 四種mode方式分別得到的輪廓結果
從圖中可以看出,圖4(a)得到的只有一個最外的輪廓,hierarchy的值為{-1,-1,-1,-1},表示除此之外沒有別的連接。其他三種方式都可以得到所有的輪廓,而hierarchy的值是不同的,反映了不同的輪廓連接方法即結果的拓撲機構。
2.method的五種方法
CV_CHAIN_CODE是用Freeman鏈碼輸出輪廓,在Freeman鏈碼中,多邊形被表示成一系列位移,每一個位移有8個方向,使用整數0~7表示。關於Freeman鏈碼的具體內容《學習opencv》這本書里有詳細的介紹。通過試驗發現用這種方式表示的輪廓不可以通過drawContours()繪制出檢測到的輪廓。
CV_CHAIN_APPROX_NONE 是將鏈碼編碼中的所有點轉換為點,也就是參數contours中的每個輪廓是用構成該輪廓的所有像素點表示的。
CV_CHAIN_APPROX_NONE是輸出多邊形頂點的序列,也就是說參數contours中的每個輪廓是用該輪廓的所有頂點表示的。
圖5分別畫出了這兩種方式下的contours中的點,用紅色的點表示。

 圖5 輪廓的表達方式
    其中(a)中由於像素之間沒有間隔已經練成線了。
    圖6是CV_CHAIN_APPROX_TC89_L1和CV_LINK_RUNS兩種方式下contours的結果。

圖6 輪廓的表達方式
從圖中可以看出,CV_CHAIN_APPROX_TC89_L1的結果也是保存的輪廓的頂點,但是仔細看可以看出輪廓線是有斷的感覺不是連貫的,這可能是輪廓逼近過程中的誤差,具體情況就不清楚了,也有可能這種方法對具有某種特征的圖逼近效果比較好。
書上說CV_LINK_RUNS是和上述算法完全不同的算法,連接所有水平層次的輪廓,但結果畫出來的是一條條垂直的線,也不清楚具體怎么回事,不過應該也肯定是有它存在的意義的。另外,在mode=CV_RETR_EXTERNAL的前提下,選用這種方式也是可以畫出所有輪廓的,而其他的方式只能畫出最外層的輪廓。還有書上說此方法只可與 CV_RETR_LIST搭配使用。我試了其他的方法,也是可以的。
所以method這個參數決定了輪廓的表達方式,這要根據自己提取輪廓后的應用選擇合適的輪廓描述方法來決定用哪一種。
3.drawContours()函數中的參數thinkness
thinkness=CV_FILLED可以填充輪廓,opencv官網對這個參數的解釋原文是“If it is negative (for example, thickness=CV_FILLED ), the contour interiors are drawn”,圖7給出了這個thinkness=CV_FILLED時的繪制結果,可以看出我們得到了跟原圖一致的結果。

 圖7 輪廓繪制結果


免責聲明!

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



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