日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

OpenCV-Python 中文教程15——OpenCV 中的轮廓

發布時間:2024/1/8 python 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OpenCV-Python 中文教程15——OpenCV 中的轮廓 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
OpenCV-Python 中文教程15——OpenCV 中的輪廓


一、初識輪廓

目標

? 理解什么是輪廓
? 學習找輪廓,繪制輪廓等

? 函數: cv2.findContours()cv2.drawContours()?

1、什么是輪廓

? ? ? ?輪廓可以簡單認為成將連續的點(連著邊界)連在一起的曲線,具有相同的顏色或者灰度。輪廓在形狀分析和物體的檢測和識別中很有用。
? ? ? ? 為了更加準確,要使用二值化圖像。在尋找輪廓之前,要進行閾值化處理或者 Canny 邊界檢測。
? ? ? ? 查找輪廓的函數會修改原始圖像。如果在找到輪廓之后還想使用原始圖像的話,那應該將原始圖像存儲到其他變量中。
? ? ? ? OpenCV 中,查找輪廓就像在黑色背景中找白色物體。

? ? ? ?那么如何在一個二值圖像中查找輪廓?函數 cv2.findContours() 有三個參數,第一個是輸入圖像,第二個是輪廓檢索模式,第三個是輪廓近似方法。返回值有三個,第一個是圖像,第二個是輪廓,第三個是(輪廓的)層析結構。輪廓(第二個返回值)是一個 Python列表,其中存儲這圖像中的所有輪廓。每一個輪廓都是一個 Numpy 數組,包含對象邊界點(xy)的坐標。

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() 的第三個參數。它到底代表什么意思呢?由于輪廓是一個形狀具有相同灰度值的邊界,會存貯形狀邊界上所有的 (xy) 坐標。但需要將所有的這些邊界點都存儲嗎?這就是這個參數要告訴函數 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),還是打開的(一條曲線)。

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] # 輪廓的面積 area = cv2.contourArea(cnt) print(area) # 輪廓的周長 perimeter = cv2.arcLength(cnt,True) print(perimeter)

運行結果:

10.0 13.656854152679443

3、輪廓的近似

? ? ? ?將輪廓形狀近似到另外一種由更少點組成的輪廓形狀,新輪廓的點的數目由我們設定的準確度來決定。使用的Douglas-Peucker算法,可以到維基百科獲得更多此算法的細節。為了幫助理解,假設需要在一幅圖像中查找一個矩形,但是由于圖像的種種原因,我們不能得到一個完美的矩形,而是一個“壞形狀”(如下圖所示)。現在你就可以使用這個函數來近似這個形狀()了。這個函數的第二個參數叫epsilon,它是從原始輪廓到近似輪廓的最大距離。它是一個準確度參數。選擇一個好的 epsilon 對于得到滿意結果非常重要。

import cv2 import numpy as np img = cv2.imread('phi.jpg',0) ret,thresh = cv2.threshold(img,127,255,0) image, contours,hierarchy = cv2.findContours(thresh, 1, 2) cnt = contours[0]epsilon = 0.1*cv2.arcLength(cnt,True) approx = cv2.approxPolyDP(cnt,epsilon,True)print(epsilon) print(approx)

運行結果:

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() 查找得到。(xy)為矩形左上角的坐標,(wh)是矩形的寬和高。
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 結構,其中包含矩形左上角角點的坐標(xy),矩形的寬和高(wh),以及旋轉角度。但是要繪制這個矩形需要矩形的 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、長寬比

? ? ? ?邊界矩形的寬高比:


x,y,w,h = cv2.boundingRect(cnt) aspect_ratio = float(w)/h

2、Extent

? ? ? ?輪廓面積與邊界矩形面積的比:


area = cv2.contourArea(cnt) x,y,w,h = cv2.boundingRect(cnt) rect_area = w*h extent = float(area)/rect_area

3、Solidity

? ? ? ? 輪廓面積與凸包面積的比?:


