登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

视觉模型多个检测框怎样按阅读顺序排序

来源:17golang原创

时间:2026-10-18 11:46:27 375浏览 收藏

视觉模型返回多个检测框后,若目标是单栏、从左到右的横排文本,我会先统一坐标,再把纵向位置接近的框分到同一行,最后按行内横坐标排序。只对所有框执行 sorted(boxes, key=(y, x)) 不够:同一行的右侧框只要略微偏上,就可能被提前输出。

我更愿意把这件事当作一段有明确适用边界的后处理,而不是要求视觉模型天然给出正确阅读顺序。下面以四个手工构造的文本框说明我会怎样组织实现,未运行模型、未测量准确率,也不以示意图代替测试结果。

先限定单栏横排和检测对象

目标检测回答的是“哪些对象位于哪里”,不自动等于“这些对象应该先读谁”。Hugging Face Transformers 的官方目标检测说明涉及边界框和标签;其官方入口为 https://huggingface.co/docs/transformers/main/en/tasks/object_detection。不同任务和模型的输出约定要分别查看,不能把返回数组的索引或置信度高低当阅读次序。

我的最小输入契约是:一页、同一坐标系、文字已经转正、单栏横排、框大致属于相同粒度且高度相近。先把非文本对象和重复检测交给上游处理。若一个框代表整段文字,另一个框代表单字,直接混排就已经失去“行”的统一含义。

这也是我不直接按面积或 score 排序的原因。面积描述覆盖范围,score 描述检测信心,它们都不能解释左右阅读方向。保留这些原始字段方便追溯,但不用它们替代坐标规则。

把所有框转换到同一张图上的 xyxy

本例统一为 [x1, y1, x2, y2],左上为原点,x 向右增加,y 向下增加,单位是同页原图像素。每个框保留唯一字符串 id,后续只增加行号,不丢弃文本、标签或置信度字段。

如果输入是 COCO 的 [x, y, width, height],右下角应换算为 x + width 和 y + height;如果是归一化中心坐标,则先按其定义换成角点,再映射到图像宽高。不要把归一化坐标与像素坐标放在同一列表里,也不要把宽高误当右下角。

对于裁切小图、缩放图或带 padding 的模型输入,先确认后处理输出属于哪个坐标空间。裁切偏移、缩放比例和旋转变换要一致地应用于所有框。本例函数不替你完成这些变换,而是检查有限数、正宽高与唯一 ID,尽早拒绝明显不合法的输入。

中心纵坐标 cy = (y1 + y2) / 2 用于分行,框高 h = y2 - y1 用于估计容差,行内位置使用左边界 x1。这些字段共同构成后续算法的几何输入。

几何输入分组包括 xyxy、cy、h,追溯与规则分组包括 id、行内位置和分行容差
图1:坐标契约静态结构图;展示几何字段的依赖与记录关联,不是检测截图。

先分行,再对每行从左到右排序

如果只是固定字体的横排表单,我会从一个可解释的启发式开始:取所有框高中位数的 0.45 倍作为纵向中心容差。按 cy 从上到下遍历,每一行以第一个成员的 cy 作为固定锚点;后续框只有与锚点足够接近,才属于这一行。行内再按 x1 排序。

固定锚点是我在这段示例中的取舍:它避免“甲靠近乙、乙靠近丙,就把越来越远的甲丙也串进同一行”的邻接链。代价是斜行和混合字号可能被拆开。0.45 不是模型标准参数,也不是适用于任何页面的阈值;应当用自己的标注样本选择。

from math import isfinite
from statistics import median

def reading_order(boxes, ratio=0.45):
    # 输入仅限同页、同方向、单栏横排的相近高度文本框;不负责检测和去重。
    if not isfinite(ratio) or not 0 

上述代码只使用 Python 标准库。分行对象 rows 包含固定 anchor 与成员 items;输入框被保存在成员的 box 字段,输出记录在原字段之外增加零起始 row。原始输入不被原地改写;如原记录已经有 row 字段,本函数会用新行号覆盖它,调用前应明确这一约定。

我保留 ID 作为最后的兜底键,是为了坐标完全相同时也能给出确定结果,而不是让随机检测数组顺序悄悄影响输出。确定性并不能解决两个重叠框谁先读的语义问题,那仍属于上游去重或区域解释。

rows 行容器关联 anchor 与 items,成员关联原框 box,输出关联 row 与 id
图2:分行容器静态关系图;重点看成员归属与字段保留,不把连线理解为执行步骤。

用四个框检查排序意图

这组手工样本中,A 和 B 的高度均为 20,中心分别是 22 与 20;C 与 D 的中心分别是 60 与 62。容差为 9,因此两组各自成行。直接按 y1 全局排序会把 B 放在 A 前面,而行内横坐标规则会把 A 放在 B 前面。

按本例规则推导,预期打印内容如下,不是已运行的输出:

[('A', 0), ('B', 0), ('C', 1), ('D', 1)]

我会在自己的测试里加入输入打乱、整体平移、等比例缩放、同坐标不同 ID、空列表和退化框。检查的不只是最终字符串:每个输入 ID 应出现一次,行内次序应符合约定,无效框应该被明确拒绝。这些是读者实施时的测试建议,不表示本文已经执行。

阶段需要确定的约定常见误区
坐标转换原点、单位、角点和图像变换把 width 当 x2
分行固定锚点与可调容差用每两个相邻框串成无限长链
行内排序本例从左到右,ID 兜底用检测 score 决定阅读次序
结果关联保留原 ID 和行号只输出文本而丢掉框映射

遇到多栏和复杂版面就停止套用

这套简单规则最不适合的场景,是两栏文章:两栏顶部的文字很可能被分进同一行,产生左栏一句、右栏一句的交错结果。若业务要求先读完整左栏再读右栏,就应先识别区域或栏,再在各区域内排序,不能仅靠扩大或缩小分行容差解决。

大标题、跨栏图片、脚注、表格、竖排文本和倾斜文本也需要更上层的结构。PaddleOCR 官方布局分析文档把阅读顺序恢复作为专门能力描述,说明框位置之外还需要布局关系。是否直接使用某个模型提供的有序输出,要按该模型和版本的明确契约决定;不要把通用目标检测输出一概重排。

对我而言,这段启发式适合固定横排模板、清楚的区域内文本,以及需要可解释基线的项目;它不适合拿来宣称“任何视觉模型输出都能正确阅读”。算力省下来的同时,适用范围也必须收窄。

能否直接按 y 坐标取整分行

固定像素桶会受图像缩放和桶边界影响。两段属于同一行的文字恰好跨桶时仍会被拆开。本例按框高中位数调整容差,但它也仍是启发式,不免除样本测试。

从右到左的文字怎么办

先明确语言与段落方向。只把行内 x 排序反转不一定能处理混合方向文字;本例仅承诺从左到右,不对双向排版作保证。

框内有多行文字还能用吗

如果每个框已经是一整段,本例会把“段落中心”当“行中心”,容易误判。应当统一粒度或改用区域级阅读顺序策略,不要混用单字、单行与整段框。

官方资料

Python 排序键与稳定性:https://docs.python.org/3/howto/sorting.html

PaddleOCR 布局分析:https://www.paddleocr.ai/main/en/version3.x/module_usage/layout_analysis.html

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>