浏览代码

feat(新增印章识别处理流程文档): 添加MinerU印章识别处理流程的详细文档,涵盖布局检测、印章文字OCR及相关模型配置,提升用户对印章识别功能的理解与使用指导。

zhch158_admin 1 月之前
父节点
当前提交
48ff3fcd63
共有 1 个文件被更改,包括 436 次插入0 次删除
  1. 436 0
      docs/mineru/印章识别-seal处理流程.md

+ 436 - 0
docs/mineru/印章识别-seal处理流程.md

@@ -0,0 +1,436 @@
+# MinerU 印章(Seal)识别处理流程
+
+## 概览
+
+MinerU 从 v2.5 版本开始在 pipeline 后端中支持印章(seal)识别。印章识别不依赖单独的专有检测模型,而是分两步完成:
+
+1. **布局检测**:PP-DocLayoutV2 模型在 25 个布局类别中包含了 `seal`(第 20 类),一次性完成印章区域的定位。
+2. **印章文字 OCR**:使用专门针对印章场景调优的 PaddleOCR 变体模型,对检测到的印章区域进行文字检测与识别。
+
+## 使用的模型
+
+| 步骤 | 模型 | 说明 |
+|------|------|------|
+| **Step 1: 印章区域检测** | PP-DocLayoutV2(基于 RT-DETR) | 在页面上定位印章区域,输出 bbox 坐标 |
+| **Step 2: 印章文字检测** | `seal_PP-OCRv4_det_server_infer.pth` | 专用 DB 文字检测器,使用 PP-HGNet_small 骨干网络,多边形检测框 |
+| **Step 2: 印章文字识别** | `ch_PP-OCRv4_rec_server_infer.pth` | 通用中文文字识别模型 |
+| **轻量版** | `seal_PP-OCRv4_det_infer.pth` + `ch_PP-OCRv4_rec_infer.pth` | CPU 环境下自动切换的轻量版 |
+
+**关键点**:不需要单独的"印章检测模型"。印章检测已整合在 PP-DocLayoutV2 中,共享同一个视觉编码器。
+
+---
+
+## 一、Step 1: PP-DocLayoutV2 布局检测
+
+### 1.1 标签定义
+
+PP-DocLayoutV2 定义了 25 个布局类别,`seal` 是第 20 类(索引 20):
+
+```python
+# mineru/model/layout/pp_doclayoutv2.py
+PP_DOCLAYOUT_V2_LABELS = [
+    "abstract",           # 0
+    "algorithm",          # 1
+    "aside_text",         # 2
+    "chart",              # 3
+    "content",            # 4
+    "display_formula",    # 5
+    "doc_title",          # 6
+    "figure_title",       # 7
+    "footer",             # 8
+    "footer_image",       # 9
+    "footnote",           # 10
+    "formula_number",     # 11
+    "header",             # 12
+    "header_image",       # 13
+    "image",              # 14
+    "inline_formula",     # 15
+    "number",             # 16
+    "paragraph_title",    # 17
+    "reference",          # 18
+    "reference_content",  # 19
+    "seal",               # 20 印章 ←
+    "table",              # 21
+    "text",               # 22
+    "vertical_text",      # 23
+    "vision_footnote",    # 24
+]
+```
+
+### 1.2 置信度阈值
+
+印章的置信度阈值设为 **0.45**,低于大多数其他类别,以确保印章不被漏检:
+
+```python
+DEFAULT_CLASS_THRESHOLDS = [
+    # ... 其他类别 ...
+    0.45,  # 20 seal  ← 较低的阈值
+    # ...
+]
+```
+
+### 1.3 MagicModel 中的映射
+
+布局检测结果进入 `MagicModel` 后,`seal` 标签被映射为 `BlockType.SEAL`:
+
+```python
+# mineru/backend/pipeline/pipeline_magic_model.py
+PP_DOCLAYOUT_V2_LABELS_TO_BLOCK_TYPES = {
+    # ...
+    "seal": BlockType.SEAL,
+    # ...
+}
+```
+
+`BlockType.SEAL` 是 MinerU 内部枚举类中定义的类型:
+
+```python
+# mineru/utils/enum_class.py
+class BlockType:
+    # ...
+    SEAL = "seal"
+    # ...
+```
+
+### 1.4 印章块的 span 构造
+
+在 `MagicModel.__build_page_blocks()` 中,`BlockType.SEAL` 类型的块被构造为纯图的 span(类似 image/table/chart):
+
+```python
+# mineru/backend/pipeline/pipeline_magic_model.py (__build_page_blocks)
+elif block["type"] in [BlockType.SEAL]:
+    span_type = ContentType.SEAL
+
+if span_type in [
+    ContentType.IMAGE,
+    ContentType.TABLE,
+    ContentType.CHART,
+    ContentType.INTERLINE_EQUATION,
+    ContentType.SEAL      # ← 印章走纯图路径
+]:
+    span = {
+        "bbox": block["bbox"],
+        "type": span_type,
+    }
+    if span_type == ContentType.SEAL:
+        span["content"] = block.get("text")  # 印章 OCR 识别出的文字
+```
+
+印章块被当作视觉块处理,不会被送入常规的文本 OCR 流。
+
+---
+
+## 二、Step 2: 印章文字 OCR
+
+### 2.1 入口:batch_analyze.py
+
+在 `batch_analyze.py` 中,所有页面的布局检测完成后,专门有一段代码处理印章 OCR(约第 873-918 行)。
+
+#### 流程:
+
+**a) 收集印章块**
+
+遍历所有页面的布局结果,收集 `label == "seal"` 的块:
+
+```python
+# mineru/backend/pipeline/batch_analyze.py
+seal_ocr_items = []
+for ocr_res_list_dict in ocr_res_list_all_page:
+    for layout_res_item in ocr_res_list_dict['layout_res']:
+        if layout_res_item.get("label") == "seal":
+            seal_ocr_items.append((ocr_res_list_dict, layout_res_item))
+```
+
+**b) 裁剪印章子图**
+
+根据布局检测输出的 bbox 从原图中裁剪出印章区域:
+
+```python
+seal_bbox = normalize_to_int_bbox(
+    layout_res_item.get("bbox"),
+    image_size=(image_h, image_w),
+)
+x0, y0, x1, y1 = seal_bbox
+seal_crop_rgb = np_img[y0:y1, x0:x1]  # 裁剪印章子图
+```
+
+**c) 加载 Seal OCR 模型**
+
+通过 `atom_model_manager.get_atom_model()` 获取 `lang="seal"` 的 `PytorchPaddleOCR` 实例:
+
+```python
+if seal_ocr_model is None:
+    seal_ocr_model = atom_model_manager.get_atom_model(
+        atom_model_name=AtomicModel.OCR,
+        lang="seal",       # ← 关键:指定印章模式
+    )
+```
+
+**d) 运行 OCR**
+
+对裁剪出的印章图片执行检测+识别:
+
+```python
+seal_crop_bgr = cv2.cvtColor(seal_crop_rgb, cv2.COLOR_RGB2BGR)
+seal_ocr_res = seal_ocr_model.ocr(seal_crop_bgr, det=True, rec=True)[0]
+```
+
+**e) 汇总文字**
+
+将识别出的所有文本段拼接为列表,存入 `layout_res_item["text"]`:
+
+```python
+seal_texts = []
+for seal_item in seal_ocr_res:
+    rec_result = seal_item[1]       # (text, score) 元组
+    rec_text = rec_result[0]
+    if rec_text:
+        seal_texts.append(rec_text)
+layout_res_item["text"] = seal_texts
+```
+
+### 2.2 Seal OCR 模型配置
+
+#### 模型文件
+
+定义在 `mineru/model/utils/pytorchocr/utils/resources/models_config.yml`:
+
+```yaml
+seal:
+    det: seal_PP-OCRv4_det_server_infer.pth    # 印章文字检测模型
+    rec: ch_PP-OCRv4_rec_server_infer.pth      # 中文文字识别模型
+    dict: ppocr_keys_v1.txt                    # 字符字典
+
+seal_lite:                                     # CPU 环境自动降级为 lite 版
+    det: seal_PP-OCRv4_det_infer.pth
+    rec: ch_PP-OCRv4_rec_infer.pth
+    dict: ppocr_keys_v1.txt
+```
+
+#### 检测模型架构
+
+`seal_PP-OCRv4_det_server_infer` 使用 PP-HGNet_small 骨干 + DB Head(`mineru/model/utils/pytorchocr/utils/resources/arch_config.yaml`):
+
+```yaml
+seal_PP-OCRv4_det_server_infer:
+  model_type: det
+  algorithm: DB
+  Backbone:
+    name: PPHGNet_small
+  Head:
+    name: DBHead
+    k: 50
+```
+
+#### 特殊参数(针对印章场景调优)
+
+在 `PytorchPaddleOCR.__init__()` 中,当 `lang == "seal"` 时,会覆盖默认的 OCR 参数:
+
+```python
+# mineru/model/ocr/pytorch_paddle.py
+if self.is_seal:
+    kwargs['det_limit_side_len'] = 736       # 最小边长限制(确保小印章不被过度缩放)
+    kwargs['det_limit_type'] = 'min'
+    kwargs['det_max_side_limit'] = 4000      # 最大边长限制
+    kwargs['det_db_thresh'] = 0.2            # 极低检测阈值(弧形细小文字不易漏检)
+    kwargs['det_db_box_thresh'] = 0.6        # 检测框阈值
+    kwargs['det_db_unclip_ratio'] = 0.5      # 文本框扩展比例
+    kwargs['det_box_type'] = 'poly'          # 使用多边形检测框(印章文字常沿弧形分布)
+    kwargs['use_dilation'] = False           # 不进行膨胀操作
+    kwargs['enable_merge_det_boxes'] = False # 不合并检测框
+    kwargs['drop_score'] = 0                 # 不丢弃任何低置信度结果
+```
+
+关键区别总结:
+
+| 参数 | seal 模式 | 普通 OCR 模式 | 原因 |
+|------|-----------|---------------|------|
+| `det_box_type` | `'poly'`(多边形) | `'quad'`(四边形) | 印章文字沿弧形分布,矩形框效果差 |
+| `det_db_thresh` | `0.2` | `0.3` | 印章文字细小,需要更低阈值 |
+| `drop_score` | `0` | `0.5` | 印章文字模糊,不丢弃低置信度 |
+| `enable_merge_det_boxes` | `False` | `True` | 印章文字排列稀疏,不合并检测框 |
+| `det_limit_side_len` | `736`(`min`模式) | `960`(`max`模式) | 确保小印章有足够分辨率 |
+
+### 2.3 多边形裁剪与校正
+
+印章文字检测后,使用专用的多边形裁剪和校正管线:
+
+```python
+# mineru/model/ocr/pytorch_paddle.py (ocr 方法)
+if self.is_seal:
+    dt_boxes = self._seal_sort_boxes(dt_boxes)              # 排序检测框
+    img_crop_list = self._seal_crop_by_polys(ori_im, dt_boxes)  # 多边形裁剪
+```
+
+`CropByPolys` 类(`mineru/model/ocr/seal_crop.py`)负责:
+- 对检测到的多边形框进行排序
+- 使用 `cv2.minAreaRect` + 透视变换进行旋转裁剪
+- 对不规则多边形区域,通过 `AutoRectifier` 进行仿射/单应性校正,将弧形文字展平为水平文字
+- 当多边形简化失败时,回退到外接矩形裁剪以保证流程不会中断
+
+---
+
+## 三、输出格式
+
+印章识别结果在多个输出文件中体现:
+
+### 3.1 content_list.json
+
+印章作为独立的内容类型 `seal` 输出:
+
+```json
+{
+    "type": "seal",
+    "img_path": "images/xxx.jpg",
+    "text": ["识别出的印章文字"],
+    "bbox": [x0, y0, x1, y1],
+    "page_idx": 0
+}
+```
+
+生成代码位于 `mineru/backend/pipeline/pipeline_middle_json_mkcontent.py` 的 `_get_seal_span` 和 `_get_seal_text` 函数。
+
+### 3.2 content_list_v2.json (3.0+)
+
+采用新版结构化格式:
+
+```json
+{
+    "type": "seal",
+    "content": {
+        "image_source": {
+            "path": "images/xxx.jpg"
+        },
+        "seal_content": [
+            {"type": "text", "content": "识别出的印章文字"}
+        ]
+    },
+    "bbox": [x0, y0, x1, y1]
+}
+```
+
+### 3.3 middle.json
+
+印章块出现在 `preproc_blocks` 中,类型为 `seal`,包含 `lines[].spans[]` 结构,span 类型为 `seal`。
+
+### 3.4 model.json
+
+印章检测结果在布局检测阶段输出,包含 `cls_id: 20`、`label: "seal"`、`score` 和 `bbox` 信息。
+
+### 3.5 layout.pdf
+
+印章区域在布局可视化 PDF 中以独立颜色块标注,与其他布局类别一同展示。
+
+---
+
+## 四、seal 内容类型的枚举定义
+
+```python
+# mineru/utils/enum_class.py
+
+class BlockType:
+    SEAL = "seal"           # 块级类型
+
+class ContentType:
+    SEAL = 'seal'           # content_list.json 中的类型
+
+class ContentTypeV2:
+    SEAL = 'seal'           # content_list_v2.json 中的类型
+```
+
+---
+
+## 五、完整流程图
+
+```
+输入 PDF 页面
+    │
+    ▼
+┌─────────────────────────────────┐
+│  PP-DocLayoutV2 布局检测        │
+│  (基于 RT-DETR)                  │
+│  检测 25 个类别,含 seal         │
+│  cls_id=20, threshold=0.45      │
+└───────────────┬─────────────────┘
+                │
+    ┌───────────▼───────────┐
+    │  过滤 label=="seal"    │
+    │  的布局检测结果         │
+    └───────────┬───────────┘
+                │
+                ▼
+    ┌───────────────────────────┐
+    │  从原图按 bbox 裁剪        │
+    │  seal_crop = img[y0:y1, x0:x1] │
+    └───────────┬───────────────┘
+                │
+                ▼
+    ┌───────────────────────────┐
+    │  PytorchPaddleOCR(lang="seal") │
+    │                           │
+    │  ┌─ 检测 (det):          │
+    │  │  seal_PP-OCRv4_det    │
+    │  │  DB + PP-HGNet_small  │
+    │  │  多边形框检测           │
+    │  └───────────────────────│
+    │                           │
+    │  ┌─ 识别 (rec):          │
+    │  │  ch_PP-OCRv4_rec      │
+    │  │  中文文字识别           │
+    │  └───────────────────────│
+    └───────────┬───────────────┘
+                │
+                ▼
+    ┌───────────────────────────┐
+    │  CropByPolys +            │
+    │  AutoRectifier            │
+    │  多边形裁剪 + 仿射校正     │
+    │  弧形文字 → 水平文字       │
+    └───────────┬───────────────┘
+                │
+                ▼
+    ┌───────────────────────────┐
+    │  输出结果                  │
+    │  • seal_text (文字列表)    │
+    │  • seal_image (截图)       │
+    │  → content_list.json      │
+    │  → middle.json            │
+    │  → model.json             │
+    │  → layout.pdf             │
+    └───────────────────────────┘
+```
+
+---
+
+## 六、调试功能
+
+Seal OCR 支持环境变量控制的调试输出:
+
+| 环境变量 | 说明 |
+|----------|------|
+| `MINERU_SEAL_OCR_DEBUG=1` | 启用调试模式 |
+| `MINERU_SEAL_OCR_DEBUG_DIR=/path/to/dir` | 指定调试输出目录(默认 `output_images/seal_ocr_debug/`) |
+
+调试模式下,每个印章识别样本会输出:
+- `input.png` — 输入图像
+- `det_vis.png` — 检测框可视化
+- `crop_NN.png` — 每个裁剪后的文字区域
+- `meta.json` — 元数据(文字、置信度)
+
+---
+
+## 七、关键文件索引
+
+| 文件 | 作用 |
+|------|------|
+| `mineru/model/layout/pp_doclayoutv2.py` | PP-DocLayoutV2 模型定义,包含 seal 类别(索引 20)|
+| `mineru/backend/pipeline/batch_analyze.py` | 印章 OCR 主流程(第 873-918 行)|
+| `mineru/backend/pipeline/pipeline_magic_model.py` | MagicModel 中的 seal 块映射与 span 构造 |
+| `mineru/backend/pipeline/model_json_to_middle_json.py` | 中间 JSON 拼装,印章图片截图 |
+| `mineru/model/ocr/pytorch_paddle.py` | PytorchPaddleOCR 类,seal 模式的参数与 OCR 流程 |
+| `mineru/model/ocr/seal_crop.py` | 印章多边形裁剪(`CropByPolys`)与仿射校正 |
+| `mineru/model/ocr/seal_det_warp.py` | 印章检测框仿射校正器(`AutoRectifier`) |
+| `mineru/model/utils/pytorchocr/utils/resources/models_config.yml` | seal/seal_lite 模型文件配置 |
+| `mineru/model/utils/pytorchocr/utils/resources/arch_config.yaml` | seal 检测模型架构配置 |
+| `mineru/utils/enum_class.py` | BlockType.SEAL / ContentType.SEAL 枚举定义 |
+| `mineru/backend/pipeline/pipeline_middle_json_mkcontent.py` | content_list.json / content_list_v2.json 的 seal 输出格式化 |