輪廓
什么是輪廓
輪廓可以簡(jiǎn)單認(rèn)為成將連續(xù)的點(diǎn)(連著邊界)連在一起的曲線,具有相同的顏色或者灰度。輪廓在形狀分析和物體的檢測(cè)和識(shí)別中很有用。
- 為了更加準(zhǔn)確,要使用二值化圖像。在尋找輪廓之前,要進(jìn)行閾值化處理或者 Canny 邊界檢測(cè)。
- 查找輪廓的函數(shù)會(huì)修改原始圖像。如果你在找到輪廓之后還想使用原始圖像的話,你應(yīng)該將原始圖像存儲(chǔ)到其他變量中。
- 在 OpenCV 中,查找輪廓就像在黑色背景中超白色物體。你應(yīng)該記住,要找的物體應(yīng)該是白色而背景應(yīng)該是黑色。
查找輪廓
函數(shù) cv2.findContours() 有三個(gè)參數(shù),第一個(gè)是輸入圖像,第二個(gè)是輪廓檢索模式,第三個(gè)是輪廓近似方法。返回值有三個(gè),第一個(gè)是圖像,第二個(gè)是輪廓,第三個(gè)是(輪廓的)層析結(jié)構(gòu)。輪廓(第二個(gè)返回值)是一個(gè) Python列表,其中存儲(chǔ)這圖像中的所有輪廓。每一個(gè)輪廓都是一個(gè) Numpy 數(shù)組,包含對(duì)象邊界點(diǎn)( x, y)的坐標(biāo)。
ret, thresh = cv2.threshold(imgray, 127, 255, 0)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.CHAIN_APPROX_NONE:儲(chǔ)存所有的邊界點(diǎn)(點(diǎn)數(shù)很多)
cv2.CHAIN_APPROX_SIMPLE:儲(chǔ)存所有的近似直線點(diǎn)(點(diǎn)數(shù)很少)
繪制輪廓
函數(shù) cv2.drawContours() 可以被用來(lái)繪制輪廓。它可以根據(jù)你提供的邊界點(diǎn)繪制任何形狀。它的第一個(gè)參數(shù)是原始圖像,第二個(gè)參數(shù)是輪廓,一個(gè) Python 列表。第三個(gè)參數(shù)是輪廓的索引(在繪制獨(dú)立輪廓是很有用,當(dāng)設(shè)置為 -1 時(shí)繪制所有輪廓)。接下來(lái)的參數(shù)是輪廓的顏色和厚度等。
- 繪制所有輪廓 image = cv2.drawContours(img, contours, -1, (0, 255, 0), 3)
- 繪制指定輪廓 image = cv2.drawContours(img, contours, 0, (0, 255, 0), 3)
import numpy as np
import cv2
# 輪廓:連著邊界連續(xù)的點(diǎn)連在一起的曲線,具有相同的顏色或者灰度。
# 輪廓在形狀分析和物體的檢測(cè)和識(shí)別中很有用。
# 1.為了準(zhǔn)確,要使用二值化圖像。需要進(jìn)行閥值化處理或Canny邊界檢測(cè)。
# 2.查找輪廓的函數(shù)會(huì)修改元素圖像。
# 3.在OpenCV中,查找輪廓就像在黑色背景中找白色物體。
# cv2.findContours() # 查找輪廓
# cv2.drawContours() # 繪制輪廓
img = cv2.imread('./resource/image/opencv-logo2.png')
imgcp = cv2.imread('./resource/image/opencv-logo2.png')
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 127, 255, 0)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# contours 輪廓
# hierarchy 層次
# image = cv2.drawContours(img, contours, -1, (0, 255, 0), 3) # 繪制所有輪廓
image = cv2.drawContours(img, contours, 3, (0, 255, 0), 3) # 繪制第4個(gè)輪廓
print(cv2.getVersionString())
print(type(contours))
print(len(contours))
cv2.imshow('img', imgcp)
cv2.imshow('image', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
輪廓特征
- 查找輪廓的不同特征:矩、面積、周長(zhǎng)(也叫弧長(zhǎng))、重心、邊界框等
圖像的矩
- 在圖像處理、計(jì)算機(jī)視覺(jué)和相關(guān)領(lǐng)域,圖像矩是圖像像素強(qiáng)度的某個(gè)特定的加權(quán)平均值(矩),或者是這種矩的函數(shù),通常被選擇為具有某種吸引人的特性或解釋。圖像矩在分割后對(duì)描述物體很有用。通過(guò)圖像矩找到的圖像的簡(jiǎn)單屬性包括面積(或總強(qiáng)度)、其中心點(diǎn)和關(guān)于其方向的信息。
- 圖像的矩可以幫助我們計(jì)算圖像的質(zhì)心,面積等。
- 函數(shù)cv2.moments()計(jì)算得到矩,返回一個(gè)字典。
根據(jù)矩值可以計(jì)算對(duì)象的重心:
C x = M 10 M 00 , C y = M 01 M 00 C_x=\frac{M_{10}}{M_{00}},C_y=\frac{M_{01}}{M_{00}} Cx?=M00?M10??,Cy?=M00?M01??
import numpy as np
import cv2
from matplotlib import pyplot as plt
# 矩:圖像的矩可以幫助我們計(jì)算圖像的質(zhì)心,面積等
# cv2.moments() 計(jì)算得到矩,以一個(gè)字典形式返回
# 讀取圖像
img = cv2.imread('./resource/opencv/image/box2.png', cv2.IMREAD_COLOR)
img1 = img.copy()
gray = cv2.imread('./resource/opencv/image/box2.png', cv2.IMREAD_GRAYSCALE)
# 閥值處理
(ret, thresh) = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 查找所有輪廓
(contours, hierarchy) = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print('找到輪廓數(shù):',len(contours))
# 計(jì)算輪廓索引為0的圖像矩
cnt = contours[0]
M = cv2.moments(cnt)
print('moments()計(jì)數(shù)結(jié)果M:',M)
# 計(jì)算重心(質(zhì)點(diǎn))
#根據(jù)這些矩值計(jì)算出對(duì)象的重心:
# Cx = M10/M00
# Cy = M01/M00
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
print(cx,cy)
# 繪制輪廓
cv2.drawContours(img1, contours, 0, (0, 255, 0), 2)
# 繪制質(zhì)點(diǎn)
cv2.circle(img1, (cx, cy), 5, (255, 0, 0), -1)
plt.subplot(121), plt.imshow(img)
plt.subplot(122), plt.imshow(img1)
plt.show()
程序運(yùn)行結(jié)果:
找到輪廓數(shù): 1
moments()計(jì)數(shù)結(jié)果M: {‘m00’: 10032.0, ‘m10’: 1294128.0, ‘m01’: 1284096.0, ‘m20’: 177807168.0, ‘m11’: 165648384.0, ‘m02’: 170838272.0, ‘m30’: 25740205920.0, ‘m21’: 22759317504.0, ‘m12’: 22038137088.0, ‘m03’: 23524638720.0, ‘mu20’: 10864656.0, ‘mu11’: 0.0, ‘mu02’: 6473984.0, ‘mu30’: 0.0, ‘mu21’: 0.0, ‘mu12’: 0.0, ‘mu03’: 0.0, ‘nu20’: 0.10795454545454546, ‘nu11’: 0.0, ‘nu02’: 0.06432748538011696, ‘nu30’: 0.0, ‘nu21’: 0.0, ‘nu12’: 0.0, ‘nu03’: 0.0}
質(zhì)點(diǎn): 129 128
下圖紅色圓點(diǎn)是質(zhì)點(diǎn),綠色框是輪廓:
輪廓面積
- 輪廓的面積可以使用函數(shù) cv2.contourArea() 計(jì)算得到,也可以使用矩
( 0 階矩), M[‘m00’]。 - area = cv2.contourArea(cnt)
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('./resource/opencv/image/box2.png', cv2.IMREAD_COLOR)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
(ret, thresh) = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
(contours, hierarchy) = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnt = contours[0]
M = cv2.moments(cnt)
area = cv2.contourArea(cnt)
print(cnt)
print(area)
print(M['m00'])
[[[ 72 84]]
[[ 72 172]]
[[186 172]]
[[186 84]]]
10032.0
10032.0
輪廓周長(zhǎng)(弧長(zhǎng))
- 也被稱為弧長(zhǎng)??梢允褂煤瘮?shù) cv2.arcLength() 計(jì)算得到。這個(gè)函數(shù)的第二參數(shù)可以用來(lái)指定對(duì)象的形狀是閉合的( True),還是打開(kāi)的(一條曲線)。
- perimeter = cv2.arcLength(cnt,True)
import numpy as np
import cv2
img = cv2.imread('./resource/opencv/image/box2.png', cv2.IMREAD_COLOR)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
(ret, thresh) = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
(contours, hierarchy) = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 第一個(gè)參數(shù)輪廓,第二次參數(shù)形狀閉合的(True),還是打開(kāi)的一條曲線
perimeter = cv2.arcLength(contours[0], True)
print(perimeter) #404.0
輪廓近似
- 將輪廓形狀近似到另外一種由更少點(diǎn)組成的輪廓形狀,新輪廓的點(diǎn)的數(shù)目由我們?cè)O(shè)定的準(zhǔn)確度來(lái)決定。使用的Douglas-Peucker算法,維基百科獲得更多此算法的細(xì)節(jié)。
- 假設(shè)要在一幅圖像中查找一個(gè)矩形,但是由于圖像的種種原因,我們不能得到一個(gè)完美的矩形,而是一個(gè)“壞形狀”?,F(xiàn)在你就可以使用這個(gè)函數(shù)來(lái)近似這個(gè)形狀()了。
- epsilon = 0.1*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('./resource/opencv/image/approx2.jpg', cv2.IMREAD_COLOR)
img_draw1 = img.copy()
img_draw2 = img.copy()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
(ret, thresh) = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
(contours, hierarchy) = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# epsilon=10%
epsilon01 = 0.1 * cv2.arcLength(contours[0], True)
# epsilon=1%
epsilon001 = 0.01 * cv2.arcLength(contours[0], True)
approx_01 = cv2.approxPolyDP(contours[0], epsilon01, True)
approx_001 = cv2.approxPolyDP(contours[0], epsilon001, True)
cv2.drawContours(img_draw1, approx_01, -1, (0, 0, 255), 5)
cv2.drawContours(img_draw2, approx_001, -1, (0, 0, 255), 5)
plt.subplot(131), plt.imshow(img), plt.title('original')
plt.subplot(132), plt.imshow(img_draw1), plt.title('epsilon=10%')
plt.subplot(133), plt.imshow(img_draw2), plt.title('epsilon=1%')
plt.show()
凸包
- 凸包與輪廓近似相似,但不同,雖然有些情況下它們給出的結(jié)果是一樣的。
- 函數(shù) cv2.convexHull() 可以用來(lái)檢測(cè)一個(gè)曲線是否具有凸性缺陷,并能糾正缺陷。
hull = cv2.convexHull(points[, hull[, clockwise[, returnPoints]])
參數(shù):
points:傳入的輪廓
hull:輸出,通常不需要
clockwise:方向標(biāo)志,True:輸出的凸包是順時(shí)針?lè)较?,F(xiàn)alse:逆時(shí)針?lè)较颉?br> returnPoints:默認(rèn)值為T(mén)rue:返回凸包上點(diǎn)的坐標(biāo)。False:返回與凸包點(diǎn)對(duì)應(yīng)的輪廓上的點(diǎn)。 - 獲取凸包:hull = cv2.convexHull(cnt)
- 一般來(lái)說(shuō),凸性曲線總是凸出來(lái)的,至少是平的。如果有地方凹進(jìn)去了就被叫做凸性缺陷。例如下圖中的手。紅色曲線顯示了手的凸包,凸性缺陷被雙箭頭標(biāo)出來(lái)了。
凸性檢測(cè)
- cv2.isContourConvex() 可以可以用來(lái)檢測(cè)一個(gè)曲線是不是凸
的。它只能返回 True 或 False。k = cv2.isContourConvex(cnt)
import numpy as np
import cv2
from matplotlib import pyplot as plt
# img = cv2.imread('./resource/opencv/image/Back_Projection_Theory2.jpg')
img = cv2.imread('./resource/opencv/image/shape.jpg')
gray = cv2.cvtColor(img.copy(), cv2.COLOR_BGR2GRAY)
# 二值化
(ret, thresh) = cv2.threshold(gray, 120, 255, cv2.THRESH_BINARY)
# (ret, thresh) = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 2)
# thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 5, 2)
# 查找輪廓
(contours, his) = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
hull_img = img.copy()
for i in range(len(contours)):
hull = cv2.convexHull(contours[i])
isConvex = cv2.isContourConvex(contours[i])
print(hull)
print(isConvex)
# 繪制凸包
cv2.drawContours(hull_img, hull, -1, (0, 255, 0), 2)
# 繪制輪廓
contours = cv2.drawContours(img.copy(), contours, -1, (255, 0, 0), 2)
plt.subplot(231), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title('img'), plt.xticks([]), plt.yticks([])
plt.subplot(232), plt.imshow(cv2.cvtColor(gray, cv2.COLOR_BGR2RGB)), plt.title('gray'), plt.xticks([]), plt.yticks([])
plt.subplot(233), plt.imshow(cv2.cvtColor(thresh, cv2.COLOR_BGR2RGB)), plt.title('thresh'), plt.xticks([]), plt.yticks([])
plt.subplot(234), plt.imshow(cv2.cvtColor(contours, cv2.COLOR_BGR2RGB)), plt.title('contours'), plt.xticks([]), plt.yticks([])
plt.subplot(235), plt.imshow(cv2.cvtColor(hull_img, cv2.COLOR_BGR2RGB)), plt.title('convex'), plt.xticks([]), plt.yticks([])
plt.show()
邊界矩形
邊界矩形有兩類(lèi):
- 直邊界矩形
- 旋轉(zhuǎn)邊界矩形
直邊界矩形
- 直邊界矩形:一個(gè)直矩形(就是沒(méi)有旋轉(zhuǎn)的矩形)。它不會(huì)考慮對(duì)象是否旋轉(zhuǎn)。所以邊界矩形的面積不是最小的。可以使用函數(shù)
- cv2.boundingRect() 函數(shù)獲取直邊界矩形。( x, y)為矩形左上角的坐標(biāo),( w, h)是矩形的寬和高。
x,y,w,h = cv2.boundingRect(cnt)
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
旋轉(zhuǎn)邊界矩形(最小面積矩形)
- 旋轉(zhuǎn)的邊界矩形:這個(gè)邊界矩形是面積最小的,因?yàn)樗紤]了對(duì)象的旋轉(zhuǎn)。
- cv2.minAreaRect()函數(shù)獲取旋轉(zhuǎn)邊界矩形。返回的是一個(gè) Box2D 結(jié)構(gòu),其中包含矩形中心點(diǎn)坐標(biāo)( x, y),矩形的寬和高( w, h),以及旋轉(zhuǎn)角度。
(center(x,y), (width, height), angle of rotation) = cv2.minAreaRect(points) - cv2.boxPoints() 函數(shù)獲取旋轉(zhuǎn)邊界矩形的 4 個(gè)角點(diǎn)。
[[x1, y1],[x2, y2], [x3, y3], [x4, y4]] = cv2.boxPoints(points)
import numpy as np
import cv2
from matplotlib import pyplot as plt
# 輪廓邊界矩形 分為兩種:直邊界矩形、旋轉(zhuǎn)邊界矩形
# 讀取圖像
img = cv2.imread('./resource/opencv/image/boundrect.jpg', cv2.IMREAD_COLOR)
draw = img.copy()
# 轉(zhuǎn)為灰度并二值化處理
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
(ret, thresh) = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
(contours, hierarchy) = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 獲取直邊界矩形
x,y,w,h = cv2.boundingRect(contours[0])
print(x,y,w,h)
# 繪制直邊界矩形
draw = cv2.rectangle(draw, (x, y), (x+w, y+h), (0, 255, 0), 2)
# 獲取旋轉(zhuǎn)邊界矩形 獲取Box2D結(jié)構(gòu) (center(x,y), (width, height), angle of rotation)
box2d = cv2.minAreaRect(contours[0])
print(box2d)
# 獲取Box2D矩形4個(gè)角點(diǎn)坐標(biāo),[[x1, y1],[x2, y2], [x3, y3], [x4, y4]]
boxpoints = cv2.boxPoints(box2d)
print(boxpoints)
boxpoints = np.int32(boxpoints)
print(boxpoints[0])
# 畫(huà) 旋轉(zhuǎn)邊界矩形4個(gè)角點(diǎn)
draw = cv2.circle(draw, tuple(boxpoints[0]) , 3, (255, 0, 0), 2)
draw = cv2.circle(draw, tuple(boxpoints[1]) , 3, (255, 0, 0), 2)
draw = cv2.circle(draw, tuple(boxpoints[2]) , 3, (255, 0, 0), 2)
draw = cv2.circle(draw, tuple(boxpoints[3]) , 3, (255, 0, 0), 2)
# 畫(huà) 旋轉(zhuǎn)邊界矩形 矩形框
draw = cv2.line(draw, tuple(boxpoints[0]), tuple(boxpoints[1]), (0, 0, 255), 2)
draw = cv2.line(draw, tuple(boxpoints[1]), tuple(boxpoints[2]), (0, 0, 255), 2)
draw = cv2.line(draw, tuple(boxpoints[2]), tuple(boxpoints[3]), (0, 0, 255), 2)
draw = cv2.line(draw, tuple(boxpoints[3]), tuple(boxpoints[0]), (0, 0, 255), 2)
plt.subplot(131), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title('original')
plt.subplot(132), plt.imshow(cv2.cvtColor(gray, cv2.COLOR_BGR2RGB)), plt.title('gray')
plt.subplot(133), plt.imshow(cv2.cvtColor(draw, cv2.COLOR_BGR2RGB)), plt.title('draw')
plt.show()
下圖"draw"標(biāo)題的圖片綠色矩形是輪廓的直邊界矩形,紅色矩形是輪廓的旋轉(zhuǎn)邊界矩形(最小面積矩形),藍(lán)色的4個(gè)點(diǎn)是旋轉(zhuǎn)邊界矩形的四個(gè)角點(diǎn):
最小外接圓
- 能包括對(duì)象所有的圓中面積最小的一個(gè)。
- cv2.minEnclosingCircle() 獲取最小外接圓
(x,y),radius = cv2.minEnclosingCircle(cnt)
import numpy as np
import cv2
from matplotlib import pyplot as plt
# 讀取圖像
img = cv2.imread('./resource/opencv/image/boundrect.jpg', cv2.IMREAD_COLOR)
# 轉(zhuǎn)為灰度并二值化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
(ret, thresh) = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 查找對(duì)象輪廓
(contours, hierarchy) = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 根據(jù)輪廓查找對(duì)象最小外接圓
(x,y), radius = cv2.minEnclosingCircle(contours[0])
print((x,y), radius)
# 畫(huà) 對(duì)象最小外接圓
img = cv2.circle(img, (int(x), int(y)), int(radius), (0, 0, 255), 1)
# 顯示圖像
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
最小外接三角
- 能包括對(duì)象所有的三角形中面積最小的一個(gè)。
- cv2.minEnclosingTriangle():返回 area, [[[x1, y1]],[[x2, y2]], [[x3, y3]]]
area, triangle = cv2.minEnclosingTriangle(points)
import numpy as np
import cv2
# 讀取圖像
img = cv2.imread('./resource/opencv/image/boundrect.jpg', cv2.IMREAD_COLOR)
# 轉(zhuǎn)為灰度并做二值化處理
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
(ret, thresh) = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 查找輪廓
(contours, hierarchy) = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 根據(jù)輪廓獲取對(duì)象的最小外接三角, area, [[[x1, y1]],[[x2, y2]], [[x3, y3]]]
area, triangle = cv2.minEnclosingTriangle(contours[0])
print(area)
print(triangle)
# 最小外接三角的3個(gè)角點(diǎn)坐標(biāo)轉(zhuǎn)為整型(獲取到的是浮點(diǎn)型,不能用于圖形繪制)
triangle = np.int32(triangle)
print(triangle[0][0])
print(triangle[1][0])
print(triangle[2][0])
# 繪制最小外接三角
img = cv2.line(img, tuple(triangle[0][0]), tuple(triangle[1][0]), (0, 0, 255), 2)
img = cv2.line(img, tuple(triangle[1][0]), tuple(triangle[2][0]), (0, 0, 255), 2)
img = cv2.line(img, tuple(triangle[2][0]), tuple(triangle[0][0]), (0, 0, 255), 2)
# 顯示圖像
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
橢圓擬合
- cv2.ellipse(),返回值其實(shí)就是旋轉(zhuǎn)邊界矩形的內(nèi)切圓
import numpy as np
import cv2
img = cv2.imread('./resource/opencv/image/boundrect.jpg', cv2.IMREAD_COLOR)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
(ret, thresh) = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
(contuors, hierarchy) = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
ellipse1 = cv2.fitEllipse(contuors[0])
ellipse2 = cv2.fitEllipseAMS(contuors[0])
ellipse3 = cv2.fitEllipseDirect(contuors[0])
print(ellipse1)
print(ellipse2)
print(ellipse3)
img = cv2.ellipse(img, ellipse1, (255, 0, 0), 3)
img = cv2.ellipse(img, ellipse2, (0, 255, 0), 2)
img = cv2.ellipse(img, ellipse3, (0, 0, 255), 1)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
直線擬合
- 根據(jù)一組點(diǎn)擬合出一條直線,同樣也可以為圖像中的白色點(diǎn)擬合出一條直線。
import numpy as np
import cv2
img = cv2.imread('./resource/opencv/image/boundrect.jpg', cv2.IMREAD_COLOR)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
(ret, thresh) = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
(contuors, hierarchy) = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 獲取并繪制 直線擬合,直接繪制獲取的坐標(biāo)只有一半的線段
L = cv2.fitLine(contuors[0], cv2.DIST_L2, 0, 0.01, 0.01)
L = np.int32(L)
img = cv2.line(img, (L[0][0], L[1][0]), (L[2][0], L[3][0]), (0, 0, 255), 2)
# 獲取并繪制 計(jì)算出整幅圖的直線擬合線
rows, cols = img.shape[:2]
[vx,vy,x,y] = cv2.fitLine(contuors[0], 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)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
輪廓的性質(zhì)
長(zhǎng)寬比
邊界矩形的長(zhǎng)寬比:
A
s
p
e
c
t
R
a
t
i
o
n
=
W
i
d
t
h
H
e
i
g
h
t
Aspect Ration = \frac{Width}{Height}
AspectRation=HeightWidth?
x,y,w,h = cv2.boundingRect(points)
aspect_ratio = float(w)/h
輪廓面積與邊界矩形面積的比(Extent)
輪廓面積與邊界矩形面積的比:
E
x
t
e
n
t
=
O
b
j
e
c
t
A
r
e
a
B
o
u
n
d
i
n
g
R
e
c
t
a
n
g
l
e
A
r
e
a
Extent = \frac{Object Area}{Bounding Rectangle Area}
Extent=BoundingRectangleAreaObjectArea?
area = cv2.contourArea(cnt)
x,y,w,h = cv2.boundingRect(cnt)
rect_area = w*h
extent = float(area)/rect_area
輪廓面積與凸包面積的比(Solidity)
輪廓面積與凸包面積的比:
S
o
l
i
d
i
t
y
=
C
o
n
t
o
u
r
A
r
e
a
C
o
n
v
e
x
H
u
l
l
A
r
e
a
Solidity= \frac{Contour Area}{Convex Hull Area}
Solidity=ConvexHullAreaContourArea?
area = cv2.contourArea(cnt)
hull = cv2.convexHull(cnt)
hull_area = cv2.contourArea(hull)
solidity = float(area)/hull_area
與輪廓面積相等的圓形的直徑(Equivalent Diameter)
與輪廓面積相等的圓形的直徑:
E
q
u
i
v
a
l
e
n
t
D
i
a
m
e
t
e
r
=
4
?
C
o
n
t
o
u
r
A
r
e
a
π
Equivalent Diameter = \sqrt{\frac{4*Contour Area}{\pi}}
EquivalentDiameter=π4?ContourArea??
area = cv2.contourArea(cnt)
equi_diameter = np.sqrt(4*area/np.pi)
輪廓對(duì)象的方向
- (x,y),(MA,ma),angle = cv2.fitEllipse(cnt)
- 返回中心坐標(biāo),長(zhǎng)軸和短軸的長(zhǎng)度,對(duì)象的方向。
輪廓的掩模和像素點(diǎn)
有時(shí)我們需要構(gòu)成對(duì)象的所有像素點(diǎn):
mask = np.zeros(imgray.shape,np.uint8)
這里一定要使用參數(shù)-1, 繪制填充的的輪廓
cv2.drawContours(mask,[cnt],0,255,-1)
pixelpoints = np.transpose(np.nonzero(mask))
或
pixelpoints = cv2.findNonZero(mask)
最大值和最小值及它們的位置
可以使用掩模圖像得到這些參數(shù):
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(imgray,mask = mask)
平均顏色及平均灰度
可以使用相同的掩模求一個(gè)對(duì)象的平均顏色或平均灰度:
mean_val = cv2.mean(im,mask = mask)
對(duì)象輪廓的極點(diǎn)
- 一個(gè)對(duì)象最上面,最下面,最左邊,最右邊的點(diǎn)。
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])
輪廓的凸缺陷
- cv.convexityDefect()以幫助我們找到凸缺陷。函數(shù)調(diào)用如下:
hull = cv2.convexHull(cnt,returnPoints = False)
defects = cv2.convexityDefects(cnt,hull)
它會(huì)返回一個(gè)數(shù)組,其中每一行包含的值是 [起點(diǎn),終點(diǎn),最遠(yuǎn)的點(diǎn),到最遠(yuǎn)點(diǎn)的近似距離]。我們可以在一張圖上顯示它。我們將起點(diǎn)和終點(diǎn)用一條綠線連接,在最遠(yuǎn)點(diǎn)畫(huà)一個(gè)圓圈,要記住的是返回結(jié)果的前三個(gè)值是輪廓點(diǎn)的索引。所以我們還要到輪廓點(diǎn)中去找它們。
import numpy as np
import cv2
img = cv2.imread('./resource/opencv/image/shape.jpg', cv2.IMREAD_COLOR)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
(ret, thresh) = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
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()
點(diǎn)到輪廓的最短距離
求解一個(gè)點(diǎn)到輪廓的最短距離:
- dist = cv2.pointPolygonTest(contours[0], (20, 20), True)
measureDist=True,返回結(jié)果:
小于零:點(diǎn)在輪廓外部。
等于零:點(diǎn)在輪廓線上。
大于零:點(diǎn)在輪廓內(nèi)部。 - stat = cv2.pointPolygonTest(contours[0], (20, 20), False)
measureDist=False,返回結(jié)果:
-1:點(diǎn)在輪廓外部
0:點(diǎn)在輪廓線上。
1:點(diǎn)在輪廓內(nèi)部。
import numpy as np
import cv2
img = cv2.imread('./resource/opencv/image/shape.jpg', cv2.IMREAD_COLOR)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
(ret, thresh) = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
(contours, hierarchy) = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 求解圖像中的一個(gè)點(diǎn)到一個(gè)對(duì)象輪廓的最短距離。
# 小于零:點(diǎn)在輪廓外部。
# 等于零:點(diǎn)在輪廓線上。
# 大于零:點(diǎn)在輪廓內(nèi)部。
dist = cv2.pointPolygonTest(contours[0], (20, 20), True)
#-1:點(diǎn)在輪廓外部
# 0:點(diǎn)在輪廓線上。
# 1:點(diǎn)在輪廓內(nèi)部。
stat = cv2.pointPolygonTest(contours[0], (20, 20), False)
print(dist)
print(stat)
形狀匹配
- match = cv2.matchShapes(cntA, cntB, cv2.CONTOURS_MATCH_I1, 0.0)
返回值越小表示兩個(gè)圖形越相似。
有3中匹配方法:
- cv2.CONTOURS_MATCH_I1
- cv2.CONTOURS_MATCH_I2
- cv2.CONTOURS_MATCH_I3
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('./resource/opencv/image/matchshapesA.jpg', cv2.IMREAD_COLOR)
imgA = cv2.imread('./resource/opencv/image/matchshapesA.jpg', cv2.IMREAD_COLOR)
imgB = cv2.imread('./resource/opencv/image/matchshapesB.jpg', cv2.IMREAD_COLOR)
imgC = cv2.imread('./resource/opencv/image/matchshapesC.jpg', cv2.IMREAD_COLOR)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
grayA = cv2.cvtColor(imgA, cv2.COLOR_BGR2GRAY)
grayB = cv2.cvtColor(imgB, cv2.COLOR_BGR2GRAY)
grayC = cv2.cvtColor(imgC, cv2.COLOR_BGR2GRAY)
(ret, th) = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
(retA, thA) = cv2.threshold(grayA, 127, 255, cv2.THRESH_BINARY)
(retB, thB) = cv2.threshold(grayB, 127, 255, cv2.THRESH_BINARY)
(retC, thC) = cv2.threshold(grayC, 127, 255, cv2.THRESH_BINARY)
(contours, hierarchy) = cv2.findContours(th, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
(contoursA, hierarchyA) = cv2.findContours(thA, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
(contoursB, hierarchyB) = cv2.findContours(thB, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
(contoursC, hierarchyC) = cv2.findContours(thC, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnt = contours[0]
cntA = contoursA[0]
cntB = contoursB[0]
cntC = contoursC[0]
m1 = cv2.matchShapes(cnt, cntA, cv2.CONTOURS_MATCH_I1, 0.0)
m2 = cv2.matchShapes(cnt, cntB, cv2.CONTOURS_MATCH_I1, 0.0)
m3 = cv2.matchShapes(cnt, cntC, cv2.CONTOURS_MATCH_I1, 0.0)
# 程序運(yùn)行結(jié)果
print(m1) # A與A的匹配度:0.0
print(m2) # A與B的匹配度:0.010547834665352251
print(m3) # A與C的匹配度:0.3313932685758914
從結(jié)果可以看出兩個(gè)圖形越相似值就越小,計(jì)算結(jié)果和圖形的旋轉(zhuǎn)相關(guān)性小,和圖形的形狀差異相關(guān)性大:
輪廓的層次結(jié)構(gòu)
(contours, hierarchy) = cv2.findContours(th, mode, method)
輪廓的檢索模式mode有如下幾種:
-
cv2.RETR_LIST:只是提取所有的輪廓,而不去創(chuàng)建任何父子關(guān)系。換句話說(shuō)就是“人人平等”,所有輪廓屬于同一級(jí)組織輪廓。
-
cv2.RETR_TREE:返回所有輪廓,并且創(chuàng)建一個(gè)完整的組織結(jié)構(gòu)列表。它甚至?xí)嬖V你誰(shuí)是爺爺,爸爸,兒子,孫子等。
-
cv2.RETR_CCOMP:返回所有的輪廓并將輪廓分為兩級(jí)組織結(jié)構(gòu)。
-
cv2.RETR_EXTERNAL:只會(huì)返回最外邊的的輪廓,所有的子輪廓都會(huì)被忽略掉。
-
不管層次結(jié)構(gòu)是什么樣的,每一個(gè)輪廓都包含自己的信息:誰(shuí)是父,誰(shuí)是子等。 OpenCV 使用一個(gè)含有四個(gè)元素的數(shù)組表示。 [Next, Previous,F(xiàn)irst_Child, Parent]。
-
Next 表示同一級(jí)組織結(jié)構(gòu)中的下一個(gè)輪廓。以下圖中的輪廓 0 為例,輪廓 1 就是他的 Next。同樣,輪廓 1 的 Next是 2, Next=2。那輪廓 2 呢?在同一級(jí)沒(méi)有 Next。這時(shí) Next=-1。而輪廓 4 的 Next為 5,所以它的 Next=5。
-
Previous 表示同一級(jí)結(jié)構(gòu)中的前一個(gè)輪廓。與前面一樣,輪廓 1 的 Previous 為輪廓 0,輪廓 2 的 Previous 為輪廓 1。輪廓 0 沒(méi)有 Previous,所以 Previous=-1。
-
First_Child 表示它的第一個(gè)子輪廓。沒(méi)有必要再解釋了,輪廓 2 的子輪廓為 2a。所以它的 First_Child 為2a。那輪廓 3a 呢?它有兩個(gè)子輪廓。但是我們只要第一個(gè)子輪廓,所以是輪廓 4(按照從上往下,從左往右的順序排序)。
-
Parent 表示它的父輪廓。與 First_Child 剛好相反。輪廓 4 和 5 的父輪廓是輪廓 3a。而輪廓 3a的父輪廓是 3。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-645466.html
輪廓的層次結(jié)構(gòu),比如輪廓之間的父子關(guān)系:
在這幅圖像中,給這幾個(gè)形狀編號(hào)為 0-5。 2 和 2a 分別代表最外邊矩形
的外輪廓和內(nèi)輪廓。在這里邊輪廓 0, 1, 2 在外部或最外邊。我們可以稱他們?yōu)椋ńM織結(jié)構(gòu))0 級(jí),簡(jiǎn)單來(lái)說(shuō)就是他們屬于同一級(jí)。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-645466.html
到了這里,關(guān)于Python-OpenCV中的圖像處理-圖像輪廓的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!