OpenCV(三十六):多边形逼近与凸包

在图像处理与计算机视觉中,轮廓形状的简化与包围区域的计算是常用操作。OpenCV 提供了两个核心函数帮助我们实现这些功能:

  • 多边形逼近(Polygon Approximation, cv2.approxPolyDP
  • 凸包(Convex Hull, cv2.convexHull

二者点集输入通常来自 cv2.findContours 提取的轮廓,但使用目的和几何意义不同:多边形逼近侧重"简化形状",凸包则是"计算最小凸多边形包围区域"。

多边形逼近(approxPolyDP)

多边形逼近用于在保持轮廓形状基本特征的前提下用更少的顶点表示轮廓 。其核心算法为 Ramer--Douglas--Peucker(RDP)算法,目标是减少不必要的点,从而得到更加紧凑的多边形表示。

RDP 算法核心思想

给定一条由若干离散点组成的折线:

  1. 连接首尾两点形成一条直线作为基准;
  2. 找出所有点中 偏离该直线距离最大的点
  3. 若最大距离 > 给定阈值 ε(epsilon),则说明折线在该点处发生显著弯曲:
    • 将折线分成两段,递归处理;
  4. 若最大距离 ≤ ε,则说明所有点几乎在同一条直线上,可以直接用首尾点替代中间点。

最终即可得到一个近似该轮廓的较低复杂度折线(多边形)。

ε(epsilon)参数的意义

OpenCV 中的 epsilon逼近精度(容差),常设为周长的某个比例:

python 复制代码
epsilon = 0.01 * cv2.arcLength(cnt, True)

epsilon 越大:

顶点更少,形状更粗略

epsilon 越小:

保留更多细节,顶点更多

多边形逼近的应用场景

  • 判断几何形状:三角形、四边形、五边形等;
  • 简化物体外轮廓,用于目标识别;
  • 动态检测手势(手部轮廓简化);
  • 降低计算量(减少点集规模);
  • 针对复杂边缘进行平滑处理。

示例

python 复制代码
import cv2
import numpy as np

img = cv2.imread("shape.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

for cnt in contours:
    epsilon = 0.02 * cv2.arcLength(cnt, True)
    approx = cv2.approxPolyDP(cnt, epsilon, True)
    
    # 绘制逼近多边形
    cv2.drawContours(img, [approx], -1, (0, 0, 255), 2)

cv2.imshow("Polygon Approx", img)
cv2.waitKey(0)

执行效果:

凸包(Convex Hull)

凸包(Convex Hull)是指 用最少的点构成一个凸多边形,使所有点都在该多边形内或边界上

常见比喻:

把钉子钉在木板上,然后用橡皮筋套住所有钉子,最终橡皮筋形成的轮廓就是凸包。

凸包的数学定义

给定一个点集 P,凸包 H§ 是一个满足:

  • H§ 是凸集;
  • P ⊆ H§;
  • H§ 是包含 P 的所有凸集中的最小一个。

OpenCV 默认使用 Graham Scan 或 Andrew's Monotone Chain 算法

凸包算法一般复杂度 O(n log n),核心思想:

  1. 按 x 坐标(次按 y 坐标)排序;
  2. 构建上包络(upper hull)和下包络(lower hull);
  3. 使用几何叉积判断"左转/右转"决定保留或舍弃某些点;
  4. 合并上下包络形成最终凸包。

凸包的常用用途

  • 检测物体的外接凸边界;
  • 手势识别(检测手指数量、指尖位置);
  • 作为形状特征,计算形状紧致度/凸度;
  • 碰撞检测、物理模拟中的包围体。

示例

python 复制代码
import cv2
import numpy as np

img = cv2.imread("shape.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

for cnt in contours:
    hull = cv2.convexHull(cnt)
    
    # 绘制凸包
    cv2.drawContours(img, [hull], -1, (255, 0, 0), 2)

cv2.imshow("Convex Hull", img)
cv2.waitKey(0)

执行效果:

综合示例

python 复制代码
import cv2
import numpy as np

# -------------------------
# Step 1: 图像读取与预处理
# -------------------------
img = cv2.imread("hand.png")                         # 输入图像
orig = img.copy()
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (7, 7), 0)
_, thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)

# 查找轮廓
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnt = max(contours, key=cv2.contourArea)             # 取最大轮廓(手势或主要目标)


# -------------------------
# Step 2: 多边形逼近 approxPolyDP
# -------------------------
epsilon = 0.01 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)


# -------------------------
# Step 3: 凸包 convexHull
# -------------------------
# returnPoints=True 返回点坐标(用于绘图)
hull_pts = cv2.convexHull(cnt)

# returnPoints=False 返回索引(用于凸缺陷)
hull_idx = cv2.convexHull(cnt, returnPoints=False)


# -------------------------
# Step 4: 凸缺陷 convexityDefects
# -------------------------
defects = cv2.convexityDefects(cnt, hull_idx)


# -------------------------
# Step 5: 绘制结果
# -------------------------

# ① 绘制原轮廓
cv2.drawContours(orig, [cnt], -1, (0, 255, 0), 2)

# ② 绘制多边形逼近(红色)
cv2.drawContours(orig, [approx], -1, (0, 0, 255), 2)

# ③ 绘制凸包(蓝色)
cv2.drawContours(orig, [hull_pts], -1, (255, 0, 0), 2)

# ④ 绘制凸缺陷点(绿色)
if defects is not None:
    for i in range(defects.shape[0]):
        s, e, f, depth = defects[i, 0]
        start = tuple(cnt[s][0])
        end   = tuple(cnt[e][0])
        far   = tuple(cnt[f][0])      # 凸缺陷最深点(凹陷点)
        
        cv2.circle(orig, start, 6, (0, 255, 255), -1)  # 起点(指节)
        cv2.circle(orig, end, 6, (0, 255, 255), -1)    # 终点
        cv2.circle(orig, far, 6, (0, 255, 0), -1)      # 凹陷处(指缝)


# -------------------------
# Step 6: 显示结果
# -------------------------
cv2.imshow("Threshold", thresh)
cv2.imshow("Contour Analysis", orig)
cv2.waitKey(0)
cv2.destroyAllWindows()

执行效果:

总结

对比项 多边形逼近(approxPolyDP) 凸包(convexHull)
核心功能 轮廓简化 计算最小凸包围区域
输出 近似原轮廓的多边形 覆盖所有点的最小凸多边形
是否保持原形状凹陷 否(会"撑平"所有凹陷)
是否保持点顺序 是(可返回索引)
顶点数量 取决于 epsilon 通常较少
常用场景 边缘简化、检测三角/矩形等形状 手势分析、碰撞检测、几何包围
相关推荐
Medeaselab5 分钟前
生理学实验之神经放电模块的应用
人工智能·功能测试·需求分析
武子康8 分钟前
调查研究-219 GPU 为什么能快,也为什么容易被大量小请求拖慢?
人工智能·llm·gpu
Drgfd9 分钟前
多模态大模型技术深耕:从模态融合到跨域迁移,重构AI理解与生成能力
人工智能·深度学习·计算机视觉
HhzZzzzz_10 分钟前
萨科微slkor2026年7月6日“每日芯闻”。国际芯闻:
人工智能·智能手机·编辑器
爱奥尼欧12 分钟前
【LangChain】2.提示词工程
人工智能·microsoft·langchain
博图光电16 分钟前
博图光电鲜奶字符缺陷检测方案
人工智能·视觉检测·制造
IT_陈寒18 分钟前
React useEffect里面写异步,我的状态怎么老丢?
前端·人工智能·后端
敲不会代码也学不会英语31 分钟前
【AI学习之旅02】AI全景图:一张图看懂AI生态——机器学习/深度学习/大模型关系与术语扫盲
人工智能·学习·机器学习
软件技术新观察32 分钟前
2026年月北京AI智能体软件定制开发头部企业综合实力测评报告
人工智能
水龙吟啸34 分钟前
华为2026.6.24机考选择题+编程题【速刷敲黑板】
人工智能·深度学习·算法·机器学习·华为