找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

4661

积分

0

好友

609

主题
发表于 2 小时前 | 查看: 6| 回复: 0

TL;DR:NaviDC-OCR 的关键不只是一个 1.2B 的 OCR/VLM 模型,而是一整套围绕“文档解析”重建的数据、训练和推理系统。论文层面,它用 Multi-node Consensus Voting、形变感知建模、CGDP 采样、自验证 VLM、四阶段渐进训练和内容-结构解耦学习,把数字文档与相机拍摄文档统一到一个框架里;代码层面,仓库把这套思路落到了一个清晰的推理链路:infer.py -> read_fn -> engine.py -> model_output_to_middle_json.py -> MagicModel -> vlm_middle_json_mkcontent.py

1. 先说结论:NaviDC-OCR 解决的不是“识字”,而是“结构化还原”

很多人把 OCR 和文档解析混为一谈,但这篇工作实际瞄准的是更难的任务:

  • 文本要读对;
  • 阅读顺序要对;
  • 表格拓扑要对;
  • 公式语法要对;
  • 图片、印章、页眉页脚不能乱;
  • 数字文档和相机拍摄文档要统一处理。

现有方法要么强依赖 layout analysis,一遇到相机拍摄的几何畸变就级联失效;要么端到端生成看似省事,但容易冗余、幻觉、结构推理不足。NaviDC-OCR 的思路是把两类路线的优点拼起来:

  • 保留 VLM 的统一生成能力;
  • 引入几何感知,补两阶段方法最怕的畸变问题;
  • 对表格和公式显式做内容-结构解耦;
  • 用数据引擎和渐进训练把这件事做稳。

与主流模型在三个 Benchmark 上的对比指标如下:

Benchmark 对比指标

2. 论文主线:一套不是“模型”,而是“系统”的方案

该项工作的整体主线如下:

NaviDC-OCR 数据引擎与训练流程

2.1 模型结构:Qwen2.5-VL 视觉编码器 + Qwen3-0.6B 语言模型

NaviDC-OCR 不是直接拿完整的 Qwen2.5-VL 做 OCR 微调。 论文给出的模型组成更具体:它约有 1.2B 参数,由三部分构成:

组件 来源 / 结构 作用
Vision Encoder inherited from Qwen2.5-VL 提取页面图像中的视觉特征与版面线索
Language Model Qwen3-0.6B 生成文本、结构标记、坐标序列和任务输出
Aligner 从零训练的 MLP 对齐视觉表示与语言模型表示空间

也就是说,NaviDC-OCR 更准确的结构是:Qwen2.5-VL 的视觉编码器 + Qwen3-0.6B 语言模型 + MLP Aligner。Qwen2.5-VL 系列模型中参数量最小的版本是 3B,在这里主要使用 3B 的模型提供视觉侧能力,而不是作为完整多模态模型被原样使用。

2.2 Multi-node Consensus Voting:伪标签不是“一个模型说了算”

论文的第一个关键点是 MCV。它不是单模型伪标签,也不是简单的多次推理投票,而是让异构模型互相投票,选择共识最高的结果。

核心形式可以写成:

[
si = \frac{1}{N-1}\sum{j \neq i} \text{sim}(p_i, p_j)
]

其中:

  • (p_i):第 (i) 个模型的预测;
  • (\text{sim}(\cdot)):不同任务对应的一致性度量;
  • (s_i):该预测与其他模型的平均共识分数。

最后选:

[
p^* = \arg\max_i s_i
]

这个设计的意义很现实:文档解析里,错误常常不是随机噪声,而是系统性偏差。单模型生成的伪标签会把偏差一并放大;MCV 的目标,是尽量让“多模型都认可”的结果进训练集。

2.3 几何感知文档建模:把“矩形框”升级成“边界点”

这也是我感觉最有意思的一个点。传统两阶段方法最脆弱的一点,是默认布局区域近似矩形。但相机拍摄文档常常弯曲、折叠、透视畸变,矩形框会漏信息。NaviDC-OCR 的做法是:

  • region-level:用多点边界表示局部区域;
  • point-level:用控制点表示整页形变。

这相当于把 layout detection 变成了更适合畸变文档的 boundary / polygon 预测问题。这类工作在 PaddleOCRVL 中也有过,从文章对比来看 NaviDC-OCR 的效果更好。也就是说,这个模型在这种场景中更加擅长。

