ndfweb.cn

python-opencv利用cv2.findContours()函數來查找檢測物體的輪廓


2021-08-17 23:00:39 (4777)



輪廓檢測


輪廓檢測也是圖像處理中經常用到的。OpenCV-Python接口中使用cv2.findContours()函數來查找檢測物體的輪廓。


實現


使用方式如下:


import cv2  

 

img = cv2.imread("./test.jpg")  

 

gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  

ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)  

 

contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)  

cv2.drawContours(img,contours,-1,(0,0,255),3)  

 

cv2.imshow("img", img)  

cv2.waitKey(0)  

  


需要注意的是cv2.findContours()函數接受的參數為二值圖,即黑白的(不是灰度圖),所以讀取的圖像要先轉成灰度的,再轉成二值圖,參見4、5兩行。第六行是檢測輪廓,第七行是繪製輪廓。


結果


原圖如下:




檢測結果如下:




注意,findcontours函數會“原地”修改輸入的圖像。這一點可通過下麵的語句驗證:


cv2.imshow("binary", binary)  

contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)  

cv2.imshow("binary2", binary)  

執行這些語句後會發現原圖被修改了。


cv2.findContours()函數


函數的原型為


cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])  

opencv2返回兩個值:contours:hierarchy。注:opencv3會返回三個值,分別是img, countours, hierarchy


參數


第一個參數是尋找輪廓的圖像;

第二個參數表示輪廓的檢索模式,有四種(本文介紹的都是新的cv2接口):

    cv2.RETR_EXTERNAL     表示隻檢測外輪廓

    cv2.RETR_LIST                檢測的輪廓不建立等級關係

    cv2.RETR_CCOMP          建立兩個等級的輪廓,上麵的一層為外邊界,裏麵的一層為內孔的邊界信息。如果內孔內還有一個連通物體,這個物體的邊界也在頂層。

    cv2.RETR_TREE            建立一個等級樹結構的輪廓。


第三個參數method為輪廓的近似辦法

    cv2.CHAIN_APPROX_NONE 存儲所有的輪廓點,相鄰的兩個點的像素位置差不超過1,即max(abs(x1-x2),abs(y2-y1))==1

    cv2.CHAIN_APPROX_SIMPLE 壓縮水平方向,垂直方向,對角線方向的元素,隻保留該方向的終點坐標,例如一個矩形輪廓隻需4個點來保存輪廓信息

    cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS 使用teh-Chinl chain 近似算法


返回值


cv2.findContours()函數返回兩個值,一個是輪廓本身,還有一個是每條輪廓對應的屬性。


contour返回值


cv2.findContours()函數首先返回一個list,list中每個元素都是圖像中的一個輪廓,用numpy中的ndarray表示。這個概念非常重要。在下麵drawContours中會看見。通過


print (type(contours))  

print (type(contours[0]))  

print (len(contours))  

 


可以驗證上述信息。會看到本例中有兩條輪廓,一個是五角星的,一個是矩形的。每個輪廓是一個ndarray,每個ndarray是輪廓上的點的集合。


由於我們知道返回的輪廓有兩個,因此可通過


cv2.drawContours(img,contours,0,(0,0,255),3)  


cv2.drawContours(img,contours,1,(0,255,0),3)  

分別繪製兩個輪廓,關於該參數可參見下麵一節的內容。同時通過


print (len(contours[0]))  

print (len(contours[1]))  

 


輸出兩個輪廓中存儲的點的個數,可以看到,第一個輪廓中隻有4個元素,這是因為輪廓中並不是存儲輪廓上所有的點,而是隻存儲可以用直線描述輪廓的點的個數,比如一個“正立”的矩形,隻需4個頂點就能描述輪廓了。


hierarchy返回值


此外,該函數還可返回一個可選的hiararchy結果,這是一個ndarray,其中的元素個數和輪廓個數相同,每個輪廓contours[i]對應4個hierarchy元素hierarchy[i][0] ~hierarchy[i][3],分別表示後一個輪廓、前一個輪廓、父輪廓、內嵌輪廓的索引編號,如果沒有對應項,則該值為負數。


通過


print (type(hierarchy))  

print (hierarchy.ndim)  

print (hierarchy[0].ndim)  

print (hierarchy.shape)  

 


可以看出,hierarchy本身包含兩個ndarray,每個ndarray對應一個輪廓,每個輪廓有四個屬性。


輪廓的繪製


OpenCV中通過cv2.drawContours在圖像上繪製輪廓。 


cv2.drawContours()函數


cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset ]]]]]) 

 


第一個參數是指明在哪幅圖像上繪製輪廓;

第二個參數是輪廓本身,在Python中是一個list。

第三個參數指定繪製輪廓list中的哪條輪廓,如果是-1,則繪製其中的所有輪廓。後麵的參數很簡單。其中thickness表明輪廓線的寬度,如果是-1(cv2.FILLED),則為填充模式。繪製參數將在以後獨立詳細介紹。

