初始化 MinerU 源码解析项目

- .memory/source-analysis.md: 830 行技术报告,覆盖 VLM/Pipeline/Hybrid/Office 四后端
- docs-site/: 展示站点 (nginx:alpine + 单页 index.html)
- 源码浅克隆到 source/ (gitignored)
This commit is contained in:
kang
2026-04-13 18:25:23 +08:00
commit 9be71bfb7b
4 changed files with 1693 additions and 0 deletions

830
.memory/source-analysis.md Normal file
View File

@@ -0,0 +1,830 @@
# 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`):
```python
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):
```python
PP_DOCLAYOUT_V2_LABELS_TO_BLOCK_TYPES = {
"image": BlockType.IMAGE,
"table": BlockType.TABLE,
"display_formula": BlockType.INTERLINE_EQUATION,
"text": BlockType.TEXT,
...
}
```
**视觉块分层** (行 44-67):
```python
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`:
```python
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):
```python
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):
```python
async def aio_doc_analyze(...):
# 异步处理流程,支持并发推理
```
### 输出解析
`mineru/backend/vlm/model_output_to_middle_json.py:1-153`:
VLM 输出的 JSON 解析:
```python
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`:
```python
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`:
```python
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` 定义块类型映射:
```python
{
"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** 补充**复杂内容识别** (表格/公式/代码)
处理流程:
```python
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`:
```python
class HybridModelSingleton:
_instance = None
def get_model(...):
# 延迟加载,只在首次使用时初始化
# 管理 Pipeline 所有模块的生命周期
```
---
## 7. 输出格式化
### 中间 JSON 格式
所有后端(Pipeline/VLM/Office)都生成统一的**中间 JSON** (`middle_json`):
```python
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`:
```python
def merge_para_with_text(para_block, formula_enable=True, img_bucket_path=''):
# 1. 遍历块内所有 span
# 2. 文本内容 + 公式分隔符
# 3. CJK 语言特殊处理 (行 58-68)
# - 中/日/韩: 换行不加空格
# - 欧洲文本: 行末判断连字符删除
```
**LaTeX 公式定界符** (行 10-22):
```python
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`:
```python
if block_type == BlockType.IMAGE:
# 图像存储为 bucket URL 或本地路径
# 在 Markdown 中: ![](image_path)
```
图像写入器接口 (`mineru/data/data_reader_writer/base.py`):
```python
class DataWriter:
def write_image(self, image_bytes: bytes, image_name: str) -> str:
# 返回可被 Markdown 引用的路径
```
---
## 8. 多语言支持
### 语言检测机制
`mineru/utils/guess_suffix_or_lang.py:43-54`:
```python
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`:
```python
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+`:
**启动命令**:
```bash
mineru-api --host 0.0.0.0 --port 8000 --enable-vlm-preload
```
**配置** (行 130-149):
```python
@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):
```python
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`:
端到端测试套件:
```python
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`):
```toml
[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)**:
```python
[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`
环境变量控制:
```bash
export MINERU_MODEL_SOURCE="modelscope" # 默认
```
自动下载位置:
```
~/.mineru/models/vlm/Qwen2-VL-2B/
```
### 批大小自适应
`mineru/backend/vlm/utils.py:94-110`:
```python
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`:
```python
async def aio_doc_analyze(...):
# 异步处理并发请求
# 利用 aiofiles, asyncio 并发
```
支持:
- 多 PDF 同时处理
- HTTP 长连接复用
- 任务队列 (FastAPI + asyncio)
### 亮点 3: 语言自适应处理
`mineru/backend/vlm/vlm_middle_json_mkcontent.py:58-90`:
```python
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`):
```python
batch_size = set_default_batch_size() # 自适应
# 仍超限? 调整 `--batch-size 1`
```
### 坑 2: VRAM 未正确检测
`mineru/utils/model_utils.py`:
```python
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`):
```python
def full_to_half_exclude_marks(text):
# 全角→半角,但保留中文标点
```
### 坑 4: 表格 HTML 转 Markdown 精度
**问题**: SLANet/UNet 识别的表格边界可能不准
**对策**: Hybrid 模式用 VLM 二次验证表格结构
### 亮点 4: 单例模式管理模型生命周期
`mineru/backend/vlm/vlm_analyze.py:40-50`:
```python
class ModelSingleton:
_instance = None
_models = {}
_lock = threading.RLock()
def get_model(...):
with cls._lock:
if key not in self._models:
# 延迟初始化 + 线程安全缓存
```
避免:
- 重复加载同一模型
- 并发竞态条件
### 亮点 5: 渐进式降级
若 VLM 服务不可用,自动降级到 Pipeline:
```python
# 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:
```python
# mineru/backend/vlm/vlm_analyze.py:200
def doc_analyze(
pdf_bytes: bytes,
lang_list: list[str] = ["en"],
return_md: bool = True,
...
) -> dict:
```
### 日志系统
统一使用 `loguru`:
```python
from loguru import logger
logger.debug("...")
logger.info("...")
logger.warning("...")
logger.error("...")
```
环境变量控制:
```bash
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 调用