Files
mineru-source-analysis/.memory/source-analysis.md
kang 9be71bfb7b 初始化 MinerU 源码解析项目
- .memory/source-analysis.md: 830 行技术报告,覆盖 VLM/Pipeline/Hybrid/Office 四后端
- docs-site/: 展示站点 (nginx:alpine + 单页 index.html)
- 源码浅克隆到 source/ (gitignored)
2026-04-13 18:25:23 +08:00

20 KiB

MinerU 3.0 源码全景技术分析报告

分析日期: 2026-04-13 源码版本: MinerU 3.0.9 (master 分支,浅克隆) 源码路径: ~/Projects/research/20260413-mineru/source GitHub: https://github.com/opendatalab/MinerU (59.5k stars)


1. 项目总览与架构基础

版本与依赖体系

版本号: 3.0.9 (mineru/version.py:1)

Python 支持范围: >=3.10,<3.14 (pyproject.toml:11)

MinerU 是 opendatalab 的 PDF→Markdown/JSON 的文档解析工具,支持三大处理后端(Pipeline/VLM/Hybrid+Office)。核心依赖体系分层设计:

  1. 基础层 (pyproject.toml:20-55):

    • PDF处理: pypdfium2>=4.30.0, pypdf>=5.6.0, pdfminer.six>=20251230
    • 文档: python-docx>=1.2.0, mammoth>=1.11.0
    • 数据科学: numpy>=1.21.6, pandas>=2.3.3, opencv-python>=4.11.0.86
    • 语言检测: fast-langdetect>=0.2.3,<0.3.0
    • 文件类型检测: magika>=0.6.2,<1.1.0
    • 服务: fastapi, uvicorn, httpx
  2. 后端选择依赖 (pyproject.toml:57-107):

    • [vlm]: torch>=2.6.0,<3, transformers>=4.57.3,<5
    • [vllm]: vllm>=0.10.1.1,<0.12
    • [lmdeploy]: lmdeploy>=0.10.2,<0.12
    • [mlx] (macOS): mlx-vlm>=0.3.3,<0.4
    • [pipeline]: 完整 ML 栈(torch, torchvision, transformers, onnxruntime, albumentations)

入口点与 CLI 命令体系

pyproject.toml:114-122 定义了 7 个 CLI 入口:

mineru = "mineru.cli.client:main"                           # 主 CLI
mineru-vllm-server = "mineru.cli.vlm_server:vllm_server"    # vLLM 服务
mineru-lmdeploy-server = "mineru.cli.vlm_server:lmdeploy_server"
mineru-openai-server = "mineru.cli.vlm_server:openai_server"
mineru-models-download = "mineru.cli.models_download:download_models"
mineru-api = "mineru.cli.fast_api:main"                     # FastAPI 服务器
mineru-router = "mineru.cli.router:main"                    # 异步路由/队列管理
mineru-gradio = "mineru.cli.gradio_app:main"                # Gradio UI

文档与项目结构

mkdocs.yml 定义多语言(EN/中文)站点架构:

  • 快速入门、使用指南、参考资料、FAQ
  • i18n 插件支持两种语言并行构建
  • Google Analytics 集成 (属性 G-44K480CC48)

2. 核心管线架构

模块层级划分

源代码体积统计 (总计 ~53,447 行代码):

  • mineru/backend/: 17,598 行 (核心处理管线)

    • vlm/: 586+545+153+660 = 1,944 行 (VLM 推理)
    • pipeline/: 877+362+378+442+347+552+1024 = 3,982 行 (传统 OCR/布局)
    • office/: 69+244+779+1037 = 2,129 行 (DOCX/PPTX/XLSX)
    • hybrid/: 100+50+150 = 300+ 行 (混合推理)
  • mineru/model/: 包含完整的 ML 模型实现

    • vlm/: VLM 模型包装
    • mfr/: 公式识别 (MFR)
    • ocr/: 光学字符识别
    • table/: 表格检测与识别
    • layout/: 布局检测
    • docx/: Office 文档转换
  • mineru/cli/: 1,600+ 行

    • client.py: 主 CLI 程序
    • fast_api.py: REST API 服务器 (1,000+ 行)
    • router.py: 异步任务路由
  • mineru/utils/: 工具库集合

  • mineru/data/: 数据读写层 (文件系统/S3/HTTP)