補充:


http://blog.csdn.net/sunny2038/article/details/12889059博客提到,可用下麵的方式計算得到輪廓的極值點,如下


pentagram = contours[1] #第二條輪廓是五角星  

  

leftmost = tuple(pentagram[:,0][pentagram[:,:,0].argmin()])  

rightmost = tuple(pentagram[:,0][pentagram[:,:,0].argmin()])  

  

cv2.circle(img, leftmost, 2, (0,255,0),3)   

cv2.circle(img, rightmost, 2, (0,0,255),3)  

 


注意!假設輪廓有100個點,OpenCV返回的ndarray的維數是(100, 1, 2)!!!而不是我們認為的(100, 2)。切記!!!人民郵電出版社出版了一本《NumPy攻略:Python科學計算與數據分析》,推薦去看一下。


 


更新:關於pentagram[:,0]的意思


在numpy的數組中,用逗號分隔的是軸的索引。舉個例子,假設有如下的數組:


a = np.array([[[3,4]], [[1,2]],[[5,7]],[[3,7]],[[1,8]]])  

 


其shape是(5, 1, 2)。與我們的輪廓是相同的。那麼a[:,0]的結果就是:


[3,4], [1,2], [5,7], [3,7], [1,8]  

 


這裏a[:,0]的意思就是a[0:5,0],也就是a[0:5,0:0:2],這三者是等價的。


回頭看一下,a的shape是(5,1,2),表明是三個軸的。在numpy的數組中,軸的索引是通過逗號分隔的。同時冒號索引“:”表示的是該軸的所有元素。因此a[:, 0]表示的是第一個軸的所有元素和第二個軸的第一個元素。在這裏既等價於a[0:5, 0]。


再者,若給出的索引數少於數組中總索引數,則將已給出的索引樹默認按順序指派到軸上。比如a[0:5,0]隻給出了兩個軸的索引,則第一個索引就是第一個軸的,第二個索引是第二個軸的,而第三個索引沒有,則默認為[:],即該軸的所有內容。因此a[0:5,0]也等價於a[0:5,0:0:2]。


再詳細一點,a的全體內容為:[[[3,4]], [[1,2]],[[5,7]],[[3,7]],[[1,8]]]。去掉第一層方括號,其中有五個元素,每個元素為[[3,4]]這樣的,所以第一個索引的範圍為[0:5]。注意OpenCV函數返回的多維數組和常見的numpy數組的不同之處!


觀察[[3,4]],我們發現其中隻有一個元素,即[3, 4],第二個索引為[0:1]。


再去掉一層方括號,我們麵對的是[3,4],有兩個元素,所以第三個索引的範圍為[0:2]。


再次強調一下OpenCVPython接口函數返回的NumPy數組和普通的NumPy數組在組織上的不同之處。


參考資料:


1、《Opencv2 Computer Vision Application Programming Cookbook》


2、《OpenCV References Manule》


3、OpenCV官方文檔Contour部分


http://blog.csdn.net/jjddss/article/details/72674704


opencv3可能會報too many values to unpack (expected 2)的錯誤


最近在OpenCV-Python接口中使用cv2.findContours()函數來查找檢測物體的輪廓。


根據網上的 教程,Python OpenCV的輪廓提取函數會返回兩個值,第一個為輪廓的點集,第二個是各層輪廓的索引。但是實際調用時我的程序報錯了,錯誤內容如下:too many values to unpack (expected 2)


其實是接受返回值不符,如果你僅僅使用一個變量a去接受返回值,調用len(a),你會發現長度為3,也就是說這個函數實際上返回了三個值


第一個,也是最坑爹的一個,它返回了你所處理的圖像

第二個,正是我們要找的,輪廓的點集

第三個,各層輪廓的索引

使用方式如下:


import cv2    

    

img = cv2.imread("./test.jpg")    

gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)    

ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)    

    

contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)    

cv2.drawContours(img,contours,-1,(0,0,255),3)    

    

cv2.imshow("img", img)    

cv2.waitKey(0)   

運行時出現錯誤: ValueError: too many values to unpack


原因:由於版本(使用的時3.2.0.7)問題 cv.findContours返回值個數發生變化,變為3個。因此應該為


aa, ctrs, hier = cv2.findContours(im_th.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)  

假如第一個參數不使用,可寫成


_, ctrs, hier = cv2.findContours(im_th.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)  

拓展:


ValueError: too many values to unpack 類錯誤,多為輸入或者輸出參數數量不一致導致。


本文版权:http://www.ndfweb.cn/news-894.html
  NDF俱乐部
  国际域名注册
  建站咨询
简体中文 NDF网站建设淘宝店 | ICO图标在线生成 | 外贸网站建设 | 联系我们
©2007-2025 NDF Corporation 鲁ICP备08005967号 Sitemap - RSSRSS订阅