目錄
1 什么是輪廓
2 尋找輪廓
2.1 mode參數
2.2 method參數
3 繪制輪廓
1 什么是輪廓
輪廓是一系列相連的點組成的曲線,代表了物體的基本外形。輪廓是連續的,邊緣不一定連續。輪廓是一個閉合的、封閉的形狀。
輪廓的作用:
-
形狀分析
-
目標識別
-
圖像分割
2 尋找輪廓
在OpenCV中,使用cv2.findContours()來進行尋找輪廓,具體的實現原理可參考:
https://zhuanlan.zhihu.com/p/107257870
尋找輪廓需要將圖像做一個二值化處理,并且根據圖像的不同選擇不同的二值化方法來將圖像中要繪制輪廓的部分置為白色,其余部分置為黑色。如下圖所示。
之后,對圖像中的像素進行遍歷,當一個白色像素相鄰(上下左右及兩條對角線)位置有黑色像素存在或者一個黑色像素相鄰(上下左右及兩條對角線)位置有白色像素存在時,那么該像素點就會被認定為邊界像素點,輪廓就是有無數個這樣的邊界點組成的。
下面具體介紹一下cv2.findContours()函數,其函數原型為:
contours,hierarchy = cv2.findContours(image,mode,method)
-
返回值:[ 輪廓點坐標 ] 和 [ 層級關系 ]。
-
contours:表示獲取到的輪廓點的列表。檢測到有多少個輪廓,該列表就有多少子列表,每一個子列表都代表了一個輪廓中所有點的坐標。
-
hierarchy:表示輪廓之間的關系。對于第i條輪廓,hierarchy[i][0], hierarchy[i][1] , hierarchy[i][2] , hierarchy[i][3]分別表示其后一條輪廓、前一條輪廓、(同層次的第一個)子輪廓、父輪廓的索引(如果沒有相應的輪廓,則對應位置為-1)。該參數的使用情況會比較少。
-
image:表示輸入的二值化圖像。
-
mode:表示輪廓的檢索模式。
-
method:輪廓的表示方法。
2.1 mode參數
輪廓查找方式。返回不同的層級關系。
mode參數共有四個選項分別為:RETR_LIST,RETR_EXTERNAL,RETR_CCOMP,RETR_TREE。
1. RETR_EXTERNAL
表示只查找最外層的輪廓。并且在hierarchy里的輪廓關系中,每一個輪廓只有前一條輪廓與后一條輪廓的索引,而沒有父輪廓與子輪廓的索引。
2. RETR_LIST
表示列出所有的輪廓。并且在hierarchy里的輪廓關系中,每一個輪廓只有前一條輪廓與后一條輪廓的索引,而沒有父輪廓與子輪廓的索引。
3. RETR_CCOMP
表示列出所有的輪廓。并且在hierarchy里的輪廓關系中,輪廓會按照成對的方式顯示。
在 RETR_CCOMP
模式下,輪廓被分為兩個層級:
-
層級 0:所有外部輪廓(最外層的邊界)。
-
層級 1:所有內部輪廓(孔洞或嵌套的區域)。
4. RETR_TREE
表示列出所有的輪廓。并且在hierarchy里的輪廓關系中,輪廓會按照樹的方式顯示,其中最外層的輪廓作為樹根,其子輪廓是一個個的樹枝。
2.2 method參數
輪廓存儲方法。輪廓近似方法。決定如何簡化輪廓點的數量。就是找到輪廓后怎么去存儲這些點。
method參數有三個選項:CHAIN_APPROX_NONE、CHAIN_APPROX_SIMPLE、CHAIN_APPROX_TC89_L1。
-
CHAIN_APPROX_NONE
表示將所有的輪廓點都進行存儲 -
CHAIN_APPROX_SIMPLE
表示只存儲有用的點,比如直線只存儲起點和終點,四邊形只存儲四個頂點,默認使用這個方法;
對于mode和method這兩個參數來說,一般使用RETR_EXTERNAL和CHAIN_APPROX_SIMPLE這兩個選項。
3 繪制輪廓
輪廓找出來后,其實返回的是一個輪廓點坐標的列表,因此我們需要根據這些坐標將輪廓畫出來,因此就用到了繪制輪廓的方法。
cv2.drawContours(image, contours, contourIdx, color, thickness)
-
image:原始圖像,一般為單通道或三通道的 numpy 數組。
-
contours:包含多個輪廓的列表,每個輪廓本身也是一個由點坐標構成的二維數組(numpy數組)。
-
contourIdx:要繪制的輪廓索引。如果設為
-1
,則會繪制所有輪廓。根據索引找到輪廓點繪制出來。默認是-1。 -
color:繪制輪廓的顏色,可以是 BGR 值或者是灰度值(對于灰度圖像)。
-
thickness:輪廓線的寬度,如果是正數,則畫實線;如果是負數,則填充輪廓內的區域。
案例:
import cv2 as cv
# 讀圖,轉灰度
img = cv.imread("./images/num.png")
number = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 二值化
ret,binary = cv.threshold(number,127,255,cv.THRESH_BINARY_INV)
# 查找輪廓: [輪廓列表],[層級關系] = cv2.findCountours(img,mode(如何找),method(如何存))
counters,h = cv.findContours(binary,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
# 繪制輪廓:cv.drawContours(img,counters,-1(要繪制輪廓的索引),color,thickness)
cv.drawContours(img,counters,-1,(0,255,0),2)
# 顯示原圖
cv.imshow("img",img)
cv.waitKey(0)
cv.destroyAllWindows()
print(counters)
print("---------")
print(h)
輸出: