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

如何获取多边形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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 14:06:18