Optimizing tool selection...表格识别模块概览(MinerU rec)
slanet_plus)和有线/有框(UNet 线检测,unet_table)。入口在 RapidTable.py,会并行跑两路并做选择。有线 UNet 流程(unet_table/main.py, table_structure_unet.py, table_recover.py)
get_table_line 等):
table_recover.py → recover_logic_table):
start/ocr_add_text):
无线 SLaNet 流程(slanet_plus/main.py, table_structure.py, matcher.py)
recover 将 bbox 按原图比率复原。matcher 将 OCR 框与 cell 对齐,生成 HTML、逻辑网格、结构 token 序列。结果选择逻辑(RapidTable.py)
关键阈值/注意点
MINERU_*_OP_NUM_THREADS 控制;大图建议下采样后再推理。典型适用建议
1) 票据/对账单等有清晰网格:优先有线 UNet。
2) 表格线缺失或扫描淡线:优先无线 SLaNet。
3) 不确定时用 RapidTable 让双路竞争并自动择优。
1) 无线先跑
table_res_list_all_page,含 table_img(1:1裁剪,供无线)和 wired_table_img(10/3放大裁剪,供有线)。AtomicModel.WirelessTable → batch_predict,生成 table_res["html"](无线 HTML)。2) 条件再跑有线并比对
AtomicModel.WiredTable.predict,传入 wired_table_img、ocr_result、以及已算好的无线 HTML(作为 fallback)。这样做的原因
若要查看选择细节,请看 unet_table/main.py 中的 UnetTableModel.predict(约257-346行)。
| 维度 | MinerU (UNet有线) | PaddleX (适配器改进) |
|---|---|---|
| 单元格来源 | 线检测 → 网格恢复 | 目标检测模型直接输出 |
| 行分组逻辑 | 全局聚类(阈值~10px) | 按垂直重叠度动态分组 |
| 合并单元格判断 | 宽高公差固定(~10px) | 动态基于行高的50% |
| 缺失单元格补全 | 无(仅靠OCR填充) | 有(推断边界) |
| 重叠单元格处理 | 无显式处理 | 显式合并(IOU阈值) |
| 坐标系统 | 单一(原图坐标) | 多阶段转换(裁剪→原图) |
unet_table/table_recover.py)# 伪代码:固定阈值聚类
rows = []
for cell in sorted_cells:
if abs(cell.y_center - last_row.y_center) < row_threshold: # 阈值~10px
last_row.append(cell)
else:
rows.append(last_row)
last_row = [cell]
问题:
build_robust_html_from_cells)# 垂直重叠度判断
overlap_height = max(0, overlap_y2 - overlap_y1)
avg_height = (cell_height + row_height) / 2
if overlap_height > avg_height * 0.5: # 相对阈值
current_row.append(cell)
row_y1 = min(row_y1, cell[1]) # 扩展行范围
row_y2 = max(row_y2, cell[3])
优点:
row_y1/row_y2更新)确保后续单元格也能被纳入。评分:PaddleX 胜(相对阈值 > 绝对阈值)
table_recover.py - recover_logic_table)# 伪代码:固定宽高公差
if abs(cell_width - adjacent_width) <= 10 and \
abs(cell_height - adjacent_height) <= 10:
# 判定为合并
问题:
merge_overlapping_cells_in_row)# 基于IOU与面积比例
inter_area = max(0, inter_x2 - inter_x1) * max(0, inter_y2 - inter_y1)
current_area = (current_cell[2] - current_cell[0]) * (current_cell[3] - current_cell[1])
if inter_area > min(current_area, next_area) * 0.5: # IOU阈值50%
# 合并为外包围框
current_cell = [min_x, min_y, max_x, max_y]
优点:
评分:PaddleX 大幅领先(相对IOU > 绝对距离)
fill_blank_rec 中对无OCR匹配的单元格做额外 OCR 推理。infer_missing_cells_from_ocr)# 步骤1:找出未被任何检测单元格覆盖的OCR框
uncovered_ocr = [ocr for ocr in all_ocr if not covered_by_any_cell(ocr)]
# 步骤2:按行分组已检测单元格
rows = group_cells_by_row(detected_cells)
# 步骤3:为每个uncovered_ocr推断单元格边界
for ocr in uncovered_ocr:
row_idx = find_row_containing(ocr)
cell_y1, cell_y2 = rows[row_idx].y_range # 统一行高
cell_x1 = max(cell.x2 for cell in rows[row_idx] if cell.x2 < ocr.x)
cell_x2 = min(cell.x1 for cell in rows[row_idx] if cell.x1 > ocr.x)
inferred_cells.append([cell_x1, cell_y1, cell_x2, cell_y2])
优点:
风险:
评分:PaddleX 关键优势(MinerU 无此能力)
# Step 1: 检测坐标(在裁剪图内)
table_cells_result # [x1,y1,x2,y2] in cropped image
# Step 2: 转换到原图坐标
crop_start_point = [table_box[0], table_box[1]]
table_cells_result_orig = convert_table_structure_pred_bbox(
table_cells_result, crop_start_point, img_shape
)
# Step 3: 在原图坐标上做后处理
html = build_robust_html_from_cells(table_cells_result_orig)
# Step 4: 用原图 OCR 结果填充
fill_html_with_ocr_by_bbox(html, overall_ocr_boxes, overall_ocr_texts)
优点:
评分:PaddleX 更规范
match_ocr_cell + plot_html_table)# 方案:逐单元格查找 OCR 匹配
for cell in polygons:
for ocr_box in ocr_result:
if is_box_contained(ocr_box, cell, threshold=0.6) or IOU > 0.8:
cell_content = ocr_text
特点:
fill_html_with_ocr_by_bbox)# 方案:按中心点快速定位
for ocr in ocr_items:
ocr_center = (ocr.x_center, ocr.y_center)
for td in html.find_all('td'):
cell_box = parse_data_bbox(td)
if cell_box.contains(ocr_center):
td.text = ocr.text
优点:
data-bbox 便于调试与微调。评分:PaddleX 略优(中心点更稳定)
| 指标 | MinerU | PaddleX | 胜者 |
|---|---|---|---|
| 行分组 | ⭐⭐⭐ 固定阈值 | ⭐⭐⭐⭐⭐ 相对重叠 | PaddleX |
| 合并判断 | ⭐⭐⭐ 固定距离 | ⭐⭐⭐⭐⭐ IOU面积比 | PaddleX |
| 缺失补全 | ❌ 无 | ⭐⭐⭐⭐⭐ 有 | PaddleX |
| 坐标管理 | ⭐⭐⭐ 单一 | ⭐⭐⭐⭐⭐ 显式转换 | PaddleX |
| 内容填充 | ⭐⭐⭐⭐ IOU/包含度 | ⭐⭐⭐⭐⭐ 中心点+排序 | PaddleX |
| 生产可靠性 | ⭐⭐⭐⭐ 验证充分 | ⭐⭐⭐⭐ 框架设计更好 | 平 |
总体胜者:PaddleX 适配器(+40% 鲁棒性)
data-bbox 记录便于调试与参数优化。若要在生产环境取得最佳效果,考虑:
# 融合方案:MinerU线检测精准 + PaddleX后处理鲁棒
1. 用MinerU的UNet检测线和初始多边形
2. 用PaddleX的行分组(相对重叠)+ 合并策略(IOU) 替换table_recover
3. 加入缺失补全逻辑 (infer_missing_cells_from_ocr)
4. 用中心点填充而非IOU匹配
这样既保留线检测的精准性,又获得现代后处理的鲁棒性。
# MinerU UNet 线检测 vs PaddleX 目标检测
┌─────────────────────────────────────────────────────────┐
│ MinerU: 线 → 交点 → 网格 │
│ ✅ 几何精准:直接从像素线推导单元格边界 │
│ ✅ 规则表格专家:对清晰线条的网格表格几乎100%准确 │
│ ✅ 无漏检风险:线不会"漏检"(只要存在就能检出) │
│ │
│ PaddleX: 直接预测单元格框 │
│ ❌ 检测模型漏检:复杂表格易漏掉某些单元格 │
│ ❌ 神经网络黑盒:难以理解为什么预测错误 │
│ ❌ 需要"补全"才能fix │
└─────────────────────────────────────────────────────────┘
原图:
┌─────┬─────┬─────┐
│ A │ B │ C │
├─────┼─────┼─────┤
│ D │ E │ F │
└─────┴─────┴─────┘
UNet输出:
横线掩码:[━━━━━━━━━━](3条清晰横线)
竖线掩码:[┃ ┃ ┃ ┃](4条清晰竖线)
→ 求交点 → 得到精确的6个单元格框
✅ 100% 对应真实网格
原图同上
检测输出:
[框A], [框B], [框C], [框D], [框E], [框F]
问题:
❌ 若模型漏检了[框E],后续补全猜测:
- E的边界在哪?靠 OCR 位置?
- 不确定!需要启动"缺失补全"逻辑
❌ 倾斜/扫描变形时,框的边界可能不对齐
结论:对于规则表格(如票据、对账单、标准报表),线检测 = 几何真理,无需猜测。
# 现实场景对比
┌────────────────────────────────────────┐
│ 场景1:清晰网格表格(常见) │
├────────────────────────────────────────┤
│ 线检测: │
│ - 输入:清晰的黑色或灰色线条 │
│ - 输出:精确的线段坐标(几何不变) │
│ - 失败率:<1%(仅在极端模糊时) │
│ │
│ 单元格检测: │
│ - 输入:单元格内的内容(背景+文字+线) │
│ - 输出:预测的单元格框(分类任务) │
│ - 失败率:5-15%(复杂表格易漏检) │
└────────────────────────────────────────┘
┌────────────────────────────────────────┐
│ 场景2:密集小单元格表格 │
├────────────────────────────────────────┤
│ 线检测: │
│ - ✅ 即使单元格只有5×5像素 │
│ - ✅ 只要线清晰,就能准确检出 │
│ │
│ 单元格检测: │
│ - ❌ 小框特别难,易漏检 │
│ - ❌ 需要后处理补全 │
└────────────────────────────────────────┘
数据支持:
# MinerU 调试过程
输入图 → UNet掩码 → 横线提取 → 竖线提取 → 交点求解 → 单元格框
每一步都可以可视化检查:
✅ 线掩码对不对?看图就知道
✅ 提取的线完整吗?看坐标就知道
✅ 为什么单元格边界在这儿?因为线交点在这儿
# PaddleX 调试过程
输入图 → 目标检测 → 预测框
❌ 为什么漏检了某个单元格?
- 模型内部特征提取失败?
- NMS阈值太高?
- 训练数据不足?
→ 黑盒!无法快速定位根本原因
❌ 为什么某些框不对齐?
- 模型预测的边界坐标本身就错了
→ 需要再训练或后处理补偿
关键差别:
# 场景:票据被倾斜扫描 5°
MinerU 处理:
1. UNet 输出倾斜的线掩码
2. 旋转矫正:从外轮廓估计角度 → 旋转回正
3. 重新提取线 → 求交点
✅ 一套流程内置处理旋转
✅ 矫正后的坐标映射回原图(逆旋)
PaddleX 处理:
1. 目标检测输出倾斜的框
2. 框本身不知道"应该是正的"
3. 依赖后处理逻辑推断倾斜角度?
❌ 框级别的几何约束较弱
❌ 旋转图像后的单元格匹配困难
| 问题 | 原因 |
|---|---|
| 漏检补全成本高 | 每次漏检都要启动 OCR 引导的推断逻辑,增加计算量 |
| 边界对齐困难 | 多个框的边界可能不一致(如相邻框左边界不对齐),需要事后对齐 |
| 小表格脆弱 | 单元格太小时,目标检测易漏检;线检测反而稳定 |
| 可维护性差 | 若准确率下降,难以快速定位是检测问题还是后处理问题 |
# 分场景选择
┌──────────────────────────────────────────────────────────┐
│ 清晰规则表格(票据、报表、网格) │
│ → 优先用 MinerU UNet 线检测 │
│ 理由:精准、快速、无需补全 │
│ │
│ 弱线条/无框表格(手写表单、复杂排版) │
│ → 用 PaddleX 单元格检测 + 补全逻辑 │
│ 理由:线条不清晰,OCR 才是主导 │
│ │
│ 高质量混合场景(同时有规则和弱线条) │
│ → MinerU UNet 先跑,若失败/置信度低 │
│ → 再用 PaddleX 补救 │
│ 理由:两路互补,取长补短 │
└──────────────────────────────────────────────────────────┘
MinerU UNet 线检测更好的本质原因:
任务本质不同:
规则表格有"几何真理":
现实中线比框更可靠:
可解释可调优:
因此,融合方案应该是:MinerU 线检测为主流,PaddleX 为 fallback。
基于 batch_analyze.py 与 unet_table/main.py
1) 为什么有线表格要放大 10/3
2) 需要按每个单元格进行 OCR 吗?为什么
table_img 做一次 OCR 检测+识别,得到 ocr_result(在无线阶段获取)。match_ocr_cell),尽量复用已有 OCR 框文本,不逐格 OCR。ocr_result 为空时,UnetTableModel 内部才会整体再跑一次 OCR,而不是逐单元格 OCR。3) 坐标一致性提醒
结论