pdf解析工具---Miner-u 本地部署记录
一、MinerU悉知及源码学习
可以做什么:MinerU是一款由上海人工智能实验室 OpenDataLab 团队开发的开源 PDF 转 Markdown 工具,可以高质量地提取 PDF 文档内容,生成结构化的 Markdown 格式文本,可用于RAG、LLM语料准备等场景。
在github上下载项目源码进行阅读。
MinerU两种后端
MinerU对文档解析的方式有两种,分别是Pipeline和VLM的方式。以下是两种方式的对比。
1. VLM 后端(视觉 - 语言模型)
- 核心模型:依赖视觉 - 语言大模型(如 Qwen2VL、LLaVA 等),具备 “看图理解内容 + 格式” 的能力,需配合
vllm
等推理引擎加速(支持批量 / 异步推理)。 - 辅助工具:仅需基础的 PDF 转图像工具(如
pdf2image
),无需其他专项模型(布局分析、OCR、表格解析等均由大模型内部完成)。 - 模型特点:单一大模型包办 “识别 + 理解”,依赖强算力(GPU),模型体积大(通常数十亿参数)。
2. Pipeline 后端(传统流水线)
- 核心模型:由多个专项轻量模型 + 规则组成工具链,分工处理不同任务:
- 布局分析:
DocLayoutYOLO
(识别标题、段落、表格等元素位置); - OCR 识别:
PaddleOCR
(提取图片中的文字); - 表格解析:
UnetTableModel
(有线表格)、RapidTableModel
(无线表格); - 公式处理:
YOLOv8MFD
(公式检测)+Unimernet
(公式识别为 LaTeX)。
- 布局分析:
- 辅助工具:需要坐标计算(如 IOU 重叠度)、规则匹配(如列表缩进判断)等工程化逻辑。
- 模型特点:多模型分工明确,单个模型体积小(百万至千万参数),可在 CPU 运行。
MinerU使用的模型
class AtomicModel:Layout = "layout"MFD = "mfd"MFR = "mfr"OCR = "ocr"WirelessTable = "wireless_table"WiredTable = "wired_table"TableCls = "table_cls"ImgOrientationCls = "img_ori_cls"
可以在下载的MinerU项目源码的model文件夹里查看默认使用的模型
pipeline
模型类型 | 模型 |
layout模型 | YOLOv10 目标检测模型,专门用于 “文档布局识别”需提前准备 YOLOv10 文档布局专用权重,使用时会自动下载。 |
mfd | 基于 YOLOv8 的公式检测模型工具类 |
ocr | paddleocr2pytorch |
ocr_cls | 基于 ONNX Runtime 的图像方向分类模型工具类(PaddleOrientationClsModel),核心功能是检测图像(主要是表格图像)的旋转角度(0°、90°、180°、270°),并将旋转的图像校正为正方向,确保后续表格识别、OCR 等处理的准确性。 |
reading_order | 基于 LayoutLMv3 模型的文档元素排序工具 |
table | 基于 ONNX Runtime 的表格类型分类模型工具类(PaddleTableClsModel) |
vlm
这里定义了怎么使用vllm来使用视觉模型的命令。
参数类别 | 关键参数示例 | 作用说明 | 默认值 / 处理逻辑 |
---|---|---|---|
模型相关 | --model /path/to/your/model | 指定 VLM 模型路径(本地已有的模型) | 若不指定,自动下载默认 VLM 模型到工具类的模型目录(如.mineru/models/vlm ) |
服务端口 | --port 8000 | 设置服务监听的端口号 | 默认30000 |
GPU 资源 | --gpu-memory-utilization 0.7 | 控制 GPU 内存利用率(0~1 之间) | 默认0.5 (50%),避免显存占用过高 |
logits 处理器 | --logits-processors "custom:Processor" | 自定义 logits 处理器(用于优化模型输出) | 若环境兼容(见enable_custom_logits_processors 逻辑),默认启用mineru_vl_utils:MinerULogitsProcessor |
vllm 原生参数 | --tensor-parallel-size 2 | vllm 支持的其他参数(如张量并行、最大序列长度等) | 直接传递给 vllm,无默认值(按 vllm 原生逻辑处理) |
根据源码解决如何更换自己想用的模型:
模型自动下载的缓存地址,重要的就是这个地址要改成自己的模型:
doclayout_yolo_weights = os.path.join(auto_download_and_get_model_root_path(ModelPath.doclayout_yolo), ModelPath.doclayout_yolo)
auto_download_and_get_model_root_path这个函数在:anaconda3/envs/minerU/lib/python3.11/site-packages/mineru/utils/models_download_utils.py这个路径下。
该函数是 MinerU 模型管理的 “总入口”,解决了 “模型从哪里下载、下载到哪里、如何读取本地模型” 的核心问题,实现了 “云端自动下载” 与 “本地路径读取” 的无缝切换,适配不同使用场景(如无网络环境用本地模型,有网络自动拉取最新模型)。
二、虚拟环境配置
pip install "mineru[vllm]":安装 mineru 主包及 vllm 推理后端相关的基础依赖。pip install "mineru-vl-utils[vllm]":安装 mineru 的视觉相关核心工具与vllm适配的组件,且确保其支持 vllm 推理。pip install torch==2.9.0+cu121 torchvision==0.24.0+cu121 torchaudio==2.8.0+cu121 --index-url https://download.pytorch.org/whl/cu121pip install loguru pypdfium2 doclayout_yolo:安装轻量辅助工具,支持日志、PDF 处理和文档布局分析。mamba install -c conda-forge tqdm vllm ultralytics:安装进度条工具、vllm 推理引擎和图像处理模型库。还可参考miner-u项目中的pyproject.toml文件。
三、本地实操,使用VLM后台
步骤1:下载miner-U模型
使用MinerU2.5-2509-1.2B视觉模型。
MinerU2.5-2509-1.2B 是由 OpenDataLab 与上海 AI 实验室于 2025 年 9 月推出的视觉语言模型,专为高精度、高效率的文档解析任务而设计。它是 MinerU 系列的最新迭代版本,聚焦于将 PDF 等复杂格式文档转化为结构化的机器可读数据(如 Markdown 、 JSON 等)。
# 1. 导入 modelscope 的核心下载函数
from modelscope import snapshot_download
import os# --- 配置区 ---# 指定要从 ModelScope Hub 下载的模型 ID
# ModelScope 会自动从 'https://modelscope.cn/models' 下载
model_id = 'OpenDataLab/MinerU2.5-2509-1.2B'# 指定你希望将模型文件保存到的本地目录
# 注意:在 modelscope 中,这个参数叫做 cache_dir
local_dir_path = './MinerU2.5'# 指定要下载的模型版本,例如 'master' (主分支) 或一个具体的 commit ID
# 这是一个好习惯,可以保证你下载版本的确定性
revision = 'master'# --- 执行区 ---print(f"准备从 ModelScope 下载模型: {model_id} (版本: {revision})")
print(f"将保存到本地目录: {local_dir_path}")try:# 2. 开始执行下载# 函数会返回实际存放模型文件的完整路径downloaded_path = snapshot_download(model_id=model_id,cache_dir=local_dir_path,revision=revision,# allow_pattern="*.safetensors", # 可选:只下载 safetensors 格式的权重文件# ignore_file_pattern=[r"\.md$", r"\.py$"], # 可选:忽略所有 Markdown 和 Python 文件)print(f"\n✅ 模型已成功下载到: {downloaded_path}")print("这个返回的路径是包含模型文件的最终目录,你可以直接使用它。")except Exception as e:print(f"\n❌ 下载过程中出现错误: {e}")print("\n排查建议:")print("1. 检查你的网络连接是否可以访问 modelscope.cn。")print("2. 确认模型 ID 'Qwen/Qwen3-8B' 是否正确。")print("3. 如果是私有模型,请确保已在终端运行 'modelscope login' 并输入了你的 Token。")
步骤2:使用VLM后台解析pdf
# Copyright (c) Opendatalab. All rights reserved.
import copy
import json
import os
from pathlib import Path
# 【核心修改】在所有其他導入之前,設定自訂的緩存路徑
# 這行程式碼必須放在腳本的最頂部
# 同時設定 MINERU_MODEL_SOURCE,確保它使用 modelscope 的緩存機制
os.environ['MINERU_MODEL_SOURCE'] = "modelscope"#从魔塔社区下载模型
os.environ['MODELSCOPE_CACHE'] = "/home/agent/models/MinerU2.5"#下载的模型存到这个路径
from loguru import loggerfrom mineru.cli.common import convert_pdf_bytes_to_bytes_by_pypdfium2, prepare_env, read_fn
from mineru.data.data_reader_writer import FileBasedDataWriter
from mineru.utils.draw_bbox import draw_layout_bbox, draw_span_bbox
from mineru.utils.enum_class import MakeMode
from mineru.backend.vlm.vlm_analyze import doc_analyze as vlm_doc_analyze
from mineru.backend.pipeline.pipeline_analyze import doc_analyze as pipeline_doc_analyze
from mineru.backend.pipeline.pipeline_middle_json_mkcontent import union_make as pipeline_union_make
from mineru.backend.pipeline.model_json_to_middle_json import result_to_middle_json as pipeline_result_to_middle_json
from mineru.backend.vlm.vlm_middle_json_mkcontent import union_make as vlm_union_make
from mineru.utils.guess_suffix_or_lang import guess_suffix_by_pathdef do_parse(output_dir, # Output directory for storing parsing resultspdf_file_names: list[str], # List of PDF file names to be parsedpdf_bytes_list: list[bytes], # List of PDF bytes to be parsedp_lang_list: list[str], # List of languages for each PDF, default is 'ch' (Chinese)backend="pipeline", # The backend for parsing PDF, default is 'pipeline'parse_method="auto", # The method for parsing PDF, default is 'auto'formula_enable=True, # Enable formula parsingtable_enable=True, # Enable table parsingserver_url=None, # Server URL for vlm-http-client backendf_draw_layout_bbox=True, # Whether to draw layout bounding boxesf_draw_span_bbox=True, # Whether to draw span bounding boxesf_dump_md=True, # Whether to dump markdown filesf_dump_middle_json=True, # Whether to dump middle JSON filesf_dump_model_output=True, # Whether to dump model output filesf_dump_orig_pdf=True, # Whether to dump original PDF filesf_dump_content_list=True, # Whether to dump content list filesf_make_md_mode=MakeMode.MM_MD, # The mode for making markdown content, default is MM_MDstart_page_id=0, # Start page ID for parsing, default is 0end_page_id=None, # End page ID for parsing, default is None (parse all pages until the end of the document)
):if backend == "pipeline":for idx, pdf_bytes in enumerate(pdf_bytes_list):new_pdf_bytes = convert_pdf_bytes_to_bytes_by_pypdfium2(pdf_bytes, start_page_id, end_page_id)pdf_bytes_list[idx] = new_pdf_bytesinfer_results, all_image_lists, all_pdf_docs, lang_list, ocr_enabled_list = pipeline_doc_analyze(pdf_bytes_list, p_lang_list, parse_method=parse_method, formula_enable=formula_enable,table_enable=table_enable)for idx, model_list in enumerate(infer_results):model_json = copy.deepcopy(model_list)pdf_file_name = pdf_file_names[idx]local_image_dir, local_md_dir = prepare_env(output_dir, pdf_file_name, parse_method)image_writer, md_writer = FileBasedDataWriter(local_image_dir), FileBasedDataWriter(local_md_dir)images_list = all_image_lists[idx]pdf_doc = all_pdf_docs[idx]_lang = lang_list[idx]_ocr_enable = ocr_enabled_list[idx]middle_json = pipeline_result_to_middle_json(model_list, images_list, pdf_doc, image_writer, _lang, _ocr_enable, formula_enable)pdf_info = middle_json["pdf_info"]pdf_bytes = pdf_bytes_list[idx]_process_output(pdf_info, pdf_bytes, pdf_file_name, local_md_dir, local_image_dir,md_writer, f_draw_layout_bbox, f_draw_span_bbox, f_dump_orig_pdf,f_dump_md, f_dump_content_list, f_dump_middle_json, f_dump_model_output,f_make_md_mode, middle_json, model_json, is_pipeline=True)else:if backend.startswith("vlm-"):backend = backend[4:]f_draw_span_bbox = Falseparse_method = "vlm"for idx, pdf_bytes in enumerate(pdf_bytes_list):pdf_file_name = pdf_file_names[idx]pdf_bytes = convert_pdf_bytes_to_bytes_by_pypdfium2(pdf_bytes, start_page_id, end_page_id)local_image_dir, local_md_dir = prepare_env(output_dir, pdf_file_name, parse_method)image_writer, md_writer = FileBasedDataWriter(local_image_dir), FileBasedDataWriter(local_md_dir)middle_json, infer_result = vlm_doc_analyze(pdf_bytes, image_writer=image_writer, backend=backend, server_url=server_url)pdf_info = middle_json["pdf_info"]_process_output(pdf_info, pdf_bytes, pdf_file_name, local_md_dir, local_image_dir,md_writer, f_draw_layout_bbox, f_draw_span_bbox, f_dump_orig_pdf,f_dump_md, f_dump_content_list, f_dump_middle_json, f_dump_model_output,f_make_md_mode, middle_json, infer_result, is_pipeline=False)def _process_output(pdf_info,pdf_bytes,pdf_file_name,local_md_dir,local_image_dir,md_writer,f_draw_layout_bbox,f_draw_span_bbox,f_dump_orig_pdf,f_dump_md,f_dump_content_list,f_dump_middle_json,f_dump_model_output,f_make_md_mode,middle_json,model_output=None,is_pipeline=True
):"""处理输出文件"""if f_draw_layout_bbox:draw_layout_bbox(pdf_info, pdf_bytes, local_md_dir, f"{pdf_file_name}_layout.pdf")if f_draw_span_bbox:draw_span_bbox(pdf_info, pdf_bytes, local_md_dir, f"{pdf_file_name}_span.pdf")if f_dump_orig_pdf:md_writer.write(f"{pdf_file_name}_origin.pdf",pdf_bytes,)image_dir = str(os.path.basename(local_image_dir))if f_dump_md:make_func = pipeline_union_make if is_pipeline else vlm_union_makemd_content_str = make_func(pdf_info, f_make_md_mode, image_dir)md_writer.write_string(f"{pdf_file_name}.md",md_content_str,)if f_dump_content_list:make_func = pipeline_union_make if is_pipeline else vlm_union_makecontent_list = make_func(pdf_info, MakeMode.CONTENT_LIST, image_dir)md_writer.write_string(f"{pdf_file_name}_content_list.json",json.dumps(content_list, ensure_ascii=False, indent=4),)if f_dump_middle_json:md_writer.write_string(f"{pdf_file_name}_middle.json",json.dumps(middle_json, ensure_ascii=False, indent=4),)if f_dump_model_output:md_writer.write_string(f"{pdf_file_name}_model.json",json.dumps(model_output, ensure_ascii=False, indent=4),)logger.info(f"local output dir is {local_md_dir}")def parse_doc(path_list: list[Path],output_dir,lang="ch",backend="pipeline",method="auto",server_url=None,start_page_id=0,end_page_id=None
):"""Parameter description:path_list: List of document paths to be parsed, can be PDF or image files.output_dir: Output directory for storing parsing results.lang: Language option, default is 'ch', optional values include['ch', 'ch_server', 'ch_lite', 'en', 'korean', 'japan', 'chinese_cht', 'ta', 'te', 'ka']。Input the languages in the pdf (if known) to improve OCR accuracy. Optional.Adapted only for the case where the backend is set to "pipeline"backend: the backend for parsing pdf:pipeline: More general.vlm-transformers: More general.vlm-vllm-engine: Faster(engine).vlm-http-client: Faster(client).without method specified, pipeline will be used by default.method: the method for parsing pdf:auto: Automatically determine the method based on the file type.txt: Use text extraction method.ocr: Use OCR method for image-based PDFs.Without method specified, 'auto' will be used by default.Adapted only for the case where the backend is set to "pipeline".server_url: When the backend is `http-client`, you need to specify the server_url, for example:`http://127.0.0.1:30000`start_page_id: Start page ID for parsing, default is 0end_page_id: End page ID for parsing, default is None (parse all pages until the end of the document)"""try:file_name_list = []pdf_bytes_list = []lang_list = []for path in path_list:file_name = str(Path(path).stem)pdf_bytes = read_fn(path)file_name_list.append(file_name)pdf_bytes_list.append(pdf_bytes)lang_list.append(lang)do_parse(output_dir=output_dir,pdf_file_names=file_name_list,pdf_bytes_list=pdf_bytes_list,p_lang_list=lang_list,backend=backend,parse_method=method,server_url=server_url,start_page_id=start_page_id,end_page_id=end_page_id)except Exception as e:logger.exception(e)if __name__ == '__main__':# args__dir__ = os.path.dirname(os.path.abspath(__file__))pdf_files_dir = os.path.join(__dir__, "pdfs")output_dir = os.path.join(__dir__, "output")pdf_suffixes = ["pdf"]image_suffixes = ["png", "jpeg", "jp2", "webp", "gif", "bmp", "jpg"]doc_path_list = []for doc_path in Path(pdf_files_dir).glob('*'):if guess_suffix_by_path(doc_path) in pdf_suffixes + image_suffixes:doc_path_list.append(doc_path)# 【關鍵修改 1】: 確保使用 modelscope 來尋找本地緩存的模型# 即使您已下載,這一步有助於函式庫正確定位# os.environ['MINERU_MODEL_SOURCE'] = "modelscope"print(f"🚀 即將使用 'vlm-vllm-engine' 後端處理 {len(doc_path_list)} 個文件...")parse_doc(doc_path_list, output_dir, backend="vlm-vllm-engine") # faster(engine)."""如果您由于网络问题无法下载模型,可以设置环境变量MINERU_MODEL_SOURCE为modelscope使用免代理仓库下载模型"""# os.environ['MINERU_MODEL_SOURCE'] = "modelscope""""Use pipeline mode if your environment does not support VLM"""# parse_doc(doc_path_list, output_dir, backend="pipeline")"""To enable VLM mode, change the backend to 'vlm-xxx'"""# parse_doc(doc_path_list, output_dir, backend="vlm-transformers") # more general.# parse_doc(doc_path_list, output_dir, backend="vlm-vllm-engine") # faster(engine).# parse_doc(doc_path_list, output_dir, backend="vlm-http-client", server_url="http://127.0.0.1:30000") # faster(client).
这个代码的主要参数在于:
- 模型加载:由
MINERU_MODEL_SOURCE="modelscope"
和MODELSCOPE_CACHE
共同控制,固定从 ModelScope 下载 / 读取,缓存到自定义路径; - 后端选择:默认用
vlm-vllm-engine
(VLM 类,速度快),可通过修改backend
参数切换为pipeline
(轻量)或其他 VLM 后端,适配不同文档类型和硬件条件。
四、本地实操,使用pipeline后台
这个过程一直报错缺少很多库,一直安装就可以了。
最后可以成功运行的代码:
# Copyright (c) Opendatalab. All rights reserved.
import copy
import json
import os
from pathlib import Path
# 强制使用 CPU,禁用 CUDA
# 【核心修改1:自定义模型保存路径】
# 修改为你需要的模型缓存目录(确保路径存在且有读写权限)
os.environ['MINERU_MODEL_SOURCE'] = "modelscope" # 从 ModelScope 下载模型
os.environ['MODELSCOPE_CACHE'] = "/home/agent/ltcode/mineru/model/MinerU_Pipeline" # 自定义模型保存路径
from loguru import loggerfrom mineru.cli.common import convert_pdf_bytes_to_bytes_by_pypdfium2, prepare_env, read_fn
from mineru.data.data_reader_writer import FileBasedDataWriter
from mineru.utils.draw_bbox import draw_layout_bbox, draw_span_bbox
from mineru.utils.enum_class import MakeMode
from mineru.backend.vlm.vlm_analyze import doc_analyze as vlm_doc_analyze
from mineru.backend.pipeline.pipeline_analyze import doc_analyze as pipeline_doc_analyze
from mineru.backend.pipeline.pipeline_middle_json_mkcontent import union_make as pipeline_union_make
from mineru.backend.pipeline.model_json_to_middle_json import result_to_middle_json as pipeline_result_to_middle_json
from mineru.backend.vlm.vlm_middle_json_mkcontent import union_make as vlm_union_make
from mineru.utils.guess_suffix_or_lang import guess_suffix_by_pathdef do_parse(output_dir, # 结果输出目录pdf_file_names: list[str], # PDF文件名列表pdf_bytes_list: list[bytes], # PDF字节流列表p_lang_list: list[str], # 每种PDF的语言,默认中文"ch"backend="pipeline", # 解析后端,默认"pipeline"parse_method="auto", # 解析方式,默认"auto"(此处会强制设为"ocr")formula_enable=True, # 【核心配置1:启用公式解析】table_enable=True, # 【核心配置2:启用表格解析】server_url=None, # vlm-http-client后端的服务地址(Pipeline后端无用)f_draw_layout_bbox=True, # 【核心配置3:启用布局边界框绘制】f_draw_span_bbox=True, # 【核心配置4:启用元素边界框绘制】f_dump_md=True, # 【核心配置5:启用Markdown输出】f_dump_middle_json=True, # 【核心配置6:启用中间JSON输出】f_dump_model_output=True, # 【核心配置7:启用模型原始输出】f_dump_orig_pdf=True, # 【核心配置8:启用原始PDF保存】f_dump_content_list=True, # 【核心配置9:启用内容列表输出】f_make_md_mode=MakeMode.MM_MD, # Markdown生成模式,默认标准MM_MDstart_page_id=0, # 起始解析页码(0开始)end_page_id=None, # 结束解析页码(None表示到最后一页)
):if backend == "pipeline":# 【关键:强制使用OCR解析】覆盖传入的parse_method,确保所有PDF用OCR分析parse_method = "ocr"# 截取指定页码范围的PDFfor idx, pdf_bytes in enumerate(pdf_bytes_list):new_pdf_bytes = convert_pdf_bytes_to_bytes_by_pypdfium2(pdf_bytes, start_page_id, end_page_id)pdf_bytes_list[idx] = new_pdf_bytes# 【Pipeline全功能调用】启用公式、表格解析,使用OCR方式infer_results, all_image_lists, all_pdf_docs, lang_list, ocr_enabled_list = pipeline_doc_analyze(pdf_bytes_list, p_lang_list, parse_method=parse_method, # 已强制为"ocr"formula_enable=formula_enable, # 启用公式解析table_enable=table_enable # 启用表格解析)# 逐个处理PDF的解析结果for idx, model_list in enumerate(infer_results):model_json = copy.deepcopy(model_list)pdf_file_name = pdf_file_names[idx]# 准备输出目录(自动创建images和md子目录)local_image_dir, local_md_dir = prepare_env(output_dir, pdf_file_name, parse_method)image_writer, md_writer = FileBasedDataWriter(local_image_dir), FileBasedDataWriter(local_md_dir)images_list = all_image_lists[idx]pdf_doc = all_pdf_docs[idx]_lang = lang_list[idx]_ocr_enable = ocr_enabled_list[idx] # OCR已强制启用,此处为True# 生成Pipeline格式的中间JSON(含OCR文本、公式/表格信息)middle_json = pipeline_result_to_middle_json(model_list, images_list, pdf_doc, image_writer, _lang, _ocr_enable, formula_enable)pdf_info = middle_json["pdf_info"]pdf_bytes = pdf_bytes_list[idx]# 输出所有配置的结果文件_process_output(pdf_info, pdf_bytes, pdf_file_name, local_md_dir, local_image_dir,md_writer, f_draw_layout_bbox, f_draw_span_bbox, f_dump_orig_pdf,f_dump_md, f_dump_content_list, f_dump_middle_json, f_dump_model_output,f_make_md_mode, middle_json, model_json, is_pipeline=True)else:# VLM后端逻辑(本代码已禁用,无需关注)if backend.startswith("vlm-"):backend = backend[4:]f_draw_span_bbox = Falseparse_method = "vlm"for idx, pdf_bytes in enumerate(pdf_bytes_list):pdf_file_name = pdf_file_names[idx]pdf_bytes = convert_pdf_bytes_to_bytes_by_pypdfium2(pdf_bytes, start_page_id, end_page_id)local_image_dir, local_md_dir = prepare_env(output_dir, pdf_file_name, parse_method)image_writer, md_writer = FileBasedDataWriter(local_image_dir), FileBasedDataWriter(local_md_dir)middle_json, infer_result = vlm_doc_analyze(pdf_bytes, image_writer=image_writer, backend=backend, server_url=server_url)pdf_info = middle_json["pdf_info"]_process_output(pdf_info, pdf_bytes, pdf_file_name, local_md_dir, local_image_dir,md_writer, f_draw_layout_bbox, f_draw_span_bbox, f_dump_orig_pdf,f_dump_md, f_dump_content_list, f_dump_middle_json, f_dump_model_output,f_make_md_mode, middle_json, infer_result, is_pipeline=False)def _process_output(pdf_info,pdf_bytes,pdf_file_name,local_md_dir,local_image_dir,md_writer,f_draw_layout_bbox,f_draw_span_bbox,f_dump_orig_pdf,f_dump_md,f_dump_content_list,f_dump_middle_json,f_dump_model_output,f_make_md_mode,middle_json,model_output=None,is_pipeline=True
):"""处理所有输出文件(按配置生成对应结果)"""# 1. 生成带布局边界框的PDF(标注标题、段落、表格位置)if f_draw_layout_bbox:draw_layout_bbox(pdf_info, pdf_bytes, local_md_dir, f"{pdf_file_name}_layout.pdf")# 2. 生成带元素边界框的PDF(标注文本块、公式、表格的具体位置)if f_draw_span_bbox:draw_span_bbox(pdf_info, pdf_bytes, local_md_dir, f"{pdf_file_name}_span.pdf")# 3. 保存原始PDF(便于对比解析前后内容)if f_dump_orig_pdf:md_writer.write(f"{pdf_file_name}_origin.pdf", pdf_bytes)image_dir = str(os.path.basename(local_image_dir))# 4. 生成Markdown文件(核心结果,可直接阅读)if f_dump_md:make_func = pipeline_union_make if is_pipeline else vlm_union_makemd_content_str = make_func(pdf_info, f_make_md_mode, image_dir)md_writer.write_string(f"{pdf_file_name}.md", md_content_str)# 5. 生成内容列表JSON(便于快速查看文档结构)if f_dump_content_list:make_func = pipeline_union_make if is_pipeline else vlm_union_makecontent_list = make_func(pdf_info, MakeMode.CONTENT_LIST, image_dir)md_writer.write_string(f"{pdf_file_name}_content_list.json",json.dumps(content_list, ensure_ascii=False, indent=4),)# 6. 生成中间JSON(结构化数据,含所有元素的位置、内容,用于二次开发)if f_dump_middle_json:md_writer.write_string(f"{pdf_file_name}_middle.json",json.dumps(middle_json, ensure_ascii=False, indent=4),)# 7. 生成模型原始输出JSON(调试用,查看模型检测的原始结果)if f_dump_model_output:md_writer.write_string(f"{pdf_file_name}_model.json",json.dumps(model_output, ensure_ascii=False, indent=4),)logger.info(f"✅ 解析完成!结果保存目录:{local_md_dir}")def parse_doc(path_list: list[Path],output_dir,lang="ch", # 【关键:指定OCR语言为中文,可改为"en"(英文)等】backend="pipeline", # 固定为Pipeline后端method="auto", # 会在do_parse中强制改为"ocr",此处仅为兼容参数server_url=None, # Pipeline后端无用start_page_id=0, # 起始页码end_page_id=None # 结束页码
):"""入口函数:读取文件列表,调用do_parse执行解析"""try:file_name_list = []pdf_bytes_list = []lang_list = []# 遍历待解析文件,读取字节流和文件名for path in path_list:if not path.exists():logger.warning(f"❌ 文件不存在:{path},已跳过")continuefile_name = str(Path(path).stem) # 取文件名(不含后缀)pdf_bytes = read_fn(path) # 读取文件字节流file_name_list.append(file_name)pdf_bytes_list.append(pdf_bytes)lang_list.append(lang) # 所有文件使用同一语言(可按需修改为不同语言)if not file_name_list:logger.error("❌ 无有效文件可解析,请检查pdfs目录下是否有PDF/图像文件")return# 调用核心解析函数do_parsedo_parse(output_dir=output_dir,pdf_file_names=file_name_list,pdf_bytes_list=pdf_bytes_list,p_lang_list=lang_list,backend=backend,parse_method=method,start_page_id=start_page_id,end_page_id=end_page_id)except Exception as e:logger.exception(f"❌ 解析过程出错:{str(e)}")if __name__ == '__main__':# 【核心配置2:指定文件目录和输出目录】__dir__ = os.path.dirname(os.path.abspath(__file__))pdf_files_dir = os.path.join(__dir__, "pdfs") # 待解析文件目录(需手动创建,放入PDF/图像)output_dir = os.path.join(__dir__, "pipeline_ocr_output") # 结果输出目录(自动创建)# 【核心配置3:支持的文件类型(PDF+常见图像格式)】pdf_suffixes = ["pdf"]image_suffixes = ["png", "jpeg", "jp2", "webp", "gif", "bmp", "jpg"]supported_suffixes = pdf_suffixes + image_suffixes# 读取pdfs目录下的所有支持类型文件doc_path_list = []if not os.path.exists(pdf_files_dir):os.makedirs(pdf_files_dir)logger.warning(f"⚠️ pdfs目录不存在,已自动创建:{pdf_files_dir},请放入待解析文件")else:for doc_path in Path(pdf_files_dir).glob('*'):suffix = guess_suffix_by_path(doc_path).lower()if suffix in supported_suffixes:doc_path_list.append(doc_path)# 打印解析前信息print(f"=====================================")print(f"🚀 开始解析:")print(f"📁 待解析文件目录:{pdf_files_dir}")print(f"📊 有效文件数量:{len(doc_path_list)}")print(f"🌐 解析方式:Pipeline后端 + OCR全量分析")print(f"🗂️ 结果输出目录:{output_dir}")print(f"🗣️ OCR识别语言:中文(ch)")print(f"=====================================")# 【核心调用:启动Pipeline+OCR解析】if doc_path_list:parse_doc(path_list=doc_path_list,output_dir=output_dir,backend="pipeline", # 固定使用Pipeline后端lang="ch", # 可改为"en"(英文)、"japan"(日文)等start_page_id=0, # 从第1页开始解析(0对应第1页)end_page_id=None # 解析到最后一页)else:print(f"❌ 无有效文件可解析,请在 {pdf_files_dir} 目录放入PDF或图像文件")
模型加载路径的关键位置
控制层级 | 代码位置 / 函数 | 作用 |
---|---|---|
1. 路径配置 | 脚本顶部 os.environ['MODELSCOPE_CACHE'] | 直接指定模型保存 / 加载的根目录 |
2. 触发加载 | do_parse 中的 pipeline_doc_analyze 调用 | 间接触发所有 Pipeline 模型的加载 |
3. 内部加载逻辑 | MinerU 内部 auto_download_and_get_model_root_path 函数 | 根据配置的根目录,返回具体模型的加载路径 |
简单来说,只需关注脚本顶部的 MODELSCOPE_CACHE
配置 —— 代码会自动从这里找模型、加载模型,无需修改其他隐藏的内部逻辑。