OvisOCR2 在扫描件里把表格和公式一起抽出来,能不能直接用,主要不取决于它识不认字,而取决于版面结构能否按块保留位置。比较稳妥的路径是先挑几页最接近真实业务的样张,把原图、输出块、坐标三者叠在一起看一遍;对不上的块集中在哪一类版面,就把那类版面划到人工重录或换流程的一侧,剩下的再谈批量和入库。
判断 OvisOCR2 能否用于扫描件数字化的关键在于版面类型和位置信息,而不在于识别率。建议按纯文字页、含表格页、含公式页各取一到两页做样张基线,逐块核对文本块、单元格、公式区能否定位回原图坐标,并记录合并单元格、跨页表格、行内公式的实际输出形态。坐标对不上或结构被压平的页面不直接入库,只有样张上稳定定位、结构可复原的版面才进入批量与抽检流程。
挑三类典型扫描页做样张
样张的价值在于可比对:同一批参数下跑出来的输出,要能和原图逐页对应。选页时避开特例,优先选业务里出现频率最高的版式;每页记下页数、来源、扫描分辨率和扫描方式(灰度、彩色、是否倾斜),后续换参数时才有对照基准。分辨率建议保持与实际入库文件一致,不要用重新导出的高清版做样张,否则线上效果不可比。
| 样张编号 | 版面类型 | 页数 | 来源 | 扫描分辨率 | 备注 |
|---|---|---|---|---|---|
| S1 | 纯文字页 | 1 | 合同正文 | 300dpi 灰度 | 无表格线 |
| S2 | 含表格页 | 2 | 报价单 | 300dpi 彩色 | 含合并单元格表头 |
| S3 | 含公式页 | 1 | 技术说明 | 300dpi 灰度 | 行内公式与正文混排 |
样张登记建议用一份简单清单保存下来,和输出文件同目录,方便回查。字段名按你实际的评测脚本改,这里只是可替换的骨架:
{
'sample_id': 'S2',
'source': '报价单归档',
'pages': 2,
'dpi': 300,
'scan_mode': 'color',
'layout': 'table',
'layout_note': '含跨页表头,表格内无公式'
}
把输出按块对照回原图位置
结构化结果能不能用,很大程度上看每个块是否带位置信息,以及位置是否准。OvisOCR2 的输出字段名在不同部署形态下可能是 bbox、poly 或 points,先原样 dump 一次原始 JSON 看清结构,再决定校验脚本怎么取坐标。核对时分三类块分别看:文本块是否覆盖整段、有没有被切成碎行;表格单元格的行列顺序是否和原图一致;公式区标注的范围是否把上下文字也框了进去。
下面这段只是把块坐标画回原图的通用骨架,图像库和字段路径按你的环境替换,跑完对着原图肉眼比对即可:
from PIL import Image, ImageDraw
import json
img = Image.open('S2_p1.png').convert('RGB')
draw = ImageDraw.Draw(img)
blocks = json.load(open('S2_p1.json'))['blocks'] # 字段路径按实际输出替换
for b in blocks:
x0, y0, x1, y1 = b['bbox']
draw.rectangle([x0, y0, x1, y1], outline=(255, 0, 0), width=2)
img.save('S2_p1_overlay.png')
对照结果按块记录,不要只写“表格识别有问题”这种结论,要指到具体单元格或行:
| 块编号 | 块类型 | 原图位置 | 输出形态 | 是否对齐 | 问题描述 |
|---|---|---|---|---|---|
| B07 | 文本块 | 第1页上部 | 整段 | 是 | — |
| T02 | 表格单元格 | 第2页表头 | 独立单元格 | 否 | 合并表头被拆成两列 |
| F01 | 公式区 | 第1页中部 | 独立块 | 部分 | 框入左侧引导文字 |
确认表格与公式的还原边界
同一个模型对简单版面和复杂版面的还原能力差别明显,判断边界要落到具体结构上。建议在样张输出里专门找三类情况,看它们最后变成了什么:
- 合并单元格:常见输出是拆成独立的行列,rowspan、colspan 之类的合并信息丢失,或者合并区文本被复制到多个单元格。核对时看表头是否出现重复文字、行列数是否比原图多。
- 跨页表格:常见输出是被切成两张独立的表,第二段没有表头,也缺少“续表”标记。核对时看两张表是否有共同的表格 ID 或页码字段;没有就得在入库前自己拼。
- 行内公式:常见输出是被拉成独立的公式块,句子在公式处断成两截,或者公式整体退化成普通字符流。核对时看公式块是否带 inline、display 之类的类型标记,正文句子是否还连续。
这三类结构如果有任何一类在你的样张上出现压平或丢失,就把该类版面标成“需要后处理”,不要默认下游能靠字段名自动还原。表格线缺失、扫描倾斜、印章覆盖单元格的情况会放大这些问题,选样张时建议至少覆盖其中一种。
写清什么情况转人工或换流程
分流规则要能在页级执行,给出可判断的触发条件,而不是让人临场决定。可以按下面的方式定三档,字段名按你的管线替换:
| 分流档位 | 适用版面 | 触发条件 | 下游动作 |
|---|---|---|---|
| 直接入库 | 纯文字页、结构简单的规整表格 | 样张上块坐标对得上,无合并单元格,无跨页,无公式 | 按块写入,保留原图与坐标便于回查 |
| 抽检 | 含少数字段合并的表格、公式与正文分离的页面 | 存在合并单元格但行列顺序可对应,或公式以独立块输出且不影响正文 | 按比例抽页人工核对后再入库 |
| 人工重录 | 跨页表格、行内公式密集页、印章或水印压字的页面 | 表格被切成多段且缺表头,或公式与正文混在一段字符流里,或关键单元格坐标对不上 | 退回人工录入,OCR 结果只作参考底稿 |
规则写成配置更容易长期维护,示例如下,条件项对应你在对照表里记录的实际字段:
route_rules:
- match: {layout: text, coord_check: pass}
action: direct_load
- match: {layout: table, merged_cell: true, cross_page: false}
action: spot_check
- match: {layout: table, cross_page: true}
action: manual_retype
- match: {layout: formula, inline: true}
action: manual_retype
- default:
action: spot_check
落地时先把样张跑通这套路由,再逐步放开新来源的扫描件。每次新增一类版面,重新做一次原图与输出块的对照并更新对照表;人工重录的页面要留存原图与输出,用来判断是参数问题还是版面本身超出能力范围。换模型版本或调整预处理参数后,同一批样张需要重跑一遍,之前“能直接入库”的结论不能直接沿用。