印章识别-seal处理流程.md 14 KB

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):

# 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,低于大多数其他类别,以确保印章不被漏检:

DEFAULT_CLASS_THRESHOLDS = [
    # ... 其他类别 ...
    0.45,  # 20 seal  ← 较低的阈值
    # ...
]

1.3 MagicModel 中的映射

布局检测结果进入 MagicModel 后,seal 标签被映射为 BlockType.SEAL

# mineru/backend/pipeline/pipeline_magic_model.py
PP_DOCLAYOUT_V2_LABELS_TO_BLOCK_TYPES = {
    # ...
    "seal": BlockType.SEAL,
    # ...
}

BlockType.SEAL 是 MinerU 内部枚举类中定义的类型:

# mineru/utils/enum_class.py
class BlockType:
    # ...
    SEAL = "seal"
    # ...

1.4 印章块的 span 构造

MagicModel.__build_page_blocks() 中,BlockType.SEAL 类型的块被构造为纯图的 span(类似 image/table/chart):

# 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" 的块:

# 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 从原图中裁剪出印章区域:

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 实例:

if seal_ocr_model is None:
    seal_ocr_model = atom_model_manager.get_atom_model(
        atom_model_name=AtomicModel.OCR,
        lang="seal",       # ← 关键:指定印章模式
    )

d) 运行 OCR

对裁剪出的印章图片执行检测+识别:

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"]

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

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):

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 参数:

# 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 736min模式) 960max模式) 确保小印章有足够分辨率

2.3 多边形裁剪与校正

印章文字检测后,使用专用的多边形裁剪和校正管线:

# 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 输出:

{
    "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+)

采用新版结构化格式:

{
    "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: 20label: "seal"scorebbox 信息。

3.5 layout.pdf

印章区域在布局可视化 PDF 中以独立颜色块标注,与其他布局类别一同展示。


四、seal 内容类型的枚举定义

# 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 输出格式化