如何获取多边形Mask的Feret直径及中心正交线长度(弃用椭圆拟合)
多边形Mask的Feret直径及中心正交线长度测量方案
需求说明
- 测量多边形Mask的最长Feret直径
- 获取该Feret直径中心处的正交线长度(垂直于最长径的横径)
- 无法使用
skimage.measure.regionprops的major_axis/minor_axis属性,因椭圆拟合对目标Mask形状可靠性极低
当前基础实现代码
import numpy as np import skimage.draw import skimage.measure # 生成多边形Mask mask_ps = np.transpose(skimage.draw.polygon2mask(im2d_ps.shape, this_ps_seg_points)) mask_ps_label = skimage.measure.label(mask_ps) props_ps = skimage.measure.regionprops(mask_ps_label) # 筛选最大面积区域 props_ps.sort(key=lambda x: x.area) print("最大面积", props_ps[-1].area) this_feret = props_ps[-1].feret_diameter_max
可行实现方案
1. 基于Skimage的扩展实现(像素Mask层面)
Skimage的regionprops已内置feret_coords属性,可获取最长Feret直径的端点坐标,以此推导中心正交线长度:
# 获取目标区域属性 target_prop = props_ps[-1] # 提取Feret直径的两个端点 feret_start, feret_end = target_prop.feret_coords # 计算Feret直径向量与中心点 feret_vec = feret_end - feret_start feret_center = (feret_start + feret_end) / 2 # 生成单位正交向量(顺时针旋转90度) ortho_vec = np.array([-feret_vec[1], feret_vec[0]]) ortho_vec = ortho_vec / np.linalg.norm(ortho_vec) # 从中心点沿正交方向遍历,找到Mask边界 def find_boundary(center, direction, mask): step = 1 point = center.copy() while True: next_point = point + direction * step # 检查坐标有效性与Mask归属 if (next_point[0] < 0 or next_point[0] >= mask.shape[0] or next_point[1] < 0 or next_point[1] >= mask.shape[1] or not mask[int(next_point[0]), int(next_point[1])]): return point point = next_point # 获取正交方向的两个边界点 boundary_a = find_boundary(feret_center, ortho_vec, mask_ps) boundary_b = find_boundary(feret_center, -ortho_vec, mask_ps) # 计算正交线长度 ortho_length = np.linalg.norm(boundary_a - boundary_b) print("最长Feret直径:", this_feret) print("中心正交线长度:", ortho_length)
2. 基于多边形几何的精确计算(顶点层面)
如果原始输入是多边形顶点this_ps_seg_points,可直接用几何库计算,避免像素化误差:
from shapely.geometry import Polygon, LineString import numpy as np # 构建多边形对象,先取凸包缩小计算范围 polygon = Polygon(this_ps_seg_points) convex_hull = polygon.convex_hull hull_points = list(convex_hull.exterior.coords)[:-1] # 遍历凸包顶点对,找到最长距离(即Feret直径) max_dist = 0 feret_pair = None for i in range(len(hull_points)): for j in range(i+1, len(hull_points)): dist = np.linalg.norm(np.array(hull_points[i]) - np.array(hull_points[j])) if dist > max_dist: max_dist = dist feret_pair = (hull_points[i], hull_points[j]) # 计算Feret直径的中心点与正交方向线 feret_line = LineString(feret_pair) feret_center = feret_line.interpolate(0.5, normalized=True) # 生成覆盖多边形范围的正交线 ortho_angle = feret_line.angle + np.pi/2 ortho_line = LineString([ feret_center.buffer(1000).boundary.interpolate(ortho_angle, normalized=True), feret_center.buffer(1000).boundary.interpolate(ortho_angle + np.pi, normalized=True) ]) # 计算正交线与多边形的交点,得到长度 intersection = polygon.intersection(ortho_line) if isinstance(intersection, LineString): ortho_length = intersection.length print("最长Feret直径:", max_dist) print("中心正交线长度:", ortho_length)
相关术语补充
你的需求对应医学影像领域的双径测量(Two-Axis Bi-Dimensional Measurement),是肺结节等病灶评估的标准测量方式,即测量最长径(长轴)及垂直于长轴的短轴。
内容的提问来源于stack exchange,提问作者hqp
相关产品推荐
相关产品推荐