area = cv2.contourArea(cnt) hull = cv2.convexHull(cnt) hull_area = cv2.contourArea(hull) solidity = float(area)/hull_area

4、Equivalent Diameter?

? ? ? ? ?與輪廓面積相等的圓形的直徑:


area = cv2.contourArea(cnt) equi_diameter = np.sqrt(4*area/np.pi)

5、方向

? ? ? 對象的方向,下面的方法還會返回長軸和短軸的長度?:

(x,y),(MA,ma),angle = cv2.fitEllipse(cnt)

6、掩模和像素點

? ? ? 有時需要構成對象的所有像素點,可以這樣做

mask = np.zeros(imgray.shape,np.uint8) # 這里一定要使用參數-1, 繪制填充的的輪廓 cv2.drawContours(mask,[cnt],0,255,-1) #Returns a tuple of arrays, one for each dimension of a, #containing the indices of the non-zero elements in that dimension. #The result of this is always a 2-D array, with a row for #each non-zero element. #To group the indices by element, rather than dimension, use: #transpose(nonzero(a)) #>>> x = np.eye(3) #>>> x #array([[ 1., 0., 0.], # [ 0., 1., 0.], # [ 0., 0., 1.]]) #>>> np.nonzero(x) #(array([0, 1, 2]), array([0, 1, 2])) #>>> x[np.nonzero(x)] #array([ 1., 1., 1.]) #>>> np.transpose(np.nonzero(x)) #array([[0, 0], # [1, 1], # [2, 2]]) pixelpoints = np.transpose(np.nonzero(mask)) #pixelpoints = cv2.findNonZero(mask)

? ? ? ?這里共有兩種方法,第一種方法使用了 Numpy 函數,第二種使用了 OpenCV 函數。結果相同,但還是有點不同。 Numpy 給出的坐標是(rowcolum)形式的。而 OpenCV 給出的格式是(xy)形式的。所以這兩個結果基本是可以互換的。 row=xcolunm=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() 可以找到凸缺陷。函數調用如下:

hull = cv2.convexHull(cnt,returnPoints = False) defects = cv2.convexityDefects(cnt,hull)

? ? ? ?值得注意的是:如果要查找凸缺陷,在使用函數 cv2.convexHull 找凸包時,參數returnPoints 一定要是 False 它會返回一個數組,其中每一行包含的值是 [起點,終點,最遠的點,到最遠點的近似距離]。我們可以在一張圖上顯示它,將起點和終點用一條綠線連接,在最遠點畫一個圓圈,要記住的是返回結果的前三個值是輪廓點的索引。因此我們還要到輪廓點中去找:

import cv2 import numpy as np img = cv2.imread('22.png') img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(img_gray, 127, 255,0) image, contours,hierarchy = cv2.findContours(thresh,2,1) cnt = contours[0] hull = cv2.convexHull(cnt,returnPoints = False) defects = cv2.convexityDefects(cnt,hull) for i in range(defects.shape[0]):s,e,f,d = defects[i,0]start = tuple(cnt[s][0])end = tuple(cnt[e][0])far = tuple(cnt[f][0])cv2.line(img,start,end,[0,255,0],2)cv2.circle(img,far,5,[0,0,255],-1)cv2.imshow('img',img) cv2.waitKey(0) cv2.destroyAllWindows()

運行結果:


2、Point Polygon Test

? ? ? ?求解圖像中的一個點到一個對象輪廓的最短距離。如果點在輪廓的外部,返回值為負。如果在輪廓上,返回值為 0。如果在輪廓內部,返回值為正。下面以點(5050)為例:

dist = cv2.pointPolygonTest(cnt,(50,50),True)

? ? ? ?此函數的第三個參數是 measureDist。如果設置為 True,就會計算最短距離。如果是 False,只會判斷這個點與輪廓之間的位置關系(返回值為+1-10)。?因此,如果不需要知道具體距離,建議將第三個參數設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-52 2a 分別代表最外邊矩形的外輪廓和內輪廓。在這里邊輪廓 012 在外部或最外邊,我們可以稱他們為(組織結構)0 級,簡單來說就是他們屬于同一級。

