如何绘制通过多边形轮廓质心的最长内部直线?
实现通过多边形质心的最长内部直线
核心思路
你之前用平均向量的方法无法得到目标直线,因为平均向量反映的是轮廓点的整体偏移趋势,和多边形的最长直径方向无关。正确的做法是遍历不同角度的直线:对每个过质心的直线,计算它与多边形轮廓的两个交点,再计算交点间的距离,最终找到距离最大的那条直线。
具体步骤
- 加载二值图像并提取目标轮廓
- 计算轮廓的质心坐标
- 遍历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,提问作者윤도현
相关产品推荐
相关产品推荐