2.4 CGDP:NaviDC-OCR 的几何建模亮点

如果只看模型骨架,NaviDC-OCR 很容易被理解成“把 Qwen2.5-VL 用在 OCR / 文档解析上”。但真正让它区别于普通文档 VLM 的地方,是它认真处理了一个传统 OCR 系统经常绕开的细节:相机拍摄文档的版面区域并不总是矩形。

论文提出 Curvature-Guided Douglas-Peucker Sampling,CGDP,它是整篇文章里很值得重点看的设计。它解决的不是“识别什么文字”,而是“当页面被拍弯、拍皱、拍斜之后,模型应该如何用有限的坐标 token 表达一个变形区域的边界”。

传统 layout detection 常把区域表示成矩形框:

[x_min, y_min, x_max, y_max]

这对数字 PDF、扫描件或者轻微透视图像通常够用。但相机拍摄文档里经常出现:

  • 页面整体弯曲,文字行呈弧形;
  • 纸张局部折叠,边界出现尖角;
  • 表格线、公式块或图片区域被非刚性拉伸;
  • 页面边缘翘起,区域轮廓不再能被一个 bbox 贴合。

这时继续使用矩形 bbox,会把几何误差传递给后续流程:裁剪区域不准、阅读顺序错位、表格和公式结构重建失败。NaviDC-OCR 的做法是把 camera-captured layout segmentation 从矩形框检测扩展成 variable-length polygonal point sets

<box:x1 y1 x2 y2 x3 y3 ...><label:category><rotate_dir>

问题随之变成:这些边界点应该怎么采? 这就是 CGDP 的位置。

蓝点表示普通轮廓采样点;红点表示因局部曲率较高而被 CGDP 提升优先级的候选点。

图:CGDP 将 DP distance 的大尺度轮廓保持能力,与 local curvature 的局部结构敏感性结合起来。

2.4.1 为什么 CGDP 可以成为文章亮点?

CGDP 的亮点不在于公式复杂,而在于它把 文档几何表达VLM 生成成本 这两个原本分开的约束连在了一起。

如果边界点太少,模型看起来输出很简洁,但复杂区域会被粗糙近似:弯曲表格被裁成矩形,折痕附近的文本块边界被抹平。反过来,如果给所有区域都密集采样,简单页面也会产生大量坐标 token,VLM 的输出序列变长,格式错误、坐标漂移和截断风险都会上升。

所以 CGDP 实际上是在回答一个很工程的问题:

在固定的结构化输出预算里,哪些坐标点最值得让 VLM 学、让 VLM 生成?

这比“把 bbox 换成 polygon”更进一步。bbox 到 polygon 只是表示能力增强;CGDP 解决的是 polygon 表示里的 点预算分配问题

2.4.2 均匀采样的问题:点数固定,但信息量不固定

最朴素的做法是沿区域边界等间隔采样。它有两个优点:实现简单,输出长度可控。但它隐含了一个很强的假设:边界上每个位置同等重要。这个假设在相机拍摄文档里并不成立:

边界区域 几何特征 均匀采样的问题
平直边缘 曲率低、形变弱 采太多点也只是重复描述直线
大范围弯曲 连续偏离矩形框 需要保留能表达整体弧度的点
折痕 / 尖角 局部方向突变、曲率高 等间隔点可能刚好错过关键拐点
局部翘边 空间范围小但影响裁剪 如果采样不足,后处理会裁偏

这也是论文批评 uniform boundary sampling 的核心:flat regions may contain redundant samples,而 high-curvature areas, such as corners and folds, may be under-sampled。

对文档解析来说,这不是一个“轮廓好不好看”的问题。边界点会影响后面的区域裁剪、layout 重建、阅读顺序组织,以及 Markdown / JSON 输出。因此,采样策略会间接影响最终文档结构质量。

2.4.3 bending 与 creasing

bendingcreasing 是论文用来描述相机拍摄文档几何畸变的两种模式。

Bending 与 Creasing 对比

Bending 指的是大范围、连续、平滑的弯曲。可以理解为“整片区域被慢慢弯过去”:比如书页自然拱起、纸张整体弯曲、页面边缘形成平滑弧线,或者表格区域整体呈弧形偏移。它的空间范围通常较大,曲线相对首尾连线会产生明显偏离,因此更容易被 Douglas-Peucker distance 捕捉。

