一、凸包是什么?
凸包就是包含某一点集所有点的最小凸多边形,特点是完全凸起、没有凹处。简单说,就像用一根橡皮筋紧紧围住所有点,松手后橡皮筋自然形成的轮廓——所有点要么在轮廓上,要么在轮廓内。
它的核心价值是简化物体形态,保留关键边界特征,常用在物体识别、手势识别、边界检测等场景。
二、4种核心凸包算法
1. 穷举法
- 核心逻辑:把点集中所有点两两配对连线,检查其他所有点是否都在这条直线的同一侧。如果是,这两个配对点就是凸包点。
- 优点:原理简单,不用复杂逻辑;
- 缺点:效率极低,仅适合极少数点的场景(比如少于50个点),实际用得少。
2. Graham扫描法
步骤
- 将纵坐标最小的点记为P0,且以该点为原点构建二维坐标系,那么P0就一定是一个凸包点。
- 计算各个点对于P0的角度,按照从小到大的顺序进行排序(逆时针顺序),当角度相同时,与P0较近的点排在前面。那么角度最大的点和角度最小的点一定是凸包点。
- 用栈来记录已知的凸包点,先将P0和P1放入栈中,然后去求下一个凸包点。
- 入栈下一个点,将栈顶的两个点相连,得到一条直线。看下一个点在直线的右侧还是左侧,如果
是右侧就执行步骤5,如果在左侧或在直线上就执行步骤6。 - 如果在右侧,说明栈顶的那个点不是凸包点,将栈顶元素出栈并执行步骤4。
- 如果在左侧或直线上,说明该点是凸包点,就将其保存。
- 检查栈顶的点是不是步骤2中角度值最大的那个点,如果是就结束了,如果不是就将当前点的下个
点作为要计算的对象,执行步骤4,直到栈顶元素就是步骤2中角度值最大的点,那么循环结束。
3. Andrew扫描链法
步骤
- 对所有点按坐标x为第一关键字、y为第二关键字排序,第1个和最后一个
肯定在凸包上。 - 先顺序遍历所有点,通过三个点所构建的两个向量的叉积来判断是不是凸
包点,这样就可以构建出下凸包。 - 然后逆序遍历所有点,按照相同的方式就可以构建出上凸包,最后将上凸
包和下凸包连接即可。
- 优点:实现最简单、稳定性最高,是工程里最常用的算法;
- 适用:几乎所有场景,从几百个点到几万个点都能高效处理。
4. QuickHull法
- 核心步骤(3步搞定):
- 找横坐标最小的P1和最大的P2,连线后把点集分成“上包”(直线上方)和“下包”(直线下方);
- 以上包为例,找距离P1P2直线最远的点P3,连线P1P3和P2P3,再筛选出新的候选点集;
- 对新的点集重复步骤2,直到没有可添加的点,下包同理处理。
- 优点:平均效率极高,适合大规模点集(比如几万个点);
- 缺点:极端情况(比如所有点共线)效率会下降。
三、代码实现凸包检测
# 导入opencv的库(计算机视觉处理核心库)
import cv2
# 1.读取图片(BGR格式,OpenCV默认读取方式)
image_np = cv2.imread('./picture.png') # 替换为自己的图片路径
# 2.灰度化(降维:3通道彩色图→1通道灰度图,减少计算量)
image_gray = cv2.cvtColor(image_np,cv2.COLOR_BGR2GRAY)
# 3.二值化(黑白分割:将灰度图转为只有0/255的黑白图,突出轮廓)
ret,image_thresh = cv2.threshold(image_gray,127,255,cv2.THRESH_BINARY)
# 4.获取轮廓点的坐标(提取物体边界的关键步骤)
contours,hierarchy = cv2.findContours(image_thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
# 5.寻找凸包(核心:从轮廓点中筛选出凸包点)
# cv2.convexHull(points,hull,clockwise,returnPoints)
cnt = contours[0] # 取第一个轮廓(假设图片只有一个物体)
hull = cv2.convexHull(cnt) # 计算该轮廓的凸包
print(type(hull)) # 输出:<class 'numpy.ndarray'>,凸包点是多维数组
# 6.绘制凸包(把凸包轮廓画在原图上)
# polylines(img,pts,isClosed,color,thickness,lineType,shift)
image_poly = cv2.polylines(image_np,[hull],True,(0,0,255),2) # 红色、线宽2
# 7.结果显示(弹出窗口展示图片)
cv2.imshow('image_np',image_np) # 原图+凸包
cv2.imshow('image_poly',image_poly) # 同image_np(因为polylines直接修改原图)
cv2.waitKey(0) # 等待按键关闭窗口
cv2.destroyAllWindows() # 补充:建议加这行,避免窗口残留
输出结果为:


四、核心总结
- 凸包的核心作用:简化形态、保留边界,为后续识别/检测提供基础;
- 算法选择优先级:Andrew扫描链法(首选,简单稳定)→ Graham扫描法(中大规模点集)→ QuickHull法(超大点集)→ 穷举法(仅学习用);
- 关键共性:所有算法都是通过“筛选最外层点”实现,核心是判断点与直线的位置关系(左侧/右侧/共线)。
259

被折叠的 条评论
为什么被折叠?



