模型统一框架.md 46 KB

金融文档处理统一框架

参考 MinerU 实现的模型统一框架,针对金融场景设计。

核心设计理念

1. 模块化与可替换性

框架采用模块化设计,各处理节点可根据不同场景灵活替换:

  • 方向识别:支持多种模型(PP-LCNet、PDF rotation等)
  • Layout检测:支持 Docling、MinerU、Paddle、DiT 等多种检测器
  • OCR识别:支持 PaddleOCR
  • 表格识别:支持 UNet(有线表格)、VLM(无线表格)等多种方案
  • 公式识别:支持 MinerU VLM、PaddleOCR-VL 等

通过配置文件(YAML)即可切换不同的模型组合,无需修改代码。

2. 统一输出格式

无论使用何种模型组合,输出格式统一,确保下游处理的一致性:

  • 统一内部描述结构middle.json(MinerU 标准格式)
  • 增强版格式enhanced.json(包含更多元数据)
  • 多格式输出:JSON、Markdown、HTML 等
  • 坐标统一:所有坐标统一转换回原图坐标系

3. 场景适配

针对不同业务场景(银行流水、财务报表等),提供预配置的配置文件:

  • 银行流水场景:bank_statement_*.yaml
  • 财务报表场景:可自定义配置
  • 支持有线/无线表格自动分类
  • 支持文字PDF和扫描件混合处理

支持场景

场景类型 特点 表格形式
银行交易流水 单栏列表形式,无合并单元格 有线表格 / 无线表格
财务报表 多栏列表形式,有合并单元格,表头复杂 有线表格 / 无线表格

模型选择

模型类型 推荐模型 说明
方向分类 PP-LCNet_x1_0_doc_ori paddle onnx格式的模型
版式检测 Docling Layout / DocLayout-YOLO Docling 或 MinerU 模型
文字识别 PaddleOCR (PyTorch) 效果好,支持角度校正
表格分类 PaddleTableClsModel 自动区分有线/无线表格 ⭐ 新增
表格结构识别 MinerU VLM / PaddleOCR-VL / UNET 有线表格 VLM 返回 HTML 结构,UNet 返回单元格坐标
公式识别 MinerU VLM 返回 LaTeX
单元格坐标匹配 TableCellMatcher OCR 检测框与 VLM 结构匹配

流程图颜色说明

本文档中的 Mermaid 流程图采用统一的颜色规范,便于快速理解处理流程:

颜色 十六进制值 用途说明 示例节点
浅蓝色 #e1f5ff 核心处理步骤:OCR识别、文本提取、Span匹配、坐标转换等关键处理节点 整页OCR、PDF文本提取、Span-Block匹配
浅橙色 #fff4e1 输出/结果节点:元素分类、格式输出、VLM识别结果等输出相关节点 元素分类、统一输出格式、VLM结构识别
浅红色 #ffe1e1 特殊标记节点:边缘过滤、后处理等需要特别注意的节点 边缘过滤、可替换组件标记
红色边框 stroke:#ff0000 可替换组件:可通过配置文件替换的组件节点 方向识别、Layout检测、表格识别器

处理流程

主流程图(简化版)

主流程采用模块化设计,各处理节点可根据场景配置替换:

graph TB
    A[输入 PDF/图片] --> B[文档加载与分类]
    
    B --> C[逐页处理<br/>详见单页处理流程]
    
    C --> D[跨页表格合并]
    
    D --> E[统一输出格式<br/>JSON/Markdown/HTML]
    
    style C fill:#e1f5ff
    style E fill:#fff4e1
    
    classDef replaceable fill:#ffe1e1,stroke:#ff0000,stroke-width:2px
    class B,C,D replaceable

关键特性

  • 模块化设计:各处理节点可独立替换(见下方"可替换组件")
  • 统一输出格式:无论使用何种模型组合,输出格式统一(见"输出格式说明")
  • 单页处理流程:详见下方详细流程图

可替换组件

  • 方向识别:PP-LCNet(扫描件) / PDF rotation(文字PDF)
  • Layout检测:Docling / MinerU / Paddle / DiT
  • OCR识别:PaddleOCR / MinerU OCR
  • 表格分类:PaddleTableClsModel(可选)
  • 表格识别:UNet(有线) / VLM(无线:MinerU VLM / PaddleOCR-VL)
  • 公式识别:MinerU VLM / PaddleOCR-VL