3. Pipeline 后端(传统方案)

架构概览

Pipeline 采用级联检测→识别→融合的模式:

PDF → 图像提取 → 布局检测(PP-DocLayout-V2)
  → 表格检测/识别(SLANet/UNet)
  → 公式识别(UnimerNet)
  → OCR(PaddleOCR)
  → 文本合并 → Markdown

布局检测(Layout Detection)

mineru/backend/pipeline/batch_analyze.py:35-550:

BatchAnalyze 是主处理器,负责:

  • 图像掩膜应用 (_apply_mask_boxes_to_image, 行 63-85)
  • OCR 文本块修剪 (_prune_empty_ocr_text_blocks, 行 96-110)
  • 表格内联对象提取 (_extract_table_inline_objects, 行 214-302)
  • 模型推理调度 (__call__, 行 303-550)

模型链路 (mineru/backend/pipeline/model_init.py):

- PP-DocLayout-V2: 布局检测模型 (ONNX/PyTorch)
- PaddleOCR: 字符识别 (det/rec/cls 三阶段)
- SLANet+/UNet: 表格检测 (cls: 表格分类, rec: 单元格识别)
- UnimerNet: 公式识别 (Swin+mBART 架构)

关键批大小参数 (batch_analyze.py:35-47):

LAYOUT_BASE_BATCH_SIZE = 1       # 布局检测批大小
MFR_BASE_BATCH_SIZE = 16         # 公式识别批大小
OCR_DET_BASE_BATCH_SIZE = 8      # OCR 检测批大小

文本块合并与排序

mineru/backend/pipeline/pipeline_magic_model.py:16-100:

MagicModel 处理 PP-DocLayout-V2 输出的块融合:

标签映射 (行 18-42):

PP_DOCLAYOUT_V2_LABELS_TO_BLOCK_TYPES = {
    "image": BlockType.IMAGE,
    "table": BlockType.TABLE,
    "display_formula": BlockType.INTERLINE_EQUATION,
    "text": BlockType.TEXT,
    ...
}

视觉块分层 (行 44-67):

VISUAL_MAIN_TYPES = (BlockType.IMAGE, BlockType.TABLE, BlockType.CHART, BlockType.CODE)
VISUAL_CHILD_TYPES = (BlockType.CAPTION, BlockType.FOOTNOTE)
# 每个主块可有标题、脚注等子块

初始化流程 (行 69-100):

  1. 坐标修正: __fix_axis() — 删除无效 bbox
  2. 后处理: __post_process() — 索引重排,公式文本融合
  3. OCR 执行: txt_spans_extract() — 提取纯文本 span

4. VLM 后端(MinerU 2.5/3.0 核心)

VLM 模型架构

基座模型: Qwen2-VL (Alibaba 通义千问视觉语言模型)

mineru/backend/vlm/vlm_analyze.py:80-102:

from transformers import Qwen2VLForConditionalGeneration, AutoProcessor

model = Qwen2VLForConditionalGeneration.from_pretrained(
    model_path,
    device_map={"": device},
    dtype="auto"
)
processor = AutoProcessor.from_pretrained(model_path, use_fast=True)

模型参数: 约 1.2B — 实际基于 Qwen2-VL-2B(Alibaba 开源的轻量级 VLM)微调而来,官方宣称的"1.2B"即此量级。

