# 金融文档处理统一框架 参考 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检测、表格识别器 | --- ## 处理流程 ### 主流程图(简化版) 主流程采用**模块化设计**,各处理节点可根据场景配置替换: ```mermaid graph TB A[输入 PDF/图片] --> B[文档加载与分类] B --> C[逐页处理
详见单页处理流程] C --> D[跨页表格合并] D --> E[统一输出格式
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 坐标) --- ### 单页处理详细流程 ```mermaid graph TB A[输入:页面图像] --> B{PDF类型?} B -->|扫描件| C[页面方向识别
PP-LCNet] B -->|文字PDF| C1[获取PDF页面rotation
转换为rotate_angle并旋转图像] C --> D[Layout 检测
Docling/MinerU/DiT] C1 --> D D --> E[去重重叠框
IoU过滤] E --> F{大文本块转表格?} F -->|是| G[面积占比判断
转换为table类型] F -->|否| H[Layout处理完成
准备文本提取] G --> H H --> H1{PDF类型?} H1 -->|扫描件| H2[整页 OCR
获取所有 text spans] H1 -->|文字PDF| H3[PDF文本提取
extract_all_text_blocks] H3 --> H4{页面类型检测} H4 -->|文字页| H5[使用PDF提取结果] H4 -->|扫描页| H2 H3 -->|提取失败| H2 H2 --> I[Span 去重
IoU过滤] H5 --> I I --> J[Span 排序
从上到下、从左到右] J --> K[Span-Block 匹配
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 **后续处理(统一)**: 3. **Span 去重**:移除高 IoU 重叠的 spans,保留置信度高的 4. **Span 排序**:按坐标排序(从上到下,从左到右),方便人工检查缺失字符 5. **Span-Block 匹配**:将 spans 按重叠比例(默认0.5)匹配到对应的 layout blocks 6. **文本合并**:将匹配到同一 block 的 spans 按阅读顺序合并 **优势**: - ✅ 避免裁剪小图 OCR 失败的问题 - ✅ OCR 可以利用更多上下文信息 - ✅ 坐标更精确(整页坐标系) - ✅ 减少重复 OCR 调用,提高效率 - ✅ **文字PDF优先使用PDF文本提取**,准确率更高,速度更快 - ✅ **自动fallback机制**:PDF提取失败或检测到扫描页时自动使用OCR - ✅ 与 MinerU 处理方式一致 **代码实现**: ```python # 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)** **新增功能**:支持自动表格分类,根据表格类型自动选择识别器 ```mermaid graph TB A[表格区域] --> B[表格 OCR 预处理] subgraph OCR预处理 B --> B1[裁剪表格区域
padding=10px] B1 --> B2[表格方向检测
PP-LCNet] B2 --> B3{有整页OCR spans?} B3 -->|是| B4[提取重叠spans
转换坐标到裁剪区域] B3 -->|否| B5[裁剪区域单独OCR
兜底方案] B4 --> B6[OCR框列表] B5 --> B6 end B6 --> C{启用表格分类?} C -->|是| C1[表格分类器
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 检测表格线
横线 + 竖线] D1 --> D2{启用倾斜矫正?} D2 -->|是| D3[基于Mask检测倾斜角度
Hough变换 + 矫正图片和OCR坐标] D2 -->|否| D4 D3 --> D4[网格恢复
grid_recovery.py] D4 --> D4a[连通域提取单元格
OCR边缘补偿] D4a --> D5[边缘过滤
edge_margin=padding×upscale×1.2] D5 --> D6[文本填充
text_filling.py] D6 --> D7[二次OCR修正
单元格裁剪OCR] D7 --> D8[生成HTML
data-bbox属性] D8 --> D9{识别成功?} D9 -->|否| E[自动Fallback到VLM] D9 -->|是| F end subgraph 无线表格路径 E --> E1[VLM识别表格结构
返回HTML] E1 --> E2[TableCellMatcher匹配
OCR框 + HTML结构] E2 --> E3[生成增强HTML
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区域的噪声线条 ```python 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中误合并问题 **关键配置**: ```yaml # 表格分类配置(推荐启用) 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. **坐标逆转换**: - 将坐标从裁剪坐标系转换回原图坐标系 ### 文本处理流程 ```mermaid 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
兜底方案] --> 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该区域,坐标需转换 **代码实现**: ```python 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%) - 尺寸比例:宽度和高度都超过一定比例(避免细长条) - 表格冲突:如果页面已有表格,不进行转换(避免误判) **配置示例**: ```yaml 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) **配置示例**: ```yaml 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 # 测试图片文件 ``` --- ## 使用方法 ### 命令行 ```bash # 处理单个 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 ```python 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, # 金额标准化 }) ``` ### 流式处理模式 对于大文档或内存受限场景,使用流式处理模式: ```bash # 命令行添加 --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](有线表格识别技术文档.md) **表格分类调试输出说明**: - 当配置 `table_classification.debug_options.enabled: true` 时,会生成表格线检测可视化图片 - 用于理解分类器如何判断表格类型(有线/无线) --- ## 配置说明 配置文件采用 YAML 格式,主要配置项: ```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 模型。 ```yaml 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 布局检测器。 ```yaml 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 内置的布局检测模型。 ```yaml layout_detection: module: "mineru" model_name: "layout" ``` #### 4. DiT Layout (Document Image Transformer) 基于 Detectron2 的 DiT 模型,在 PubLayNet 数据集上训练。 ```yaml 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 OCR**:`module: "mineru"` - **PaddleOCR**:`module: "paddle"`(需配置模型路径) --- ### 表格识别器(Table Recognition) #### 表格分类器(可选) 通过 `table_classification.module` 配置: - **PaddleTableClsModel**:`module: "paddle"`(自动区分有线/无线表格) #### 有线表格识别 通过 `table_recognition_wired.use_wired_unet` 配置: - **UNet 有线表格**:`use_wired_unet: true`(MinerU UNet模型) #### 无线表格识别 通过 `vl_recognition.module` 配置: - **MinerU VLM**:`module: "mineru"` - **PaddleOCR-VL**:`module: "paddle"`(需配置服务地址) **后端支持**: - `backend: "http-client"`:HTTP 客户端 - `backend: "vllm-engine"`:vLLM 引擎 - `backend: "transformers"`:Transformers 库 --- ### 公式识别器(Equation Recognition) 通过 `vl_recognition.module` 配置(与无线表格共享): - **MinerU VLM**:`module: "mineru"` - **PaddleOCR-VL**:`module: "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) - 金额数字标准化 **导入方式**: ```python 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**): ```python # 实际使用的导入路径 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` | 数字解析工具 | 数字解析和标准化 | **导入示例**: ```python # 主要输出工具 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 - 日志 --- ### 安装依赖 ```bash # 基础依赖 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 ```