? ? ? ? 接下來輪廓 2a,我們把它當成輪廓 2 的子輪廓。它就成為(組織結構)第1 級,同樣輪廓 3 是輪廓 2 的子輪廓,成為(組織結構)第 3 級。最后輪廓4,5 是輪廓 3a 的子輪廓,成為(組織結構)4 級(最后一級)。按照這種方式給這些形狀編號,我們可以說輪廓 4 是輪廓 3a 的子輪廓(當然輪廓 5 也是)。

2、OpenCV 中層次結構

? ?? ? 不管層次結構是什么樣的,每一個輪廓都包含自己的信息:誰是父,誰是子等。 OpenCV 使用一個含有四個元素的數組表示。 [NextPreviousFirst_ChildParent]

? ? ? ? Next 表示同一級組織結構中的下一個輪廓。

? ? ? ? 以上圖中的輪廓 0 為例,輪廓 1 就是他的 Next。同樣,輪廓 1 Next2Next=2那輪廓 2 呢?在同一級沒有 Next。這時 Next=-1。而輪廓 4 Next5,所以它的 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 級):輪廓012。下面是我選擇這種模式得到的結果:? >>> hierarchy array([[[ 1, -1, -1, -1], [ 2, 0, -1, -1], [-1, 1, -1, -1]]]) ? ? ? ? 當只想得到最外邊的輪廓時,可以選擇這種模式,這在有些情況下很有用。
? ? ? ? RETR_CCOMP 在這種模式下會返回所有的輪廓并將輪廓分為兩級組織結構。例如,一個對象的外輪廓為第 1 級組織結構。而對象內部中空洞的輪廓為第 2 級組織結構,空洞中的任何對象的輪廓又是第 1 級組織結構。空洞的組織
結構為第
2 級。

? ? ? ?想象一下一副黑底白字的圖像,圖像中是數字 00 的外邊界屬于第一級組織結構, 0 的內部屬于第 2 級組織結構。以下圖為例簡單介紹一下,其中紅色數字為這些輪廓編號,并用綠色數字代表它們的組織結構。順序與 OpenCV 檢測輪廓的順序一致。


? ? ? ?現在我們考慮輪廓 0,它的組織結構為第 1 級。其中有兩個空洞 1 2,它們屬于第 2 級組織結構。所以對于輪廓 0 來說跟他屬于同一級組織結構的下一個(Next)是輪廓 3,并且沒有 Previous。它的 Fist_Child 為輪廓1組織結構為 2。由于它是第 1 級,所以沒有父輪廓。因此它的組織結構數組為[3-11-1]

? ? ? ?輪廓 1,它是第 2 級。處于同一級的下一個輪廓為 2。沒有 Previous,也沒有 Child,(因為是第 2 級所以有父輪廓)父輪廓是 0。所以數組是[2-1-10]
? ? ? ?輪廓
2:它是第 2 級。在同一級的組織結構中沒有 NextPrevious 為輪廓 1。沒有子,父輪廓為 0,所以數組是 [-11-10]。
? ? ? ?輪廓 3:它是第 1 級。在同一級的組織結構中 Next 5Previous 為輪廓 0。子為 4,沒有父輪廓,所以數組是 [504-1]。
? ? ? ?輪廓 4:它是第 2 級。在同一級的組織結構中沒有 Next。沒有 Previous,沒有子,父輪廓為 3,所以數組是 [-1-1-13]?。
? ? ? ?下面是得到的答案:
>>> 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-11-1]
? ? ? ?輪廓
1 的組織結構為 1,同一級中沒有其他,沒有 Previous。子輪廓是2,父輪廓為 0。所以數組是 [-1-120]

? ? ? ?剩下的自己試試計算一下。下面是結果:

>>> 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 中的轮廓的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。