Creasing 指的是小范围、局部、方向突变的折痕或尖角。可以理解为“某个局部突然折了一下”:比如纸张折痕、页面局部翘起、边缘尖角、表格线局部急转。它的空间范围通常较小,即使局部曲率很高,相对首尾连线的最大距离也不一定大,因此普通 DP 可能把它当成可以忽略的细节。

维度 bending creasing
直观理解 整段边界慢慢弯了 某个局部突然折了一下
空间范围 大范围 小范围
变化方式 连续、平滑 局部突变、方向不连续
几何表现 整体偏离矩形或直线 高曲率、尖角、折痕
更适合的度量 DP distance local curvature
文档场景 书页拱起、纸张整体弯曲、边缘弧形 纸张折痕、局部翘边、表格线急转

所以,论文里的关键判断可以概括为:DP distance 更擅长捕捉 region-level bending;local curvature 更擅长捕捉 point-level creasing。 CGDP 的设计价值,就在于把这两个信号组合起来:用 DP distance 保留大尺度轮廓偏离,用 curvature 补上局部折痕和尖角。

2.4.4 DP 距离:擅长保留大尺度 bending

经典 Douglas-Peucker,DP 算法的目标是曲线简化。给定一段曲线的起点 (P_s) 和终点 (P_e),它用线段 (P_sP_e) 近似这段曲线,然后找出中间点里距离这条线段最远的点:

[
d_i = \text{dist}(P_i, P_sP_e)
]

如果最大距离超过阈值,就保留这个点,并以它为分割点递归处理左右两段;如果没有超过阈值,就认为这段曲线可以用首尾线段近似。

这套机制天然适合捕捉 bending:也就是论文所说的大尺度、连续形变。因为当页面边缘整体弯曲时,中间点会明显偏离首尾连线,DP distance 会变大,算法自然会保留这些点。但 DP distance 不是万能的。论文用一个近似关系说明了它的偏置:

[
d_{\text{DP}} \sim \kappa \cdot L^2
]

其中:

符号 含义 直观解释
(d_{\text{DP}}) DP distance 曲线相对首尾连线的最大偏离程度
(\kappa) local curvature 局部弯曲或方向变化强度
(L) region scale 当前递归段的尺度 / 弦长

这个公式最关键的地方是 (L^2)。它说明 DP distance 不只由曲率决定,还强烈受区域尺度影响。一个大范围缓慢弯曲的边界,即使曲率不高,也可能因为 (L^2) 很大而产生明显 DP distance;一个局部折痕即使曲率很高,也可能因为空间范围小而没有足够大的 DP error。

因此,DP 更偏向捕捉 region-level geometric deviations,也就是区域尺度上的整体偏离。

2.4.5 Creasing 的难点:局部很重要,但 DP error 可能不大

相机拍摄文档里还有另一类形变:creasing。它不是整页慢慢弯过去,而是局部方向突然发生变化,比如纸张折痕、尖角、边缘翘起、表格线局部弯折。

论文对 bending 和 creasing 的区分可以整理为:

形变类型 空间范围 几何表现 单靠 DP 是否可靠 更需要什么
bending 连续弯曲、整体偏移 通常可靠 DP distance
creasing 局部方向不连续、高曲率 可能漏掉 local curvature

这就是 CGDP 要补的缺口:DP 看的是“离当前线段有多远”,但 creasing 更关心“这里方向变得有多急”。

换句话说,DP distance 衡量的是区域级偏离,curvature 衡量的是点级结构重要性。两者不是替代关系,而是互补关系。

2.4.6 CGDP 公式:用曲率调制 DP 点的重要性

基于这个观察,论文提出 CGDP,用 curvature-aware modulation 调整 DP 点的重要性:

[
S_i = d_i \cdot (1 + \lambda \cdot \hat{\kappa}_i)
]

其中:

符号 含义 在算法里的作用
(S_i) 第 (i) 个候选点的采样重要性分数 用来决定该点是否值得成为递归节点
(d_i) DP distance 衡量相对当前线段的大尺度偏离
(\hat{\kappa}_i) normalized local curvature 衡量局部折痕、尖角、方向突变的重要性
(\lambda) curvature modulation weight 控制曲率项对采样优先级的放大程度

