一、凸包
通俗的说,就是完全凸起,没有凹处的多边形,也叫做凸多边形。一 般来说凸包都是伴随着某类点集存在的,也被称为某个点集的凸包。
对于一个点集来说,如果该点集存在凸包,那么这个点集中的所有的点要么 在这个凸包上,要么在这个凸包内。
凸包检测常用在物体识别、手势识别及边界检测等领域。
二、常见的凸包算法
2.1、穷举法
将点集中的点进行两两配对,并进行连线,对于每条直线,检查其余 的所有的点是否处于该直线的同一侧,如果所有的其余的点都处于该直线的同 一侧,那么说明构成这条直线的两个点就是凸包点,其用户的线依次进行计算,从而获取所有的凸包点。

2.2、Graham扫描法

-
将纵坐标最小的点记为P0,且以该点为原点构建二维坐标系,那么P0就一定是一个凸包点。
-
计算各个点对于P0的角度
,按照从小到大的顺序进行排序(逆时针顺序),当角度相同时,与P0 较近的点排在前面。那么角度最大的点和角度最小的点一定是凸包点。
-
用栈来记录已知的凸包点,先将P0和P1放入栈中,然后去求下一个凸包点。
-
入栈下一个点,将栈顶的两个点相连,得到一条直线。看下一个点在直线的右侧还是左侧,如果 是右侧就执行步骤5,如果在左侧或在直线上就执行步骤6。
-
如果在右侧,说明栈顶的那个点不是凸包点,将栈顶元素出栈并执行步骤4。
-
如果在左侧或直线上,说明该点是凸包点,就将其保存。
7.检查栈顶的点是不是步骤2中角度值最大的那个点,如果是就结束了,如果不是就将当前点的下个 点作为要计算的对象,执行步骤4,直到栈顶元素就是步骤2中角度值最大的点,那么循环结束。
2.3、Andrew扫描链法

-
对所有点按坐标x为第一关键字、y为第二关键字排序,第1个和最后一个 肯定在凸包上。
-
先顺序遍历所有点,通过三个点所构建的两个向量的叉积来判断是不是凸 包点,这样就可以构建出下凸包。
-
然后逆序遍历所有点,按照相同的方式就可以构建出上凸包,最后将上凸 包和下凸包连接即可。
2.4、QuickHull法

