Python + OpenCV : 已知多边形轮廓的点坐标,自动识别多边形顶点坐标的算法

Python + OpenCV : 已知多边形轮廓的点坐标,自动识别多边形顶点坐标的算法

最近做工程,根据提供的目标物体的坐标(比如说分割结果输出的目标像素坐标),标其中遇到了RT说述问题,OpenCV并没有提供现成的函数,并且网上关于这块几乎没有内容,所以在这里记录一下。先来啰嗦一下两个基础函数,熟悉可略过。

基础函数

1.OpenCV提供了cv2.drawContours()函数可根据所提供的边界点绘制任何形状的轮廓。
eg:cv2.drawContours(image, [np.array(points, dtype=np.int32)], -1, (0, 0, 255), 2)
参数列表:它的第一个参数是原始图像,第二个参数是轮廓,一个坐标列表(一般为int32类型,否则会报错),第三个参数是轮廓的索引,接下来的参数是轮廓的颜色和厚度。
返回值:绘制轮廓后的原图(详见API)。

2.OpenCV函数提供了cv2.findContours()来寻找目标的边界
eg:cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
参数列表:它的第一个参数是输入图像(注意哦,二值图,即黑白的,并不是灰度图,所以读取的图像要先转成灰度的,再转成二值图),第二个参数是轮廓检索模式,第三个参数是轮廓近似方法。
返回值:这里OpenCV版本不同,返回值个数也不同,调用len()可以得到返回值的个数;当返回两个值,第一个为轮廓的点集,第二个是各层轮廓的索引(每一个轮廓都是一个Numpy数组,包含对象边界点(x,y)的坐标。)。当返回三个值,第一个是返回了你所处理的图像,第二个是我们要找的轮廓的点集,第三个是各轮廓的索引(详见API)。

寻找多边形的顶点坐标

第一步
为了准确,要使用二值化图像,需要进行阀值化处理。 在OpenCV中,阈值化操作通俗来讲,就在一个二值图像中查找轮廓,就像在黑色背景中寻找白色目标。但是查找轮廓的函数会修改原始图像。如果之后想继续使用原始图像,应该将原始图像储存到其他变量中,我在这里初始化了一张与原图同样大小的空白图像(image1 = np.zeros_like(image))。
第二步
先用cv2.convexHull()和cv2.approxPolyDP()简化点集,过滤掉部分点方便后续顶点识别,顶点识别算法的主要思想:求轮廓点集中每相邻的三个点形成的夹角的大小,当相邻三个点位于多边形的边上时,夹角趋于180,而顶点形成的夹角要小于180度,用一个二维数组记录相邻三点形成的夹角和三点中角点在源点集中的下标,最后对该数据排序,返回形成角度最小的角点,即为多边形的顶点。代码如下:

def bubble_sort_flag(list, axis):#根据数组的某一维排序
    length = len(list)
    for index in range(length):
        # 标志位
        flag = True
        for j in range(1, length - index):
            if axis == 0 and list[j - 1][0] > list[j][0]:
                list[j - 1][0], list[j][0] = list[j][0], list[j - 1][0]
                list[j - 1][1], list[j][1] = list[j][1], list[j - 1][1]
                flag = False
            if axis == 1 and list[j - 1][1] > list[j][1]:
                list[j - 1][0], list[j][0] = list[j][0], list[j - 1][0]
                list[j - 1][1], list[j][1] = list[j][1], list[j - 1][1]
                flag = False
        if flag:
            # 没有发生交换,直接返回list
            return list
    return list
 
'''atan2 方法返回一个 -pi 到 pi 之间的数值,表示点 (x, y) 对应的偏移角度。这是一个逆时针角度,以弧度为单位,正X轴和点 (x, y) 与原点连线 之间

因为atan2返回的是弧度值,也就是从-PI到PI,如下图所示,一个半圆是180度=弧度PI,所以1度 = PI/180。'''

def angle(v1, v2):
    dx1 = v1[2] - v1[0]
    dy1 = v1[3] - v1[1]
    dx2 = v2[2] - v2[0]
    dy2 = v2[3] - v2[1]

    angle1 = math.atan2(dy1, dx1)
    angle1 = int(angle1 * 180 / math.pi)
   
    angle2 = math.atan2(dy2, dx2)
    angle2 = int(angle2 * 180 / math.pi)
    
    if angle1 * angle2 >= 0:
        included_angle = abs(angle1 - angle2)
    else:
        included_angle = abs(angle1) + abs(angle2)
        if included_angle > 180:
            included_angle = 360 - included_angle
    return included_angle
hull = cv2.convexHull(max_contour)#求凸包,去掉曲线上的点
epsilon = 2#根据实际情况去设定
hull = cv2.approxPolyDP(hull1, epsilon, True)  
length = len(hull)
if(length > 4):
   ang = []
   for p in range(0, length):
      temp_1 = np.array([hull[(p + 1) % length][0], hull[(p + 1) % length][1], hull[p][0], hull[p][1]])
      temp_2 = np.array([hull[(p + 1) % length][0], hull[(p + 1) % length][1],hull[(p + 2) % length][0], hull[(p + 2) % length][1]])
      ang.append([angle(temp_1, temp_2), (p + 1) % length])
ang = np.array(ang)
ang = bubble_sort_flag(ang_1, 0)#根据数组的第一维排序


   
                                                                           

你可能感兴趣的:(python,opencv,计算机视觉,算法)