一、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來使用視覺模型的命令。
|
參數類別
|
關鍵參數示例
|
作用説明
|
默認值 / 處理邏輯
|
|
模型相關 |
|
指定 VLM 模型路徑(本地已有的模型)
|
若不指定,自動下載默認 VLM 模型到工具類的模型目錄(如 |
|
服務端口 |
|
設置服務監聽的端口號
|
默認 |
|
GPU 資源 |
|
控制 GPU 內存利用率(0~1 之間)
|
默認 |
|
logits 處理器 |
|
自定義 logits 處理器(用於優化模型輸出)
|
若環境兼容(見 |
|
vllm 原生參數 |
|
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/cu121
pip 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 logger
from 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_path
def do_parse(
output_dir, # Output directory for storing parsing results
pdf_file_names: list[str], # List of PDF file names to be parsed
pdf_bytes_list: list[bytes], # List of PDF bytes to be parsed
p_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 parsing
table_enable=True, # Enable table parsing
server_url=None, # Server URL for vlm-http-client backend
f_draw_layout_bbox=True, # Whether to draw layout bounding boxes
f_draw_span_bbox=True, # Whether to draw span bounding boxes
f_dump_md=True, # Whether to dump markdown files
f_dump_middle_json=True, # Whether to dump middle JSON files
f_dump_model_output=True, # Whether to dump model output files
f_dump_orig_pdf=True, # Whether to dump original PDF files
f_dump_content_list=True, # Whether to dump content list files
f_make_md_mode=MakeMode.MM_MD, # The mode for making markdown content, default is MM_MD
start_page_id=0, # Start page ID for parsing, default is 0
end_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_bytes
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, 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 = False
parse_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_make
md_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_make
content_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 0
end_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 logger
from 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_path
def 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_MD
start_page_id=0, # 起始解析頁碼(0開始)
end_page_id=None, # 結束解析頁碼(None表示到最後一頁)
):
if backend == "pipeline":
# 【關鍵:強制使用OCR解析】覆蓋傳入的parse_method,確保所有PDF用OCR分析
parse_method = "ocr"
# 截取指定頁碼範圍的PDF
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_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 = False
parse_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_make
md_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_make
content_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},已跳過")
continue
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) # 所有文件使用同一語言(可按需修改為不同語言)
if not file_name_list:
logger.error("❌ 無有效文件可解析,請檢查pdfs目錄下是否有PDF/圖像文件")
return
# 調用核心解析函數do_parse
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,
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. 路徑配置
|
腳本頂部 |
直接指定模型保存 / 加載的根目錄
|
|
2. 觸發加載
|
|
間接觸發所有 Pipeline 模型的加載
|
|
3. 內部加載邏輯
|
MinerU 內部 |
根據配置的根目錄,返回具體模型的加載路徑
|
簡單來説,只需關注腳本頂部的 MODELSCOPE_CACHE 配置 —— 代碼會自動從這裏找模型、加載模型,無需修改其他隱藏的內部邏輯。