目錄
1 什麼是層次結構 2 OpenCV 中層次結構 3 輪廓檢索模式
在前面的内容中我們使用函數 cv2.findContours (https://blog.csdn.net/yukinoai/article/details/87891698)來查找輪廓,我們需要傳入一個參數:輪廓提取模式(Contour_Retrieval_Mode)。我們總是把它設定為 cv2.RETR_LIST 或者是 cv2.RETR_TREE,效果還可以。但 是它們到底代表什麼呢? 同時,我們得到的結果包含 3 個數組,第一個圖像,第二個是輪廓,第三個是層次結構。但是我們從來沒有用過層次結構。層次結構是用來幹嘛的呢? 層次結構與輪廓提取模式有什麼關系呢?
1 什麼是層次結構
通常我們使用函數 cv2.findContours 在圖檔中查找一個對象。有時對象可能位于不同的位置。還有些情況,一個形狀在另外一個形狀的内部。這種 情況下我們稱外部的形狀為父,内部的形狀為子。按照這種方式分類,一幅圖像中的所有輪廓之間就建立父子關系。這樣我們就可以确定一個輪廓與其他輪廓是怎樣連接配接的,比如它是不是某個輪廓的子輪廓,或者是父輪廓。這種關系 就成為組織結構。
下圖就是一個簡單的例子:

在這幅圖像中,我給這幾個形狀編号為 0-5。
2 和 2a 分别代表最外邊矩形 的外輪廓和内輪廓。
在這裡邊輪廓 0,1,2 在外部或最外邊。我們可以稱他們為(組織結構) 0 級,簡單來說就是他們屬于同一級。
接下來輪廓 2a。我們把它當成輪廓 2 的子輪廓。它就成為(組織結構)第1級。
同樣輪廓 3 是輪廓 2 的子輪廓,成為(組織結構)第 3 級。
最後輪廓 4,5 是輪廓 3a 的子輪廓,成為(組織結構)4 級(最後一級)。
按照這種方式 給這些形狀編号,我們可以說輪廓 4 是輪廓 3a 的子輪廓(當然輪廓 5 也是)。
2 OpenCV 中層次結構
不管層次結構是什麼樣的,每一個輪廓都包含自己的資訊:誰是父,誰是子等。OpenCV 使用一個含有四個元素的數組表示。
[Next, Previous, First_Child, Parent]
- Next 表示同一級組織結構中的下一個輪廓。
以上圖中的輪廓 0 為例,輪廓 1 就是他的 Next。同樣,輪廓 1 的 Next是 2,Next=2。 那輪廓 2 呢?在同一級沒有 Next。這時 Next=-1。而輪廓 4 的 Next為 5,是以它的 Next=5。
- Previous 表示同一級結構中的前一個輪廓。
與前面一樣,輪廓 1 的 Previous 為輪廓 0,輪廓 2 的 Previous 為輪廓 1。輪廓 0 沒有 Previous,是以 Previous=-1。
- First_Child 表示它的第一個子輪廓。
沒有必要再解釋了,輪廓 2 的子輪廓為 2a。是以它的 First_Child 為2a。那輪廓 3a 呢?它有兩個子輪廓。但是我們隻要第一個子輪廓,是以是輪 廓 4(按照從上往下,從左往右的順序排序)。
- Parent 表示它的父輪廓。
與 First_Child 剛好相反。輪廓 4 和 5 的父輪廓是輪廓 3a。而輪廓 3a的父輪廓是 3。 注意:如果沒有父或子,就為 -1。
現在我麼了解了 OpenCV 中的輪廓組織結構。我們還是根據上邊的圖檔再學習一下 OpenCV 中的輪廓檢索模式。 cv2.RETR_LIST,cv2.RETR_TREE,cv2.RETR_CCOMP,cv2.RETR_EXTERNAL 到底代表什麼意思?
3 輪廓檢索模式
RETR_LIST
從解釋的角度來看,這中應是最簡單的。它隻是提取所有的輪 廓,而不去建立任何父子關系。換句話說就是“人人平等”,它們屬于同一級組 織輪廓。
是以在這種情況下,組織結構數組的第三和第四個數都是 -1。但是,很明顯,Next 和 Previous 要有對應的值。
下面就是我得到的結果,每一行是對應輪廓的組織結構細節。例如,第一行對應的是輪廓 0。下一個輪廓為 1,是以 Next=1。前面沒有其他輪廓,是以 Previous=0。接下來的兩個參數就是 -1,與剛才我們說的一樣。
>>> hierarchy
array([[[ 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]]])
如果你不關心輪廓之間的關系,這是一個非常好的選擇。
RETR_EXTERNAL
如果你選擇這種模式的話,隻會傳回最外邊的的輪廓, 所有的子輪廓都會被忽略掉。 是以在上圖中使用這種模式的話隻會傳回最外邊的輪廓(第 0 級):輪廓 0,1,2。下面是我選擇這種模式得到的結果:
>>> hierarchy
array([[[ 1, -1, -1, -1],
[ 2, 0, -1, -1],
[-1, 1, -1, -1]]])
當你隻想得到最外邊的輪廓時,你可以選擇這種模式。這在有些情況下很有用。
RETR_CCOMP
在這種模式下會傳回所有的輪廓并将輪廓分為兩級組織結 構。例如,一個對象的外輪廓為第 1 級組織結構。而對象内部中空洞的輪廓為 第 2 級組織結構,空洞中的任何對象的輪廓又是第 1 級組織結構。空洞的組織 結構為第 2 級。
想象一下一副黑底白字的圖像,圖像中是數字 0。0 的外邊界屬于第一級組織結構,0 的内部屬于第 2 級組織結構。
我們可以以下圖為例簡單介紹一下。我們已經用紅色數字為這些輪廓編号, 并用綠色數字代表它們的組織結構。順序與 OpenCV 檢測輪廓的順序一直。
現在我們考慮輪廓 0,它的組織結構為第 1 級。其中有兩個空洞 1 和 2,它們屬于第 2 級組織結構。是以對于輪廓 0 來說跟他屬于同一級組織結構的 下一個(Next)是輪廓 3,并且沒有 Previous。它的 Fist_Child 為輪廓 1, 組織結構為 2。由于它是第 1 級,是以沒有父輪廓。是以它的組織結構數組為 [3,-1,1,-1]。
- 輪廓 1,它是第 2 級。處于同一級的下一個輪廓為 2。沒有 Previous,也沒有 Child,(因為是第 2 級是以有父輪廓)父輪廓是 0。是以數組是 [2,-1,-1,0]。
- 輪廓 2:它是第 2 級。在同一級的組織結構中沒有 Next。Previous 為輪廓 1。沒有子,父輪廓為 0,是以數組是 [-1,1,-1,0]
- 輪廓 3:它是第 1 級。在同一級的組織結構中 Next 為 5。Previous 為
- 輪廓 0。子為 4,沒有父輪廓,是以數組是 [5,0,4,-1]
- 輪廓 4:它是第 2 級。在同一級的組織結構中沒有 Next。沒有 Previous, 沒有子,父輪廓為 3,是以數組是 [-1,-1,-1,3]
下面是我得到的答案:
>>> hierarchy
array([[[ 3, -1, 1, -1],
[2, -1, -1, 0],
[-1, 1, -1, 0],
[ 5, 0, 4, -1],
[-1, -1, -1, 3],
[ 7, 3, 6, -1],
[-1, -1, -1, 5],
[ 8, 5, -1, -1],
[-1, 7, -1, -1]]])
RETR_TREE
終于到最後一個了,也是最完美的一個。這種模式下會傳回 所有輪廓,并且建立一個完整的組織結構清單。它甚至會告訴你誰是爺爺,爸爸,兒子,孫子等。
還是以上圖為例,使用這種模式,對 OpenCV 傳回的結果重新排序并分 析它,紅色數字是邊界的序号,綠色是組織結構。
輪廓 0 的組織結構為 0,同一級中 Next 為 7,沒有 Previous。子輪廓是 1,沒有父輪廓。是以數組是 [7,-1,1,-1]。
輪廓 1 的組織結構為 1,同一級中沒有其他,沒有 Previous。子輪廓是2,父輪廓為 0。是以數組是 [-1,-1,2,0]。
下面是結果:
>>> hierarchy
array([[[ 7, -1, 1, -1],
[-1, -1, 2, 0],
[-1, -1, 3, 1],
[-1, -1, 4, 2],
[-1, -1, 5, 3],
[ 6, -1, -1, 4],
[-1, 5, -1, 4],
[ 8, 0, -1, -1],
[-1, 7, -1, -1]]])