You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何绘制通过多边形轮廓质心的最长内部直线?

实现通过多边形质心的最长内部直线

核心思路

你之前用平均向量的方法无法得到目标直线,因为平均向量反映的是轮廓点的整体偏移趋势,和多边形的最长直径方向无关。正确的做法是遍历不同角度的直线:对每个过质心的直线,计算它与多边形轮廓的两个交点,再计算交点间的距离,最终找到距离最大的那条直线。

具体步骤

  • 加载二值图像并提取目标轮廓
  • 计算轮廓的质心坐标
  • 遍历0°到180°的角度(步长越小精度越高):
    • 对每个角度生成过质心的直线方程
    • 计算直线与多边形每条边的交点
    • 筛选出在线段上的有效交点,取距离质心最远的两个点
    • 计算这两个点的距离,记录最大值及对应点
  • 绘制最长直线并可视化

完整代码实现

import numpy as np
import cv2
import matplotlib.pyplot as plt

def line_contour_intersections(contour, line_point, line_angle_rad):
    """计算直线与多边形轮廓的有效交点"""
    intersections = []
    # 直线的一般式:Ax + By + C = 0
    dir_x = np.cos(line_angle_rad)
    dir_y = np.sin(line_angle_rad)
    A = dir_y
    B = -dir_x
    C = dir_x * line_point[1] - dir_y * line_point[0]

    # 遍历轮廓的每条边
    for i in range(len(contour)):
        p1 = contour[i][0]
        p2 = contour[(i+1)%len(contour)][0]
        # 边的一般式:A1x + B1y + C1 = 0
        A1 = p2[1] - p1[1]
        B1 = p1[0] - p2[0]
        C1 = p2[0]*p1[1] - p1[0]*p2[1]

        # 计算两条直线的交点
        det = A * B1 - A1 * B
        if abs(det) < 1e-6:
            # 直线平行,跳过
            continue
        x = (B * C1 - B1 * C) / det
        y = (A1 * C - A * C1) / det

        # 检查交点是否在当前边的线段范围内
        min_x, max_x = min(p1[0], p2[0]), max(p1[0], p2[0])
        min_y, max_y = min(p1[1], p2[1]), max(p1[1], p2[1])
        if (min_x - 1e-6 <= x <= max_x + 1e-6) and (min_y - 1e-6 <= y <= max_y + 1e-6):
            intersections.append((x, y))
    
    # 去重(避免同一点被多次检测)
    if len(intersections) > 1:
        unique_intersections = []
        seen = set()
        for pt in intersections:
            rounded_pt = (round(pt[0], 4), round(pt[1], 4))
            if rounded_pt not in seen:
                seen.add(rounded_pt)
                unique_intersections.append(pt)
        return unique_intersections
    return intersections

# 1. 加载图像
binary_img = cv2.imread('C:/Users/MSDL-DESK-02/Documents/result1/6.png', cv2.IMREAD_GRAYSCALE)

# 2. 提取外部轮廓
contours, _ = cv2.findContours(binary_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contour = contours[0]

# 3. 计算质心(保留浮点精度,避免整数误差)
M = cv2.moments(contour)
cx = M["m10"] / M["m00"]
cy = M["m01"] / M["m00"]
centroid = (cx, cy)

# 4. 遍历角度寻找最长直线
max_distance = 0
best_points = None
step = 0.5  # 角度步长,可根据精度调整
for angle_deg in np.arange(0, 180, step):
    angle_rad = np.deg2rad(angle_deg)
    intersections = line_contour_intersections(contour, centroid, angle_rad)
    if len(intersections) >= 2:
        # 计算所有交点到质心的距离,取最远的两个点
        distances = [np.sqrt((x - cx)**2 + (y - cy)**2) for x, y in intersections]
        sorted_idx = np.argsort(distances)[-2:]
        pt1, pt2 = intersections[sorted_idx[0]], intersections[sorted_idx[1]]
        current_dist = np.sqrt((pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2)
        if current_dist > max_distance:
            max_distance = current_dist
            best_points = (pt1, pt2)

# 5. 可视化结果
plt.figure(figsize=(8,8))
plt.imshow(binary_img, cmap='gray')
plt.scatter(cx, cy, color='red', s=20, label='质心')
if best_points:
    pt1, pt2 = best_points
    plt.plot([pt1[0], pt2[0]], [pt1[1], pt2[1]], color='blue', linewidth=2, label=f'最长直线 (长度: {max_distance:.2f})')
plt.axis('off')
plt.legend()
plt.title('通过质心的最长内部直线')
plt.show()

关键说明

  • line_contour_intersections函数:通过直线与线段的一般式求交,过滤出在线段范围内的有效交点,避免误判。
  • 角度遍历:只遍历0-180度即可(180度与0度为同一直线),步长越小结果精度越高,但计算量会相应增加。
  • 浮点精度处理:质心坐标保留浮点型,避免整数截断导致的误差;交点判断加入微小容差,解决浮点计算的精度问题。

内容的提问来源于stack exchange,提问作者윤도현

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 08:01:04