这个公式可以拆成两层理解:

  1. 基础分数是 (d_i):CGDP 没有丢掉 DP,因此仍然保留对大尺度 bending 的敏感性;
  2. 乘上 ((1 + \lambda \hat{\kappa}_i)):高曲率点会获得额外加权,从而提高 creasing、corners、folds 被采到的概率。

它有一个很好的退化性质:当局部曲率较低时,(\hat{\kappa}_i \to 0),于是:

[
S_i \approx d_i
]

这时 CGDP 近似退化为标准 DP。

递归节点的选择条件是:

[
\max_i S_i > \tau
]

当当前曲线段中最大的 (S_i) 超过阈值 (\tau),对应点就被选为新的 recursive node,然后继续递归分割边界。阈值 (\tau) 控制采样密度:阈值越低,越容易保留点;阈值越高,边界越容易被简化。

2.4.7 一个小例子:为什么高曲率点会被“救回来”

假设一段边界里有三个候选点:

候选点 场景 DP distance (d_i) normalized curvature (\hat{\kappa}_i) 直觉
A 大范围弯曲中点 DP 本来就容易选中
B 平直边缘上的普通点 没必要保留
C 局部折痕尖点 普通 DP 可能漏掉,CGDP 会提高优先级

普通 DP 只比较 (d_i),因此更偏向 A。CGDP 比较的是:

[
S_i = d_i \cdot (1 + \lambda \hat{\kappa}_i)
]

如果 C 的曲率足够高,那么即使它的 DP distance 不是最大,也可能因为曲率调制项被提升为高优先级点。这个机制正好对应相机拍摄文档里的局部折痕:它的空间范围不大,但对区域裁剪和结构还原很重要。

2.4.8 算法流程:它本质上还是递归曲线简化

从算法上看,CGDP 可以理解成“把 DP 的最大距离准则替换成最大曲率调制分数准则”。伪代码如下:

def cgdp(points, start, end, tau, lambda_):
    # 1. 用当前首尾点构造近似线段
    line = segment(points[start], points[end])

    best_score = -float("inf")
    best_idx = None

    # 2. 遍历当前曲线段中的候选边界点
    for i in range(start + 1, end):
        # region-level deviation: 大尺度几何偏离
        d_i = distance_to_line(points[i], line)

        # point-level structural importance: 局部结构重要性
        kappa_hat_i = normalized_curvature(points, i)

        # curvature-aware modulation
        score_i = d_i * (1 + lambda_ * kappa_hat_i)

        if score_i > best_score:
            best_score = score_i
            best_idx = i

    # 3. 分数没有超过阈值,则当前段可被首尾线段近似
    if best_score <= tau:
        return [points[start], points[end]]

    # 4. 否则保留最高分点,并递归处理左右两段
    left = cgdp(points, start, best_idx, tau, lambda_)
    right = cgdp(points, best_idx, end, tau, lambda_)
    return merge_without_duplicate(left, right)

自适应多边形边界点提取算法流程

这段算法的复杂之处不在递归本身,而在评分函数的设计:它让一个点能否被保留,不再只取决于“离线段多远”,还取决于“是否处在局部结构变化剧烈的位置”。

2.4.9 它在 NaviDC-OCR 数据构造中的位置

用 CGDP 生成 region boundary points,让模型在训练阶段学习这种自适应 polygon 边界表达。到了推理阶段,模型不是再跑一遍 CGDP 去采样边界,而是直接生成已经学过的 variable-length boundary points。

NaviDC-OCR 会把模型输出转换成中间结构,再进入版面重建:

model output -> middle_json -> MagicModel -> markdown / layout pdf / content

在这个链路中,多点边界不是装饰信息,而会影响:

  • model_output_to_middle_json.py 如何承接模型生成的结构化区域;
  • cut_image_and_table 如何根据区域边界裁剪图像、表格等块;
  • MagicModel 如何组织 page_infospanspara_blocks
  • vlm_middle_json_mkcontent.py 如何把结构化中间结果还原成最终 Markdown。

因此,CGDP 的价值主要是在 训练层面,让模型学习更符合相机拍摄文档的 polygon 边界监督。

2.4.10 三种采样策略的差别