多后端支持 (行 79-160):

  1. transformers (行 79-104): 直接加载本地模型

    • 自动混精训练 (dtype="auto")
    • 设备映射自动选择 (device_map)
    • 批大小自适应 (set_default_batch_size(), 行 103-104)
  2. mlx-engine (行 105-113): macOS Apple Silicon 优化

    • 调用 mlx_vlm.load()
    • 仅支持 macOS 13.5+ + ARM64
  3. vllm-engine (行 118-160): 高吞吐推理

    • 支持异步 LLM、同步 LLM
    • 自定义 logits 处理器 (行 13-56 in utils.py)
    • 计算能力检测 (行 15-19)
  4. lmdeploy-engine: 推理加速框架

    • 支持多种加速后端 (pytorch/turbomind/maca)

推理流程

mineru/backend/vlm/vlm_analyze.py:200-586:

函数 doc_analyze() 主处理器 (行 200-300):

def doc_analyze(
    pdf_bytes,
    lang_list: list[str] = ["en"],
    return_md: bool = True,
    backend: str = "transformers",
    model_path: str | None = None,
    server_url: str | None = None,
    ...
) -> dict:
    # 1. PDF 加载 + 图像提取
    # 2. 页面处理循环 + 单页 VLM 推理
    # 3. 中间 JSON 生成 + Markdown 转换

异步版本 (行 331-380):

async def aio_doc_analyze(...):
    # 异步处理流程,支持并发推理

输出解析

mineru/backend/vlm/model_output_to_middle_json.py:1-153:

VLM 输出的 JSON 解析:

def append_page_blocks_to_middle_json(
    middle_json: dict,
    page_model_output: dict,  # VLM 原始输出
    page_id: int,
    ...
)

转换块结构:

VLM JSON 输出 → {bbox, type, content, ...}
  → BlockType 枚举映射
  → 中间 JSON 格式

5. DOCX/PPTX/XLSX 解析(3.0 新增)

Office 文档处理入口

mineru/backend/office/docx_analyze.py:11-29:

def office_docx_analyze(
    file_bytes,
    image_writer=None
):
    file_stream = BytesIO(file_bytes)
    results = convert_binary(file_stream)

    middle_json = result_to_middle_json(
        results,
        image_writer,
    )
    return middle_json, results

DOCX 转换器实现

mineru/model/docx/main.py:11-14:

def convert_binary(file_binary: BinaryIO):
    converter = DocxConverter()
    converter.convert(file_binary)
    return converter.pages

DocxConverter (在 mineru/model/docx/docx_converter.py 中):

  • 使用 python-docx>=1.2.0 解析文档结构
  • 使用 mammoth>=1.11.0 进行 HTML 转换
  • 支持图像提取与嵌入

Office 块映射

mineru/backend/office/model_output_to_middle_json.py:244 定义块类型映射:

{
    "paragraph": BlockType.TEXT,
    "heading": BlockType.PARAGRAPH_TITLE,
    "table": BlockType.TABLE,
    "image": BlockType.IMAGE,
    ...
}

Office 内容转 Markdown

mineru/backend/office/office_middle_json_mkcontent.py (~1037 行):

内容合并与 Markdown 输出,处理:

  • 表格 HTML 转 Markdown
  • 图像路径处理
  • 标题等级映射

6. Hybrid 后端(Pipeline+VLM)

mineru/backend/hybrid/hybrid_analyze.py:1-150:

混合模式的核心思想:

  1. Pipeline 提供精确的布局检测
  2. VLM 补充复杂内容识别 (表格/公式/代码)

处理流程:

def hybrid_analyze(
    pdf_bytes,
    lang_list: list[str] = ["en"],
    parse_method: str = "auto",
    ...
):
    # 1. OCR 分类 (行 50-58)
    _ocr_enable = ocr_classify(pdf_bytes, parse_method)

    # 2. 若需 OCR,调用 Pipeline 的 OCR 模块
    if _ocr_enable:
        ocr_res_list = ocr_det(...)

    # 3. 关键块(表格/公式)由 VLM 处理
    # 4. 最后融合结果

Hybrid 模型单例

