目录
凸包就是将一张图片中物体最外层的点连接起来构成的凸多边形,它能包含物体中所有的内容。
一般来说,凸包都是伴随着某类点集存在的,也被称为某个点集的凸包。
对于一个点集来说,如果该点集存在凸包,那么这个点集里面的所有点要么在凸包上,要么在凸包内。
凸包检测常用在物体识别、手势识别、边界检测等领域。
-
穷举法
-
QuickHull法
1 穷举法
-
将集中的点进行两两配对,并进行连线,对于每条直线,检查其余所有的点是否处于该直线的同一侧,如果是,那么说明构成该直线的两个点就是凸包点,其余的线依次进行计算,从而获取所有的凸包点。
-
用向量的思想,点都是有坐标的,连起来就可以构成一个向量。再以其中一个点,连接另一个点,构成另一个向量,让两个向量做外积,就是叉积。也就是std=|向量a|*|向量b|*sin(\theta),能控制std的正负的只能是\theta,如果计算出来的std的正负都相同,说明这些点都在这条直线的同一侧,那么这两个点就是凸包的边界点。然后换两个点,就是说换一条直线,换一个向量,继续进行检测,直到找到凸包的所有的边界点。
-
缺点:时间复杂度高,不断使用for循环,耗时。
2 QuickHull法
-
将所有点放在二维坐标系中,找到横坐标最小和最大的两个点
和
并连线。此时整个点集被分为两部分,直线上为上包,直线下为下包。
-
以上保暖为例,找到上包中的点距离该直线最远的点
,连线并寻找直线
左侧的点和
右侧的点,然后重复本步骤,直到找不到为止。对下包也是这样操作。
我们以点集来举例,假如有这么一些点,其分布如下图所示:
那么经过凸包检测并绘制之后,其结果应该如下图所示:
可以看到,原图像在经过凸包检测之后,会将最外围的几个点进行连接,剩余的点都在这些点的包围圈之内。那么凸包检测到底是怎么检测出哪些点是最外围的点呢?
我们还是以上面的点集为例,假设我们知道这些点的坐标,那么我们就可以找出处于最左边和最右边的点,如下图所示:
接着将这两个点连接,并将点集分为上半区和下半区,我们以上半区为例:
找到上面这些点离直线最远的点,其中,这条直线由于有两个点的坐标,所以其表示的直线方程是已知的,并且上面的点的坐标也是已知的,那么我们就可以根据点到直线的距离公式来进行计算哪个点到直线的距离最远,假设直线的方程为:,那么点
到直线的距离公式为:
然后我们就可以得到距离这条线最远的点,将其与左右两点连起来,并分别命名为y1和y2,如下图所示:
然后分别根据点的坐标求出y1和y2的直线方程,之后将上半区的每个点的坐标带入下面公式中:
当d=0时,表明该点在直线上;当d>0时,表明点在直线的上方,在这里就代表该点在上图所围成的三角形的外面,也就意味着该三角形并没有完全包围住上半区的所有点,需要重新寻找凸包点;当d<0时,表明点在直线的下方,在这里就代表该点在上图所围成的三角形的里面,也就代表着这类点就不用管了。
当出现d>0时,我们需要将出现这种结果的两个计算对象:某点和y1或y2这条线标记,并在最后重新计算出现这种现象的点集到y1或y2的距离来获取新的凸包点的坐标。在本例子中,也就是如下图所示的点和y2这条直线:
由于本例子中只有这一个点在这个三角形之外,所以毫无疑问的它就是一个凸包点,因此直接将它与y2直线的两个端点相连即可。当有很多点在y2直线外时,就需要计算每个点到y2的距离,然后找到离得最远的点与y2构建三角形,并重新计算是否还有点在该三角形之外,如果没有,那么这个点就是新的凸包点,如果有,那就需要重复上面的步骤,直到所有的点都能被包围住,那么构建直线的点就是凸包点。这是上半区寻找凸包点的过程,下半区寻找凸包点的思路与此一模一样,只不过是需要筛选d<0(也就是点在直线的下方)的点,并重新构建三角形,寻找新的凸包点。
上面的过程都是基于我们知道点的坐标进行的,实际上,对于未经处理的图像,我们无法直接获取点的坐标。特别是对于彩色图像,我们需要将其转换为二值图像,并使用轮廓检测技术来获取轮廓边界的点的坐标。然后,我们才能进行上述寻找凸包点的过程。因此,在处理图像时,我们需要将彩色图像转换为二值图像,并通过轮廓检测技术来获取轮廓边界的点的坐标,然后才能进行凸包点的寻找过程。
2.1 获取凸包点
API:
cv2.convexHull(points)
-
points
:输入参数,图像的轮廓
2.2 绘制凸包
API:
cv2.polylines(image, pts, isClosed, color, thickness=1)
-
image
:要绘制线条的目标图像,它应该是一个OpenCV格式的二维图像数组(如numpy数组)。 -
pts
:一个二维 numpy 数组,每个元素是一维数组,代表一个多边形的一系列顶点坐标。 -
isClosed
:布尔值,表示是否闭合多边形,如果为 True,会在最后一个顶点和第一个顶点间自动添加一条线段,形成封闭的多边形。 -
color
:线条颜色,可以是一个三元组或四元组,分别对应BGR或BGRA通道的颜色值,或者是灰度图像的一个整数值。 -
thickness
(可选):线条宽度,默认值为1。
案例:
import cv2
import cv2 as cv
# 读图
img = cv.imread("./images/num.png")
# 转灰度
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 二值化
_,binary = cv.threshold(gray,127,255,cv.THRESH_OTSU+cv.THRESH_BINARY_INV)# OTSU+反阈值法
# 查找轮廓
counters,h = cv.findContours(binary,cv.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
# 寻找凸包 cv2.convexHull(points)
for i in range(len(counters)):
hull = cv.convexHull(counters[i])
cv.polylines(img, [hull], True, (0, 255, 0), 1, cv.LINE_AA)
# 显示图像
cv.imshow("binary",binary)
cv.imshow("img",img)
cv.waitKey(0)
cv.destroyAllWindows()
输出:
![]() | ![]() |
原图 | 凸包检测 |