凸包(Convex Hull)
數學定義:在一個向量空間中,對於給定幾何X, 所有包含X的凸集的交集S被稱為X的凸包
場景:
圖像處理過程中,常常需要尋找圖像中,包圍某個物體的凸包。凸包跟多邊形逼近很像,只不過它是包圍
物體外層的一個凸集,這個凸集是所有能包圍這個物體的凸集的交集。
圖中,綠色線條所包圍的凸集,集為白色圖案的凸包。
opencv中,convexhull()能夠得到一系列點的凸包,比如由點組成的輪廓,通過ConvexHull函數,得到凸包。
可以用來做手勢的識別。
幾何圖形
我們用以下的Python代碼來自己繪制一張簡單的多邊形的圖片
import cv2
import numpy as np
# 新建512*512的空白圖片
img = np.zeros((512,512,3), np.uint8)
# 平面點集
pts = np.array([[200,250], [250,300], [300, 270], [270,200], [120, 240]], np.int32)
pts = pts.reshape((-1,1,2))
# 繪制填充的多邊形
cv2.fillPoly(img, [pts], (255,255,255))
# 保存圖片
cv2.imwrite('F://polygon.png', img)
接着我們需要尋找這個多邊形的凸包,利用OpenCV的convexHull函數,然后再將這個凸包繪制出來,得到直觀的展示結果。
import cv2
# 讀取圖片並轉至灰度模式
imagepath = 'F://convex.png'
img = cv2.imread(imagepath, 1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值化
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 圖片輪廓
image, contours, hierarchy = cv2.findContours(thresh, 2, 1)
cnt = contours[0]
# 尋找凸包並繪制凸包(輪廓)
hull = cv2.convexHull(cnt)
print(hull)
length = len(hull)
for i in range(len(hull)):
cv2.line(img, tuple(hull[i][0]), tuple(hull[(i+1)%length][0]), (0,255,0), 2)
# 顯示圖片
cv2.imshow('line', img)
cv2.waitKey()
結果:
[[[300 270]]
[[299 271]]
[[254 298]]
[[250 300]]
[[120 240]]
[[122 239]]
[[257 203]]
[[269 200]]
[[270 200]]
[[273 206]]
[[300 269]]]
手勢識別
我們將介紹一張稍微難一點的圖片——手勢圖片
我們將會來尋找這個手勢的凸包。基本的處理思路還是和之前的一致,只是要在二值化以及凸包點集集合的大小上做一些處理,
取二值化的閾值為235,凸包點集中的點個數大於5
import cv2
# 讀取圖片並轉至灰度模式
imagepath = 'F://finger.jpg'
img = cv2.imread(imagepath, 1)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值化,取閾值為235
ret, thresh = cv2.threshold(gray, 235, 255, cv2.THRESH_BINARY)
# 尋找圖像中的輪廓
image, contours, hierarchy = cv2.findContours(thresh, 2, 1)
# 尋找物體的凸包並繪制凸包的輪廓
for cnt in contours:
hull = cv2.convexHull(cnt)
length = len(hull)
# 如果凸包點集中的點個數大於5
if length > 5:
# 繪制圖像凸包的輪廓
for i in range(length):
cv2.line(img, tuple(hull[i][0]), tuple(hull[(i+1)%length][0]), (0,0,255), 2)
cv2.imshow('finger', img)
cv2.waitKey()
檢測到的凸包如下圖所示
可以發現,一共檢測到2個凸包,一個是整個手勢外圍的凸包,正好包圍整個手,另一個是兩個手指形成的內部的圖形,
類似於O的凸包,這符合我們的預期