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 通常较少
常用场景 边缘简化、检测三角/矩形等形状 手势分析、碰撞检测、几何包围
相关推荐
AI医影跨模态组学13 分钟前
如何将纵向MRI深度学习特征与局部晚期直肠癌新辅助放化疗后的免疫微环境建立关联,并解释其对pCR及预后的机制
人工智能·深度学习·论文·医学·医学影像·影像组学
Empty-Filled14 分钟前
AI生成测试用例功能怎么测:一个完整实战案例
网络·人工智能·测试用例
eastyuxiao14 分钟前
设计一个基于 OpenClaw 的 AI 智能体来辅助交易
人工智能
波动几何44 分钟前
因果动力学架构技能cda
人工智能
Lucas_coding1 小时前
【Claude Code Router】 Claude Code 兼容 OpenAI 格式 API, Claude code 接入本地部署模型
人工智能·python
jinanwuhuaguo1 小时前
(第二十七篇)OpenClaw四月的演化风暴:OpenClaw 2026年4月全版本更新的文明级解读
大数据·人工智能·架构·kotlin·openclaw
测试员周周1 小时前
【AI测试系统】第5篇:从 Archon 看 AI 工程化落地:为什么"确定性编排+AI 弹性智能"是终局?
人工智能·python·测试
RxGc1 小时前
微软AI Agent框架深度测评:Microsoft Agent Framework 1.0 vs OpenClaw/Claude企业级能力对比
人工智能·agent
随便写写1 小时前
第四章 智能体经典范式构建
人工智能
穿过生命散发芬芳1 小时前
基于CodeBuddy Agent智能体平台构建自己第一个SKILL——相机推荐
人工智能