OpenCV-Python 中文教程15——OpenCV 中的轮廓
一、初識輪廓
目標
? 學習找輪廓,繪制輪廓等
? 函數: cv2.findContours(), cv2.drawContours()?
1、什么是輪廓
? ? ? ?輪廓可以簡單認為成將連續的點(連著邊界)連在一起的曲線,具有相同的顏色或者灰度。輪廓在形狀分析和物體的檢測和識別中很有用。
? ? ? ? 為了更加準確,要使用二值化圖像。在尋找輪廓之前,要進行閾值化處理或者 Canny 邊界檢測。
? ? ? ? 查找輪廓的函數會修改原始圖像。如果在找到輪廓之后還想使用原始圖像的話,那應該將原始圖像存儲到其他變量中。
? ? ? ? 在 OpenCV 中,查找輪廓就像在黑色背景中找白色物體。
? ? ? ?那么如何在一個二值圖像中查找輪廓?函數 cv2.findContours() 有三個參數,第一個是輸入圖像,第二個是輪廓檢索模式,第三個是輪廓近似方法。返回值有三個,第一個是圖像,第二個是輪廓,第三個是(輪廓的)層析結構。輪廓(第二個返回值)是一個 Python列表,其中存儲這圖像中的所有輪廓。每一個輪廓都是一個 Numpy 數組,包含對象邊界點(x, y)的坐標。
2、如何繪制輪廓
? ? ? 函數 cv2.drawContours() 可以被用來繪制輪廓。它可以根據你提供的邊界點繪制任何形狀。它的第一個參數是原始圖像,第二個參數是輪廓,一個 Python 列表。第三個參數是輪廓的索引(在繪制獨立輪廓是很有用,當設置為 -1 時繪制所有輪廓)。接下來的參數是輪廓的顏色和厚度等。參考博客:Opencv—輪廓檢測。
? ? ? 在一幅圖像上繪制所有的輪廓:
import numpy as np import cv2 img = cv2.imread('cat.jpg')imgray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret,thresh = cv2.threshold(imgray,127,255,0) image1, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)# 繪制獨立輪廓,如第四個輪廓: image2 = cv2.drawContours(thresh, contours, -1, (0,255,0), 3)# image3 = cv2.drawContours(thresh, contours, 3, (0,0,255), 3)cv2.namedWindow("img") cv2.imshow("img",img)cv2.namedWindow("image1") cv2.imshow("image1",image1)cv2.namedWindow("image2") cv2.imshow("image2",image2) # # cv2.namedWindow("image3") # cv2.imshow("image3",image3) cv2.waitKey(0) cv2.destroyAllWindows()運行結果:
3、輪廓的近似方法
? ? ? ?這是函數 cv2.findCountours() 的第三個參數。它到底代表什么意思呢?由于輪廓是一個形狀具有相同灰度值的邊界,會存貯形狀邊界上所有的 (x, y) 坐標。但需要將所有的這些邊界點都存儲嗎?這就是這個參數要告訴函數 cv2.findContours 的。
? ? ? ?這個參數如果被設置為 cv2.CHAIN_APPROX_NONE,所有的邊界點都會被存儲。但是真的需要這么多點嗎?例如,當需要尋找的邊界是一條直線時,用直線上所有的點來表示直線嗎?不是的,只需要這條直線的兩個端點而已。這就是 cv2.CHAIN_APPROX_SIMPLE 要做的。?它會將輪廓上的冗余點都去掉,壓縮輪廓,從而節省內存開支。
? ? ? 我們用下圖中的矩形來演示這個技術。在輪廓列表中的每一個坐標上畫一個藍色圓圈。第一個圖顯示使用 cv2.CHAIN_APPROX_NONE 的效果,共有很多個點。第二個圖是使用 cv2.CHAIN_APPROX_SIMPLE 的結果,只有 4 個點。
二、輪廓特征(參考:https://jingyan.baidu.com/article/4f7d5712fe74cf1a20192782.html)
目標
? 查找輪廓的不同特征,例如面積,周長,重心,邊界框等。
? 你會學到很多輪廓相關函數
1、矩
? ? ? ?圖像的矩可以幫助我們計算圖像的質心,面積等?,具體參考Image Moments。函數 cv2.moments() 會將計算得到的矩以一個字典的形式返回。例如:
import cv2 import numpy as np img = cv2.imread('cat.jpg',0) ret,thresh = cv2.threshold(img,127,255,0) image, contours,hierarchy = cv2.findContours(thresh, 1, 2) cnt = contours[0] M = cv2.moments(cnt) print(M) #字典形式返回# 根據這些矩的值,我們可以計算出對象的重心 cx = int(M['m10']/M['m00']) cy = int(M['m01']/M['m00'])print(cx) print(cy) 運行結果:{'m11': 403860.0, 'mu02': 3.0, 'm30': 40207125.0, 'm03': 163872926.0, 'm02': 645163.0, 'm01': 2540.0, 'mu30': 7.450580596923828e-09, 'mu12': 0.0, 'nu20': 0.2166666666665697, 'm20': 252831.66666666666, 'nu11': 0.0, 'mu11': 0.0, 'mu03': 0.0, 'm21': 64219243.333333336, 'nu21': 1.5645846789371704e-11, 'mu20': 21.666666666656965, 'nu03': 0.0, 'm12': 102580917.0, 'nu30': 2.3560804576936214e-11, 'm10': 1590.0, 'm00': 10.0, 'nu12': 0.0, 'mu21': 4.94765117764473e-09, 'nu02': 0.030000000000000006} 159 254 2、輪廓的面積與周長
? ? ? 輪廓的面積可以使用函數 cv2.contourArea() 計算得到,也可以使用矩(0 階矩), M['m00']。輪廓的周長被稱為弧長,可以使用函數 cv2.arcLength() 計算得到。這個函數的第二參數可以用來指定對象的形狀是閉合的(True),還是打開的(一條曲線)。
運行結果:
3、輪廓的近似
? ? ? ?將輪廓形狀近似到另外一種由更少點組成的輪廓形狀,新輪廓的點的數目由我們設定的準確度來決定。使用的Douglas-Peucker算法,可以到維基百科獲得更多此算法的細節。為了幫助理解,假設需要在一幅圖像中查找一個矩形,但是由于圖像的種種原因,我們不能得到一個完美的矩形,而是一個“壞形狀”(如下圖所示)。現在你就可以使用這個函數來近似這個形狀()了。這個函數的第二個參數叫epsilon,它是從原始輪廓到近似輪廓的最大距離。它是一個準確度參數。選擇一個好的 epsilon 對于得到滿意結果非常重要。
運行結果:
36.409040009975435 [[[186 75]][[ 91 55]][[ 52 109]]]? ? ? ?下邊第二幅圖中的綠線是當 epsilon = 10% 時得到的近似輪廓,第三幅圖是當 epsilon = 1% 時得到的近似輪廓。第三個參數設定弧線是否閉合。
示例代碼:
import cv2# 1.先找到輪廓 img = cv2.imread('phi.jpg', 0) _, thresh = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) image, contours, hierarchy = cv2.findContours(thresh, 3, 2) cnt = contours[0]# 2.進行多邊形逼近,得到多邊形的角點 approx = cv2.approxPolyDP(cnt, 3, True)# 3.畫出多邊形 image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) cv2.polylines(image, [approx], True, (0, 255, 0), 2)cv2.namedWindow("img") cv2.imshow("img",image) cv2.waitKey(0) cv2.destroyAllWindows()運行結果:
? ? ? ??其中cv2.approxPolyDP()的參數2(epsilon)是一個距離值,表示多邊形的輪廓接近實際輪廓的程度,值越小,越精確;參數3表示是否閉合。
4、凸包
? ? ? ?凸包與輪廓近似相似,但不同,雖然有些情況下它們給出的結果是一樣的。函數 cv2.convexHull() 可以用來檢測一個曲線是否具有凸性缺陷,并能糾正缺陷。一般來說,凸性曲線總是凸出來的,至少是平的。如果有地方凹進去了就被叫做凸性缺陷。參考文章:https://www.jianshu.com/p/d53bdfb1051f,示例代碼:
import cv2 im = cv2.imread('phi.jpg') g = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) ret,g = cv2.threshold(g,200,255,0) a,b,c = cv2.findContours(g,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE) cnt = b[0] hull = cv2.convexHull(cnt) print(hull) #輪廓的索引點 t = cv2.cvtColor(a, cv2.COLOR_GRAY2BGR) i = cv2.polylines(im, [hull], True, (255, 0, 0), 2)cv2.namedWindow("img") cv2.imshow("img",i) cv2.waitKey(0) cv2.destroyAllWindows()運行結果:
[[[360 221]][[358 405]][[356 414]][[ 94 417]][[ 94 287]][[ 96 52]][[133 44]][[233 44]][[347 46]][[360 105]]]參數說明:
hull = cv2.convexHull(points, hull, clockwise, returnPoints]? ? ? ?? points 我們要傳入的輪廓
? ? ? ?? hull 輸出,通常不需要
? ? ? ?? clockwise 方向標志。如果設置為 True,輸出的凸包是順時針方向的。否則為逆時針方向。
? ? ? ?? returnPoints 默認值為 True。它會返回凸包上點的坐標。如果設置為 False,就會返回與凸包點對應的輪廓上的點。
? ? ? ?要獲得上圖的凸包,下面的命令就可以:hull = cv2.convexHull(cnt)
? ? ? ?但如果想獲得凸性缺陷,需要把 returnPoints 設置為 False。
5、凸性檢測
? ? ? ?函數 cv2.isContourConvex() 可以用來檢測一個曲線是不是凸的,它只能返回 True 或 False。
k = cv2.isContourConvex(cnt)6、邊界矩形
(1)直邊界矩形
? ? ? ? 一個直矩形(就是沒有旋轉的矩形)。它不會考慮對象是否旋轉。所以邊界矩形的面積不是最小的。可以使用函數 cv2.boundingRect() 查找得到。(x, y)為矩形左上角的坐標,(w, h)是矩形的寬和高。import cv2 im = cv2.imread('11.png') g = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) ret,g = cv2.threshold(g,200,255,0) a,b,c = cv2.findContours(g,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE) cnt = b[0]x,y,w,h = cv2.boundingRect(cnt) img = cv2.rectangle(im,(x,y),(x+w,y+h),(0,255,0),2)cv2.namedWindow("img") cv2.imshow("img",img) cv2.waitKey(0) cv2.destroyAllWindows() 運行結果:
(2)旋轉的邊界矩形? ? ? ? ?這個邊界矩形是面積最小的,因為它考慮了對象的旋轉。用到的函數為 cv2.minAreaRect()。返回的是一個 Box2D 結構,其中包含矩形左上角角點的坐標(x, y),矩形的寬和高(w, h),以及旋轉角度。但是要繪制這個矩形需要矩形的 4 個角點,可以通過函數 cv2.boxPoints() 獲得。 import cv2 import numpy as npim = cv2.imread('22.png') imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) ret,thresh = cv2.threshold(imgray,180,255,cv2.THRESH_BINARY) image, contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE) cnt = contours[0]# 旋轉的邊界矩形 rect = cv2.minAreaRect(cnt) box = cv2.boxPoints(rect) box = np.int0(box) cv2.drawContours(im, [box], 0, (0, 0, 255), 2)cv2.imshow('result',im) cv2.waitKey(0) 運行結果:
7、最小外接圓
? ? ? ? 函數 cv2.minEnclosingCircle() 可以幫我們找到一個對象的外切圓。它是所有能夠包括對象的圓中面積最小的一個。
(x,y),radius = cv2.minEnclosingCircle(cnt) center = (int(x),int(y)) radius = int(radius) img = cv2.circle(img,center,radius,(0,255,0),2) 8、橢圓擬合
? ? ? 使用的函數為 cv2.ellipse(),返回值其實就是旋轉邊界矩形的內切圓。? ellipse = cv2.fitEllipse(cnt) im = cv2.ellipse(im,ellipse,(0,255,0),2) 9、直線擬合 ? ? ? 可以根據一組點擬合出一條直線,同樣我們也可以為圖像中的白色點擬合出一條直線。
rows,cols = img.shape[:2] #cv2.fitLine(points, distType, param, reps, aeps[, line ]) → line #points – Input vector of 2D or 3D points, stored in std::vector<> or Mat. #line – Output line parameters. In case of 2D fitting, it should be a vector of #4 elements (likeVec4f) - (vx, vy, x0, y0), where (vx, vy) is a normalized #vector collinear to the line and (x0, y0) is a point on the line. In case of #3D fitting, it should be a vector of 6 elements (like Vec6f) - (vx, vy, vz, #x0, y0, z0), where (vx, vy, vz) is a normalized vector collinear to the line #and (x0, y0, z0) is a point on the line. #distType – Distance used by the M-estimator #distType=CV_DIST_L2 #ρ(r) = r2 /2 (the simplest and the fastest least-squares method) #param – Numerical parameter ( C ) for some types of distances. If it is 0, an optimal value #is chosen. #reps – Sufficient accuracy for the radius (distance between the coordinate origin and the #line). #aeps – Sufficient accuracy for the angle. 0.01 would be a good default value for reps and #aeps. [vx,vy,x,y] = cv2.fitLine(cnt, cv2.DIST_L2,0,0.01,0.01) lefty = int((-x*vy/vx) + y) righty = int(((cols-x)*vy/vx)+y) img = cv2.line(img,(cols-1,righty),(0,lefty),(0,255,0),2) 綜上所述的完整代碼: import cv2 import numpy as npim = cv2.imread('22.png') imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) ret,thresh = cv2.threshold(imgray,180,255,cv2.THRESH_BINARY) image, contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE) cnt = contours[0]# 直邊界矩形 x,y,w,h = cv2.boundingRect(cnt) cv2.rectangle(im,(x,y),(x+w,y+h),(0,255,0),2)# 旋轉的邊界矩形 rect = cv2.minAreaRect(cnt) box = cv2.boxPoints(rect) box = np.int0(box) cv2.drawContours(im, [box], 0, (0, 0, 255), 2)# 最小外接圓 (x,y),radius = cv2.minEnclosingCircle(cnt) center = (int(x),int(y)) radius = int(radius) cv2.circle(im,center,radius,(255,0,0),2)# 橢圓擬合 ellipse = cv2.fitEllipse(cnt) cv2.ellipse(im,ellipse,(255,255,0),2)# 直線擬合 rows,cols = im.shape[:2] [vx,vy,x,y] = cv2.fitLine(cnt, cv2.DIST_L2,0,0.01,0.01) lefty = int((-x*vy/vx) + y) righty = int(((cols-x)*vy/vx)+y) im = cv2.line(im,(cols-1,righty),(0,lefty),(0,255,255),2)cv2.imshow('result',im) cv2.waitKey(0)
運行結果:
三、輪廓的性質
? ? ? ?本節將學習提取一些經常使用的對象特征,參考網址:Matlab regionprops documentation。
1、長寬比
? ? ? ?邊界矩形的寬高比:
2、Extent
? ? ? ?輪廓面積與邊界矩形面積的比:
3、Solidity
? ? ? ? 輪廓面積與凸包面積的比?:
4、Equivalent Diameter?
? ? ? ? ?與輪廓面積相等的圓形的直徑:
5、方向
? ? ? 對象的方向,下面的方法還會返回長軸和短軸的長度?:
(x,y),(MA,ma),angle = cv2.fitEllipse(cnt)6、掩模和像素點
? ? ? 有時需要構成對象的所有像素點,可以這樣做:
? ? ? ?這里共有兩種方法,第一種方法使用了 Numpy 函數,第二種使用了 OpenCV 函數。結果相同,但還是有點不同。 Numpy 給出的坐標是(row,colum)形式的。而 OpenCV 給出的格式是(x, y)形式的。所以這兩個結果基本是可以互換的。 row=x, colunm=y。
7、最大值和最小值及它們的位置
? ? ? ?可以使用掩模圖像得到這些參數?:
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(imgray,mask = mask)8、平均顏色及平均灰度
? ? ? ?可以使用相同的掩模求一個對象的平均顏色或平均灰度:
mean_val = cv2.mean(im,mask = mask)9、極點
? ? ? ? 一個對象最上面,最下面,最左邊,最右邊的點?:
leftmost = tuple(cnt[cnt[:,:,0].argmin()][0]) rightmost = tuple(cnt[cnt[:,:,0].argmax()][0]) topmost = tuple(cnt[cnt[:,:,1].argmin()][0]) bottommost = tuple(cnt[cnt[:,:,1].argmax()][0])實際例子:
import cv2 import numpy as npim = cv2.imread('22.png') imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) ret,thresh = cv2.threshold(imgray,180,255,cv2.THRESH_BINARY) image, contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE) cnt = contours[0]leftmost = tuple(cnt[cnt[:,:,0].argmin()][0]) rightmost = tuple(cnt[cnt[:,:,0].argmax()][0]) topmost = tuple(cnt[cnt[:,:,1].argmin()][0]) bottommost = tuple(cnt[cnt[:,:,1].argmax()][0])cv2.drawMarker(im,leftmost,(0,255,0),2) cv2.drawMarker(im,rightmost,(0,255,0),2) cv2.drawMarker(im,topmost,(0,255,0),2) cv2.drawMarker(im,bottommost,(0,255,0),2)cv2.imshow('result',im) cv2.waitKey(0)運行結果:
四、輪廓:更多函數
目標
? 凸缺陷,以及如何找凸缺陷
? 找某一點到一個多邊形的最短距離
? 不同形狀的匹配
1、凸缺陷
? ? ? ?輪廓的凸包對象上的任何凹陷都被成為凸缺陷。OpenCV 中有一個函數 cv.convexityDefect() 可以找到凸缺陷。函數調用如下:
? ? ? ?值得注意的是:如果要查找凸缺陷,在使用函數 cv2.convexHull 找凸包時,參數returnPoints 一定要是 False。 它會返回一個數組,其中每一行包含的值是 [起點,終點,最遠的點,到最遠點的近似距離]。我們可以在一張圖上顯示它,將起點和終點用一條綠線連接,在最遠點畫一個圓圈,要記住的是返回結果的前三個值是輪廓點的索引。因此我們還要到輪廓點中去找:
運行結果:
2、Point Polygon Test
? ? ? ?求解圖像中的一個點到一個對象輪廓的最短距離。如果點在輪廓的外部,返回值為負。如果在輪廓上,返回值為 0。如果在輪廓內部,返回值為正。下面以點(50, 50)為例:
dist = cv2.pointPolygonTest(cnt,(50,50),True)? ? ? ?此函數的第三個參數是 measureDist。如果設置為 True,就會計算最短距離。如果是 False,只會判斷這個點與輪廓之間的位置關系(返回值為+1, -1, 0)。?因此,如果不需要知道具體距離,建議將第三個參數設False,這樣速度會提高 2 到 3 倍。
3、形狀匹配
? ? ? 函數 cv2.matchShape() 可以幫我們比較兩個形狀或輪廓的相似度。如果返回值越小,匹配越好。它是根據 Hu 矩來計算的。文檔中對不同的方法都有解釋。
import cv2 import numpy as np img1 = cv2.imread('22.png',0) img2 = cv2.imread('11.png',0) ret, thresh = cv2.threshold(img1, 127, 255,0) ret2, thresh2 = cv2.threshold(img2, 127, 255,0) image, contours,hierarchy = cv2.findContours(thresh,2,1) cnt1 = contours[0] image2, contours2,hierarchy2 = cv2.findContours(thresh2,2,1) cnt2 = contours2[0] ret = cv2.matchShapes(cnt1,cnt2,1,0.0) print(ret)運行結果:
2.0089771107078076? ? ? ?及時發生了旋轉對匹配的結果影響也不是非常大,Hu 矩是歸一化中心矩的線性組合,之所以這樣做是為了能夠獲取代表圖像的某個特征的矩函數,這些矩函數對某些變化如縮放,旋轉,鏡像映射(除了 h1)具有不變形。
五、輪廓的層次結構
目標——學習輪廓的層次結構,比如輪廓之間的父子關系
? ? ? ?使用函數 cv2.findContours 來查找輪廓,需傳入一個參數:輪廓提取模式(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]]])總結
以上是生活随笔為你收集整理的OpenCV-Python 中文教程15——OpenCV 中的轮廓的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: unity图片指定任意不规则区域显示
- 下一篇: websocket python爬虫_p