图像的凹包特征检测是一种用于提取复杂形状轮廓的技术,它能够更准确地反映物体的实际形状,尤其是当物体具有凹陷部分时。
凸包(Convex Hull)是一个计算机几何图形学中的概念, 简单来说, 给定二维平面点集, 凸包就是能够将最外层的点连接起来构成的凸多边形, 它能够包含点集中所有的点。在凸包中,任意两点之间的连线都不会超出多边形的边界。物体的凸包检测场应用在物体识别、手势识别及边界检测等领域。
例如,一个手的轮廓的凸包会是一个简单的凸多边形,但无法反映手指之间的凹陷部分。

凹包特征检测在以下领域有广泛应用: - 物体识别:能够更准确地描述物体的轮廓,尤其是当物体具有复杂的凹陷部分。 - 手势识别:例如在手势识别中,凹包可以更好地捕捉手指之间的凹陷区域。 - 边界检测:在图像分割和边界检测中,凹包能够提供更精细的边界信息。
在 OpenCV 中,可以使用 cv2.convexHull 函数来计算一个点集的凸包。虽然这直接给出的是凸包,但你可以通过这种方式找到凹包的候选区域。
下面是一个使用Python和OpenCV对图片进行凸包检测的例子。这个示例将涵盖图像读取、灰度转换、Canny边缘检测、轮廓提取、凸包计算和绘制等步骤。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像
img = cv2.imread('./images/jpg/hand04.jpg')
if img is None:
print("Error: 图像读取失败。")
exit()
# 显示原始图像
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title('Original Image')
plt.axis('off')
plt.show()
# 得到灰度图
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 应用Canny边缘检测
edges = cv2.Canny(img_gray, 100, 200)
# 提取轮廓
contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 绘制轮廓
img_contour = img.copy()
cv2.drawContours(img_contour, contours, -1, (0, 255, 0), 2)
# 显示轮廓图像
plt.imshow(cv2.cvtColor(img_contour, cv2.COLOR_BGR2RGB))
plt.title('Contours')
plt.axis('off')
plt.show()
# 凸包检测
hulls = []
for contour in contours:
hull = cv2.convexHull(contour)
hulls.append(hull)
# 绘制凸包
img_convex_hull = img.copy()
cv2.drawContours(img_convex_hull, hulls, -1, (0, 0, 255), 2)
# 显示凸包图像
plt.imshow(cv2.cvtColor(img_convex_hull, cv2.COLOR_BGR2RGB))
plt.title('Convex Hull')
plt.axis('off')
plt.show()

凸包缺陷(Convexity Defects)是指一组点在其凸包和形状之间的偏差部分。换句话说,凸包缺陷是原始图形的轮廓与其凸包之间的差异区域。这些差异区域通常表现为图形中的凹陷部分。
下面是一个使用Python和OpenCV实现图片凸包缺陷检测的例子,读取一个手势图片并且实现手势识别。这个例子将包括图像读取、HSV色彩空间转换、轮廓检测、凸包计算与绘制、凸包缺陷检测等步骤。
import cv2
import os
import numpy as np
# 获取脚本的绝对路径
script_path = os.path.abspath(__file__)
# 获取脚本所在的目录
script_dir = os.path.dirname(script_path)
# 更改当前工作目录到脚本所在的目录
os.chdir(script_dir)
def show_img(img, WindowsName=None):
"""
显示图片在窗口中,并等待用户关闭窗口。
Args:
img (numpy.ndarray): 要显示的图片,类型为numpy数组。
WindowsName (str, optional): 窗口名称。默认为None,此时窗口名默认为'window'。
Returns:
None
"""
cv2.imshow(WindowsName, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
def detect_hand_contours_and_defects(img_path):
"""
检测手部轮廓和缺陷。
Args:
img_path (str): 图片路径。
Returns:
None
"""
img = cv2.imread(img_path)
if img is None:
print(f"Error: 图像 {img_path} 读取失败。")
return
# 显示原始图片
show_img(img, WindowsName="src_img")
# 转换为HSV色彩空间
hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# 提取肤色
lower = np.array([0, 30, 100], dtype="uint8")
upper = np.array([128, 255, 255], dtype="uint8")
# 创建肤色掩膜
skin_mask_hsv = cv2.inRange(hsv_img, lower, upper)
# show_img(skin_mask_hsv, WindowsName="skin_mask")
blurred_skin_mask = cv2.blur(skin_mask_hsv, (2, 2))
# show_img(blurred_skin_mask, WindowsName="skin_mask")
ret, thresh = cv2.threshold(blurred_skin_mask, 0, 255, cv2.THRESH_BINARY)
show_img(thresh, WindowsName="skin_mask")
# 查找轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
if contours:
max_contour = max(contours, key=lambda x: cv2.contourArea(x))
# 绘制最大轮廓
cv2.drawContours(img, [max_contour], -1, (0, 0, 255), 2)
# 在原图上展示轮廓
# show_img(img, WindowsName="max_contours")
# 对轮廓进行凸包检测
hull = cv2.convexHull(max_contour)
cv2.drawContours(img, [hull], -1, (0, 255, 0), 2) # 绘制凸包
show_img(img, WindowsName="hull")
# 凸包缺陷检测
hull_detect = cv2.convexHull(max_contour, returnPoints=False) # 返回凸包缺陷
defects = cv2.convexityDefects(max_contour, hull_detect)
# 如果存在缺陷,则绘制
if defects is not None:
cnt = 0
for i in range(defects.shape[0]):
s, e, f, d = defects[i, 0] # 获取缺陷信息, s, e, f分别表示起始点、终点、最远点, d表示距离
start = tuple(max_contour[s][0])
end = tuple(max_contour[e][0])
far = tuple(max_contour[f][0])
a = np.sqrt((end[0] - start[0]) ** 2 + (end[1] - start[1]) ** 2) # 计算边长
b = np.sqrt((far[0] - start[0]) ** 2 + (far[1] - start[1]) ** 2) # 计算边长
c = np.sqrt((end[0] - far[0]) ** 2 + (end[1] - far[1]) ** 2) # 计算边长
angle = np.arccos((b ** 2 + c ** 2 - a ** 2) / (2 * b * c)) # 计算角度
if angle <= np.pi / 2: # 角度小于90度,认为为手指
cnt += 1
# 绘制手指缺陷相关的图形
cv2.line(img, start, end, [0, 255, 0], 1) # 绘制缺陷的起始和结束点之间的线
cv2.circle(img, far, 5, [255, 0, 0], -1) # 绘制缺陷的远点(蓝色)
# 仅在检测到手指缺陷时,才显示手指数量和绘制相关图形
if cnt > 0:
cnt += 1
cv2.putText(img, str(cnt), (0, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (255, 0, 0), 2,
cv2.LINE_AA) # 在原图上绘制手指数量
show_img(img, WindowsName="defects")
def process_hand_image(img_path):
"""
处理手部图像。
Args:
img_path (str): 图片路径。
Returns:
None
"""
# 读取图片
detect_hand_contours_and_defects(img_path)
if __name__ == '__main__':
# 测试手部图像
img_path = "./images/jpg/hand02.jpg"
process_hand_image(img_path)
