opencv cv.findContours 函數詳解 圖像輪廓層級 圖像輪廓檢索方式詳解


函數 cv.findContours

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

  1. 參數1:源圖像
  2. 參數2:輪廓的檢索方式,這篇文章主要講解這個參數
  3. 參數3:一般用 cv.CHAIN_APPROX_SIMPLE,就表示用盡可能少的像素點表示輪廓
  4. contours:圖像輪廓坐標,是一個鏈表
  5. hierarchy:[Next, Previous, First Child, Parent],文中有詳細解釋

我們使用cv.findContours()尋找輪廓時,參數2表示輪廓的檢索方式(RetrievalModes),當我們傳入的是cv.RETR_TREE,它表示什么意思呢?另外,函數返回值hierarchy有什么用途呢?下面我們就來研究下這兩個問題。

理解輪廓層級

輪廓層級展示
圖中總共有8條輪廓,2和2a分別表示外層和里層的輪廓,3和3a也是一樣。從圖中看得出來:

  1. 輪廓0/1/2是最外層的輪廓,我們可以說它們處於同一輪廓等級:0級
  2. 輪廓2a是輪廓2的子輪廓,反過來說2是2a的父輪廓。輪廓2a算一個等級:1級
  3. 同樣3是2a的子輪廓,輪廓3處於一個等級:2級
  4. 類似的,3a是3的子輪廓,等等…………
    這里面OpenCV關注的就是兩個概念:同一輪廓等級和輪廓間的子屬關系。

如果我們打印出cv.findContours()函數的返回值hierarchy,會發現它是一個包含4個值的數組:[Next, Previous, First Child, Parent]
Next:與當前輪廓處於同一層級的下一條輪廓
舉例來說,前面圖中跟0處於同一層級的下一條輪廓是1,所以Next=1;同理,對輪廓1來說,Next=2;那么對於輪廓2呢?沒有與它同一層級的下一條輪廓了,此時Next=-1。
Previous:與當前輪廓處於同一層級的上一條輪廓
跟前面一樣,對於輪廓1來說,Previous=0;對於輪廓2,Previous=1;對於輪廓2a,沒有上一條輪廓了,所以Previous=-1。
First Child:當前輪廓的第一條子輪廓
比如對於輪廓2,第一條子輪廓就是輪廓2a,所以First Child=2a;對輪廓3,First Child=3a。
Parent:當前輪廓的父輪廓
比如2a的父輪廓是2,Parent=2;輪廓2沒有父輪廓,所以Parent=-1。

OpenCV中找到的輪廓序號跟前面講的不同
opencv中的輪廓序號
現在既然我們了解了輪廓層級的概念,那么類似cv.RETR_TREE的輪廓尋找方式又是啥意思呢?

輪廓尋找方式

OpenCV中有四種輪廓尋找方式RetrievalModes,下面分別來看下:

1. RETR_LIST

這是最簡單的一種尋找方式,它不建立輪廓間的子屬關系,也就是所有輪廓都屬於同一層級。這樣,hierarchy中的后兩個值[First Child, Parent]都為-1。比如同樣的圖,我們使用cv.RETR_LIST來尋找輪廓:

contours, hierarchy = cv2.findContours(thresh, cv2.RETR_LIST, 2)
print(hierarchy)
# 結果如下
[[[ 1 -1 -1 -1]
  [ 2  0 -1 -1]
  [ 3  1 -1 -1]
  [ 4  2 -1 -1]
  [ 5  3 -1 -1]
  [ 6  4 -1 -1]
  [ 7  5 -1 -1]
  [-1  6 -1 -1]]]

因為沒有從屬關系,所以輪廓0的下一條是1,1的下一條是2……

如果你不需要輪廓層級信息的話,cv.RETR_LIST更推薦使用,因為性能更好

2. RETR_TREE

cv.RETR_TREE就是之前我們一直在使用的方式,它會完整建立輪廓的層級從屬關系,前面已經詳細說明過了。

3. RETR_EXTERNAL

這種方式只尋找最高層級的輪廓,也就是它只會找到前面我們所說的3條0級輪廓:

實驗講解 RETR_EXTERNAL

contours, hierarchy = cv.findContours(thresh, cv.RETR_EXTERNAL, 2)
print(len(contours), hierarchy, sep='\n')
# 結果如下
3
[[[ 1 -1 -1 -1]
  [ 2  0 -1 -1]
  [-1  1 -1 -1]]]

實驗結果

最外層輪廓展示

4. RETR_CCOMP

相比之下cv.RETR_CCOMP比較難理解,它把所有的輪廓只分為2個層級,不是外層的就是里層的。結合代碼和圖片,我們來理解下:

實驗講解 RETR_CCOMP

contours, hierarchy = cv.findContours(thresh, cv.RETR_CCOMP, 2)
print(hierarchy)
# 結果如下
[[[ 1 -1 -1 -1]
  [ 2  0 -1 -1]
  [ 4  1  3 -1]
  [-1 -1 -1  2]
  [ 6  2  5 -1]
  [-1 -1 -1  4]
  [ 7  4 -1 -1]
  [-1  6 -1 -1]]]

實驗結果

2個層級輪廓圖解
使用這個參數找到的輪廓序號與之前不同。
圖中括號里面1代表外層輪廓,2代表里層輪廓。比如說對於輪廓2,Next就是4,Previous是1,它有里層的輪廓3,所以First Child=3,但因為只有兩個層級,它本身就是外層輪廓,所以Parent=-1。大家可以針對其他的輪廓自己驗證一下。

實驗:繪制圖像輪廓

import cv2 as cv
import numpy

# 1.讀入圖片
img = cv.imread('test_contours.jpg')
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
contours, thresh = cv.threshold(img_gray, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU)

# 2.尋找輪廓
contours, hierarchy = cv.findContours(thresh, cv.RETR_LIST, 2)

print(len(contours),hierarchy)

# 3.繪制輪廓
cv.drawContours(img, contours, -1, (0, 0, 255), 2)

cv.imshow('result',img)
cv.waitKey(0)
cv.destroyAllWindows()

實驗結果

給圖像輪廓加紅色邊框


免責聲明!

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



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