采样方式 采样依据 主要优点 主要问题 在 NaviDC-OCR 语境中的意义
均匀采样 等间隔点位 简单、稳定、实现成本低 平坦区域浪费点,高曲率区域可能采样不足 适合规则边界,不适合复杂相机拍摄形变
DP 点到线段距离 (d_i) 能保留大尺度轮廓偏离 小尺度高曲率折痕可能被忽略 能表达 bending,但对 creasing 不够敏感
CGDP (S_i = d_i(1+\lambda \hat{\kappa}_i)) 同时关注全局轮廓和局部结构 依赖曲率估计,超参需要控制 更适合 variable-length polygonal point sets

更直白地说:DP 关心“偏离直线多少”,CGDP 额外关心“这里弯得有多急”。

2.4.11 工程边界与局限

CGDP 的设计很有启发,但也不能过度解读。它至少有几个工程边界:

  1. 曲率估计依赖边界质量
    如果原始边界点或合成形变边界本身有噪声,局部高曲率可能并不代表真实折痕,而只是标注抖动。此时曲率调制可能会放大噪声。

  2. (\lambda)、(\tau) 和最大点数需要共同调节
    (\lambda) 太小,CGDP 接近普通 DP;(\lambda) 太大,高曲率噪声会被过度保留。(\tau) 太低会导致 polygon 过长,增加 VLM 输出压力;(\tau) 太高又会丢失细节。

  3. 论文没有充分给出 uniform / DP / CGDP 的系统消融
    从动机和公式看,CGDP 是合理的;但论文没有把采样策略单独做成非常完整的量化对比。因此更稳妥的表述是:CGDP 是一个强设计点,而不是已经被充分证明的唯一最优采样方案。

  4. 可变长度 polygon 对 VLM 解码仍然有压力
    自适应采样减少了无效点,但复杂区域仍可能输出较长坐标序列。坐标格式、顺序一致性、截断控制和后处理鲁棒性仍然重要。

2.4.12 小结:CGDP 的一句话价值

CGDP 的本质是:

用 Douglas-Peucker 保留大尺度弯曲,用曲率项补足局部折痕和尖角,在有限坐标 token 预算下生成更有信息量的文档区域边界。

这也是为什么它值得作为亮点来讲。它把相机拍摄文档解析中的一个关键矛盾说清楚了:几何表达越细,VLM 生成成本越高;表示太粗,又会伤害裁剪和结构恢复。 CGDP 正是在这两者之间做了一个简洁而有效的折中。

论文中一些数据对比:

复杂表格文档识别效果对比

折叠纸质文件识别效果对比

2.5 Self-Judgement VLM:把验证从 image-text 改成 image-to-image

数据构造与错误检测流程

这部分很容易被忽视,但很重要。作者发现,通用 VLM 做 zero-shot verification 并不可靠,所以把结构化输出先渲染成图像,再和原图做一致性判断。换句话说,验证对象从“文本”变成了“图像”。

这比直接问“这段 markdown 对不对”稳定得多,因为表格和公式的结构错误,最终都会反映到渲染结果里。

2.6 四阶段训练:先对齐,再感知几何,再解耦结构,再用 RL 收尾

主流模型训练的通用范式基础上,NaviDC-OCR 增加了几何改制的文档解析训练。NaviDC-OCR 的训练是四阶段:

  1. 基础 vision-language 对齐;
  2. 几何感知文档解析;
  3. 内容-结构解耦学习;
  4. GRPO 强化学习。

先让模型知道“页面里有什么”,再让它知道“页面怎么弯了”,再让它知道“表格和公式的结构怎么写”,最后再用任务指标去拧细节。

四阶段训练流程与内容结构解耦示意

2.7 内容-结构解耦:表格和公式不是普通文本

这是论文另一个值得单独拿出来讲的点,这个做法也和现有的其他模型有所区别。

  • 公式不能只学字符序列,还要学语法结构;
  • 表格不能只学单元格文本,还要学拓扑;
  • 科学图表到表格,本质上也是结构恢复。

这也是为什么它在 Sci-ImageMiner Challenge 上能拿到第一:模型不是把图里字抄出来,而是把结构一起拉回来。从开源代码来看主要是在训练阶段做了增强,训练端对现有的公式和 OTSL 语言进行拆分,就比较容易构建对应的结构性训练数据。

3. 从论文到代码:仓库到底把哪些东西落地了?

3.1 infer.py:最外层入口很朴素,但足够清楚