-
将所有的点放在二维坐标系中,找到最横坐标最小和最大的两个点P1和 P2并连线。此时整个点集被该直线分为两部分,直线上方叫上包,直线 下方叫下包。
-
以上包为例,找到上包中的点距离该直线最远的点P3,连线P1、P3,并 寻找直线P1P3左侧的点与直线P2P3右侧的点,然后重复本步骤,直到找 不到为止。对下包也是这么操作。
三、凸包特征检测
导入模块
python
import cv2
输入图像
python
img=cv2.imread('tubao.png')
灰度化
python
img_gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
二值化
python
ret,img_threshold=cv2.threshold(img_gray,127,255,cv2.THRESH_BINARY)
寻找轮廓
python
contours,hierarch=cv2.findContours(img_threshold,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
查找凸包
python
cnt=contours[0]
hull=cv2.convexHull(cnt)
绘制轮廓
python
img_polyline=cv2.polylines(img,[hull],isClosed=True,color=(0,0,255),thickness=3)
输出图像
python
cv2.imshow('img',img)
cv2.waitKey(0)
完整代码
python
import cv2
# 读取名为"tubao.png"的图像。
img=cv2.imread('tubao.png')
# 将图像从 BGR(蓝、绿、红)色彩空间转换为灰度。
img_gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# 将灰度图像进行二值化。所有高于阈值 127 的像素都设置为 255(白色),而所有低于 127 的像素都设置为 0(黑色)。
ret,img_threshold=cv2.threshold(img_gray,127,255,cv2.THRESH_BINARY)
# 在二值化图像中查找轮廓。
# cv2.RETR_EXTERNAL 模式仅检索外部轮廓。
# cv2.CHAIN_APPROX_SIMPLE 压缩水平、垂直和对角线段,仅保留它们的端点。
contours,hierarch=cv2.findContours(img_threshold,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
# 我们假设图像中只有一个轮廓。
cnt=contours[0]
# 计算轮廓的凸包。
hull=cv2.convexHull(cnt)
# 在图像上绘制凸包。
# img:要在其上绘制的图像。
# [hull]:凸包点数组。
# isClosed=True:绘制的折线是闭合的。
# color=(0,0,255):颜色是红色。
# thickness=3:折线的粗细为 3 像素。
img_polyline=cv2.polylines(img,[hull],isClosed=True,color=(0,0,255),thickness=3)
# 显示图像。
cv2.imshow('img',img)
# 等待用户按下某个键。
cv2.waitKey(0)
四、库函数
4.1、findContours()
python
cv.findContours( image, mode, method[, contours[, hierarchy[, offset]]] ) -> contours, hierarchy
方法 | 描述 |
---|---|
image | 源,一个 8 位单通道图像。非零像素被视为 1。零像素仍为 0,因此图像被视为 binary 。您可以使用 compare、inRange、threshold、adaptiveThreshold、Canny 等创建灰度或彩色图像的二进制图像。如果 mode 等于 RETR_CCOMP 或 RETR_FLOODFILL,则输入也可以是标签的 32 位整数图像 (CV_32SC1)。 |
contours | 检测到的轮廓。每个轮廓都存储为点向量(例如 std::vector<std::vector<cv::P oint> >)。 |
hierarchy | 可选的输出向量(例如 std::vector<cv::Vec4i>),包含有关图像拓扑的信息。它的单元数与等值线的数量一样多。对于每个第 i 个轮廓轮廓[i],元素 hierarchy[i][0] 、hierarchy[i][1] 、hierarchy[i][2] 和 hierarchy[i][3] 在同一层次结构级别的下一个和上一个轮廓的轮廓中分别设置为从 0 开始的索引,即第一个子轮廓和父轮廓。如果轮廓 i 没有 next、previous 、parent 或 nested 等值线,则 hierarchy[i] 的相应元素将为负数。 |
mode | 等值线检索模式,参见 RetrievalModes |
method | 等值线近似方法,请参阅 ContourApproximationModes |
offset | 每个等值线点移动的可选偏移量。如果轮廓是从图像 ROI 中提取的,然后应该在整个图像上下文中对其进行分析,这将非常有用。 |
|-----------------------------------------|----------------------------------------------------------------------------|
| RETR_EXTERNAL Python:cv.RETR_EXTERNAL | 仅检索极端外轮廓。它为所有轮廓设置。hierarchy[i][2]=hierarchy[i][3]=-1
|
| RETR_LIST Python:cv.RETR_LIST | 检索所有等值线,而不建立任何分层关系。 |
| RETR_CCOMP Python:cv.RETR_CCOMP | 检索所有等值线并将它们组织到一个两级层次结构中。在顶层,有组件的外部边界。在第二层,有孔的边界。如果已连接零部件的孔内有其他轮廓,则仍将其置于顶层。 |
| RETR_TREE Python:cv.RETR_TREE | 检索所有等值线并重新构建嵌套等值线的完整层次结构。 |
| RETR_FLOODFILL Python:cv.RETR_FLOODFILL |
|---------------------------------------------------------|------------------------------------------------------------------------------------------------------------------|
| CHAIN_APPROX_NONE Python:cv.CHAIN_APPROX_NONE | 绝对存储所有等高线点。也就是说,等值线的任意 2 个后续点 (x1,y1) 和 (x2,y2) 将是水平、垂直或对角线相邻点,即 max(abs(x1-x2),abs(y2-y1))==1。 |
| CHAIN_APPROX_SIMPLE Python:cv.CHAIN_APPROX_SIMPLE | 压缩水平、垂直和对角线段,仅保留其端点。例如,一个直立的矩形轮廓用 4 个点编码。 |
| CHAIN_APPROX_TC89_L1 Python:cv.CHAIN_APPROX_TC89_L1 | 应用了 Teh-Chin 链近似算法的一种风格 [266] |
| CHAIN_APPROX_TC89_KCOS Python:cv.CHAIN_APPROX_TC89_KCOS | 应用了 Teh-Chin 链近似算法的一种风格 [266] |
4.2、convexHull()
python
cv.convexHull( points[, hull[, clockwise[, returnPoints]]] ) -> hull
| 方法 | 描述 |
| points | 输入 2D 点集,存储在 std::vector 或 Mat 中。 |
| hull | 输出凸包。它可以是索引的整数向量或点的向量。在第一种情况下,外壳元素是原始数组中凸外壳点的从 0 开始的索引(因为凸外壳点集是原始点集的子集)。在第二种情况下,外壳单元是凸外壳点本身。 |
| clockwise | 方向标志。如果为 true,则输出凸包按顺时针方向。否则,它将按逆时针方向排列。假定的坐标系的 X 轴指向右侧,Y 轴指向上方。 |
returnPoints | Operation 标志。对于矩阵,当 flag 为 true 时,该函数返回凸包点。否则,它将返回凸包点的索引。当输出数组为 std::vector 时,将忽略该标志,输出取决于向量的类型:std::vector<int> 表示 returnPoints=false,std::vector<Point> 表示 returnPoints=true。 |
---|
4.3、polylines()
python
cv.polylines( img, pts, isClosed, color[, thickness[, lineType[, shift]]] ) -> img
| 方法 | 描述 |
| img | 图像。 |
| pts | 多边形曲线数组。 |
| isClosed | 指示绘制的多段线是否闭合的标志。如果它们是闭合的,则该函数将从每条曲线的最后一个顶点到其第一个顶点绘制一条线。 |
| color | 多段线颜色。 |
| thickness | 多段线边线的粗细。 |
| lineType | 线段的类型。请参阅线型 |
shift | 顶点坐标中的小数位数。 |
---|
Enumerator | |
---|---|
FILLED Python: cv.FILLED | |
LINE_4 Python: cv.LINE_4 | 4-connected line |
LINE_8 Python: cv.LINE_8 | 8-connected line |
LINE_AA Python: cv.LINE_AA | antialiased line |