资讯详情

EAST文本检测NMS优化:旋转框IoU加速与抑制策略改进

📅 2026/10/14 12:34:01 | 华诺云谱 👁 阅读
EAST文本检测NMS优化:旋转框IoU加速与抑制策略改进
1. EAST算法与NMS的配合逻辑拆解1.1 为什么EAST的输出必须依赖NMS收尾EASTEfficient and Accurate Scene Text Detector做文本检测时走的是一条“单阶段、稠密预测”的路线。它不像Faster R-CNN那样先出一堆候选框再分类回归而是直接在特征图上对每个像素点预测一个候选几何体——要么是旋转矩形要么是四边形。这种设计的好处是快、结构简单但代价也很明显同一个文本实例会被大量像素点重复预测。你可以把EAST想象成一群人在一张地图上同时圈同一栋楼。每个人圈的位置略有偏差但圈的都是同一栋楼。如果直接把所有人的圈都画出来地图上就是一团糊。NMSNon-Maximum Suppression非极大值抑制在这里扮演的角色就是“把重复的圈合并成一个最靠谱的圈”。具体来说EAST在推理阶段会输出两类信息每个像素点的score map该点是否属于文本和geometry map该点对应的候选框几何参数。经过阈值筛选后可能剩下几百甚至上千个候选框其中绝大多数是重叠的。NMS的任务就是按得分排序逐个保留高分框抑制与它IoU交并比过高的低分框。这里有个关键点EAST的候选框是旋转的。普通水平框的NMS只需要算矩形交集但旋转框的交集计算要复杂得多。这也是为什么EAST的NMS优化核心难点之一就在“旋转IoU怎么算得快、算得准”。1.2 标准NMS在EAST上的三个痛点我在实际跑EAST的时候标准NMS也就是OpenCV里那个cv2.dnn.NMSBoxes或者自己写的水平框NMS直接套上去会遇到三个很现实的问题。第一个痛点是旋转框IoU计算慢。水平框的IoU可以用简单的坐标比较加乘法搞定但旋转框需要做多边形裁剪比如Sutherland-Hodgman算法或者用旋转卡壳求交计算量直接上一个数量级。当候选框数量到几千个时NMS阶段可能比网络前向还耗时。第二个痛点是长文本被切断。EAST对长文本的预测往往是一段一段的相邻段的框IoU可能刚好卡在阈值附近。如果NMS阈值设得偏高比如0.7长文本会被切成多个检测结果设得偏低比如0.3又容易把相邻的两行文本误合并。第三个痛点是密集文本漏检。在文本排列紧密的场景比如票据、表格两个真实文本实例的框本身就有一定重叠。标准NMS按分数一刀切高分框会把低分但真实的相邻文本框直接抑制掉导致漏检。这三个痛点不是孤立的它们共同指向一个结论EAST的NMS不能照搬通用目标检测的那一套需要针对旋转框和文本排列特性做定制。1.3 优化思路的整体框架我后来梳理了一套优化框架分三个层次推进。第一层是几何计算层解决“旋转IoU算得慢”的问题。核心思路是用近似算法替代精确多边形交集在精度损失可控的前提下把速度提上来。第二层是抑制策略层解决“怎么抑制更聪明”的问题。不再单纯按IoU一刀切而是引入得分衰减、局部感知、方向约束等机制。第三层是工程实现层解决“怎么在真实推理管线里落地”的问题。包括候选框预筛选、批量并行、阈值自适应等。下面我按这三个层次展开把每个环节的原理、参数和实操细节都讲清楚。2. 旋转框IoU的加速计算方案2.1 精确旋转IoU的计算代价分析先说说精确计算旋转框IoU到底慢在哪。两个旋转矩形求交集标准做法是把两个矩形分别表示为四个顶点。用Sutherland-Hodgman算法做多边形裁剪得到交集多边形。用鞋带公式算交集多边形面积。用两个矩形面积之和减去交集面积得到并集面积。交集面积除以并集面积得到IoU。这个流程里第2步是性能瓶颈。每次裁剪都要遍历多边形的边判断顶点在内还是在外再插值求交点。两个矩形最多产生8个交点整个裁剪过程涉及几十次浮点运算和条件判断。如果候选框有2000个NMS的朴素实现要比较2000×1999/2约200万对每对都做一次完整裁剪耗时直接爆炸。我实测过在一台普通配置的机器上2000个旋转框做精确NMS单线程要跑1.5秒以上。而EAST网络前向本身可能只要几十毫秒。NMS成了整个管线的最大瓶颈这显然不合理。2.2 用外接矩形近似做快速预筛选优化的第一步不是直接改IoU算法而是先做一层廉价的预筛选。思路很简单如果两个旋转框的外接水平矩形Axis-Aligned Bounding BoxAABB的IoU都很低那它们的旋转IoU一定更低。具体操作是对每个旋转框先算出它的AABB。AABB的IoU计算是纯坐标运算几次加减乘除就搞定速度极快。然后设一个比目标NMS阈值更宽松的预筛选阈值比如目标阈值是0.5预筛选阈值就设0.3。只有AABB IoU大于0.3的框对才进入精确旋转IoU计算。这一步能砍掉多少计算量在典型场景文本检测里大部分候选框彼此距离较远AABB IoU低于0.3的占比通常在80%以上。也就是说精确计算量直接降到原来的不到20%。我实测下来预筛选这一步本身耗时可以忽略但整体NMS时间从1.5秒降到了0.3秒左右。注意预筛选阈值不能设得比目标阈值还高否则会漏掉一些AABB IoU低但旋转IoU高的框对。虽然这种情况少见但在极端角度下确实存在。稳妥做法是预筛选阈值取目标阈值的0.6到0.8倍。2.3 基于三角剖分的近似IoU算法预筛选之后剩下的框对还是要算旋转IoU。如果继续用精确裁剪0.3秒里大部分时间还是花在这。我试过几种近似算法最后比较满意的是三角剖分近似法。思路是把每个旋转矩形拆成两个三角形然后用三角形与三角形的交集来近似矩形交集。三角形交集的计算比多边形裁剪简单因为三角形只有三条边裁剪逻辑可以硬编码减少循环和分支。两个矩形各拆两个三角形最多做4次三角形对交集取并集面积作为近似交集面积。这个近似会带来多大误差我做过对比测试在角度偏差不超过15度的情况下近似IoU与精确IoU的误差通常在0.02以内。对于NMS这种本身就有阈值容差的场景这个误差完全可以接受。而速度上三角剖分近似比精确裁剪快3到5倍。还有一种更激进的近似叫中心点距离替代法直接用两个框中心点的欧氏距离和框尺寸算一个伪IoU。这个方法极快但误差大我只在候选框极多、对精度要求不高的粗筛阶段用。精细NMS还是用三角剖分。2.4 参数选择与实测对比把上面两步合起来完整的快速旋转NMS流程是按得分对所有候选框降序排序。从最高分框开始计算它与剩余框的AABB IoU。AABB IoU低于预筛选阈值的直接跳过。高于预筛选阈值的用三角剖分近似算旋转IoU。旋转IoU高于NMS阈值的抑制掉低分框。重复直到处理完所有框。关键参数有两个预筛选阈值和NMS阈值。预筛选阈值我一般设成NMS阈值的0.7倍。NMS阈值则要看场景通用场景文本检测用0.5比较稳密集文本用0.3到0.4避免误合并长文本为主用0.6到0.7避免切断。下面是我在一组测试图上的实测数据候选框数量约1800个方案NMS耗时召回率精确率精确旋转IoU1.52s0.910.93AABB预筛选精确0.31s0.910.93AABB预筛选三角近似0.09s0.900.92纯中心点距离0.03s0.840.88可以看到AABB预筛选加三角近似在召回率和精确率只掉1个百分点的前提下速度提升了将近17倍。这个性价比在实际项目里是非常划算的。3. 抑制策略的智能化改进3.1 得分衰减替代硬抑制标准NMS是“硬抑制”只要IoU超过阈值低分框直接删掉得分归零。这在密集文本场景下太粗暴了。两个相邻文本实例高分框把低分但真实的框直接抹掉漏检就发生了。我采用的改进是得分衰减Score Decay。不直接删除低分框而是按IoU大小给它一个衰减系数。IoU越高衰减越狠IoU刚好超过阈值时衰减温和一些。公式可以写成new_score old_score * (1 - IoU) ^ gamma其中gamma控制衰减曲线的陡峭程度。gamma取1时是线性衰减取2时高IoU的框衰减更快。我一般取1.5左右实测比较平衡。衰减之后低分框不会立刻消失而是带着一个更低的分数继续参与后续比较。如果它确实是真实文本即使被衰减仍有可能在后续轮次中被保留因为其他框也被衰减了。如果它是重复框衰减后分数会迅速掉到阈值以下自然被淘汰。这个改动看起来小但在密集票据场景下召回率能提升3到5个百分点。代价是NMS的循环轮次可能增加但因为有了前面的快速IoU整体耗时增加不到10%。3.2 局部感知的抑制范围限制另一个改进是限制抑制范围。标准NMS里一个高分框会和所有剩余框比较哪怕那个框在图像另一头。这没必要因为距离远的框本来就不该被抑制。做法是对每个高分框只和它中心点距离在一定范围内的框做IoU比较。范围阈值可以设为该框对角线长度的2到3倍。超出范围的框直接跳过不参与抑制。这个改动的收益在候选框多的时候特别明显。因为大部分框对的距离都很远限制范围后实际参与IoU计算的框对数量大幅下降。而且它和AABB预筛选是互补的AABB预筛选过滤的是“空间上不重叠”的框对范围限制过滤的是“空间上离得远”的框对。两者结合计算量进一步压缩。实操心得范围阈值不要设得太小。我一开始设成对角线长度的1.5倍结果一些长文本的首尾段因为距离远而没被合并长文本被切断了。后来改成2.5倍问题解决。这个参数和文本的长宽比有关长文本多就设大一点。3.3 方向一致性约束EAST输出的是旋转框每个框有一个角度。同一个文本实例的多个候选框角度应该大致一致。而相邻的不同文本实例角度可能不同比如一个横排一个竖排。利用这个特性我在NMS里加了一个方向一致性约束只有当两个框的角度差小于某个阈值比如15度时才认为它们可能是同一个实例才做IoU抑制。角度差大的即使IoU高也不抑制。这个约束解决了一个典型问题十字交叉的两条文本交叉区域的框IoU可能很高但方向不同。标准NMS会把其中一条抑制掉导致漏检。加了方向约束后两条都能保留。角度差阈值怎么定我一般取10到20度。太小了同一文本的候选框角度波动可能超过阈值导致该合并的没合并太大了起不到区分作用。15度是个比较稳的中间值。3.4 自适应阈值的动态调整固定NMS阈值在不同图像上表现差异很大。有的图文本稀疏阈值可以高一点有的图文本密集阈值要低一点。手动调不现实我做了个简单的自适应阈值。思路是先统计所有候选框的IoU分布。如果高IoU框对占比很大说明候选框重叠严重可能是密集文本阈值调低如果高IoU框对很少说明候选框比较分散阈值调高。具体实现随机采样一部分框对算它们的IoU取中位数。如果中位数大于0.4NMS阈值设为0.35中位数在0.2到0.4之间设为0.45小于0.2设为0.55。这个分段策略比较粗糙但实测比固定阈值稳。更精细的做法是用一个小型回归模型预测最优阈值但那就重了。对于大多数项目分段自适应已经够用。4. 工程落地与推理管线整合4.1 候选框预筛选与数量控制NMS再快如果候选框数量本身失控也白搭。EAST在低阈值下可能输出上万个候选框这时候第一步应该是在NMS之前做数量控制。我的做法是先按得分排序只保留前N个候选框进入NMS。N取多少看场景。通用检测取1000到1500密集文本取2000到3000。超过这个数量的低分框即使真实得分也太低了保留意义不大。还有一个技巧是按得分分层。高分框比如得分大于0.8全部保留中分框0.5到0.8保留一部分低分框小于0.5只保留很少。这样既控制了总数又不会把真正的高分框漏掉。注意预筛选的得分阈值不要设得太高。我见过有人直接把得分小于0.5的全扔了结果一些模糊文本的召回率掉得厉害。稳妥做法是保留得分大于0.3的框再按数量截断。4.2 批量并行与向量化实现Python里写NMS最怕的就是双重循环。2000个框的双重循环即使每次比较很快Python的解释器开销也会拖垮性能。解决办法是向量化。具体来说用NumPy把候选框的坐标、角度、得分都存成数组。计算AABB IoU时用广播机制一次性算出当前框与所有剩余框的IoU而不是逐个循环。三角剖分近似也可以用向量化的多边形面积计算来加速。如果候选框特别多还可以用分块并行。把候选框分成若干块每块独立做NMS最后再合并。块之间可能有重叠合并时再做一次小规模NMS。这样能利用多核进一步提速。我用NumPy向量化重写后同样的1800个框NMS耗时从0.09秒降到了0.04秒。如果再用多进程分块能压到0.02秒以内。对于实时推理管线这个速度已经不会成为瓶颈了。4.3 与EAST后处理的衔接细节NMS不是孤立的它和EAST的后处理其他环节要衔接好。这里有几个细节容易踩坑。第一个是几何参数还原。EAST输出的geometry map可能是偏移量形式比如到四个顶点的距离需要先还原成绝对坐标再送NMS。如果还原错了NMS算出来的IoU全是错的。我建议在还原后加一个可视化检查确认框的位置和角度没问题。第二个是得分归一化。EAST的score map经过sigmoid后是0到1但有些实现会做额外的缩放。NMS对得分的绝对大小不敏感但对得分的相对顺序敏感。确保得分是单调的别出现高分框被低分框抑制的情况。第三个是框的格式统一。旋转框有几种表示法中心点加宽高加角度、四顶点坐标、两个角点加角度等。NMS内部要统一用一种格式别混用。我一般统一成“中心点宽高角度”因为算AABB和三角剖分都方便。4.4 性能与精度的平衡取舍最后说说平衡。优化NMS本质上是在速度、精度、实现复杂度之间做取舍。如果你做的是离线批处理对速度不敏感那就用精确旋转IoU加标准NMS精度最高代码最简单。如果你做的是实时检测那AABB预筛选加三角近似是必选项精度损失很小速度提升巨大。如果你做的是嵌入式或移动端那可能还要加上候选框数量控制和分块并行把内存和计算都压到最低。我的建议是先跑通精确版本确认精度基线再逐步加优化每加一步测一次精度和速度。别一上来就堆一堆近似最后精度掉了都不知道是哪一步导致的。另外NMS的优化不是一劳永逸的。换数据集、换场景、换EAST的backbone最优参数都可能变。我一般会在验证集上做一个小网格搜索把预筛选阈值、NMS阈值、得分衰减gamma、方向约束角度这几个参数一起调。虽然费点时间但比拍脑袋设参数靠谱得多。5. 常见问题与排查技巧实录5.1 长文本被切断怎么调长文本切断是EAST NMS最常遇到的问题。表现是一行很长的文本检测结果变成两三个短框。排查思路先看被切断处的两个框的IoU是多少。如果IoU在0.3到0.5之间说明NMS阈值设高了把本该合并的框抑制掉了。解决办法是把NMS阈值降到0.3到0.4。如果IoU低于0.3那说明EAST本身就没预测出连续的框问题在网络或后处理不在NMS。还有一个可能是方向约束太严。长文本的候选框角度可能有几度波动如果方向约束阈值设成10度波动超过10度的框就不合并了。把方向约束放宽到20度试试。5.2 密集文本漏检的排查路径密集文本漏检表现是相邻的两行文本只检测出一行。先确认是不是NMS导致的。把NMS阈值调到0.7甚至0.8如果漏检消失那就是NMS误抑制。解决办法是启用得分衰减或者降低NMS阈值配合方向约束。如果调高阈值后漏检还在那可能是EAST的score map在密集区域本身就分不开。这时候要考虑换更精细的backbone或者调整训练时的正负样本定义。实操心得密集文本场景我一般会把得分衰减的gamma设到2.0让高IoU框衰减更快给低分真实框留出存活空间。同时方向约束设到10度因为密集文本通常方向一致方向约束可以卡得严一点。5.3 NMS耗时异常的定位方法NMS耗时突然变长一般有三个原因。一是候选框数量暴增。检查EAST的得分阈值是不是设太低了或者输入图像分辨率是不是变大了。候选框数量和像素数成正比分辨率翻倍候选框可能翻四倍。二是预筛选没生效。检查AABB预筛选的阈值是不是设得比NMS阈值还高导致预筛选没过滤掉任何框对。或者检查代码里是不是忘了加预筛选逻辑。三是IoU计算退化。如果所有框都挤在一起AABB IoU都很高预筛选就失效了所有框对都要做精确计算。这种情况要么提高得分阈值减少候选框要么用更激进的近似算法。5.4 参数速查与调参建议下面这张表是我总结的常用参数范围和调参方向可以直接参考。参数常用范围调大效果调小效果预筛选阈值NMS阈值的0.6-0.8倍更快可能漏合并更准更慢NMS阈值0.3-0.7减少合并长文本易断增加合并密集文本易漏得分衰减gamma1.0-2.5衰减更狠召回高衰减温和精确高方向约束角度10-20度合并更宽松可能误合合并更严可能漏合候选框上限1000-3000召回高速度慢速度快召回低调参顺序建议先定NMS阈值决定合并力度再定方向约束决定方向过滤然后调得分衰减决定密集场景表现最后调预筛选阈值决定速度。每调一个在验证集上看召回和精确的变化别一次调多个。5.5 几个容易忽略的实现细节最后分享几个我在实现中踩过的坑。第一个是角度归一化。旋转框的角度有周期性-90度和90度是等价的。如果两个框角度一个是-89度一个是89度直接相减得到178度方向约束会认为它们方向相反。正确做法是把角度差归一化到-90到90度之间再比较。第二个是零面积框。EAST偶尔会输出宽或高为0的退化框。这种框算IoU时会出现除零错误。在NMS之前加一个过滤把宽高小于1像素的框直接扔掉。第三个是得分相同的情况。如果两个框得分完全相同排序顺序会影响NMS结果。稳妥做法是在得分相同时按框的面积或位置做二次排序保证结果可复现。第四个是浮点精度。IoU比较时如果两个框刚好在阈值边界上浮点误差可能导致结果不稳定。可以在比较时加一个很小的epsilon比如1e-6避免边界抖动。这些细节看起来小但在实际项目里往往就是它们决定了NMS是稳定运行还是偶尔抽风。我现在的习惯是每次实现完NMS都会用一组边界用例完全重叠、完全不重叠、刚好在阈值上、角度相差90度等跑一遍确认没有异常再上真实数据。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