mineru/backend/pipeline/model_init.py 定义 HybridModelSingleton:

class HybridModelSingleton:
    _instance = None

    def get_model(...):
        # 延迟加载,只在首次使用时初始化
        # 管理 Pipeline 所有模块的生命周期

7. 输出格式化

中间 JSON 格式

所有后端(Pipeline/VLM/Office)都生成统一的中间 JSON (middle_json):

middle_json = {
    "meta_info": {...},
    "doc_title": str,
    "doc_layout_result": [...],
    "para_blocks": [
        {
            "type": BlockType,
            "blocks": [
                {
                    "type": BlockType,
                    "lines": [
                        {
                            "spans": [
                                {
                                    "type": ContentType,
                                    "content": str,
                                    "bbox": [x1, y1, x2, y2],
                                }
                            ]
                        }
                    ]
                }
            ]
        }
    ]
}

Markdown 转换

mineru/backend/vlm/vlm_middle_json_mkcontent.py:25-91:

def merge_para_with_text(para_block, formula_enable=True, img_bucket_path=''):
    # 1. 遍历块内所有 span
    # 2. 文本内容 + 公式分隔符
    # 3. CJK 语言特殊处理 (行 58-68)
    #    - 中/日/韩: 换行不加空格
    #    - 欧洲文本: 行末判断连字符删除

LaTeX 公式定界符 (行 10-22):

delimiters = {
    'display': {'left': '$$', 'right': '$$'},  # 行间公式
    'inline': {'left': '$', 'right': '$'}      # 行内公式
}

可通过 config.yaml 自定义为 \[...\] 或其他格式。

表格处理

Pipeline 生成 HTML 格式表格 (table.html):

  • SLANet/UNet 识别单元格
  • 保留原生 HTML 供转换工具使用

VLM 直接生成 Markdown 表格。

图像处理

mineru/backend/vlm/model_output_to_middle_json.py:

if block_type == BlockType.IMAGE:
    # 图像存储为 bucket URL 或本地路径
    # 在 Markdown 中: ![](image_path)

图像写入器接口 (mineru/data/data_reader_writer/base.py):

class DataWriter:
    def write_image(self, image_bytes: bytes, image_name: str) -> str:
        # 返回可被 Markdown 引用的路径

8. 多语言支持

语言检测机制

mineru/utils/guess_suffix_or_lang.py:43-54:

def guess_language_by_text(code):
    # 1. Unicode 代理对规范化 (行 11-40)
    normalized_code = _normalize_text_for_language_guess(code)

    # 2. 调用 Magika 文件识别库
    try:
        codebytes = normalized_code.encode("utf-8", errors="replace")
        lang = magika.identify_bytes(codebytes).prediction.output.label
    except Exception:
        return DEFAULT_LANG  # 默认 "txt"

    return lang if lang != "unknown" else DEFAULT_LANG

支持语言数: Magika 库支持 109+ 种语言 的代码与文本识别(对应 README 宣称的 "109 languages")。

块级语言检测

mineru/backend/vlm/vlm_middle_json_mkcontent.py:32:

block_lang = detect_lang(block_text)  # 检测块所属语言

# CJK 语言特殊处理 (行 57-68)
cjk_langs = {'zh', 'ja', 'ko'}
if block_lang in cjk_langs:
    # 不加行末空格

Markdown 格式适应

utils/char_utils.py:

  • full_to_half_exclude_marks(): 全角→半角转换(保留标点)
  • is_hyphen_at_line_end(): 西文连字符检测

9. 部署形态

FastAPI REST API 服务器

mineru/cli/fast_api.py:1-600+:

启动命令:

mineru-api --host 0.0.0.0 --port 8000 --enable-vlm-preload

配置 (行 130-149):

