OpenCV--任意多边形最大内接圆python实现

任意多边形内接圆

算法思路
推荐
在多边形形成的二维平面区域中,高度为H,宽度为WW。将空间进行等分, 高度为n份, 宽度为m份;
在空间形成网格,将多边形区域外的点剔除掉。在剩下的点中对每个点,找到边最近的距离。在这样
很多点中找出最大距离点。以种子点为中心的区域再剖分,循环迭代。

"""
#!/usr/bin/env python
# -*- coding:utf-8 -*-
@desc: 获取最大内接圆
"""
import cv2 as cv
import numpy as np
from shapely.geometry import Polygon, MultiPolygon, Point
from math import sqrt


N_CELLS = 20 # 高等分
M_CELLS = 20 # 宽等分

def geometry_find_pia(polygon_coord, bounds):
    """
    找到多边形内接圆
    :param polygon_coord:
    :param bounds:
    :return:
    """
    increment_x = (bounds[1] - bounds[0]) / N_CELLS
    increment_y = (bounds[3] - bounds[2]) / M_CELLS
    # 获取圆心
    max_distance = 0
    circle_center_x = 0
    circle_center_y = 0
    for i in range(N_CELLS):
        tmp_x = bounds[0] + i * increment_x
        for j in range(M_CELLS):
            tmp_y = bounds[2] + j * increment_y
            distance = cv.pointPolygonTest(np.array(polygon_coord), (tmp_x, tmp_y), True)  # 里面为正,外面为负,边界为0
            if distance > max_distance:
                max_distance = distance
                circle_center_x = tmp_x
                circle_center_y = tmp_y

    return [circle_center_x, circle_center_y], max_distance


def find_inscribed_circle_center(polygon_coord):
    """
    获取任意多边形,内接圆
    :param polygon_coord: 多边形坐标
    :return:
    """
    polygon = Polygon(polygon_coord)
    if polygon.area <= 0:
        return None
    # 默认图片所在的区域, 像素左下 右上
    bounds = polygon.bounds
    bounds = [bounds[0],bounds[2],bounds[1],bounds[3]] # 表示x的范围,y的范围[xmin, xmax, ymin, ymax]
    img = np.zeros((512, 512, 3), np.uint8)
    cv.polylines(img, [np.array(polygon_coord)], True, (0, 255, 255), 5)
    # 调整图片区域
    count = 1
    while True:
        count += 1
        circle_cent, radius = geometry_find_pia(polygon_coord, bounds) # 初略得到内接圆的圆心和半径,不精准
        circle_center_x, circle_center_y = circle_cent
        # 更新边界,调整边界值,来对圆心和半径,进行调整
        flt_tmp = (bounds[1] - bounds[0]) / (sqrt(2) * 2)
        bounds[0] = circle_center_x - flt_tmp
        bounds[1] = circle_center_x + flt_tmp
        flt_tmp = (bounds[3] - bounds[2]) / (sqrt(2) * 2)
        bounds[2] = circle_center_y - flt_tmp
        bounds[3] = circle_center_y + flt_tmp

        if (bounds[1] - bounds[0]) < 0.001 or (bounds[3] - bounds[2]) < 0.001:
            print(f'一共循环{count}次')
            break
    cv.circle(img, (int(circle_center_x), int(circle_center_y)), int(radius), (255, 255, 0),2)
    cv.imshow('image', img)
    cv.waitKey(0)
    cv.destroyAllWindows()

    return [circle_center_x, circle_center_y, radius]



if __name__ == '__main__':
    rec_coord = [[100,100],[300,80],[150,150],[100,300],[100,100]]
    rec = Polygon(rec_coord)
    cir_center = find_inscribed_circle_center(rec_coord)
    print(cir_center)

结果:

参考博客:
https://blog.csdn.net/u011533238/article/details/88844092

方法二:
使用图像


"""
#!/usr/bin/env python
# -*- coding:utf-8 -*-
@desc: 获取不规则图形的最大内接圆 此方法依赖图片比较耗时
"""
import cv2
import numpy as np

mask_path = r"D:\workplace\data\opencv\circle_test.png"

mask = cv2.imread(mask_path)
mask_gray = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY) # 三通道BGR转为灰度图

# 识别轮廓
if '3.4' in cv2.__version__:
    _, contours, hierarchy = cv2.findContours(mask_gray, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
else:
    contours, hierarchy = cv2.findContours(mask_gray,  cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

# 计算到轮廓的距离
raw_dist = np.empty(mask_gray.shape, dtype=np.float32)
for i in range(mask_gray.shape[0]):
    for j in range(mask_gray.shape[1]):
        raw_dist[i, j] = cv2.pointPolygonTest(contours[0], (j, i), True)

# 获取最大值,内接圆半径, 中心坐标
min_val, max_val, _, max_dist_pt = cv2.minMaxLoc(raw_dist)
min_val = abs(min_val)
max_val = abs(max_val)

# 画出最大内接圆
result = cv2.cvtColor(mask_gray, cv2.COLOR_GRAY2BGR)
radius = np.int(max_val)
center_of_circle = max_dist_pt
cv2.circle(result, max_dist_pt, radius, (255, 0, 255), 6, 1, 0)
cv2.imshow('Maximum inscribed circle', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

此种方法比较耗时,不太建议使用。

posted @ 2022-05-19 19:40  酷酷的排球  阅读(1193)  评论(0编辑  收藏  举报