infer.py 做的事情非常工程化,构建了 CLI:

  • 读取输入目录;
  • 过滤掉 .json.html
  • 通过 read_fn 统一把图片或 PDF 变成 PDF bytes;
  • 调用 do_parseaio_do_parse
  • 把结果写到输出目录。

3.2 NaviOCR/config.py:默认值就是服务端的现实约束

默认配置很能说明作者怎么想:

  • BACKEND = "vllm-async-engine"
  • LAYOUT_MODE = "Detection"
  • MAX_MODEL_LEN = 16384
  • GPU_MEMORY_UTILIZATION = 0.95
  • PDF_TOOLS = "pypdfium2"
  • PDF_TOOLS_WORKER_MAX_NUM = 4
  • MAX_PIXELS = 8000 * 8000

明确实际服务部署里的现实约束:长上下文、大图、PDF 解析、多线程 worker,都在这里落成了明确参数。

3.3 NaviOCR/tools/read_file.py:输入统一成 PDF bytes

read_fn 逻辑很简单,也很关键:

  • 如果是图片格式,就转成 PDF bytes;
  • 如果本来就是 PDF,就直接读 bytes;
  • 其他格式直接报错。

这样做的好处是,后面的解析管线只需要面对一种输入抽象:PDF bytes。

3.4 NaviOCR/engine.py:真正的解析主流程

doc_analyze / aio_doc_analyze 这两条路径几乎是整个系统的骨架:

  1. _init_model()NaviOCRMODEL_SERVICE 里取模型;
  2. load_images_from_pdf(...) 把 PDF 变成 page images;
  3. predictor.batch_two_step_extract(...) 做页面级推理;
  4. result_to_middle_json(...) 把模型输出转成统一中间结构;
  5. 再由后处理导出 Markdown、middle json 和 layout bbox pdf。

3.5 NaviOCR/src/model_output_to_middle_json.py:把模型输出变成页面信息

这一步最有信息量。它做了三件事:

  • MagicModel(page_blocks, width, height) 规范化版面块;
  • 对 IMAGE/SEAL/CHAR span 调 cut_image_and_table,裁剪出页面局部图;
  • 组装出 page_info,最后形成 middle_json

也就是说,模型输出不是最终结果,只是进入结构化后处理的原材料。

3.6 NaviOCR/src/vlm_middle_json_mkcontent.py:把结构化块拼回 Markdown

这是一个很有意思的地方,因为它不炫技,但很懂文档。

它对不同 block 的处理很细:

  • 普通文本:merge_para_with_text
  • 标题:按层级前缀 #
  • 列表:逐 item 拼接
  • 图片:caption、body、footnote 三段合成
  • 表格:优先保留 HTML
  • 代码:用 fenced code block 包起来,并带上猜测语言

merge_para_with_text 里还有几个很实际的小细节:

  • 全角转半角,但保留标点;
  • 自动做语言检测;
  • CJK 语言不乱塞空格;
  • 英文遇到行尾连字符时尝试去断词;
  • 行间公式和内联公式分开处理。

这类规则看起来琐碎,但恰恰决定了最终 Markdown 是否“像人写出来的”。

4. MagicModel:这份代码里最像“版面重建引擎”的东西

engine.py 像是流程调度,MagicModel 像是结构修复核心。

可以概括成三层:

  1. 坐标标准化:把归一化 bbox 转成像素坐标;
  2. 类型标准化:把原始 block 映射到内部 BlockType / ContentType
  3. 结构重建:把 body、caption、footnote、list 重新绑定。

4.1 caption / footnote / body 的重组

仓库里的 magic_model_utils.py 用的是基于 index 的关联策略,再配合 bbox distance 作为 tie-breaker。这个设计比纯 IoU 更适合文档,因为很多 caption 并不严格和 body 重叠,而是沿阅读顺序邻近。

4.2 列表不是普通文本

fix_list_blocks 会把落在 list bbox 内、且 overlap 足够高的 text/ref_text 拉回列表块里。这个动作很重要,因为很多模型会把列表渲染成一堆散行正文;没有这一步,最终 Markdown 的结构感会差很多。

4.3 图片、表格、代码都被提升成“两层结构”

fix_two_layer_blocks 会把:

  • image_body + image_caption + image_footnote
  • table_body + table_caption + table_footnote
  • code_body + code_caption + code_footnote