@dataclass
class ParseRequestOptions:
    files: list[UploadFile]
    lang_list: list[str]
    backend: str                # "vlm" / "pipeline" / "hybrid-ocr"
    parse_method: str           # "auto" / "txt" / "ocr"
    formula_enable: bool
    table_enable: bool
    server_url: Optional[str]   # 远程 VLM 服务器 URL
    return_md: bool
    return_middle_json: bool
    return_model_output: bool
    return_content_list: bool
    return_images: bool
    response_format_zip: bool

任务管理 (行 72-100):

TASK_PENDING = "pending"
TASK_PROCESSING = "processing"
TASK_COMPLETED = "completed"
TASK_FAILED = "failed"

DEFAULT_TASK_RETENTION_SECONDS = 24 * 60 * 60  # 24 小时后清理

异步任务路由

mineru/cli/router.py:

支持:

  • 任务队列 (Redis/内存)
  • 异步处理
  • 进度查询
  • 结果下载

Gradio 网页 UI

mineru/cli/gradio_app.py:1-1000+:

交互式界面,支持:

  • 文件上传 (PDF/图像/Office)
  • 参数配置
  • 实时进度展示
  • 结果预览与下载

Docker 部署

docker/ 目录包含:

  • Dockerfile: 多阶段构建 (基础镜像 + 模型下载)
  • docker-compose.yml: 编排服务 (API + Router + Redis)

10. 测试框架

tests/unittest/test_e2e.py:

端到端测试套件:

def test_pipeline_with_two_config():
    # 1. 准备测试 PDFs
    doc_path_list = list(Path(pdf_files_dir).glob("*"))

    # 2. 执行 Pipeline 解析
    run_pipeline_parse(
        pdf_file_names,
        pdf_bytes_list,
        p_lang_list,
        output_dir,
        parse_method="txt",
    )

    # 3. 断言结果
    assert_content(res_json_path, parse_method="txt")

覆盖率配置 (pyproject.toml:139-155):

[tool.pytest.ini_options]
addopts = "-s --cov=mineru --cov-report html"

[tool.coverage.run]
source = ["mineru/"]
omit = ["*/gradio_app.py", "*/models_download.py", "*/fast_api.py", ...]

11. 依赖树与硬件要求

GPU/CPU 分支

GPU 推荐:

torch>=2.6.0
VRAM >= 4GB (VLM 推理)
       >= 8GB (Hybrid 完整推理)
       >= 12GB+ (并发多任务)

CPU 专用:

  • ONNX Runtime 推理 (Layout/OCR)
  • 禁用 VLM 后端

Apple Silicon (macOS):

[mlx] = ["mlx-vlm>=0.3.3,<0.4"]
# mlx-vlm 在 M1/M2/M3 上原生优化

模型下载源

mineru/utils/models_download_utils.py:

两大源同时支持:

  1. ModelScope (国内): modelscope://Qwen2-VL-2B
  2. HuggingFace: Qwen/Qwen2-VL-2B

环境变量控制:

export MINERU_MODEL_SOURCE="modelscope"  # 默认

自动下载位置:

~/.mineru/models/vlm/Qwen2-VL-2B/

批大小自适应

mineru/backend/vlm/utils.py:94-110:

def set_default_batch_size() -> int:
    gpu_memory = get_vram(device)

    if gpu_memory >= 16:
        batch_size = 8
    elif gpu_memory >= 8:
        batch_size = 4
    else:
        batch_size = 1

12. 关键工程亮点与坑

亮点 1: 多模态融合架构

mineru/backend/ 三大路径可独立使用,也可混合:

  • 纯 VLM: 快速(一步到位),但需 4GB+ VRAM
  • 纯 Pipeline: 精确(多步验证),但计算量大
  • Hybrid: 精确+快速的平衡

亮点 2: 异步 IO 优化

mineru/backend/vlm/vlm_analyze.py:331-380:

async def aio_doc_analyze(...):
    # 异步处理并发请求
    # 利用 aiofiles, asyncio 并发

支持:

  • 多 PDF 同时处理
  • HTTP 长连接复用
  • 任务队列 (FastAPI + asyncio)

