# 金融文档处理统一框架

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

## 支持场景

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

## 模型选择

| 模型类型 | 推荐模型 | 说明 |
|---------|---------|------|
| **版式检测** | Docling Layout / DocLayout-YOLO | HuggingFace 或 MinerU 模型 |
| **文字识别** | PaddleOCR (PyTorch) | 效果好，支持角度校正 |
| **表格结构识别** | MinerU VLM / PaddleOCR-VL | VLM 返回 HTML 结构 |
| **公式识别** | MinerU VLM | 返回 LaTeX |
| **方向识别** | PP-LCNet | 沿用 MinerU 实现 |
| **单元格坐标匹配** | TableCellMatcher | OCR 检测框与 VLM 结构匹配 |

---

## 处理流程

```mermaid
graph TB
    A[输入 PDF/图片] --> B{PDF 分类}
    
    B -->|扫描件/图片| C1[页面方向识别<br/>PP-LCNet]
    B -->|数字原生PDF| D
    C1 --> D[Layout 检测<br/>去重叠框]
    
    D --> E[整页 OCR<br/>获取所有 text spans]
    E --> F[Span-Block 匹配<br/>SpanMatcher]
    F --> G{元素分类}
    
    G --> H[文本类]
    G --> I[表格类]
    G --> J[图片类]
    G --> K[公式类]
    G --> L[丢弃类]
    
    subgraph 文本处理
        H --> H1{有匹配的spans?}
        H1 -->|是| H2[合并 spans 文本]
        H1 -->|否| H3{PDF类型?}
        H3 -->|数字PDF| H4[PDF 字符提取]
        H4 --> H5{成功?}
        H5 -->|否| H6[裁剪区域 OCR]
        H5 -->|是| H7[文本结果]
        H3 -->|扫描件| H6
        H6 --> H7
        H2 --> H7
    end
    
    subgraph 表格处理
        I --> I1[OCR 检测<br/>获取文本框坐标]
        I --> I2[VLM 结构识别<br/>返回 HTML]
        I1 --> I3[坐标匹配<br/>TableCellMatcher]
        I2 --> I3
        I3 --> I4[带坐标的表格]
    end
    
    subgraph 图片处理
        J --> J1[裁剪保存]
    end
    
    subgraph 公式处理
        K --> K1[VLM 识别<br/>返回 LaTeX]
    end
    
    subgraph 丢弃元素
        L --> L1{有匹配的spans?}
        L1 -->|是| L2[合并 spans 文本]
        L1 -->|否| L3[裁剪区域 OCR]
        L2 --> L4[保留备用]
        L3 --> L4
    end
    
    H7 --> M[合并所有结果]
    I4 --> M
    J1 --> M
    K1 --> M
    L4 --> M
    
    M --> N[按阅读顺序排序]
    N --> O[坐标转换回原图]
    O --> P[合并跨页表格]
    P --> Q[金额数字标准化]
    Q --> R[多格式输出]
```

### 关键改进：整页 OCR + Span 匹配

参考 MinerU 的处理方式，新流程采用 **整页 OCR → Span-Block 匹配** 策略：

1. **整页 OCR**：先对整个页面进行 OCR，获取所有 text spans（包含坐标和文本）
2. **Span 去重**：移除高 IoU 重叠的 spans，保留置信度高的
3. **Span-Block 匹配**：将 OCR spans 按重叠比例匹配到对应的 layout blocks
4. **文本合并**：将匹配到同一 block 的 spans 按阅读顺序合并

**优势**：
- ✅ 避免裁剪小图 OCR 失败的问题
- ✅ OCR 可以利用更多上下文信息
- ✅ 坐标更精确（整页坐标系）
- ✅ 与 MinerU 处理方式一致

### 元素分类说明

| 元素类别 | 包含类型 | 处理方式 |
|---------|---------|---------|
| **文本类** | 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关闭 |
| **表格区域** | - | 可选，VLM 有一定容忍度，OCR 自带角度校正 |
| **文本区域** | - | 不需要，OCR 自带校正 |

---

## 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 检测（坐标） + VLM（结构） |
| **数字原生 PDF** | 整页 OCR → Span 匹配 / PDF 字符提取 | OCR 检测（坐标） + VLM（结构） |

**关键点**：
- **整页 OCR 优先**：先对整页进行 OCR，再将结果匹配到 layout blocks
- 数字原生 PDF 在 spans 匹配失败时，会尝试 PDF 字符提取
- **表格处理无论 PDF 类型都需要 OCR 检测**，用于获取单元格内文本的精确坐标
- VLM 只返回表格结构（HTML），不返回单元格坐标，需要与 OCR 检测结果匹配
- 当前实现仅使用 VLM（MinerU VLM 或 PaddleOCR-VL）进行表格结构识别