重新绑定成一个语义单元。

这一步很适合文档解析:图片、表格、代码本来就不是孤立块,它们和说明文字、注脚是一体的。

5. 代码层最值得盯住的两个小细节

5.1 cut_image_and_table:裁图前先修 bbox

NaviOCR/tools/cut_image.py 里先检查 bbox 是否有效,再决定要不要裁图。若 bbox 超过 4 个点,会先合并成外接矩形。这意味着它能同时兼容普通框和多点轮廓。

这和论文里的“几何感知”是能对上的:多点边界不是摆设,后处理真的要吃得进去。

5.2 NaviOCR/vlm_utils/NaviOCR_model.py:推理后端是可切换的

模型服务封装支持:

  • transformers
  • vllm-engine
  • vllm-async-engine

而默认很明显偏向 vLLM 异步引擎。

6. vLLM 插件告诉我们什么?

由于模型是组合的(qwen2.5vl 视觉模块 + align mlp + qwen3 0.6b),对于 vllm 这类框架就没有对应的模型,需要进行定制化。仓库里的 NaviOCR-vllm/NaviOCR_vllm/qwen2_5_vl.py 不是单纯复制原版模型,而是一个 out-of-tree plugin。它的关键变化是:

  • 保留 Qwen2.5-VL 的视觉塔和多模态处理;
  • 语言模型侧接到 Qwen3ForCausalLM
  • 适配多模态占位符、权重映射和异步推理。

7. 结果怎么看才不失真?

论文和 README 的结果很强,但解读时要分场景。先看看几个指标:

OmniDocBench v1.6 性能对比表

Wild OmniDocBench 性能对比表

PureDocBench 干净与退化场景对比表

7.1 数字文档:整体很稳

论文在 OmniDocBench v1.6 上的 Overall 是 96.87,文本、公式、表格、阅读顺序都很强。这说明它不是只偏某一项,而是整体结构还原能力比较均衡。

7.2 相机拍摄文档:明显比很多 pipeline 更抗畸变

在 Wild-OmniDocBench 上它拿到 88.53,说明几何感知和自适应采样确实在起作用。

7.3 真实退化:不是绝对无敌

在 PureDocBench 的 Real track 上,NaviDC-OCR 不是全场第一。这很重要,因为它提醒我们:

  • 端到端方案很强;
  • 但对真实拍摄退化、极端噪声、复杂手写场景,还不是终局答案。

7.4 复杂表格:这是它最有说服力的胜点之一

Complex-table subset 上,它的 missing rate 很低,说明端到端建模确实减少了 layout 漏检带来的不可恢复错误。从 OmniDocBench v1.6 表格结构和表格内容指标来看很强。与一些同类模型对比图如下:

表格识别效果对比:地形阶梯表

表格识别效果对比:农产品数据表

表格识别效果对比:财务凭证表

8. 总结

NaviDC-OCR 的真正贡献,不是“又做了一个 OCR 模型”,而是把文档解析拆成了一条完整链路:

  • 用 MCV 和合成数据把训练标签做稳;
  • 用几何感知和 CGDP 让模型读得懂相机拍摄文档;
  • 用内容-结构解耦让表格和公式不再只是字符序列;
  • MagicModelmiddle_json 和 Markdown 重建把模型输出变成工程可用结果。

所以它的本质更接近一句话:

不是把整页图像“识字”,而是把整页文档“恢复成结构”。

References

  1. NaviDC-OCR 论文:arXiv:2608.12898
  2. NaviDC-OCR 仓库:https://github.com/caipeng328/NaviDC-OCR
  3. 论文摘要与 HTML 正文中提取的 MCV、CGDP、Self-Judgement、四阶段训练与内容-结构解耦信息
  4. 仓库代码文件:infer.pyNaviOCR/engine.pyNaviOCR/src/model_output_to_middle_json.pyNaviOCR/src/vlm_middle_json_mkcontent.pyNaviOCR/src/vlm_magic_model.pyNaviOCR/tools/cut_image.pyNaviOCR/vlm_utils/NaviOCR_model.pyNaviOCR-vllm/NaviOCR_vllm/qwen2_5_vl.py



上一篇:Spring Cloud Gateway 限流正常,为什么突发流量还是打挂下游服务?
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-9-6 10:59 , Processed in 1.104577 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表