资讯动态

极坐标表示下的单阶段实例分割:PolarMask原理与工程复现指南

发布时间:2026/10/13 12:54:11 来源:尧图企业网站定制
很多人第一次看实例分割想的都是“给每个像素打标签”。我在做移动端目标轮廓提取时也是这个思路结果两阶段模型的算力开销压得人头疼。后来复现了 PolarMask 这篇工作才意识到一个更本质的问题实例分割的难点不在于你最后输出的是 mask 还是向量而在于你怎么把“轮廓”这件事变成网络容易回归的东西。PolarMask 给出的答案是用极坐标把掩模拆成一个中心点和一组射线距离。这篇文章我来聊聊它的核心思路、损失函数设计、复现细节以及我踩过的那些坑。1. 从两阶段到单阶段实例分割的切换成本到底在哪1.1 像素级掩模回归“贵”在哪里经典的两阶段实例分割流程是先做目标检测拿到建议框然后对每个框内的区域单独做一次像素级的 mask 分割。这种做法的好处是精度高但坏处也很明显ROI 区域越多需要单独过 mask 头的区域就越多。一个画面里几十个目标每个目标都要做一次 ROI 内的卷积计算量是成倍叠加的。我之前在嵌入式设备上跑过这种模型印象最深的是显存占用特别不稳定。目标一多中间 feature map 的复制和 ROI 对齐操作就会把有限的带宽吃满推理延迟直接翻倍。这也是单阶段实例分割会成为一个研究方向的直接原因能不能别为每个目标单独做一次区域卷积直接在一个全图上输出所有实例的掩模1.2 单阶段检测其实已经铺好了路PolarMask 的基座是 anchor-free 的单阶段检测器。这类检测器在 2019 年前后已经比较成熟核心逻辑是对特征图上的每个位置直接回归它到目标四条边的距离或者回归它和中心点的偏置。也就是说目标已经被建模成“一个点 一组回归量”不再需要先枚举一堆候选框。这套思路往前再推一步就是 PolarMask 的起点既然框可以由一个中心点和四个距离来描述那轮廓为什么不能由一个中心点和 N 个距离来描述四个距离对应的是矩形N 个距离对应的是任意多边形。把 N 取成 36相当于从中心点向四周发射 36 条射线每条射线碰到目标轮廓后记下一个距离。36 个距离够了轮廓形状也就出来了。1.3 框架对比Mask R-CNN 和 PolarMask 的实际差异拿我自己复现时的感受来说两者的差异不只是“单阶段 vs 两阶段”而是整个输出空间的性质变了对比维度两阶段逐像素掩模方法PolarMask 极坐标方法掩模输出形式每像素 0/1 概率图每个位置一组射线距离是否依赖 ROI依赖每个实例单独处理不依赖全图一次前向掩模头计算量随实例数量线性增长固定为距离回归分支后处理复杂度需要综合多个 ROI 结果中心点筛选后直接拼多边形对小目标友好度依赖 ROI 尺寸对齐依赖中心点定位与射线密度这个对比不是要否定逐像素方法而是说明 PolarMask 的路子更“轻”它把问题从二维的“区域划分”压缩成了一维的“距离回归”换来了速度也付出了一些形状表达上的代价。这个代价具体在哪下一节详细讲。2. 极坐标表示用 36 个距离画出一个不规则多边形2.1 从中心点到轮廓一个点加一串距离极坐标表示的核心定义很简单对目标实例先确定一个中心点 (c(c_x, c_y))然后从该点向四周均匀发射 (N) 条射线相邻射线夹角为 (\Delta\theta2\pi/N)。第 (i) 条射线与目标轮廓的交点距离记为 (d_i)那么目标掩模就可以用这组点来逼近[ (x_i, y_i) (c_x d_i \cos\theta_i,\ c_y d_i \sin\theta_i),\quad i0,1,\dots,N-1 ]把这些点按顺序连起来就是一个闭合的多边形也就是这个实例的预测掩模。网络要做的就是两件事一是判断某个位置是不是目标中心二是预测中心到轮廓的 N 个距离。前者是分类任务后者是回归任务。我在复现时一直提醒自己这里的中心点不是随便选的它必须在目标内部。如果中心点偏到实例外面哪怕距离回归得很准最后拼出来的多边形也会严重失真。这正是 PolarMask 训练时特别重视中心采样质量的原因后面会展开说。2.2 掩模如何变成训练标签如果你手头只有像素级别的 mask 标注想用 PolarMask 训练第一步就是把 GT mask 转成极坐标距离数组。转换过程大概是这样的计算实例轮廓得到一系列轮廓点确定中心点一般用 mask 的质心如果质心不在实例内部则向内收缩到实例内对每个方向角 (\theta_i)从中心点发出一条射线计算该射线与轮廓多边形的所有交点取离中心点最远的那个交点的距离把 N 个方向的距离存成一个向量。这里为什么要取“最远交点”而不是最近交点因为极坐标表示默认轮廓是“从中心点往外看是单调的”一条射线如果和目标轮廓相交了多次说明这个形状相对于中心点是内凹的。对这种内凹情况取最远交点至少还能保留一个整体轮廓的近似最近交点会丢掉太多信息。2.3 星形假设与能表示的形状边界本质上PolarMask 能良好表达的是“星形区域”从中心点出发任意方向的射线与轮廓最多交于一点。人、车、大多数常见商品都接近这种形状所以效果可以接受。但一旦遇到半月形、C 形、环形这类带凹或带空的实例问题就来了。比如一个甜甜圈中心正好在中心孔里那么几乎每条射线都会穿过两个边界一个外圆一个内圆取最远距离后内孔信息就完全丢了。这种几何限制不是训练技巧能解决的而是表示方式本身决定的。所以后来有不少工作专门做可形变轮廓或频域轮廓表示本质上都是在绕过星形限制我最后会讲。3. Polar IoU Loss绕不开的损失设计3.1 为什么 L1 和逐像素损失都不够用如果输出只是 36 个距离值朴素想法是直接对这 36 个值算 L1 loss再加一个中心分类 loss。但我在实验里发现这条路走不通。原因是 L1 loss 对每个方向的距离是独立看待的它感知不到“这些距离组合出来的多边形面积到底对不对”。一个很典型的失败案例模型预测出来 36 个距离都偏小但彼此非常平滑L1 误差可能不高可拼出来的多边形比 GT 小了一圈IoU 很低。反过来某些距离错得很离谱但 L1 平均之后误差被掩盖。独立回归距离本质上没有在优化“轮廓与轮廓的重叠程度”。逐像素 BCE loss 也有问题它虽然直接优化 mask但想让每个位置都输出一张 H×W 的 mask等于在特征图的每个点上都做一次全图分割计算和显存开销都受不了。PolarMask 需要的是一个能直接度量多边形交并比、同时还能求梯度的损失函数。3.2 射线对齐条件下的解析交并比PolarMask 的巧妙之处在于它发现当预测多边形和 GT 多边形的射线方向完全对齐都是同一套固定角度时交并比可以用解析式算出来。假设预测距离是 (d_i)GT 距离是 (d_i)。对相邻两条射线 i 和 i1预测多边形在这一小份区域内的面积是[ S_i^{pred} \frac{1}{2} \cdot d_i \cdot d_{i1} \cdot \sin(\Delta\theta) ]GT 区域面积是[ S_i^{gt} \frac{1}{2} \cdot di \cdot d{i1} \cdot \sin(\Delta\theta) ]而预测和 GT 在这一角度区间内的交集区域因为两条射线方向相同可以用“两边各取较短距离”来构造三角形[ S_i^{inter} \frac{1}{2} \cdot \min(d_i, di) \cdot \min(d{i1}, d_{i1}) \cdot \sin(\Delta\theta) ]对所有角度区间累加就能得到预测多边形总面积 (S_{pred})、GT 总面积 (S_{gt}) 和交集面积 (S_{inter})。并集面积就是[ S_{union} S_{pred} S_{gt} - S_{inter} ]于是 IoU 就是 (\frac{S_{inter}}{S_{union}})。整个过程里只有乘法和 min 操作完全可微这就是 Polar IoU Loss 的核心。3.3 可微性与梯度传导的注意点用这种解析式算 IoU梯度是能传回去的但有个细节要特别注意min函数在某个距离成为最小值的那一侧梯度会为零。比如预测距离 (d_i) 比 GT 的 (d_i) 大时交集区域用的是 (di)那么预测距离 (d_i) 在这个角度区间内对交集面积的梯度就是 0。这时候只能靠并集面积里的 (S{pred}) 项往回传递梯度信号会变弱。我在训练前期经常看到距离分支的 loss 降得很慢排查下来就是这个问题。解决手法一般是把它做成组合损失比如 Polar IoU Loss 为主再加上一个小权重的 L1 或 Smooth L1 作为辅助确保哪怕 IoU 梯度在某些区域失效距离本身仍然有一个明确的回归目标。这样训练会稳很多。4. 训练与推理的工程细节中心采样和 polar centerness4.1 中心点选得好不好直接影响全部 36 条射线PolarMask 把掩模完全托付给了中心点这意味着中心点质量成了整个模型的上限。如果中心点偏了两个像素36 条射线的起点就整体偏移最后拼接出来的多边形整体平移IoU 掉得很快。所以训练时不能简单把目标框内所有像素都当成正样本而是要用中心采样策略只有距离实例中心比较近的像素才作为强正样本参与距离回归离中心远但仍落在框内的像素可以参与分类但距离回归权重降低。这个思路是从 anchor-free 检测器里继承来的但在 PolarMask 里尤为重要。我做的实验里把中心采样半径调得过大模型会把一些“偏离中心的落点”也当成正样本结果这些样本生成的极坐标距离数组彼此冲突同一个角度的距离目标忽大忽小损失曲线直接震荡。后来把半径缩回来收敛速度明显正常了。4.2 Polar centerness用距离极值比过滤低质量中心光有中心采样还不够推理时模型会从每个位置预测距离但并非每个位置都能当中心。有的位置落在目标内部但偏得很靠边从那里发射的射线长短差异极大生成的轮廓质量会很差。所以 PolarMask 引了一个类似 centerness 的指标叫 polar centerness。它的定义思路和 anchor-free 检测器里的 centerness 很像对每个样本计算 36 个射线距离的最小值和最大值的比值再开根号。如果中心点很正各方向距离比较均匀这个值接近 1如果中心点偏在目标边缘某个方向距离很小、另一个方向距离很大这个值就趋近于 0。推理时把 polar centerness 和分类分数乘在一起就能压住那些“中心不理想但分类分较高”的误检。这也是 PolarMask 输出质量稳定性的一个重要保障。4.3 推理时从距离场到掩模的组装推理流程比两阶段方法简洁很多图像经过骨干网络和特征金字塔得到每个位置的中心分类概率、polar centerness、36 维距离对分类分数和 centerness 做加权用阈值筛掉低分位置对中心点做简单邻域抑制避免同一个实例被多个相邻中心同时命中对保留下来的位置把 36 个距离还原成 36 个轮廓点连成多边形。这里最省事的一点是不需要对不同 ROI 做对齐也不需要做逐像素上采样网络一次性输出全图的距离场后处理只做组装。我自己在 CPU 上跑过推理速度比同配置下的两阶段方案快出一截尤其是目标数量多的时候优势更明显。当然省下的时间也不是白来的。逐像素掩模的精细度是高射线多边形在轮廓细节上还是会差一点尤其遇到目标边缘比较复杂的情况36 个点连出来的轮廓会显得“多棱多角”。这个属于表示方式带来的天然精度上限。5. 复现到自己的项目把任意 mask 转为极坐标样本5.1 样本转换的最小实现如果你想把 PolarMask 用在自己的数据集上第一步是把现有 mask 标注转成极坐标距离。我建议先离线转换训练时直接读距离数组不要在数据加载里实时做否则速度会拖累训练。伪代码如下import numpy as np def mask_to_polar(mask, center, num_rays36): # mask: 单个实例的二值掩模 # center: 实例中心点 (cx, cy) # 返回: num_rays 个距离值 ys, xs np.where(mask 0) contour extract_contour(mask) # 轮廓点序列 distances [] for i in range(num_rays): theta 2 * np.pi * i / num_rays ray_dir np.array([np.cos(theta), np.sin(theta)]) hits [] for p1, p2 in zip(contour, np.roll(contour, -1, axis0)): # 计算射线与线段 p1-p2 的交点 t ray_segment_intersection(center, ray_dir, p1, p2) if t is not None and t 1e-6: hits.append(t) if hits: distances.append(max(hits)) # 取最远交点 else: distances.append(0.0) # 理论上中心点在实例内时不会发生 return np.array(distances)这里有几个细节值得说。第一max(hits)对应前面讲的最远交点策略第二中心点一定要先确认在 mask 内部否则某些角度没有交点距离为 0 会导致训练样本损坏第三如果质心落在实例外部最简单的处理是把中心往内部移动比如用轮廓内距离最近的像素点做中心。5.2 训练时最容易踩的坑第一个坑是距离没有归一化。36 维距离如果直接用原始像素值回归小目标几十像素大目标几百像素回归头压力很大训练也难收敛。我习惯按图片短边或目标框最大边归一化一下让数值落在合理范围推理时再乘回去。第二个坑是数据增强。如果做旋转增强必须在 mask 层面做旋转然后重新采样极坐标不能直接对已经生成的极坐标向量做平移或旋转。因为极坐标是相对于中心点的掩模旋转后中心点和每个角度的对应关系都变了。我当时图省事只对向量做随机扰动结果目标稍微转个角度轮廓预测就崩溃。第三个坑是射线数量。论文里一般用 36 条但你的任务如果目标很小36 条射线在微小轮廓上采不到足够的细节多边形容易趋近圆形。反过来目标特别大、轮廓特别复杂时36 条又不够。我的建议是先用 36 跑一版然后跑一个 64 的对比实验看 AP 提升幅度和训练时长是否能接受。5.3 什么任务适合这种表示用极坐标表示做实例分割最大的适用前提是目标近似星形且中心点容易定义。我做过电力巡检里的绝缘子分割、航拍车辆分割这类目标都符合。但如果你的任务是工业零件缺陷检测缺陷形状经常是长条裂纹或不规则凹痕用中心点加射线的方式就会很吃力裂纹方向一旦和射线方向平行距离回归就会严重失真。另外一个适合的场景是部署资源紧张的地方。PolarMask 的掩模头只是一个回归分支运行时的计算开销和最终实例数量无关这对边缘设备非常友好。6. 限制与后续演进6.1 凹形轮廓与重叠实例始终是硬伤星形假设是 PolarMask 最明显的天花板。凹形轮廓、环形目标、多连通区域都不是靠调参能解决的。哪怕你用 72 条射线也没法表达出“一条射线上有两个不同距离的边界”这种结构。所以如果数据的掩模里充满了 C 形、U 形、环形我不建议直接上 PolarMask。重叠实例也是个难点。两个实例叠在一起时中心点预测通常会受遮挡影响靠近遮挡边缘的中心很容易被过滤掉。虽然两阶段方法也会受影响但它毕竟是对每个 ROI 独立做 mask抗遮挡能力会好一些。6.2 射线数量、方向选择与表示的弹性固定角度的发射方式简单但不是最优。后续有不少工作在 PolarMask 基础上做了改进比如让网络预测每个方向的角度偏置或者先粗预测一个轮廓再根据轮廓位置动态调整射线分布。这类做法本质上是给“中心点加射线”的框架增加弹性让射线去贴合目标的实际分布。另一个思路是用频域表示替代极坐标距离。轮廓可以看作一个闭合的周期信号可以用傅里叶系数或 DCT 系数来表示用少量系数拟合复杂轮廓。这类方法在表达凹形轮廓时比固定角度的极坐标更灵活我当时也在自己的数据集上试过效果确实有提升但训练复杂度也上来了需要平衡。6.3 PolarMask 这个思路留下的真正遗产回头看 PolarMask它最值得学习的并不是 36 条射线这个具体数字而是“把掩模问题重新参数化”的思考方式。它把二维掩模回归变成了中心加一维距离回归用一缕轻量且可微的损失函数让单阶段实例分割具备了落地可能。这种“换一种表示方式”的思路后来也影响了很多工作比如把实例分割建模成轮廓点回归或者用可变形卷积去逐步外扩出目标边界再或者用 Transformer 的 query 直接生成目标轮廓。它们的底层逻辑都是像素不是唯一的表达形态向量化、参数化、结构化的表示往往更符合下游部署的需求。我在实际项目里收获最大的一点是任何先进方法都有它的几何前提。复现 PolarMask 之前最好先把自己数据集里的 mask 转成极坐标再转回来算一下往返误差。如果这一步就已经损失了几个点的 IoU那不管训练技巧多好最终效果都会被这一步的上限卡住。反之如果往返误差小这套方法就非常值得一试尤其是当你对推理速度和显存开销都敏感的时候。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