### 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等）
```

**参数调优建议**：
- **表格密集场景**：`det_threshold=0.6`, `enable_merge_det_boxes=false`
- **文本稀疏场景**：`det_threshold=0.4`, `enable_merge_det_boxes=true`
- **扫描件质量差**：`det_threshold=0.3`, `unclip_ratio=1.8`

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

---

## 目录结构

```
universal_doc_parser/
├── 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）
│
├── core/                                # 核心处理模块
│   ├── pipeline_manager_v2.py          # 主流水线管理器 ⭐
│   ├── element_processors.py           # 元素处理器（文本、表格、图片等）
│   ├── coordinate_utils.py             # 坐标转换工具
│   ├── layout_utils.py                 # 布局处理工具（排序、去重、SpanMatcher）⭐
│   ├── pdf_utils.py                    # PDF 处理工具
│   ├── config_manager.py               # 配置管理
│   └── model_factory.py                # 模型工厂
│
├── models/                              # 模型适配器
│   └── adapters/
│       ├── base.py                     # 适配器基类
│       ├── mineru_adapter.py           # MinerU 适配器
│       ├── paddle_vl_adapter.py        # PaddleOCR-VL 适配器
│       ├── paddle_layout_detector.py   # PaddleX RT-DETR 布局检测器
│       └── docling_layout_adapter.py   # Docling 布局检测器 ⭐
│
├── utils/                               # 输出工具模块
│   ├── output_formatter_v2.py          # 统一输出格式化器 ⭐
│   ├── json_formatters.py              # JSON 格式化（middle.json, page.json）
│   ├── markdown_generator.py           # Markdown 生成器
│   ├── html_generator.py               # HTML 生成器
│   ├── visualization_utils.py          # 可视化工具（layout/OCR 图片）
│   └── normalize_financial_numbers.py  # 金额数字标准化
│
├── main_v2.py                           # 命令行入口 ⭐
└── 模型统一框架.md                       # 本文档
```

---

## 使用方法

### 命令行

```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 utils import OutputFormatterV2

# 初始化流水线
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,   # 金额标准化
    })
```

---

## 输出文件说明

| 输出文件 | 说明 |
|---------|------|
| `{doc}_middle.json` | MinerU 标准格式 JSON |
| `{doc}_page_001.json` | 每页独立 JSON（包含单元格坐标） |
| `{doc}.md` | 完整文档 Markdown |
| `{doc}_page_001.md` | 每页独立 Markdown（带坐标注释） |
| `tables/*.html` | 表格 HTML 文件（带 data-bbox 坐标） |
| `images/` | 提取的图片元素 |
| `{doc}_page_001_layout.png` | Layout 可视化图片（debug 模式） |
| `{doc}_page_001_ocr.png` | OCR 可视化图片（debug 模式） |
| `*_original.*` | 标准化前的原始文件（如有修改） |

---

## 配置说明

配置文件采用 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"

# 输出配置
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     # 金额数字标准化
```

---

## 支持的布局检测器

### 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"
```

---

## 类别映射

所有布局检测器的输出都会统一映射到 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()` - 表格处理（VLM + OCR 坐标匹配）
- `process_image_element()` - 图片处理
- `process_equation_element()` - 公式处理
- `process_code_element()` - 代码处理
- `process_discard_element()` - 丢弃元素处理（支持 pre_matched_spans）

### 3. OutputFormatterV2 (`output_formatter_v2.py`)

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

### 4. 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` - 方向分类模型

### Merger 组件（来自 ocr_verify）
- `merger.table_cell_matcher.TableCellMatcher` - 单元格坐标匹配
- `merger.text_matcher.TextMatcher` - 文本匹配

### 其他依赖
- transformers - Docling 模型加载
- huggingface_hub - 模型下载
- onnxruntime - ONNX 模型推理
- Pillow - 图像处理
- NumPy - 数值计算
- BeautifulSoup4 - HTML 解析
- PyYAML - 配置文件解析
- loguru - 日志

---

## 安装 merger 模块

```bash
# 1. 进入 ocr_verify 目录
cd /Users/zhch158/workspace/repository.git/ocr_verify

# 2. 安装 merger 模块（可编辑模式）
pip uninstall -y merger && pip install -e .

# 3. 验证安装
python3 -c "from merger.table_cell_matcher import TableCellMatcher; print('✅ 安装成功')"
```

完成后，在任何 Python 文件中都可以直接导入：

```python
from merger.table_cell_matcher import TableCellMatcher
from merger.text_matcher import TextMatcher
from merger.bbox_extractor import BBoxExtractor
```

---

## 项目结构

```
/Users/zhch158/workspace/repository.git/
├── ocr_verify/                          # 源项目
│   ├── setup.py                         # 安装配置
│   └── merger/                          # 表格匹配模块
│       ├── table_cell_matcher.py        # 表格单元格匹配
│       ├── text_matcher.py              # 文本匹配
│       └── bbox_extractor.py            # 边界框提取
│
└── MinerU/                              # 目标项目
    └── zhch/
        └── universal_doc_parser/        # 金融文档处理框架
            ├── core/                    # 核心处理模块
            ├── models/adapters/         # 模型适配器
            ├── utils/                   # 输出工具
            └── config/                  # 配置文件
```