统一输出格式

  • {doc}_middle.json:MinerU 标准格式(统一内部描述结构)
  • {doc}_enhanced.json:增强版格式(包含更多元数据)
  • {doc}_page_XXX.json:每页独立 JSON(包含单元格坐标)
  • {doc}.md / {doc}_page_XXX.md:Markdown 格式
  • tables/*.html:表格 HTML(带 data-bbox 坐标)

单页处理详细流程

graph TB
    A[输入:页面图像] --> B{PDF类型?}
    
    B -->|扫描件| C[页面方向识别<br/>PP-LCNet]
    B -->|文字PDF| C1[获取PDF页面rotation<br/>转换为rotate_angle并旋转图像]
    C --> D[Layout 检测<br/>Docling/MinerU/DiT]
    C1 --> D
    
    D --> E[去重重叠框<br/>IoU过滤]
    E --> F{大文本块转表格?}
    
    F -->|是| G[面积占比判断<br/>转换为table类型]
    F -->|否| H[Layout处理完成<br/>准备文本提取]
    G --> H
    
    H --> H1{PDF类型?}
    H1 -->|扫描件| H2[整页 OCR<br/>获取所有 text spans]
    H1 -->|文字PDF| H3[PDF文本提取<br/>extract_all_text_blocks]
    
    H3 --> H4{页面类型检测}
    H4 -->|文字页| H5[使用PDF提取结果]
    H4 -->|扫描页| H2
    H3 -->|提取失败| H2
    
    H2 --> I[Span 去重<br/>IoU过滤]
    H5 --> I
    I --> J[Span 排序<br/>从上到下、从左到右]
    J --> K[Span-Block 匹配<br/>overlap_threshold=0.5]
    
    K --> L[元素分类]
    
    L --> L1[文本类]
    L --> L2[表格类]
    L --> L3[图片类]
    L --> L4[公式类]
    L --> L5[代码类]
    L --> L6[丢弃类]
    
    L1 --> M[处理文本元素]
    L2 --> N[处理表格元素]
    L3 --> O[处理图片元素]
    L4 --> P[处理公式元素]
    L5 --> Q[处理代码元素]
    L6 --> R[处理丢弃元素]
    
    M --> S[合并所有结果]
    N --> S
    O --> S
    P --> S
    Q --> S
    R --> S
    
    S --> T[按阅读顺序排序]
    T --> U[坐标转换回原图]
    
    style H fill:#e1f5ff
    style H2 fill:#e1f5ff
    style H3 fill:#e1f5ff
    style H5 fill:#e1f5ff
    style K fill:#e1f5ff
    style L fill:#fff4e1
    style L1 fill:#fff4e1
    style L2 fill:#fff4e1
    style L3 fill:#fff4e1
    style L4 fill:#fff4e1
    style L5 fill:#fff4e1
    style L6 fill:#fff4e1

关键改进:整页 OCR/PDF提取 + Span 匹配

参考 MinerU 的处理方式,新流程采用 整页文本提取 → Span-Block 匹配 策略:

处理路径

  1. 扫描件/图片

    • 整页 OCR:先对整个页面进行 OCR,获取所有 text spans(包含坐标和文本)
    • 获取所有 OCR spans
  2. 文字PDF

    • PDF文本提取:从PDF直接提取文本块(extract_all_text_blocks
    • 页面类型检测:检测该页是否为扫描页(detect_page_type
      • 如果是文字页:使用PDF提取结果
      • 如果是扫描页:fallback到OCR
    • 提取失败时:自动fallback到OCR

后续处理(统一)

  1. Span 去重:移除高 IoU 重叠的 spans,保留置信度高的
  2. Span 排序:按坐标排序(从上到下,从左到右),方便人工检查缺失字符
  3. Span-Block 匹配:将 spans 按重叠比例(默认0.5)匹配到对应的 layout blocks
  4. 文本合并:将匹配到同一 block 的 spans 按阅读顺序合并

优势

  • ✅ 避免裁剪小图 OCR 失败的问题
  • ✅ OCR 可以利用更多上下文信息
  • ✅ 坐标更精确(整页坐标系)
  • ✅ 减少重复 OCR 调用,提高效率
  • 文字PDF优先使用PDF文本提取,准确率更高,速度更快
  • 自动fallback机制:PDF提取失败或检测到扫描页时自动使用OCR
  • ✅ 与 MinerU 处理方式一致

代码实现

# 1. 根据PDF类型选择文本提取方式
if pdf_type == 'txt' and pdf_doc is not None:
    # 文字PDF:从PDF提取文本块
    actual_page_type = PDFUtils.detect_page_type(pdf_doc, page_idx)
    if actual_page_type == 'txt':
        all_text_spans, rotation = PDFUtils.extract_all_text_blocks(
            pdf_doc, page_idx, scale=scale, return_upright_coords=True
        )
        all_text_spans = self._convert_pdf_blocks_to_spans(
            all_text_spans, detection_image.shape
        )
    else:
        # 扫描页,使用OCR
        all_text_spans = self.ocr_recognizer.recognize_text(detection_image)
else:
    # 扫描件:整页 OCR
    all_text_spans = self.ocr_recognizer.recognize_text(detection_image)

# 2. 去除重复 spans
all_text_spans = SpanMatcher.remove_duplicate_spans(all_text_spans)

# 3. 按坐标排序
all_text_spans = self._sort_spans_by_position(all_text_spans)

# 4. 将 spans 匹配到 layout blocks
matched_spans = SpanMatcher.match_spans_to_blocks(
    all_text_spans, layout_results, overlap_threshold=0.5
)

# 5. 在元素处理时使用预匹配的 spans
element = self.element_processors.process_text_element(
    detection_image, item, pdf_type, pdf_doc, page_idx, scale,
    pre_matched_spans=matched_spans.get(block_idx, [])
)

表格处理流程

表格处理支持两种路径:有线表格(UNET)无线表格(VLM)

新增功能:支持自动表格分类,根据表格类型自动选择识别器

graph TB
    A[表格区域] --> B[表格 OCR 预处理]
    
    subgraph OCR预处理
        B --> B1[裁剪表格区域<br/>padding=10px]
        B1 --> B2[表格方向检测<br/>PP-LCNet]
        B2 --> B3{有整页OCR spans?}
        B3 -->|是| B4[提取重叠spans<br/>转换坐标到裁剪区域]
        B3 -->|否| B5[裁剪区域单独OCR<br/>兜底方案]
        B4 --> B6[OCR框列表]
        B5 --> B6
    end
    
    B6 --> C{启用表格分类?}
    
    C -->|是| C1[表格分类器<br/>PaddleTableClsModel]
    C1 --> C2{分类结果?}
    C2 -->|wired| D[UNet 有线表格路径]
    C2 -->|wireless| E[VLM 无线表格路径]
    
    C -->|否| C3{手动配置?}
    C3 -->|use_wired_unet=true| D
    C3 -->|否| E
    
    subgraph 有线表格路径
        D --> D1[UNet 检测表格线<br/>横线 + 竖线]
        D1 --> D2{启用倾斜矫正?}
        D2 -->|是| D3[基于Mask检测倾斜角度<br/>Hough变换 + 矫正图片和OCR坐标]
        D2 -->|否| D4
        D3 --> D4[网格恢复<br/>grid_recovery.py]
        D4 --> D4a[连通域提取单元格<br/>OCR边缘补偿]
        D4a --> D5[边缘过滤<br/>edge_margin=padding×upscale×1.2]
        D5 --> D6[文本填充<br/>text_filling.py]
        D6 --> D7[二次OCR修正<br/>单元格裁剪OCR]
        D7 --> D8[生成HTML<br/>data-bbox属性]
        D8 --> D9{识别成功?}
        D9 -->|否| E[自动Fallback到VLM]
        D9 -->|是| F
    end
    
    subgraph 无线表格路径
        E --> E1[VLM识别表格结构<br/>返回HTML]
        E1 --> E2[TableCellMatcher匹配<br/>OCR框 + HTML结构]
        E2 --> E3[生成增强HTML<br/>data-bbox属性]
        E3 --> F
    end
    
    F[坐标逆转换] --> G[回到原图坐标系]
    G --> H[返回表格元素]
    
    style B fill:#e1f5ff
    style B1 fill:#e1f5ff
    style B2 fill:#e1f5ff
    style B4 fill:#e1f5ff
    style B5 fill:#e1f5ff
    style B6 fill:#e1f5ff
    style C1 fill:#fff4e1
    style D fill:#e1f5ff
    style D1 fill:#e1f5ff
    style D3 fill:#e1f5ff
    style D4 fill:#e1f5ff
    style D4a fill:#e1f5ff
    style D6 fill:#e1f5ff
    style D7 fill:#e1f5ff
    style D8 fill:#e1f5ff
    style D5 fill:#ffe1e1
    style E1 fill:#e1f5ff
    style E2 fill:#e1f5ff
    style E3 fill:#fff4e1
    style F fill:#e1f5ff
    style G fill:#e1f5ff

表格分类(可选)

配置项table_classification.enabled: true

启用后,系统会自动对每个表格进行分类,判断是有线表格还是无线表格:

  1. 分类模型:使用 MinerU 的 PaddleTableClsModel
  2. 分类结果
    • wired:有线表格(带边框)→ 使用 UNet 识别
    • wireless:无线表格(无边框)→ 使用 VLM 识别
  3. 辅助判断:使用线条检测辅助判断
    • 只有横线或只有竖线 → 强制判断为无线表格
    • 无线条 → 判断为无线表格
  4. 置信度阈值confidence_threshold: 0.5(可配置)

优势

  • ✅ 自动选择最优识别器,无需手动配置
  • ✅ 提高识别准确率(有线表格用UNet,无线表格用VLM)
  • ✅ 支持混合场景(同一文档中同时存在有线/无线表格)

表格OCR预处理(共享逻辑)

无论使用哪种表格识别方法,都需要先进行OCR预处理:

  1. 裁剪表格区域:添加 padding=10px 保护边缘内容
  2. 表格方向检测:使用 PP-LCNet 检测并矫正表格方向
  3. 收集OCR框
    • 优先:使用整页OCR结果中与表格区域重叠的spans(仅当表格未旋转时)
    • 兜底:对裁剪后的表格区域单独OCR
  4. 坐标转换:将坐标从原图转换到裁剪后的表格图像坐标系

关键点

  • padding=10px 在原图坐标系中
  • 表格图像的 (0,0) 对应原图的 (bbox[0]-padding, bbox[1]-padding)
  • UNet处理时会放大 upscale_ratio≈3.333 倍
  • 文字PDF支持:对于文字PDF(pdf_type='txt'),会传递 pdf_type 参数,支持PDF字符提取作为OCR补充

有线表格识别流程(UNET)

配置项:table_recognition_wired.use_wired_unet: true

处理步骤

  1. UNet表格线检测

    • 检测横线(rows)和竖线(cols)坐标
    • 坐标在放大后的图像坐标系(upscale≈3.333倍)
  2. 倾斜检测与矫正(可选,enable_deskew: true):

    • 基于Mask的倾斜检测:使用UNet检测的横线mask进行Hough变换,检测倾斜角度
    • 坐标系:Y轴向下,逆时针旋转为正角度
    • 矫正逻辑:correction_angle = -detected_angle(反向旋转)
    • 同步更新表格图像和OCR框坐标
    • 矫正后重新运行UNet,确保mask与矫正后的图像对齐
  3. 网格恢复grid_recovery.py):

    • 连通域提取:使用连通域法提取单元格(替代投影法)
    • OCR边缘补偿:使用OCR框信息补偿边缘单元格,提高边缘单元格识别准确率
    • 边缘过滤:过滤padding区域的噪声线条

      edge_margin = int(crop_padding × upscale × 1.2)  # 例如:10 × 3.333 × 1.2 = 40px
      
    • 合并距离过近的线条(row_threshold, col_threshold

    • 生成单元格网格

  4. 文本填充text_filling.py):

    • 计算OCR框与单元格的中心点匹配
    • 匹配OCR文本到对应单元格
    • 支持置信度过滤(ocr_conf_threshold
  5. 二次OCR修正text_filling.py):

    • 总是执行,不区分PDF类型
    • 对单元格进行裁剪OCR,补充或修正文本
    • 处理空文本单元格、低置信度文本、OCR误合并等情况
    • 对于文字PDF(pdf_type='txt'),空文本单元格不触发二次OCR,为了解决文字PDF中误合并问题
  6. HTML生成

    • 生成标准表格HTML
    • 添加 data-bbox 属性记录单元格坐标
  7. 坐标逆转换

    • 将所有坐标从裁剪+放大坐标系转换回原图坐标系
    • 如果进行了倾斜矫正,需要逆向旋转坐标
  8. 自动Fallback

    • 如果有线识别失败(返回空HTML或空cells),自动切换到VLM识别
    • 无需手动干预,确保识别鲁棒性

文字PDF支持

  • 支持传递 pdf_type='txt' 参数到有线表格识别器
  • 二次OCR修正,处理空文本单元格、低置信度文本、OCR误合并等情况
  • 对于文字PDF(pdf_type='txt'),空文本单元格不触发二次OCR,为了解决文字PDF中误合并问题

关键配置

# 表格分类配置(推荐启用)
table_classification:
  enabled: true                 # 启用自动表格分类
  module: "paddle"              # 使用 PaddleTableClsModel
  confidence_threshold: 0.5     # 分类置信度阈值

# 有线表格识别配置
table_recognition_wired:
  use_wired_unet: true          # 启用有线表格
  upscale_ratio: 3.333          # 固定放大比例
  enable_deskew: true           # 启用倾斜矫正(基于Mask检测)
  row_threshold: 10             # 行合并阈值(像素)
  col_threshold: 15             # 列合并阈值(像素)
  ocr_conf_threshold: 0.8       # OCR置信度阈值
  use_custom_postprocess: true  # 使用自定义后处理(支持OCR边缘补偿)

无线表格识别流程(VLM)

配置项:vl_recognition.module: "paddle""mineru"

处理步骤

  1. VLM结构识别

    • 输入:表格图像
    • 输出:表格HTML结构(无坐标信息)
  2. TableCellMatcher匹配

    • 解析VLM返回的HTML结构
    • 使用动态规划算法匹配OCR框到表格单元格
    • 考虑合并单元格(rowspan/colspan)
  3. 生成增强HTML

    • 在原HTML基础上添加 data-bbox 属性
    • 记录每个单元格的精确坐标
  4. 坐标逆转换

    • 将坐标从裁剪坐标系转换回原图坐标系

文本处理流程

graph TB
    A[文本区域] --> B{有预匹配spans?}
    
    B -->|是| C[使用整页OCR spans]
    C --> D[SpanMatcher合并文本]
    D --> E{文本非空?}
    
    B -->|否| F
    E -->|是| M[返回文本结果]
    E -->|否| F
    
    F{PDF类型?} -->|数字PDF| G[尝试PDF字符提取]
    F -->|扫描件| H
    
    G --> I{提取成功?}
    I -->|是| M
    I -->|否| H
    
    H[裁剪区域OCR<br/>兜底方案] --> M
    
    style C fill:#e1f5ff
    style D fill:#e1f5ff
    style G fill:#e1f5ff
    style H fill:#e1f5ff

处理优先级

  1. 优先级1:使用预匹配的 spans(整页 OCR 结果)

    • 效率最高,避免重复OCR
    • 坐标精确(整页坐标系)
  2. 优先级2:数字PDF字符提取

    • 仅适用于 pdf_type='txt'
    • 提取PDF内嵌的文本和坐标
  3. 优先级3:裁剪区域OCR(兜底方案)

    • 适用于spans匹配失败或扫描件
    • 单独OCR该区域,坐标需转换

代码实现

def process_text_element(
    self,
    image: np.ndarray,
    layout_item: Dict[str, Any],
    pdf_type: str,
    pdf_doc: Optional[Any],
    page_idx: int,
    scale: float,
    pre_matched_spans: Optional[List[Dict[str, Any]]] = None
) -> Dict[str, Any]:
    bbox = layout_item.get('bbox', [0, 0, 0, 0])
    
    # 优先级1:使用预匹配的 spans
    if pre_matched_spans and len(pre_matched_spans) > 0:
        text_content, sorted_spans = SpanMatcher.merge_spans_to_text(
            pre_matched_spans, bbox
        )
        if text_content.strip():
            return {..., 'extraction_method': 'whole_page_ocr_spans'}
    
    # 优先级2:PDF字符提取
    if pdf_type == 'txt' and pdf_doc is not None:
        extracted_text = PDFUtils.extract_text_from_region(...)
        if extracted_text.strip():
            return {..., 'extraction_method': 'pdf_extraction'}
    
    # 优先级3:裁剪区域OCR
    cropped_region = CoordinateUtils.crop_region(image, bbox)
    ocr_result = self.ocr_recognizer.recognize_text(cropped_region)
    return {..., 'extraction_method': 'cropped_region_ocr'}

元素分类说明

元素类别 包含类型 处理方式 是否使用pre_matched_spans
文本类 text, title, header, footer, ref_text, table_caption, image_caption 等 优先spans → PDF提取 → 裁剪OCR
表格类 table, table_body OCR预处理 + (UNET/VLM) ✅(仅OCR预处理)
图片类 image, image_body, figure 裁剪保存
公式类 interline_equation, equation VLM识别 → LaTeX
代码类 code, code_body, code_caption, algorithm 优先spans → PDF提取 → 裁剪OCR
丢弃类 abandon, discarded 优先spans → 裁剪OCR(保留备用)

坐标转换说明

处理过程中涉及多个坐标系的转换:

  1. 原图坐标系:PDF/图片的原始坐标
  2. 检测图像坐标系:可能经过旋转(方向矫正)
  3. 裁剪区域坐标系:表格/文本区域裁剪后的坐标(可能有padding)
  4. 放大坐标系:UNet处理时的放大坐标(upscale≈3.333)

坐标转换流程

原图坐标 → 检测图像坐标 → 裁剪区域坐标 → 放大坐标(UNET)
          ↓                  ↓                 ↓
       方向矫正           添加padding         upscale放大
          ↓                  ↓                 ↓
      逆向转换           逆向转换           逆向转换

所有处理完成后,坐标统一转换回原图坐标系,确保输出一致性。


元素分类说明

元素类别 包含类型 处理方式
文本类 text, title, header, footer, ref_text, table_caption, image_caption 等 优先使用匹配的 spans,否则 PDF 提取或裁剪 OCR
表格类 table, table_body OCR 坐标 + VLM 结构
图片类 image, image_body, figure 裁剪保存
公式类 interline_equation, equation VLM 识别
丢弃类 abandon, discarded 优先使用匹配的 spans,否则裁剪 OCR

方向识别策略

处理阶段 方向识别 建议
页面级 扫描件:PP-LCNet
文字PDF:PDF rotation
扫描件使用PP-LCNet模型识别
文字PDF从PDF元数据获取rotation并旋转图像
表格区域 - 可选,VLM 有一定容忍度,OCR 自带角度校正
文本区域 - 不需要,OCR 自带校正

文字PDF方向处理

  • 从PDF页面元数据获取 rotation 角度
  • 转换为统一的 rotate_angle 定义(图像需要逆时针旋转的角度)
  • 旋转图像为正视方向,确保后续Layout检测和文本提取的准确性

Layout 后处理

大面积文本块转表格

当Layout检测将大面积的表格区域误识别为文本框时,可以通过后处理自动转换:

判断规则

  • 面积占比:占页面面积超过阈值(默认25%)
  • 尺寸比例:宽度和高度都超过一定比例(避免细长条)
  • 表格冲突:如果页面已有表格,不进行转换(避免误判)

配置示例

layout:
  convert_large_text_to_table: true  # 是否启用
  min_text_area_ratio: 0.25          # 最小面积占比(25%)
  min_text_width_ratio: 0.4          # 最小宽度占比(40%)
  min_text_height_ratio: 0.3         # 最小高度占比(30%)

详细说明请参考:Layout后处理-文本转表格.md


OCR 使用策略

PDF 类型 文字块处理 表格处理
扫描件/图片 整页 OCR → Span 匹配 OCR 检测(坐标) + 表格分类 → UNet/VLM(结构)
数字原生 PDF(文字PDF) 整页 OCR → Span 匹配 / PDF 字符提取 OCR 检测(坐标) + 表格分类 → UNet/VLM(结构)
+ 单元格OCR修正(PDF字符提取优先)

关键点

  • 整页 OCR 优先:先对整页进行 OCR,再将结果匹配到 layout blocks
  • 数字原生 PDF 在 spans 匹配失败时,会尝试 PDF 字符提取
  • 表格处理无论 PDF 类型都需要 OCR 检测,用于获取单元格内文本的精确坐标
  • 表格分类:支持自动分类(有线/无线),自动选择最优识别器
  • 有线表格(UNet)
    • 支持文字PDF,在二次OCR修正时优先使用PDF字符提取
    • 支持OCR边缘补偿,提高边缘单元格识别准确率
    • 识别失败时自动fallback到VLM
  • 无线表格(VLM)
    • VLM 只返回表格结构(HTML),不返回单元格坐标
    • 需要与 OCR 检测结果匹配(TableCellMatcher)

OCR 参数配置

默认参数(已优化,参考PPStructureV3)

  • det_threshold: 0.6(检测框置信度阈值,提高可减少噪声框)
  • unclip_ratio: 1.5(检测框扩展比例,降低可提高框精确度)
  • enable_merge_det_boxes: False(是否合并检测框,表格场景建议False)

配置示例

ocr:
  det_threshold: 0.6              # 检测阈值(0.3-0.7,越高越严格)
  unclip_ratio: 1.5               # 扩展比例(1.3-1.8,越低越精确)
  enable_merge_det_boxes: false   # 合并框(表格场景建议false)
  language: ch                     # 语言(ch/ch_lite/en等)

OCR 参数实际使用对比

不同配置文件针对不同场景优化了OCR参数,选择合适的配置可以显著提升识别效果:

配置文件 det_threshold unclip_ratio enable_merge 适用场景 说明
bank_statement_yusys_v2.yaml 0.6 1.5 false 银行流水(标准质量) 推荐配置,减少噪声框
bank_statement_mineru_v2.yaml 0.6 1.5 false 财务报表(标准质量) 推荐配置,高精度
bank_statement_paddle_vl.yaml 0.3 1.8 false 扫描质量差 宽松配置,增强召回
bank_statement_wired_unet.yaml 0.5 1.8 false 有线表格 平衡配置,兼顾精度与召回

参数选择建议

  1. det_threshold(检测阈值)

    • 0.6:严格模式,适合高质量扫描件/数字PDF,减少噪声框和误检
    • 0.5:平衡模式,适合有线表格场景,兼顾精度和召回
    • 0.3:宽松模式,适合低质量扫描件,捕获更多文本但可能产生噪声
  2. unclip_ratio(扩展比例)

    • 1.5:精确模式,检测框更贴近文本边界,减少相邻单元格干扰
    • 1.8:宽松模式,检测框扩大,避免文本截断但可能重叠
  3. enable_merge_det_boxes(合并框)

    • false:表格场景推荐,保持单元格独立性
    • true:长文本场景,自动合并相邻文本框

快速选择

  • 银行流水/财务报表(高质量)→ 0.6 / 1.5 / false
  • 有线表格(需要精确坐标)→ 0.5 / 1.8 / false
  • 扫描质量差(老旧文档)→ 0.3 / 1.8 / false

详细分析请参考:OCR识别差异分析与改进方案.md


目录结构

universal_doc_parser/
├── __init__.py
├── main_v2.py # 命令行入口 ⭐
│
├── config/ # 配置文件
│ ├── bank_statement_yusys_v2.yaml # 银行流水配置(Docling + PaddleOCR-VL)
│ ├── bank_statement_mineru_v2.yaml # 银行流水配置(MinerU layout + MinerU VLM)
│ ├── bank_statement_mineru_vl.yaml # 银行流水配置(MinerU VLM)
│ ├── bank_statement_paddle_vl.yaml # 银行流水配置(PaddleOCR-VL)
│ └── bank_statement_wired_unet.yaml # 有线表格配置(Docling + UNET)⭐ 新增
│
├── core/ # 核心处理模块
│ ├── pipeline_manager_v2.py # 主流水线管理器 ⭐
│ ├── pipeline_manager_v2_streaming.py # 流式处理流水线 ⭐ 新增
│ ├── element_processors.py # 元素处理器(文本、表格、图片等)
│ ├── coordinate_utils.py # 坐标转换工具
│ ├── layout_utils.py # 布局处理工具(排序、去重、SpanMatcher)⭐
│ ├── pdf_utils.py # PDF 处理工具
│ ├── config_manager.py # 配置管理
│ └── model_factory.py # 模型工厂
│
├── models/ # 模型适配器
│ └── adapters/
│   ├── __init__.py
│   ├── base.py # 适配器基类
│   ├── mineru_adapter.py # MinerU 适配器
│   ├── paddle_vl_adapter.py # PaddleOCR-VL 适配器
│   ├── paddle_layout_detector.py # PaddleX RT-DETR 布局检测器
│   ├── paddle_table_classifier.py # Paddle表格分类器 ⭐ 新增
│   ├── docling_layout_adapter.py # Docling 布局检测器 ⭐
│   ├── dit_layout_adapter.py # DiT 布局检测器 ⭐ 新增
│   ├── mineru_wired_table.py # MinerU 有线表格识别器 ⭐ 新增
│   └── wired_table/ # 有线表格子模块 ⭐ 新增
│     ├── __init__.py
│     ├── debug_utils.py # 调试工具
│     ├── ocr_formatter.py # OCR 格式转换
│     ├── skew_detection.py # 倾斜检测与矫正
│     ├── grid_recovery.py # 网格恢复
│     ├── text_filling.py # 文本填充
│     ├── html_generator.py # HTML 生成
│     └── visualization.py # 可视化
│
├── dit_support/ # DiT 布局检测器支持模块 ⭐
│ ├── configs/ # DiT 配置文件
│ │ ├── Base-RCNN-FPN.yaml
│ │ └── cascade/
│ │   └── cascade_dit_large.yaml
│ ├── ditod/ # DiT 模型实现
│ │ ├── __init__.py
│ │ ├── backbone.py
│ │ ├── beit.py
│ │ ├── config.py
│ │ └── deit.py
│ └── README.md
│
├── utils/ # 输出工具模块(已迁移到 ocr_utils)⚠️
│ └── __init__.py # 仅作重新导出接口
│
└── tests/ # 测试模块
  ├── test_dit_layout_adapter.py # DiT 布局检测器测试
  ├── test_doclayoutyolo.py # DocLayout-YOLO 测试
  ├── test_layout_detector.py # 布局检测器测试
  ├── test_pdf_rotation.py # PDF 旋转测试
  ├── test_skew_correction.py # 倾斜矫正测试
  ├── test_table_routing.py # 表格路由测试
  └── *.png # 测试图片文件

使用方法

命令行

# 处理单个 PDF 文件
python main_v2.py -i document.pdf -c config/bank_statement_yusys_v2.yaml

# 处理图片目录
python main_v2.py -i ./images/ -c config/bank_statement_yusys_v2.yaml

# 开启 debug 模式(输出可视化图片)
python main_v2.py -i doc.pdf -c config/bank_statement_yusys_v2.yaml --debug

# 指定输出目录
python main_v2.py -i doc.pdf -c config/bank_statement_yusys_v2.yaml -o ./my_output/

Python API

from core.pipeline_manager_v2 import EnhancedDocPipeline
from ocr_utils import OutputFormatterV2  # 从 ocr_utils 导入

# 初始化流水线
with EnhancedDocPipeline("config/bank_statement_yusys_v2.yaml") as pipeline:
    # 处理文档
    results = pipeline.process_document("document.pdf")
    
    # 保存结果
    formatter = OutputFormatterV2("./output")
    output_paths = formatter.save_results(results, {
        'save_json': True,
        'save_markdown': True,
        'save_html': True,
        'save_layout_image': True,  # debug
        'save_ocr_image': True,     # debug
        'normalize_numbers': True,   # 金额标准化
    })

流式处理模式

对于大文档或内存受限场景,使用流式处理模式:

# 命令行添加 --streaming 参数
python main_v2.py -i large_document.pdf -c config.yaml --streaming -o ./output

流式处理特点:

✅ 按页处理:每处理完一页立即保存结果,不累积在内存中 ✅ 内存占用低:适合处理数百页的大文档 ✅ 容错性好:某页处理失败不影响已保存的页面 ✅ 输出一致:与批量模式输出格式完全一致 适用场景:

处理超过50页的大型PDF文档 服务器内存受限(如4GB以下) 需要实时查看处理进度


输出格式说明(统一描述格式)

核心原则:无论使用何种模型组合,输出格式统一,确保下游处理的一致性。

主要输出文件

输出文件 格式说明 用途
{doc}_middle.json MinerU 标准格式(统一内部描述结构) 标准输出,兼容 MinerU 生态
{doc}_enhanced.json 增强版 JSON(包含更多元数据)⭐ 包含识别方法、置信度等详细信息
{doc}_page_001.json 每页独立 JSON(包含单元格坐标) 按页输出,包含精确坐标信息
{doc}.md 完整文档 Markdown 人类可读格式
{doc}_page_001.md 每页独立 Markdown(带坐标注释) 按页 Markdown,便于检查
tables/*.html 表格 HTML(带 data-bbox 坐标) 表格结构化输出,包含单元格坐标
images/ 提取的图片元素 图片资源

统一格式特点

  1. 坐标统一:所有坐标统一转换回原图坐标系,确保一致性
  2. 结构统一:无论使用何种识别方法,输出结构统一
  3. 元数据完整:包含识别方法、置信度、处理时间等元数据
  4. 多格式支持:同时输出 JSON、Markdown、HTML 等多种格式

调试输出文件(可选)

输出文件 说明 启用条件
{doc}_pdf_page_001.png PDF转换的页面图片 output.save_pdf_images: true
{doc}_page_001_layout.png Layout 可视化图片 output.save_layout_image: true
{doc}_page_001_ocr.png OCR 可视化图片 output.save_ocr_image: true
{doc}_page_001_table_001_lines.png 有线表格线可视化 table_recognition_wired.debug_options.enabled: true
{doc}_page_001_table_001_grid.png 有线表格网格结构 table_recognition_wired.debug_options.enabled: true
{doc}_page_001_table_001_text.png 有线表格文本覆盖 table_recognition_wired.debug_options.enabled: true
{doc}_page_001_table_001_components.png 有线表格连通域 table_recognition_wired.debug_options.enabled: true
{doc}_page_001_table_001_paddle_table_lines.png 表格分类线条检测可视化 table_classification.debug_options.enabled: true

有线表格调试输出说明

  • 当配置 table_recognition_wired.debug_options.enabled: true 时,会生成详细的可视化图片
  • 这些图片帮助理解表格识别的各个处理阶段(表格线提取、网格恢复、文本填充等)
  • 详细说明请参考:有线表格识别技术文档.md

表格分类调试输出说明

  • 当配置 table_classification.debug_options.enabled: true 时,会生成表格线检测可视化图片
  • 用于理解分类器如何判断表格类型(有线/无线)

配置说明

配置文件采用 YAML 格式,主要配置项:

# 场景名称
scene_name: "bank_statement"

# 输入配置
input:
  supported_formats: [".pdf", ".png", ".jpg", ".jpeg"]
  dpi: 200  # PDF 转图片的 DPI

# 预处理(方向识别)
preprocessor:
  module: "mineru"
  orientation_classifier:
    enabled: true  # 扫描件自动开启

# 版式检测
layout_detection:
  module: "docling"  # 可选: "mineru", "paddle", "docling"
  model_name: "docling-layout-old"
  model_dir: "ds4sd/docling-layout-old"  # HuggingFace 模型仓库
  device: "cpu"
  conf: 0.3

# VL 识别(表格、公式)
vl_recognition:
  module: "paddle"  # 可选: "mineru", "paddle"
  backend: "http-client"
  server_url: "http://xxx:8110"
  table_recognition:
    return_cells_coordinate: true

# OCR 识别
ocr_recognition:
  module: "mineru"
  language: "ch"

# 表格分类配置(自动区分有线/无线表格)
table_classification:
  enabled: true               # 是否启用自动表格分类(默认关闭,使用手动配置)
  module: "paddle"            # 分类模型:paddle(MinerU PaddleTableClsModel)
  confidence_threshold: 0.5   # 分类置信度阈值
  batch_size: 16              # 批处理大小
  debug_options:
    enabled: false            # 是否开启调试可视化输出
    save_table_lines: true    # 保存表格线可视化

# 有线表格识别配置
table_recognition_wired:
  use_wired_unet: true        # 是否启用有线表格识别
  upscale_ratio: 3.333        # 放大比例
  enable_deskew: true         # 是否启用倾斜矫正
  row_threshold: 10           # 行合并阈值(像素)
  col_threshold: 15           # 列合并阈值(像素)
  ocr_conf_threshold: 0.8     # OCR置信度阈值
  debug_options:
    enabled: false            # 是否开启调试可视化输出
    save_table_lines: true    # 保存表格线可视化
    save_connected_components: true  # 保存连通域提取的单元格图
    save_grid_structure: true  # 保存逻辑网格结构
    save_text_overlay: true   # 保存文本填充覆盖图

# 输出配置
output:
  create_subdir: false        # 是否创建子目录
  save_json: true
  save_markdown: true
  save_html: true
  save_layout_image: false    # debug 模式开启
  save_ocr_image: false       # debug 模式开启
  normalize_numbers: true     # 金额数字标准化

可替换组件说明

框架采用适配器模式,各组件可根据场景灵活替换。通过修改配置文件即可切换,无需修改代码。

布局检测器(Layout Detection)

支持多种布局检测模型,通过 layout_detection.module 配置切换:

1. Docling Layout (推荐)

基于 HuggingFace transformers 的 RT-DETR 模型。

layout_detection:
  module: "docling"
  model_name: "docling-layout-old"
  model_dir: "ds4sd/docling-layout-old"

支持的模型:

  • ds4sd/docling-layout-old
  • ds4sd/docling-layout-heron
  • ds4sd/docling-layout-egret-medium
  • ds4sd/docling-layout-egret-large

2. PaddleX RT-DETR (ONNX)

基于 ONNX Runtime 的 PaddleX 布局检测器。

layout_detection:
  module: "paddle"
  model_name: "RT-DETR-H_layout_17cls"
  model_dir: "/path/to/RT-DETR-H_layout_17cls.onnx"

3. MinerU DocLayout-YOLO

MinerU 内置的布局检测模型。

layout_detection:
  module: "mineru"
  model_name: "layout"

4. DiT Layout (Document Image Transformer)

基于 Detectron2 的 DiT 模型,在 PubLayNet 数据集上训练。

layout_detection:
  module: "dit"
  config_file: "dit_support/configs/cascade/cascade_dit_large.yaml"
  model_weights: "path/to/model.pth"
  device: "cpu"
  conf: 0.3

支持类别:text、title、list、table、figure

特点:基于 Vision Transformer 架构,适合学术文档和出版物


OCR识别器(OCR Recognition)

通过 ocr_recognition.module 配置切换:

  • MinerU OCRmodule: "mineru"
  • PaddleOCRmodule: "paddle"(需配置模型路径)

表格识别器(Table Recognition)

表格分类器(可选)

通过 table_classification.module 配置:

  • PaddleTableClsModelmodule: "paddle"(自动区分有线/无线表格)

有线表格识别

通过 table_recognition_wired.use_wired_unet 配置:

  • UNet 有线表格use_wired_unet: true(MinerU UNet模型)

无线表格识别

通过 vl_recognition.module 配置:

  • MinerU VLMmodule: "mineru"
  • PaddleOCR-VLmodule: "paddle"(需配置服务地址)

后端支持

  • backend: "http-client":HTTP 客户端
  • backend: "vllm-engine":vLLM 引擎
  • backend: "transformers":Transformers 库

公式识别器(Equation Recognition)

通过 vl_recognition.module 配置(与无线表格共享):

  • MinerU VLMmodule: "mineru"
  • PaddleOCR-VLmodule: "paddle"

方向识别器(Orientation Classification)

  • 扫描件:PP-LCNet(preprocessor.module: "mineru"
  • 文字PDF:自动从PDF元数据获取rotation(无需配置)

配置示例:见 config/bank_statement_*.yaml 文件


类别映射

所有布局检测器的输出都会统一映射到 MinerU/EnhancedDocPipeline 类别体系:

类别分类 包含类别
文本类 (TEXT) text, title, header, footer, page_number, ref_text, page_footnote, aside_text, ocr_text
表格类 (TABLE) table, table_body, table_caption, table_footnote
图片类 (IMAGE) image, image_body, figure, image_caption, image_footnote
公式类 (EQUATION) interline_equation, inline_equation, equation
代码类 (CODE) code, code_body, code_caption, algorithm
丢弃类 (DISCARD) abandon, discarded

核心组件

1. EnhancedDocPipeline (pipeline_manager_v2.py)

主流水线管理器,实现完整处理流程:

  • PDF 分类(扫描件/数字原生)
  • 页面方向识别
  • Layout 检测与去重
  • 整页 OCR + Span-Block 匹配
  • 表格自动分类(可选)⭐ 新增
  • 元素分类处理(支持智能选择表格识别器)
  • 阅读顺序排序
  • 坐标转换

2. SpanMatcher (layout_utils.py)

OCR Span 与 Layout Block 匹配器,参考 MinerU 实现:

  • match_spans_to_blocks() - 将 spans 匹配到对应的 blocks
  • merge_spans_to_text() - 将多个 spans 合并为文本
  • remove_duplicate_spans() - 去除重复 spans
  • poly_to_bbox() - 多边形坐标转 bbox

3. ElementProcessors (element_processors.py)

元素处理器,处理不同类型的元素:

  • process_text_element() - 文本处理(支持 pre_matched_spans)
  • process_table_element_wired() - 有线表格处理(UNet + OCR 坐标匹配,支持文字PDF)
  • process_table_element_vlm() - 无线表格处理(VLM + OCR 坐标匹配)
  • process_image_element() - 图片处理
  • process_equation_element() - 公式处理
  • process_code_element() - 代码处理
  • process_discard_element() - 丢弃元素处理(支持 pre_matched_spans)

3.1. PaddleTableClassifier (paddle_table_classifier.py) ⭐ 新增

表格分类器,自动区分有线表格和无线表格:

  • classify() - 分类单个表格图像
  • batch_classify() - 批量分类表格图像
  • 使用 MinerU 的 PaddleTableClsModel 进行分类
  • 支持线条检测辅助判断(只有横线或竖线时强制判断为无线表格)

3.1. PaddleTableClassifier (paddle_table_classifier.py) ⭐ 新增

表格分类器,自动区分有线表格和无线表格:

  • classify() - 分类单个表格图像
  • batch_classify() - 批量分类表格图像
  • 使用 MinerU 的 PaddleTableClsModel 进行分类
  • 支持线条检测辅助判断(只有横线或竖线时强制判断为无线表格)
  • 输出分类结果:wired(有线表格)或 wireless(无线表格)

4. OutputFormatterV2 (来自 ocr_utils)

统一输出格式化器,位于 ocr_utils 包中:

  • MinerU 标准 middle.json 格式
  • 每页独立 JSON(含单元格坐标)
  • Markdown 输出(完整版 + 按页)
  • 表格 HTML(带 data-bbox 属性)
  • 可视化图片(Layout/OCR)
  • 金额数字标准化

导入方式

from ocr_utils import OutputFormatterV2

5. TableCellMatcher (来自 merger)

表格单元格坐标匹配器:

  • 使用动态规划进行行内单元格匹配
  • 将 OCR 检测框与 VLM 表格结构匹配
  • 输出带坐标的增强 HTML

依赖说明

MinerU 组件

  • mineru.utils.pdf_image_tools - PDF 图像处理
  • mineru.utils.pdf_text_tool - PDF 文本提取
  • mineru.utils.boxbase - 边界框计算
  • mineru.model.ocr - OCR 模型
  • mineru.model.ori_cls - 方向分类模型

OCR Tools 组件(来自 ocr_tools.ocr_merger)⚠️

实际导入路径(注意:不是 ocr_verify):

# 实际使用的导入路径
from ocr_tools.ocr_merger import TableCellMatcher, TextMatcher

# 模块位置:ocr_tools/ocr_merger/
├── table_cell_matcher.py  # 单元格坐标匹配
├── text_matcher.py        # 文本匹配
└── merger_core.py         # 核心匹配算法

功能说明

  • TableCellMatcher:将 OCR 检测框与 VLM 表格结构匹配,使用动态规划算法
  • TextMatcher:文本相似度匹配工具

OCR Utils 工具包(ocr_utils)⭐

定位:统一的OCR工具包,提供文档处理相关的工具函数,支持跨项目复用。

主要模块

模块 功能 说明
OutputFormatterV2 统一输出格式化器 生成 JSON、Markdown、HTML 等格式
JSONFormatters JSON 格式化工具 格式化 MinerU 标准 JSON、增强 JSON 等
MarkdownGenerator Markdown 生成器 生成完整文档和按页 Markdown
HTMLGenerator HTML 生成器 生成表格 HTML(带 data-bbox 坐标)
VisualizationUtils 可视化工具 Layout、OCR 可视化图片生成
normalize_financial_numbers 金额标准化 金融数字标准化(金额、百分比等)
PDFUtils PDF 处理工具 PDF 文本提取、坐标转换等
BBoxExtractor 边界框提取器 从 HTML 等格式提取边界框
file_utils 文件处理工具 文件列表处理、PDF 转图片等
image_utils 图像处理工具 图像解码、预处理、坐标转换等
html_utils HTML 处理工具 HTML 表格解析、图片处理等
number_utils 数字解析工具 数字解析和标准化

导入示例

# 主要输出工具
from ocr_utils import (
    OutputFormatterV2,           # 统一输出格式化器
    JSONFormatters,              # JSON格式化
    MarkdownGenerator,           # Markdown生成
    HTMLGenerator,               # HTML生成
    VisualizationUtils,          # 可视化工具
    normalize_financial_numbers, # 金额标准化
)

# PDF 工具(延迟导入)
from ocr_utils import PDFUtils, BBoxExtractor

# 文件工具
from ocr_utils import (
    get_input_files,
    convert_pdf_to_images,
    parse_page_range
)

优势

  • 统一管理:所有输出工具集中管理,避免代码重复
  • 跨项目复用:可在多个 OCR 相关项目中复用
  • 版本控制:独立版本管理,便于维护和升级
  • 延迟导入:PDF 相关工具支持延迟导入,避免依赖问题

其他依赖 . transformers - Docling 模型加载 . huggingface_hub - 模型下载 . detectron2 - DiT 模型推理 ⭐ 新增 . onnxruntime - ONNX 模型推理 . Pillow - 图像处理 . NumPy - 数值计算 . OpenCV (cv2) - 图像处理和表格线检测 ⭐ . BeautifulSoup4 - HTML 解析 . PyYAML - 配置文件解析 . loguru - 日志


安装依赖

# 基础依赖
pip install transformers huggingface_hub onnxruntime pillow numpy opencv-python beautifulsoup4 pyyaml loguru

# MinerU(必需)
pip install magic-pdf[full]

# OCR Merger(必需)
cd /path/to/ocr_tools && pip install -e .

# DiT支持(可选,如果使用DiT布局检测器)
pip install detectron2 -f https://dl.fbaipublicfiles.com/detectron2/wheels/cpu/torch2.0/index.html