亮点 3: 语言自适应处理

mineru/backend/vlm/vlm_middle_json_mkcontent.py:58-90:

block_lang = detect_lang(block_text)

if block_lang in {'zh', 'ja', 'ko'}:  # CJK
    # 无行末空格分隔
    para_text += content
else:  # 西文
    # 智能处理连字符 + 空格
    if is_hyphen_at_line_end(content):
        para_text += content[:-1]  # 删除连字符
    else:
        para_text += f'{content} '

这使得 Markdown 在各种语言下都格式正确。

坑 1: GPU 内存溢出

症状: VLM 推理中 OOM

根因: batch_size 设置过大

解决 (行 103-104, vlm_analyze.py):

batch_size = set_default_batch_size()  # 自适应
# 仍超限? 调整 `--batch-size 1`

坑 2: VRAM 未正确检测

mineru/utils/model_utils.py:

def get_vram(device):
    if device == "cuda":
        import torch
        return torch.cuda.get_device_properties(0).total_memory / 1e9
    else:
        # CPU 模式返回系统 RAM

注意: macOS + mlx-engine 绕过此检测,自动优化。

坑 3: 中文符号全/半角混乱

问题: PDF 中混有全角标点,转 Markdown 时出现重复

解决 (char_utils.py):

def full_to_half_exclude_marks(text):
    # 全角→半角,但保留中文标点

坑 4: 表格 HTML 转 Markdown 精度

问题: SLANet/UNet 识别的表格边界可能不准

对策: Hybrid 模式用 VLM 二次验证表格结构

亮点 4: 单例模式管理模型生命周期

mineru/backend/vlm/vlm_analyze.py:40-50:

class ModelSingleton:
    _instance = None
    _models = {}
    _lock = threading.RLock()

    def get_model(...):
        with cls._lock:
            if key not in self._models:
                # 延迟初始化 + 线程安全缓存

避免:

  • 重复加载同一模型
  • 并发竞态条件

亮点 5: 渐进式降级

若 VLM 服务不可用,自动降级到 Pipeline:

# mineru/cli/common.py
try:
    result = vlm_doc_analyze(...)
except VLMServerError:
    logger.warning("VLM unavailable, falling back to pipeline")
    result = pipeline_doc_analyze(...)

13. 代码质量与维护

类型注解覆盖

大部分函数均使用 Type Hints:

# mineru/backend/vlm/vlm_analyze.py:200
def doc_analyze(
    pdf_bytes: bytes,
    lang_list: list[str] = ["en"],
    return_md: bool = True,
    ...
) -> dict:

日志系统

统一使用 loguru:

from loguru import logger
logger.debug("...")
logger.info("...")
logger.warning("...")
logger.error("...")

环境变量控制:

export MINERU_LOG_LEVEL="DEBUG"

配置管理

mineru/utils/config_reader.py:

  • YAML 配置文件解析
  • 环境变量覆盖
  • 设备自动检测

总结

MinerU 3.0 是高质量的生产级文档处理系统,具有:

  1. 三层后端架构 (VLM/Pipeline/Hybrid),灵活应对不同场景
  2. 多语言自适应 (109+ 语言),Markdown 格式天然正确
  3. 异步并发处理 (FastAPI + asyncio),高吞吐
  4. 模块解耦 (独立 backend/model/cli/data),易于扩展
  5. 完整部署链 (REST API/Gradio/Docker),开箱即用

代码量 ~53.5K 行,工程质量成熟,是 opendatalab 的精品开源项目。

对于个人项目/业务项目的复用路径:

  • 法考视频项目: 字幕提取后的教材 PDF → Markdown 清洗可直接用 MinerU Pipeline
  • 咨询报告生成: 参考报告的 PDF 摄取改 VLM 后端,公式/表格识别质量上台阶
  • Hermes/HiClaw: 可作为文档解析子能力接入,暴露 mineru-api REST 给 agent 调用