17 圖像輪廓特征查找
圖像輪廓特征查找其實就是他的外接輪廓。
應用:
????????圖像分割
????????形狀分析
????????物體檢測與識別
根據輪廓點進行,所以要先找到輪廓。
先灰度化、二值化。目標物體白色,非目標物體黑色,選擇合適的兒值化方式。
有了輪廓點就可以找到最上、最下、最左、最右的四個坐標,X_{min}、X_{max}、Y_{min}、Y_{max}。就可以繪制出矩形。
17.1 外接矩形
boundingRect(輪廓點)
形狀的外接矩形有兩種,如下圖,綠色的叫外接矩形,表示不考慮旋轉并且能包含整個輪廓的矩形。其中,外接矩形可根據獲得到的輪廓坐標中最上、最下、最左、最右的點的坐標來繪制外接矩形,也就是下圖中的綠色矩形。藍色的是最小外接矩形,會考慮面積。
cv.boundingRect
函數用于計算一組點(通常是輪廓)的最小外接矩形。這個矩形是一個 軸對齊的矩形,也就是說,它的邊與圖像坐標軸平行。
作用
快速獲取一個能夠包含目標輪廓的矩形邊界。
簡單直觀地定位目標輪廓的大致區域。
返回參數
x
:外接矩形左上角的 x 坐標。
y
:外接矩形左上角的 y 坐標。
w
:外接矩形的寬度。
h
:外接矩形的高度。
import cv2 as cv
import numpy as np
img = cv.imread('images/ikun4.jpg')
#灰度圖
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
#二值化處理,閾值法 因為目標是白色我們需要白色
ret, binary = cv.threshold(gray, 200, 255, cv.THRESH_BINARY_INV)
#查找輪廓
contours, hierarchy = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
for i in range(len(contours)):#查找凸包hull = cv.convexHull(contours[i])#繪制凸包cv.polylines(img, [hull], True, (100, 100, 255), 3)
#畫出外接矩形
for i in range(len(contours)):x, y, w, h = cv.boundingRect(contours[i])cv.rectangle(img, (x, y), (x + w, y + h), (255, 255, 0), 2)
cv.imshow('1', img)
# cv.imshow('2', binary)
cv.waitKey(0)
cv.destroyAllWindows()
獲取方式
在代碼中,通過循環遍歷查找到的輪廓
contours
,對每個輪廓調用cv.boundingRect
函數來獲取其外接矩形的位置和大小參數,包括左上角頂點坐標(x,y)、寬度 w 和高度 h。繪制目的
使用
cv.rectangle
函數在原圖上繪制出這些外接矩形,通過矩形的邊界將目標輪廓大致框選出來,便于更直觀地觀察和分析目標在圖像中的分布情況,以及與其他圖像元素的相對位置關系。
17.2 最小外接矩形
定義與原理
最小外接矩形 是能夠完全包含目標輪廓的最小面積的矩形,它允許矩形相對于圖像坐標軸旋轉,因此可以更好地貼合輪廓的形狀,相比普通外接矩形(軸對齊的外接矩形),它可以更精確地描述輪廓的形狀和方向。
其基本原理是通過計算輪廓點集的最小面積矩形,這個矩形的邊不一定與圖像坐標軸對齊。它需要找到一組四個點,使得這四個點構成的矩形能夠包含所有輪廓點,并且面積是所有可能的包含該輪廓的矩形中最小的。
計算方法
從輪廓點集出發,利用幾何算法來確定最小外接矩形的邊界。
一種常見的方法是基于旋轉卡殼(Rotating Calipers)算法,它通過旋轉兩條平行的切線來尋找能夠包含整個點集的最小矩形。具體步驟為:首先找到點集的凸包;然后在凸包上選擇兩個點作為初始兩個的頂點,這兩點確定一條邊;接著,尋找與該邊相對的另一條邊,使得這兩條邊之間的距離最大,從而形成一個初始的矩形;最后,逐步旋轉這兩條邊,找到使矩形面積最小的那一對邊,從而確定最小外接矩形。
在 OpenCV 中,可以通過 cv.minAreaRect
函數獲取輪廓的最小外接矩形,該函數返回一個包含中心坐標、長寬以及旋轉角度的元組,利用這些參數可以繪制出最小外接矩形。
需要使用到的API說明:
contours, hierarchy = cv2.findContours(image_np_thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours為二值圖像上查找所有的外部輪廓
rect = cv2.minAreaRect(cnt)
傳入的cnt參數為contours中的輪廓,可以遍歷contours中的所有輪廓,然后計算出每個輪廓的小面積外接矩形
rect 是計算輪廓最小面積外接矩形:rect 結構通常包含中心點坐標 (x, y)
、寬度 width
、高度 height
和旋轉角度 angle
cv2.boxPoints(rect).astype(int)
cv2.boxPoints(rect)返回 是一個形狀為 4行2列的數組,每一行代表一個點的坐標(x, y),順序按照逆時針或順時針方向排列
將最小外接矩形轉換為邊界框的四個角點,并轉換為整數坐標
cv2.drawContours(image, contours, contourIdx, color, thickness)
image:原圖像,一般為 numpy 數組,通常為灰度或彩色圖像。
contours:一個包含多個輪廓的列表,可以用上一個api得到的 [box]
contourIdx:要繪制的輪廓索引。如果設置為 -1
,則繪制所有輪廓。
color:輪廓的顏色,可以是 BGR 顏色格式的三元組,例如 (0, 0, 255)
表示紅色。
thickness:輪廓線的粗細,如果是正數,則繪制實線;如果是 0,則繪制輪廓點;如果是負數,則填充輪廓內部區域。
#最小外接矩形 旋轉卡殼法
#讀取圖像
img = cv.imread('images/feiwu.png')
gary = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
#二值化
ret, binary = cv.threshold(gary, 200, 255, cv.THRESH_BINARY_INV)
#查找輪廓
contours, hierarchy = cv.findContours(binary, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
for i in range(len(contours)):#最小外接矩形 rect = (center(x,y), (width, height), angle)rect = cv.minAreaRect(contours[i])box = cv.boxPoints(rect).astype(np.int32)cv.drawContours(img, [box], -1, (0, 0, 255), 2,cv.LINE_AA)
#畫出外接矩形
for i in range(len(contours)):x, y, w, h = cv.boundingRect(contours[i])cv.rectangle(img, (x, y), (x + w, y + h), (0, 0, 0), 2)
cv.imshow("1", img)
cv.waitKey(0)
cv.destroyAllWindows()
繪制外接矩形和最小外接矩形是有區別的
17.3 最小外接圓
尋找最小外接圓使用的算法是Welzl算法。Welzl算法基于一個定理:希爾伯特圓定理表明,對于平面上的任意三個不在同一直線上的點,存在一個唯一的圓同時通過這三個點,且該圓是最小面積的圓(即包含這三個點的圓中半徑最小的圓,也稱為最小覆蓋圓)。
進一步推廣到任意 n 個不在同一圓上的點,總存在一個唯一的最小覆蓋圓包含這 n 個點。
若已經存在平面上互不共線(或共圓)的 n 個點,并確定了它們的最小覆蓋圓,那么添加第 n+1 個點,并且要求這個點不在原來的最小覆蓋圓內(即在圓外),為了使新的包含 n+1 個點的最小覆蓋圓的半徑增大,新加入的點必須位于由原 n 個點確定的最小覆蓋圓的邊界上(即圓周上)。這是因為,如果新點在原最小覆蓋圓的內部,顯然不會影響最小覆蓋圓;如果新點在原最小覆蓋圓之外但不在圓周上,那么通過新點和至少兩個原有圓上的點可以構造出一個更大的圓,這個圓必然比原最小覆蓋圓更大,因此不是包含所有 n+1 個點的最小覆蓋圓。所以,按照這一邏輯,當第 n+1 個點在原 n 個點的最小覆蓋圓外時,確實這個點會位于包含所有 n+1 個點的新最小覆蓋圓的圓周上。
有了這個定理,就可以先取3個點建立一個圓(不共線的三個點即可確定一個圓,如果共線就取距離最遠的兩個點作為直徑建立圓),然后遍歷剩下的所有點,對于遍歷到的點P來說:
如果該點在圓內,那么最小覆蓋圓不變。
如果該點在圓外,根據上述定理,該點一定在想要求得的最小覆蓋圓的圓周上,又因為三個點才能確定一個圓,所以需要枚舉P點之前的點來找其余的兩個點。當找到與P點組成的圓能夠將所有點都包含在圓內或圓上,該圓就是這些點的最小外接圓。
在OpenCV中,可以直接使用cv2.minEnclosingCircle()來獲取最小外接圓,該函數只需要輸入一個參數,就是要繪制最小外接圓的點集的坐標,然后會返回最小外接圓的圓心坐標與半徑。通過該函數返回的內容信息即可繪制某點集的最小外接圓。如下圖所示:
需要使用的API說明
cv2.minEnclosingCircle(points) -> (center, radius)
參數說明:
points
:輸入參數圖片輪廓數據
返回值:
center
:一個包含圓心坐標的二元組 (x, y)
。
radius
:浮點數類型,表示計算得到的最小覆蓋圓的半徑。
cv2.circle(img, center, radius, color, thickness)
img
:輸入圖像,通常是一個numpy數組,代表要繪制圓形的圖像。
center
:一個二元組 (x, y)
,表示圓心的坐標位置。
radius
:整型或浮點型數值,表示圓的半徑長度。
color
:顏色標識,可以是BGR格式的三元組 (B, G, R)
,例如 (255, 0, 0)
表示紅色。
thickness
:整數,表示圓邊框的寬度。如果設置為 -1
,則會填充整個圓。
?
img = cv.imread('images/haimbb.jpg')
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 100, 255, cv.THRESH_BINARY_INV)
#查找輪廓
contours, hierarchy = cv.findContours(binary, cv.RETR_TREE, cv.CHAIN_APPROX_SIMPLE)
for i in range(len(contours)):#繪制外接圓(x, y), radius = cv.minEnclosingCircle(contours[i])center = (int(x), int(y))radius = int(radius)cv.circle(img, center, radius, (128, 128, 128), 2)#繪制外接矩形rect = cv.minAreaRect(contours[i])box = cv.boxPoints(rect)
cv.imshow("1", img)
cv.imshow("2", binary)
cv.waitKey(0)
cv.destroyAllWindows()