MinerU

由上海人工智能实验室开源的高质量PDF解析工具,能精准地将包含图片、公式、表格的复杂PDF转换为Markdown和JSON等机器可读的格式。

安装准备

在开始安装前,需要做好以下准备工作:

  • 系统环境:确保你的系统是 Windows、Linux 或 macOS。
  • Python版本:请安装 Python 3.10 到 3.13 之间的版本,这是稳定运行的前提。
  • 虚拟环境(推荐):强烈建议使用Conda或Venv创建一个独立的Python虚拟环境,以避免包冲突。
  1. # 使用Conda创建环境示例
  2. conda create -n mineru_env python=3.11
  3. conda activate mineru_env
  • 硬件要求:
    • CPU:推荐8核心以上。
    • 内存:推荐32GB。
    • GPU(可选):如果需要进行GPU加速,推荐使用NVIDIA Turing架构及以上(如RTX 20/30/40系列)且显存不小于8GB的显卡,并确保已安装合适版本的CUDA驱动。

详细安装步骤

MinerU使用 HuggingFace 和 ModelScope 作为模型仓库,用户可以根据需要切换模型源或使用本地模型。

  • HuggingFace 是默认的模型源,在全球范围内提供了优异的加载速度和极高稳定性。
  • ModelScope 是中国大陆地区用户的最佳选择,提供了无缝兼容的SDK模块,适用于无法访问HuggingFace的用户。

方法一:使用pip或uv安装MinerU

1、安装MinerU

在激活的虚拟环境中,执行以下命令安装MinerU。国内用户建议使用国内镜像源以加速下载。

  1. # 使用阿里云镜像安装
  2. pip install --upgrade pip -i https://mirrors.aliyun.com/pypi/simple
  3. pip install uv -i https://mirrors.aliyun.com/pypi/simple
  4. uv pip install -U "mineru[core]" -i https://mirrors.aliyun.com/pypi/simple

> uv 是一个用 Rust 编写的高速 Python 包管理器和项目工作流工具,由 Astral 公司开发(也是 Ruff 的开发者)。它旨在替代 pip、pip-tools、virtualenv、poetry 等工具,提供极快的性能和现代化的开发体验。uv 是一个用 Rust 编写的高速 Python 包管理器和项目工作流工具,由 Astral 公司开发(也是 Ruff 的开发者)。它旨在替代 pip、pip-tools、virtualenv、poetry 等工具,提供极快的性能和现代化的开发体验。

2、下载模型

MinerU的功能依赖预训练模型,安装后需要下载模型文件。

  1. # 默认从Huggingface下载,国内网络可能较慢
  2. mineru-models-download

国内用户,更推荐使用Modelscope源,速度更快:

  1. # 通过环境变量切换
  2. # 在任何情况下可以通过设置环境变量来切换模型源,这适用于所有命令行工具和API调用。
  3. exportMINERU_MODEL_SOURCE=modelscope
  4. # 查看
  5. echo$MINERU_MODEL_SOURCE
  6. mineru-models-download
  7. # 或
  8. import os
  9. os.environ["MINERU_MODEL_SOURCE"]="modelscope"

其余方法可参考官网:快速开始 - MinerU

配置本地模型路径

让MINERU使用本地模型,主要通过修改其配置文件来实现。

  1. 找到或创建配置文件:MINERU的配置文件通常是 mineru.json 或 magic-pdf.json 。它可能位于你的用户主目录(例如 C:\Users\用户名\mineru.json )或项目目录下。如果不存在,你可以根据模板创建一个 。
  2. 编辑配置文件:在配置文件中,你需要指定本地模型文件的位置。关键在于正确设置 models-dir 字段 。 如果你的Pipeline模式和VLM模式模型存放在不同路径,可以这样配置:
    1. {
    2. "models-dir": {
    3. "pipeline": "/path/to/your/pipeline/models",
    4. "vlm": "/path/to/your/vlm/models"
    5. }
    6. }

如果所有模型都在同一个目录下,可以直接指定根路径:

  1. {
  2. "models-dir": "/path/to/your/all/models"
  3. }
  1. {
  2. "bucket_info": {
  3. "bucket-name-1": [
  4. "ak",
  5. "sk",
  6. "endpoint"
  7. ],
  8. "bucket-name-2": [
  9. "ak",
  10. "sk",
  11. "endpoint"
  12. ]
  13. },
  14. "latex-delimiter-config": {
  15. "display": {
  16. "left": "$$",
  17. "right": "$$"
  18. },
  19. "inline": {
  20. "left": "$",
  21. "right": "$"
  22. }
  23. },
  24. "llm-aided-config": {
  25. "title_aided": {
  26. "api_key": "your_api_key",
  27. "base_url": "https://dashscope.aliyuncs.com/compatible-mode/v1",
  28. "model": "qwen3-next-80b-a3b-instruct",
  29. "enable_thinking": false,
  30. "enable": false
  31. }
  32. },
  33. "models-dir": {
  34. "pipeline": "D:\\modelscope\\hub\\models\\OpenDataLab\\PDF-Extract-Kit-1___0",
  35. "vlm": "D:\\modelscope\\hub\\models\\OpenDataLab\\MinerU2___5-2509-1___2B"
  36. },
  37. "config_version": "1.3.1"
  38. }

使用本地模型

  • 设置环境变量:通过设置环境变量 MINERU_MODEL_SOURCE=local,告诉MINERU从本地路径读取模型 。
    1. exportMINERU_MODEL_SOURCE=local
  • Docker部署时的注意事项:如果你通过Docker部署,需要在启动容器时,将存放本地模型的目录挂载到容器内部,并在容器内正确设置上述环境变量和配置文件路径 。

配置选项

语言支持

  1. # 支持的语言
  2. ['ch','ch_server','ch_lite','en','korean','japan',
  3. 'chinese_cht','ta','te','ka']

后端选项详解

Pipeline 后端

  • 特点:通用性强,支持多种文档类型
  • 方法选项:
    • auto:自动选择
    • txt:文本提取
    • ocr:OCR识别

      VLM 后端(视觉语言模型)

  • vlm-transformers:通用VLM
  • vlm-vllm-engine:高性能推理引擎
  • vlm-http-client:HTTP客户端模式
  • vlm-mlx-engine:macOS优化引擎

    输出文件类型

    1、可视化文件:

  • {filename}_layout.pdf - 布局边界框
  • {filename}_span.pdf - 文本跨度边界框

2、内容文件:

  • {filename}.md - Markdown格式
  • {filename}_content_list.json - 结构化内容

3、中间文件:

  • {filename}_middle.json - 中间结果
  • {filename}_model.json - 模型原始输出

Python调用示例

  1. import os
  2. import copy
  3. import json
  4. from pathlib import Path
  5. from loguru import logger
  6. from mineru.cli.common import convert_pdf_bytes_to_bytes_by_pypdfium2, prepare_env, read_fn
  7. from mineru.data.data_reader_writer import FileBasedDataWriter
  8. from mineru.utils.draw_bbox import draw_layout_bbox, draw_span_bbox
  9. from mineru.utils.enum_class import MakeMode
  10. from mineru.backend.vlm.vlm_analyze import doc_analyze as vlm_doc_analyze
  11. from mineru.backend.pipeline.pipeline_analyze import doc_analyze as pipeline_doc_analyze
  12. from mineru.backend.pipeline.pipeline_middle_json_mkcontent import union_make as pipeline_union_make
  13. from mineru.backend.pipeline.model_json_to_middle_json import result_to_middle_json as pipeline_result_to_middle_json
  14. from mineru.backend.vlm.vlm_middle_json_mkcontent import union_make as vlm_union_make
  15. from mineru.utils.guess_suffix_or_lang import guess_suffix_by_path
  16. defdo_parse(
  17. output_dir,# Output directory for storing parsing results
  18. pdf_file_names:list[str],# List of PDF file names to be parsed
  19. pdf_bytes_list:list[bytes],# List of PDF bytes to be parsed
  20. p_lang_list:list[str],# List of languages for each PDF, default is 'ch' (Chinese)
  21. backend="pipeline",# The backend for parsing PDF, default is 'pipeline'
  22. parse_method="auto",# The method for parsing PDF, default is 'auto'
  23. formula_enable=True,# Enable formula parsing
  24. table_enable=True,# Enable table parsing
  25. server_url=None,# Server URL for vlm-http-client backend
  26. f_draw_layout_bbox=True,# Whether to draw layout bounding boxes
  27. f_draw_span_bbox=True,# Whether to draw span bounding boxes
  28. f_dump_md=True,# Whether to dump markdown files
  29. f_dump_middle_json=True,# Whether to dump middle JSON files
  30. f_dump_model_output=True,# Whether to dump model output files
  31. f_dump_orig_pdf=True,# Whether to dump original PDF files
  32. f_dump_content_list=True,# Whether to dump content list files
  33. f_make_md_mode=MakeMode.MM_MD,# The mode for making markdown content, default is MM_MD
  34. start_page_id=0,# Start page ID for parsing, default is 0
  35. end_page_id=None,# End page ID for parsing, default is None (parse all pages until the end of the document)
  36. ):
  37. if backend =="pipeline":
  38. for idx, pdf_bytes inenumerate(pdf_bytes_list):
  39. new_pdf_bytes = convert_pdf_bytes_to_bytes_by_pypdfium2(pdf_bytes, start_page_id, end_page_id)
  40. pdf_bytes_list[idx]= new_pdf_bytes
  41. 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)
  42. for idx, model_list inenumerate(infer_results):
  43. model_json = copy.deepcopy(model_list)
  44. pdf_file_name = pdf_file_names[idx]
  45. local_image_dir, local_md_dir = prepare_env(output_dir, pdf_file_name, parse_method)
  46. image_writer, md_writer = FileBasedDataWriter(local_image_dir), FileBasedDataWriter(local_md_dir)
  47. images_list = all_image_lists[idx]
  48. pdf_doc = all_pdf_docs[idx]
  49. _lang = lang_list[idx]
  50. _ocr_enable = ocr_enabled_list[idx]
  51. middle_json = pipeline_result_to_middle_json(model_list, images_list, pdf_doc, image_writer, _lang, _ocr_enable, formula_enable)
  52. pdf_info = middle_json["pdf_info"]
  53. pdf_bytes = pdf_bytes_list[idx]
  54. _process_output(
  55. pdf_info, pdf_bytes, pdf_file_name, local_md_dir, local_image_dir,
  56. md_writer, f_draw_layout_bbox, f_draw_span_bbox, f_dump_orig_pdf,
  57. f_dump_md, f_dump_content_list, f_dump_middle_json, f_dump_model_output,
  58. f_make_md_mode, middle_json, model_json, is_pipeline=True
  59. )
  60. else:
  61. if backend.startswith("vlm-"):
  62. backend = backend[4:]
  63. f_draw_span_bbox =False
  64. parse_method ="vlm"
  65. for idx, pdf_bytes inenumerate(pdf_bytes_list):
  66. pdf_file_name = pdf_file_names[idx]
  67. pdf_bytes = convert_pdf_bytes_to_bytes_by_pypdfium2(pdf_bytes, start_page_id, end_page_id)
  68. local_image_dir, local_md_dir = prepare_env(output_dir, pdf_file_name, parse_method)
  69. image_writer, md_writer = FileBasedDataWriter(local_image_dir), FileBasedDataWriter(local_md_dir)
  70. middle_json, infer_result = vlm_doc_analyze(pdf_bytes, image_writer=image_writer, backend=backend, server_url=server_url)
  71. pdf_info = middle_json["pdf_info"]
  72. _process_output(
  73. pdf_info, pdf_bytes, pdf_file_name, local_md_dir, local_image_dir,
  74. md_writer, f_draw_layout_bbox, f_draw_span_bbox, f_dump_orig_pdf,
  75. f_dump_md, f_dump_content_list, f_dump_middle_json, f_dump_model_output,
  76. f_make_md_mode, middle_json, infer_result, is_pipeline=False
  77. )
  78. def_process_output(
  79. pdf_info,
  80. pdf_bytes,
  81. pdf_file_name,
  82. local_md_dir,
  83. local_image_dir,
  84. md_writer,
  85. f_draw_layout_bbox,
  86. f_draw_span_bbox,
  87. f_dump_orig_pdf,
  88. f_dump_md,
  89. f_dump_content_list,
  90. f_dump_middle_json,
  91. f_dump_model_output,
  92. f_make_md_mode,
  93. middle_json,
  94. model_output=None,
  95. is_pipeline=True
  96. ):
  97. """处理输出文件"""
  98. if f_draw_layout_bbox:
  99. draw_layout_bbox(pdf_info, pdf_bytes, local_md_dir,f"{pdf_file_name}_layout.pdf")
  100. if f_draw_span_bbox:
  101. draw_span_bbox(pdf_info, pdf_bytes, local_md_dir,f"{pdf_file_name}_span.pdf")
  102. if f_dump_orig_pdf:
  103. md_writer.write(
  104. f"{pdf_file_name}_origin.pdf",
  105. pdf_bytes,
  106. )
  107. image_dir =str(os.path.basename(local_image_dir))
  108. if f_dump_md:
  109. make_func = pipeline_union_make if is_pipeline else vlm_union_make
  110. md_content_str = make_func(pdf_info, f_make_md_mode, image_dir)
  111. md_writer.write_string(
  112. f"{pdf_file_name}.md",
  113. md_content_str,
  114. )
  115. if f_dump_content_list:
  116. make_func = pipeline_union_make if is_pipeline else vlm_union_make
  117. content_list = make_func(pdf_info, MakeMode.CONTENT_LIST, image_dir)
  118. md_writer.write_string(
  119. f"{pdf_file_name}_content_list.json",
  120. json.dumps(content_list, ensure_ascii=False, indent=4),
  121. )
  122. if f_dump_middle_json:
  123. md_writer.write_string(
  124. f"{pdf_file_name}_middle.json",
  125. json.dumps(middle_json, ensure_ascii=False, indent=4),
  126. )
  127. if f_dump_model_output:
  128. md_writer.write_string(
  129. f"{pdf_file_name}_model.json",
  130. json.dumps(model_output, ensure_ascii=False, indent=4),
  131. )
  132. logger.info(f"local output dir is {local_md_dir}")
  133. defparse_doc(
  134. path_list:list[Path],
  135. output_dir,
  136. lang="ch",
  137. backend="pipeline",
  138. method="auto",
  139. server_url=None,
  140. start_page_id=0,
  141. end_page_id=None
  142. ):
  143. """
  144. Parameter description:
  145. path_list: List of document paths to be parsed, can be PDF or image files.
  146. output_dir: Output directory for storing parsing results.
  147. lang: Language option, default is 'ch', optional values include['ch', 'ch_server', 'ch_lite', 'en', 'korean', 'japan', 'chinese_cht', 'ta', 'te', 'ka']。
  148. Input the languages in the pdf (if known) to improve OCR accuracy. Optional.
  149. Adapted only for the case where the backend is set to "pipeline"
  150. backend: the backend for parsing pdf:
  151. pipeline: More general.
  152. vlm-transformers: More general.
  153. vlm-vllm-engine: Faster(engine).
  154. vlm-http-client: Faster(client).
  155. without method specified, pipeline will be used by default.
  156. method: the method for parsing pdf:
  157. auto: Automatically determine the method based on the file type.
  158. txt: Use text extraction method.
  159. ocr: Use OCR method for image-based PDFs.
  160. Without method specified, 'auto' will be used by default.
  161. Adapted only for the case where the backend is set to "pipeline".
  162. server_url: When the backend is `http-client`, you need to specify the server_url, for example:`http://127.0.0.1:30000`
  163. start_page_id: Start page ID for parsing, default is 0
  164. end_page_id: End page ID for parsing, default is None (parse all pages until the end of the document)
  165. """
  166. try:
  167. file_name_list =[]
  168. pdf_bytes_list =[]
  169. lang_list =[]
  170. for path in path_list:
  171. file_name =str(Path(path).stem)
  172. pdf_bytes = read_fn(path)
  173. file_name_list.append(file_name)
  174. pdf_bytes_list.append(pdf_bytes)
  175. lang_list.append(lang)
  176. do_parse(
  177. output_dir=output_dir,
  178. pdf_file_names=file_name_list,
  179. pdf_bytes_list=pdf_bytes_list,
  180. p_lang_list=lang_list,
  181. backend=backend,
  182. parse_method=method,
  183. server_url=server_url,
  184. start_page_id=start_page_id,
  185. end_page_id=end_page_id
  186. )
  187. except Exception as e:
  188. logger.exception(e)
  189. if __name__ =='__main__':
  190. """如果您由于网络问题无法下载模型,可以设置环境变量MINERU_MODEL_SOURCE为modelscope使用免代理仓库下载模型"""
  191. os.environ["MINERU_MODEL_SOURCE"]="local"# "modelscope"
  192. # args
  193. __dir__ = os.path.dirname(os.path.abspath(__file__))
  194. pdf_files_dir = os.path.join(__dir__,"picture")
  195. output_dir = os.path.join(__dir__,"output_MinerU")
  196. pdf_suffixes =["pdf"]
  197. image_suffixes =["png","jpeg","jp2","webp","gif","bmp","jpg"]
  198. doc_path_list =[]
  199. for doc_path in Path(pdf_files_dir).glob('*'):
  200. if guess_suffix_by_path(doc_path)in pdf_suffixes + image_suffixes:
  201. doc_path_list.append(doc_path)
  202. """Use pipeline mode if your environment does not support VLM"""
  203. parse_doc(doc_path_list, output_dir, backend="pipeline")
  204. """To enable VLM mode, change the backend to 'vlm-xxx'"""
  205. # parse_doc(doc_path_list, output_dir, backend="vlm-transformers") # more general.
  206. # parse_doc(doc_path_list, output_dir, backend="vlm-mlx-engine") # faster than transformers in macOS 13.5+.
  207. # parse_doc(doc_path_list, output_dir, backend="vlm-vllm-engine") # faster(engine).
  208. # parse_doc(doc_path_list, output_dir, backend="vlm-http-client", server_url="http://127.0.0.1:30000") # faster(client).

核心函数解析

1. do_parse() - 主解析函数

  • output_dir:解析结果的输出目录路径

  • pdf_file_names:要解析的PDF文件名列表(字符串列表)

  • pdf_bytes_list:要解析的PDF字节数据列表(字节列表)

  • p_lang_list:每个PDF对应的语言列表,默认是中文(‘ch’)

  • backend:解析PDF的后端引擎,默认’pipeline’

  • parse_method:解析方法,默认’auto’(自动选择)

  • formula_enable:是否启用公式解析,默认True

  • table_enable:是否启用表格解析,默认True

  • server_url:VLM HTTP客户端后端使用的服务器URL

  • f_draw_layout_bbox:是否绘制布局边界框,默认True

  • f_draw_span_bbox:是否绘制文本跨度边界框,默认True

  • f_dump_md:是否输出Markdown文件,默认True

  • f_dump_middle_json:是否输出中间JSON文件,默认True

  • f_dump_model_output:是否输出模型原始输出文件,默认True

  • f_dump_orig_pdf:是否输出原始PDF文件,默认True

  • f_dump_content_list:是否输出内容列表文件,默认True

  • f_make_md_mode:生成Markdown内容的模式,默认是MM_MD

  • start_page_id:解析起始页码,默认0(第一页)

  • end_page_id:解析结束页码,默认None(解析到文档末尾)

> 支持两种后端模式:

  • pipeline 后端:通用解析,支持 OCR 和文本提取
  • vlm 后端:基于视觉语言模型,支持多种推理引擎- vlm 后端:基于视觉语言模型,支持多种推理引擎

2. _process_output() - 输出处理函数

  1. def_process_output(
  2. pdf_info,# PDF信息字典
  3. pdf_bytes,# PDF字节数据
  4. pdf_file_name,# PDF文件名
  5. local_md_dir,# 本地Markdown目录
  6. local_image_dir,# 本地图像目录
  7. md_writer,# Markdown写入器
  8. f_draw_layout_bbox,# 是否绘制布局边界框
  9. f_draw_span_bbox,# 是否绘制跨度边界框
  10. f_dump_orig_pdf,# 是否转储原始PDF
  11. f_dump_md,# 是否转储Markdown
  12. f_dump_content_list,# 是否转储内容列表
  13. f_dump_middle_json,# 是否转储中间JSON
  14. f_dump_model_output,# 是否转储模型输出
  15. f_make_md_mode,# Markdown生成模式
  16. middle_json,# 中间JSON数据
  17. model_output=None,# 模型原始输出
  18. is_pipeline=True# 是否为pipeline后端
  19. ):
  20. """处理输出文件"""

3. parse_doc() - 用户接口函数

  1. defparse_doc(
  2. path_list:list[Path],# 要解析的文档路径列表
  3. output_dir,# 解析结果输出目录
  4. lang="ch",# 语言选项,默认是中文
  5. backend="pipeline",# 解析后端,默认pipeline
  6. method="auto",# 解析方法,默认auto
  7. server_url=None,# 服务器URL(用于vlm-http-client
  8. start_page_id=0,# 起始页码
  9. end_page_id=None# 结束页码
  10. ):

> 提供简化的用户接口,支持多种语言和解析配置。

使用Docker部署Mineru

一、Docker镜像构建

1.1 下载与构建镜像

  1. # 下载Dockerfile文件
  2. wget https://gcore.jsdelivr.net/gh/opendatalab/MinerU@master/docker/china/Dockerfile
  3. # 打包docker镜像
  4. docker build -t mineru:latest -f Dockerfile .

1.2 Dockerfile配置说明

  1. # 基础镜像选择(根据GPU架构选择)
  2. # 计算能力≥8.0(Ampere及以上架构)
  3. FROM docker.m.daocloud.io/vllm/vllm-openai:v0.10.1.1
  4. # 计算能力<8.0(Turing及更早架构)
  5. # FROM docker.m.daocloud.io/vllm/vllm-openai:v0.10.2
  6. # 配置Ubuntu国内镜像源并安装依赖
  7. RUN sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
  8. sed -i 's/security.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list && \
  9. apt-get update && \
  10. apt-get install -y \
  11. fonts-noto-core \
  12. fonts-noto-cjk \
  13. fontconfig \
  14. libgl1 && \
  15. fc-cache -fv && \
  16. apt-get clean && \
  17. rm -rf /var/lib/apt/lists/*
  18. # 安装Mineru核心包
  19. RUN python3 -m pip install -U 'mineru[core]' -i https://mirrors.aliyun.com/pypi/simple --break-system-packages && \
  20. python3 -m pip cache purge
  21. # 下载模型文件
  22. RUN /bin/bash -c "mineru-models-download -s modelscope -m all"
  23. # 设置入口点
  24. ENTRYPOINT ["/bin/bash", "-c", "export MINERU_MODEL_SOURCE=local && exec \"$@\"", "--"]

> GPU架构说明:

  • 查看GPU计算能力:CUDA GPU列表
  • vLLM v0.10.1.1 支持计算能力≥8.0的显卡
  • vLLM v0.10.2 支持更早架构的显卡

二、启动Docker容器

2.1 基础启动命令

  1. docker run --gpus all --shm-size 32g -p 30000:30000 -p 7860:7860 -p 8000:8000 --ipc=host -it mineru:latest /bin/bash

2.2 端口映射说明

  • 30000: OpenAI兼容服务器端口
  • 7860: Gradio WebUI端口
  • 8000: FastAPI接口端口

2.3 vLLM加速要求

使用vLLM加速VLM模型推理需满足:

  1. 硬件要求:Volta及以上架构GPU,显存≥8GB
  2. 驱动要求:NVIDIA驱动支持CUDA 12.8+
  3. 容器配置:已正确挂载GPU设备

2.4 容器停止后如何重新启动服务

  1. # 启动指定容器
  2. docker start <容器ID或名称>
  3. # 进入容器交互终端
  4. docker exec -it <容器ID或名称> /bin/bash

2.5 通过配置环境变量来使用本地模型

  1. exportMINERU_MODEL_SOURCE=local

三、服务启动与使用

3.1 启动OpenAI兼容服务器

  1. # 默认启动(自动选择引擎)
  2. mineru-openai-server
  3. # 指定vLLM引擎
  4. mineru-openai-server --engine vllm --port 30000
  5. # 指定lmdeploy引擎
  6. mineru-openai-server --engine lmdeploy --server-port 30000

3.2 启动FastAPI服务

  1. mineru-api --host 0.0.0.0 --port 8000

> 访问API文档:http://127.0.0.1:8000/docs

3.3 启动Gradio WebUI

  1. # 通用后端
  2. mineru-gradio --server-name 0.0.0.0 --server-port 7860
  3. # 启用vLLM加速
  4. mineru-gradio --server-name 0.0.0.0 --server-port 7860 --enable-vllm-engine true
  5. # 启用lmdeploy加速
  6. mineru-gradio --server-name 0.0.0.0 --server-port 7860 --enable-lmdeploy-engine true

> 访问WebUI:http://127.0.0.1:7860

3.4 HTTP客户端调用

  1. # 连接到OpenAI兼容服务器
  2. mineru -p <input_path> -o <output_path> -b vlm-http-client -u http://127.0.0.1:30000

3.5 所有vllm/lmdeploy官方支持的参数都可用通过命令行参数传递给 MinerU,包括以下命令:mineru、mineru-openai-server、mineru-gradio、mineru-api

  1. mineru-openai-server --engine vllm --port 30000 --gpu-memory-utilization 0.8 --max-model-len 7520 --max-num-batched-tokens 32768

四、API参数详解

4.1 基本参数

参数 类型 说明 示例
files array 待解析文件列表 [“doc.pdf”, “img.png”]
output_dir string 输出目录 “./output”

4.2 解析配置

参数 类型 说明 选项
backend string 解析引擎 pipeline、vlm-transformers、vlm-vllm-async-engine、vlm-lmdeploy-engine、vlm-http-client
parse_method string PDF解析方法 auto、txt、ocr
lang_list array OCR语言设置 ch、en、korean、japan等

4.3 功能开关

参数 类型 说明
formula_enable boolean 公式识别开关
table_enable boolean 表格识别开关
start_page_id integer 起始页码(0开始)
end_page_id integer 结束页码

4.4 服务器配置

参数 类型 说明 示例
server_url string OpenAI兼容服务器地址 http://127.0.0.1:30000

4.5 输出选项

  • return_md:返回Markdown格式
  • return_middle_json:返回中间JSON
  • return_model_output:返回模型原始输出
  • return_content_list:返回内容列表
  • return_images:返回提取的图片
  • response_format_zip:打包为ZIP文件(默认JSON)
  1. import requests
  2. import pathlib
  3. import os
  4. import json
  5. import time
  6. from typing import List, Optional, Dict, Any
  7. from datetime import datetime
  8. API_URL ="http://localhost:8000/file_parse"
  9. INPUT_DIR ="./images"
  10. OUTPUT_DIR ="./output"
  11. # 支持的图片格式
  12. SUPPORTED_EXTENSIONS ={'.jpg','.jpeg','.png','.bmp','.tiff','.tif','.gif','.pdf'}
  13. defparse_file(
  14. file_path: pathlib.Path,
  15. output_dir:str= OUTPUT_DIR,
  16. lang_list: Optional[List[str]]=None,
  17. backend:str="vlm-transformers",
  18. parse_method:str="auto",
  19. formula_enable:bool=False,
  20. table_enable:bool=True,
  21. server_url: Optional[str]=None,
  22. return_md:bool=True,
  23. return_middle_json:bool=False,
  24. return_model_output:bool=False,
  25. return_content_list:bool=False,
  26. return_images:bool=False,
  27. response_format_zip:bool=False,
  28. start_page_id:int=0,
  29. end_page_id:int=0
  30. ):
  31. """
  32. 解析单个文件并保存结果
  33. 参数说明:
  34. -----------
  35. file_path (pathlib.Path): 需要解析的文件路径
  36. output_dir (str): 解析结果的输出目录
  37. lang_list (List[str], optional): OCR语言设置,提高识别准确率
  38. - 'ch'/'ch_server'/'ch_lite': 简体中文
  39. - 'en': 英文
  40. - 'korean': 韩文
  41. - 'japan': 日文
  42. - 其他语言选项...
  43. backend (str): 解析后端引擎选择
  44. - 'pipeline': 通用型,功能全面
  45. - 'vlm-transformers': 通用但较慢
  46. - 'vlm-mlx-engine': 快速(需Apple Silicon + macOS 13.5+)
  47. - 'vlm-vllm-async-engine': 快速(需vllm库)
  48. - 'vlm-lmdeploy-engine': 快速(需lmdeploy库)
  49. - 'vlm-http-client': 快速(连接OpenAI兼容服务器)
  50. parse_method (str): PDF解析方法(仅pipeline后端)
  51. - 'auto': 自动判断
  52. - 'txt': 文本提取(适用于可选中文本的PDF)
  53. - 'ocr': OCR识别(适用于扫描版/图片PDF)
  54. formula_enable (bool): 是否启用公式识别
  55. table_enable (bool): 是否启用表格识别
  56. server_url (str, optional): OpenAI兼容服务器地址(仅vlm-http-client后端)
  57. return_md (bool): 是否返回Markdown格式内容
  58. return_middle_json (bool): 是否返回中间JSON
  59. return_model_output (bool): 是否返回模型原始输出
  60. return_content_list (bool): 是否返回内容列表
  61. return_images (bool): 是否返回提取的图片
  62. response_format_zip (bool): 是否打包为ZIP文件返回(默认返回JSON)
  63. start_page_id (int): 解析起始页码(从0开始计数)
  64. end_page_id (int): 解析结束页码(从0开始计数)
  65. """
  66. start_time = time.time()
  67. # 创建输出目录
  68. output_path = pathlib.Path(output_dir)
  69. output_path.mkdir(exist_ok=True)
  70. # 为当前文件创建输出子目录
  71. file_output_dir = output_path / file_path.stem
  72. file_output_dir.mkdir(exist_ok=True)
  73. print(f"[{datetime.now().strftime('%H:%M:%S')}] 开始处理文件: {file_path.name}")
  74. try:
  75. # 准备multipart/form-data请求
  76. files =[('files',(file_path.name,open(file_path,'rb'),'application/octet-stream'))]
  77. # 准备表单数据
  78. data ={
  79. 'output_dir':str(file_output_dir),
  80. 'backend': backend,
  81. 'parse_method': parse_method,
  82. 'formula_enable':str(formula_enable).lower(),
  83. 'table_enable':str(table_enable).lower(),
  84. 'return_md':str(return_md).lower(),
  85. 'return_middle_json':str(return_middle_json).lower(),
  86. 'return_model_output':str(return_model_output).lower(),
  87. 'return_content_list':str(return_content_list).lower(),
  88. 'return_images':str(return_images).lower(),
  89. 'response_format_zip':str(response_format_zip).lower(),
  90. 'start_page_id':str(start_page_id),
  91. 'end_page_id':str(end_page_id)
  92. }
  93. # 添加可选参数
  94. if lang_list:
  95. # 修复:lang_list 应该以数组形式传递
  96. for lang in lang_list:
  97. data['lang_list']= lang
  98. if server_url:
  99. data['server_url']= server_url
  100. # 发送请求
  101. print(f" 发送请求到 {API_URL}...")
  102. request_start = time.time()
  103. response = requests.post(API_URL, files=files, data=data)
  104. request_time = time.time()- request_start
  105. print(f" 请求完成,耗时: {request_time:.2f}秒")
  106. if response.status_code !=200:
  107. print(f" 错误: API返回状态码 {response.status_code}")
  108. print(f" 错误信息: {response.text[:500]}")# 只显示前500字符
  109. return
  110. # 根据返回格式处理响应
  111. if response_format_zip:
  112. # 保存ZIP文件
  113. zip_filename = file_output_dir /f"{file_path.stem}_results.zip"
  114. withopen(zip_filename,'wb')as f:
  115. f.write(response.content)
  116. print(f" 已保存ZIP文件到: {zip_filename}")
  117. else:
  118. # 解析JSON响应
  119. result = response.json()
  120. # 保存完整的响应JSON以便调试
  121. debug_filename = file_output_dir /f"{file_path.stem}_debug.json"
  122. withopen(debug_filename,'w', encoding='utf-8')as f:
  123. json.dump(result, f, ensure_ascii=False, indent=2)
  124. print(f" 已保存调试JSON到: {debug_filename}")
  125. # 根据实际API响应结构处理数据
  126. # API返回格式: {"backend": "...", "version": "...", "results": {"filename": {"md_content": "..."}}}
  127. if'results'in result and result['results']:
  128. for filename, file_result in result['results'].items():
  129. # 保存markdown内容
  130. if'md_content'in file_result:
  131. md_content = file_result['md_content']
  132. md_filename = file_output_dir /f"{filename}.md"
  133. withopen(md_filename,'w', encoding='utf-8')as f:
  134. f.write(md_content)
  135. print(f" 已保存Markdown到: {md_filename}")
  136. # 保存中间JSON(如果API返回)
  137. if return_middle_json and'middle_json'in file_result:
  138. json_filename = file_output_dir /f"{filename}_middle.json"
  139. withopen(json_filename,'w', encoding='utf-8')as f:
  140. json.dump(file_result['middle_json'], f, ensure_ascii=False, indent=2)
  141. print(f" 已保存中间JSON到: {json_filename}")
  142. # 保存模型输出(如果API返回)
  143. if return_model_output and'model_output'in file_result:
  144. json_filename = file_output_dir /f"{filename}_model_output.json"
  145. withopen(json_filename,'w', encoding='utf-8')as f:
  146. json.dump(file_result['model_output'], f, ensure_ascii=False, indent=2)
  147. print(f" 已保存模型输出到: {json_filename}")
  148. # 保存内容列表(如果API返回)
  149. if return_content_list and'content_list'in file_result:
  150. json_filename = file_output_dir /f"{filename}_content_list.json"
  151. withopen(json_filename,'w', encoding='utf-8')as f:
  152. json.dump(file_result['content_list'], f, ensure_ascii=False, indent=2)
  153. print(f" 已保存内容列表到: {json_filename}")
  154. # 兼容旧版API格式:直接返回markdown字段
  155. elif'markdown'in result:
  156. md_content = result['markdown']
  157. md_filename = file_output_dir /f"{file_path.stem}.md"
  158. withopen(md_filename,'w', encoding='utf-8')as f:
  159. f.write(md_content)
  160. print(f" 已保存Markdown到: {md_filename}")
  161. else:
  162. print(f" 警告: API响应中没有找到预期的数据结构")
  163. print(f" 响应结构: {list(result.keys())ifisinstance(result,dict)else'非字典类型'}")
  164. total_time = time.time()- start_time
  165. print(f" 处理完成,总耗时: {total_time:.2f}秒\n")
  166. except Exception as e:
  167. print(f" 处理文件 {file_path.name} 时出错: {e}")
  168. import traceback
  169. traceback.print_exc()
  170. print()
  171. finally:
  172. # 确保文件被关闭
  173. for file_info in files:
  174. ifhasattr(file_info[1][1],'close'):
  175. file_info[1][1].close()
  176. defprocess_all_files(
  177. input_dir:str= INPUT_DIR,
  178. output_dir:str= OUTPUT_DIR,
  179. **kwargs
  180. ):
  181. """
  182. 处理输入目录中的所有支持的文件
  183. 参数说明:
  184. -----------
  185. input_dir (str): 输入目录路径,包含需要处理的文件
  186. output_dir (str): 输出目录路径,用于保存处理结果
  187. **kwargs: 其他参数,将传递给parse_file函数
  188. - 包括: lang_list, backend, parse_method, formula_enable, table_enable等
  189. """
  190. start_time = time.time()
  191. input_path = pathlib.Path(input_dir)
  192. ifnot input_path.exists():
  193. print(f"输入目录不存在: {input_dir}")
  194. return
  195. # 创建输出目录
  196. output_path = pathlib.Path(output_dir)
  197. output_path.mkdir(exist_ok=True)
  198. # 获取所有支持的文件
  199. all_files =[]
  200. for ext in SUPPORTED_EXTENSIONS:
  201. all_files.extend(input_path.glob(f"*{ext}"))
  202. all_files.extend(input_path.glob(f"*{ext.upper()}"))
  203. # 去重并排序
  204. all_files =sorted(set(all_files))
  205. ifnot all_files:
  206. print(f"在 {input_dir} 目录中未找到支持的图片或PDF文件")
  207. return
  208. print(f"找到 {len(all_files)} 个待处理文件")
  209. # 处理每个文件
  210. file_count =0
  211. for file_path in all_files:
  212. if file_path.is_file():
  213. parse_file(file_path, output_dir=output_dir,**kwargs)
  214. file_count +=1
  215. total_time = time.time()- start_time
  216. print(f"所有文件处理完成! 共处理了 {file_count} 个文件")
  217. print(f"总运行时间: {total_time:.2f}秒,平均每个文件: {total_time /max(file_count,1):.2f}秒")
  218. defprocess_single_file(
  219. file_path:str,
  220. output_dir:str= OUTPUT_DIR,
  221. **kwargs
  222. ):
  223. """
  224. 处理单个文件
  225. 参数说明:
  226. -----------
  227. file_path (str): 需要处理的文件路径
  228. output_dir (str): 输出目录路径,用于保存处理结果
  229. **kwargs: 其他参数,将传递给parse_file函数
  230. - 包括: lang_list, backend, parse_method, formula_enable, table_enable等
  231. """
  232. start_time = time.time()
  233. file_path_obj = pathlib.Path(file_path)
  234. ifnot file_path_obj.exists():
  235. print(f"文件不存在: {file_path}")
  236. return
  237. if file_path_obj.suffix.lower()notin SUPPORTED_EXTENSIONS:
  238. print(f"不支持的文件格式: {file_path_obj.suffix}")
  239. print(f"支持的格式: {', '.join(SUPPORTED_EXTENSIONS)}")
  240. return
  241. parse_file(file_path_obj, output_dir=output_dir,**kwargs)
  242. total_time = time.time()- start_time
  243. print(f"单文件处理完成,总耗时: {total_time:.2f}秒")
  244. # 使用示例
  245. if __name__ =="__main__":
  246. total_start_time = time.time()
  247. print("="*60)
  248. print("MinerU API 客户端 - 文件解析工具")
  249. print(f"开始时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
  250. print("="*60)
  251. # 示例1: 基本用法 - 处理所有图片,提取表格
  252. # print("\n示例1: 基本用法 - 处理所有图片,提取表格")
  253. # print("-" * 40)
  254. # process_all_files(
  255. # input_dir="./images",
  256. # output_dir="./output",
  257. # table_enable=True, # 启用表格识别
  258. # return_md=True, # 返回Markdown格式
  259. # backend="vlm-transformers" # 使用通用但较慢的后端
  260. # )
  261. # exit()
  262. # 示例2: 处理PDF文件,支持中文OCR
  263. # print("\n示例2: 处理PDF文件,支持中文OCR")
  264. # print("-" * 40)
  265. # # 确保有PDF文件
  266. # pdf_files = list(pathlib.Path("./images").glob("*.pdf"))
  267. # if pdf_files:
  268. # parse_file(
  269. # file_path=pdf_files[0],
  270. # output_dir="./output/pdf_example",
  271. # lang_list=["ch", "en"], # 设置中文和英文OCR
  272. # backend="pipeline", # 使用pipeline后端
  273. # parse_method="auto", # 自动判断解析方法
  274. # table_enable=True, # 启用表格识别
  275. # formula_enable=False, # 不启用公式识别
  276. # return_md=True, # 返回Markdown格式
  277. # return_content_list=True # 返回内容列表
  278. # )
  279. # exit()
  280. # 示例3: 使用vlm-http-client后端(需要服务器)
  281. print("\n示例3: 使用vlm-http-client后端(需要服务器)")
  282. print("-"*40)
  283. print("注意: 此示例需要先启动OpenAI兼容服务器")
  284. # 取消注释并填写实际的服务器URL
  285. parse_file(
  286. file_path=pathlib.Path("./images/sample.jpg"),
  287. output_dir="./output/vlm_example",
  288. backend="vlm-http-client",# 使用HTTP客户端后端
  289. server_url="http://127.0.0.1:30000",# 服务器地址
  290. table_enable=True,
  291. return_md=True
  292. )
  293. exit()
  294. # 示例4: 批量处理特定类型的文件
  295. # print("\n示例4: 批量处理PNG文件")
  296. # print("-" * 40)
  297. # png_files = list(pathlib.Path("./images").glob("*.png"))
  298. # for i, png_file in enumerate(png_files[:2]): # 只处理前2个,避免太多
  299. # print(f"处理第 {i + 1} 个PNG文件: {png_file.name}")
  300. # parse_file(
  301. # file_path=png_file,
  302. # output_dir="./output/png_files",
  303. # backend="pipeline", # 使用pipeline后端
  304. # parse_method="ocr", # 使用OCR解析方法
  305. # table_enable=True, # 启用表格识别
  306. # return_md=True, # 返回Markdown格式
  307. # start_page_id=0,
  308. # end_page_id=0
  309. # )
  310. total_time = time.time()- total_start_time
  311. print("\n"+"="*60)
  312. print(f"所有示例执行完成!")
  313. print(f"总运行时间: {total_time:.2f}秒")
  314. print(f"结束时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
  315. print("="*60)

常见问题

1、MinerU 在 Docker 中部署时出现 CUDA 错误:flash-attn 兼容性问题与解决方案

问题描述及原因

Windows 系统下使用 RTX 5060 显卡通过 Docker 部署 MinerU 时,可能出现以下 CUDA 相关错误,导致服务启动失败:

  1. CUDA error (/__w/xformers/xformers/third_party/flash-attention/hopper/flash_fwd_launch_template.h:188): invalid argument
  2. RuntimeError: Engine core initialization failed. See root cause above.

该错误通常与 vllm-flash-attn 在视觉模块中存在兼容性问题有关,系统已自动回退至 xformers 后端。为解决此问题,可尝试手动安装正确版本的 flash-attn

解决方案:安装 flash-attn

  1. # 通过以下命令安装指定版本的 flash-attn:
  2. pip install flash-attn==2.8.3
  3. # --verbose 是 pip 命令的一个可选参数,用于显示详细的安装过程信息。
  4. pip install flash-attn==2.8.3 --verbose

若安装过程缓慢,可直接从以下链接下载预编译的 wheel 文件,并放入 Docker 容器中安装:

  1. # 安装过程信息会显示安装的哪个版本
  2. https://github.com/Dao-AILab/flash-attention/releases/download/v2.8.3/flash_attn-2.8.3+cu12torch2.7cxx11abiTRUE-cp312-cp312-linux_x86_64.whl

下载后,在 Docker 内执行:

  1. pip install /path/to/flash_attn-2.8.3+cu12torch2.7cxx11abiTRUE-cp312-cp312-linux_x86_64.whl

MinerU 输出文件说明

Mineru 命令执行后,除了输出主要的 markdown 文件外,还会生成多个辅助文件用于调试、质检和进一步处理。

可视化调试文件

  • 模型输出(使用原始输出):
    • model.json
  • 调试和验证(使用可视化文件):
    • layout.pdf
    • spans.pdf
  • 内容提取(使用简化文件):
    • *.md
    • content_list.json
  • 二次开发(使用结构化文件):
    • middle.json

布局分析文件 (layout.pdf)

文件命名格式:{原文件名}_layout.pdf 功能说明:

  • 可视化展示每一页的布局分析结果
  • 每个检测框右上角的数字表示阅读顺序
  • 使用不同背景色块区分不同类型的内容块

使用场景:

  • 检查布局分析是否正确
  • 确认阅读顺序是否合理
  • 调试布局相关问题 MinerU PDF解析工具:从安装配置到本地部署与API调用的全流程指南 - 图1

文本片段文件 (spans.pdf)

> 仅适用于 pipeline 后端

文件命名格式:{原文件名}_spans.pdf 功能说明:

  • 根据 span 类型使用不同颜色线框标注页面内容
  • 用于质量检查和问题排查

使用场景:

  • 快速排查文本丢失问题
  • 检查行内公式识别情况
  • 验证文本分割准确性 MinerU PDF解析工具:从安装配置到本地部署与API调用的全流程指南 - 图2

结构化数据文件

> pipeline 后端 输出结果

模型推理结果 (model.json)

文件命名格式:{原文件名}_model.json

数据结构定义

  1. from pydantic import BaseModel, Field
  2. from enum import IntEnum
  3. class CategoryType(IntEnum):
  4. """内容类别枚举"""
  5. title = 0 # 标题
  6. plain_text = 1 # 文本
  7. abandon = 2 # 包括页眉页脚页码和页面注释
  8. figure = 3 # 图片
  9. figure_caption = 4 # 图片描述
  10. table = 5 # 表格
  11. table_caption = 6 # 表格描述
  12. table_footnote = 7 # 表格注释
  13. isolate_formula = 8 # 行间公式
  14. formula_caption = 9 # 行间公式的标号
  15. embedding = 13 # 行内公式
  16. isolated = 14 # 行间公式
  17. text = 15 # OCR 识别结果
  18. class PageInfo(BaseModel):
  19. """页面信息"""
  20. page_no: int = Field(description="页码序号,第一页的序号是 0", ge=0)
  21. height: int = Field(description="页面高度", gt=0)
  22. width: int = Field(description="页面宽度", ge=0)
  23. class ObjectInferenceResult(BaseModel):
  24. """对象识别结果"""
  25. category_id: CategoryType = Field(description="类别", ge=0)
  26. poly: list[float] = Field(description="四边形坐标,格式为 [x0,y0,x1,y1,x2,y2,x3,y3]")
  27. score: float = Field(description="推理结果的置信度")
  28. latex: str | None = Field(description="LaTeX 解析结果", default=None)
  29. html: str | None = Field(description="HTML 解析结果", default=None)
  30. class PageInferenceResults(BaseModel):
  31. """页面推理结果"""
  32. layout_dets: list[ObjectInferenceResult] = Field(description="页面识别结果")
  33. page_info: PageInfo = Field(description="页面元信息")
  34. # 完整的推理结果
  35. inference_result: list[PageInferenceResults] = []

坐标系统说明

poly 坐标格式:[x0, y0, x1, y1, x2, y2, x3, y3]

  • 分别表示左上、右上、右下、左下四点的坐标
  • 坐标原点在页面左上角 MinerU PDF解析工具:从安装配置到本地部署与API调用的全流程指南 - 图3

示例数据

  1. [
  2. {
  3. "layout_dets": [
  4. {
  5. "category_id": 2,
  6. "poly": [
  7. 99.1906967163086,
  8. 100.3119125366211,
  9. 730.3707885742188,
  10. 100.3119125366211,
  11. 730.3707885742188,
  12. 245.81326293945312,
  13. 99.1906967163086,
  14. 245.81326293945312
  15. ],
  16. "score": 0.9999997615814209
  17. }
  18. ],
  19. "page_info": {
  20. "page_no": 0,
  21. "height": 2339,
  22. "width": 1654
  23. }
  24. },
  25. {
  26. "layout_dets": [
  27. {
  28. "category_id": 5,
  29. "poly": [
  30. 99.13092803955078,
  31. 2210.680419921875,
  32. 497.3183898925781,
  33. 2210.680419921875,
  34. 497.3183898925781,
  35. 2264.78076171875,
  36. 99.13092803955078,
  37. 2264.78076171875
  38. ],
  39. "score": 0.9999997019767761
  40. }
  41. ],
  42. "page_info": {
  43. "page_no": 1,
  44. "height": 2339,
  45. "width": 1654
  46. }
  47. }
  48. ]

中间处理结果 (middle.json)

文件命名格式:{原文件名}_middle.json

顶层结构

字段名 类型 说明
pdf_info list[dict] 每一页的解析结果数组
_backend string 解析模式:pipeline 或 vlm
_version_name string MinerU 版本号

页面信息结构 (pdf_info)

字段名 说明
preproc_blocks PDF 预处理后的未分段中间结果
page_idx 页码,从 0 开始
page_size 页面的宽度和高度 [width, height]
images 图片块信息列表
tables 表格块信息列表
interline_equations 行间公式块信息列表
discarded_blocks 需要丢弃的块信息
para_blocks 分段后的内容块结果

块结构层次

  1. 一级块 (table | image)
  2. └── 二级块
  3. └── (line)
  4. └── 片段 (span)

一级块字段

字段名 说明
type 块类型:table 或 image
bbox 块的矩形框坐标 [x0, y0, x1, y1]
blocks 包含的二级块列表

二级块字段

字段名 说明
type 块类型(详见下表)
bbox 块的矩形框坐标
lines 包含的行信息列表

二级块类型

类型 说明
image_body 图像本体
image_caption 图像描述文本
image_footnote 图像脚注
table_body 表格本体
table_caption 表格描述文本
table_footnote 表格脚注
text 文本块
title 标题块
index 目录块
list 列表块
interline_equation 行间公式块

行和片段结构

  • 行 (line) 字段:

    • bbox:行的矩形框坐标
    • spans:包含的片段列表
  • 片段 (span) 字段:

    • bbox:片段的矩形框坐标
    • type:片段类型(image、table、text、inline_equation、interline_equation)
    • content | img_path:文本内容或图片路径

示例数据

  1. {
  2. "pdf_info": [
  3. {
  4. "preproc_blocks": [
  5. {
  6. "type": "text",
  7. "bbox": [
  8. 52,
  9. 61.956024169921875,
  10. 294,
  11. 82.99800872802734
  12. ],
  13. "lines": [
  14. {
  15. "bbox": [
  16. 52,
  17. 61.956024169921875,
  18. 294,
  19. 72.0000228881836
  20. ],
  21. "spans": [
  22. {
  23. "bbox": [
  24. 54.0,
  25. 61.956024169921875,
  26. 296.2261657714844,
  27. 72.0000228881836
  28. ],
  29. "content": "dependent on the service headway and the reliability of the departure ",
  30. "type": "text",
  31. "score": 1.0
  32. }
  33. ]
  34. }
  35. ]
  36. }
  37. ],
  38. "layout_bboxes": [
  39. {
  40. "layout_bbox": [
  41. 52,
  42. 61,
  43. 294,
  44. 731
  45. ],
  46. "layout_label": "V",
  47. "sub_layout": []
  48. }
  49. ],
  50. "page_idx": 0,
  51. "page_size": [
  52. 612.0,
  53. 792.0
  54. ],
  55. "_layout_tree": [],
  56. "images": [],
  57. "tables": [],
  58. "interline_equations": [],
  59. "discarded_blocks": [],
  60. "para_blocks": [
  61. {
  62. "type": "text",
  63. "bbox": [
  64. 52,
  65. 61.956024169921875,
  66. 294,
  67. 82.99800872802734
  68. ],
  69. "lines": [
  70. {
  71. "bbox": [
  72. 52,
  73. 61.956024169921875,
  74. 294,
  75. 72.0000228881836
  76. ],
  77. "spans": [
  78. {
  79. "bbox": [
  80. 54.0,
  81. 61.956024169921875,
  82. 296.2261657714844,
  83. 72.0000228881836
  84. ],
  85. "content": "dependent on the service headway and the reliability of the departure ",
  86. "type": "text",
  87. "score": 1.0
  88. }
  89. ]
  90. }
  91. ]
  92. }
  93. ]
  94. }
  95. ],
  96. "_backend": "pipeline",
  97. "_version_name": "0.6.1"
  98. }

内容列表 (content_list.json)

文件命名格式:{原文件名}_content_list.json

功能说明

这是一个简化版的 middle.json,按阅读顺序平铺存储所有可读内容块,去除了复杂的布局信息,便于后续处理。

内容类型

类型 说明
image 图片
table 表格
text 文本/标题
equation 行间公式

文本层级标识

通过 text_level 字段区分文本层级:

  • 无 text_level 或 text_level: 0:正文文本
  • text_level: 1:一级标题
  • text_level: 2:二级标题
  • 以此类推…

通用字段

  • 所有内容块都包含 page_idx 字段,表示所在页码(从 0 开始)。
  • 所有内容块都包含 bbox 字段,表示内容块的边界框坐标[x0, y0, x1, y1]映射在0-1000范围内的结果。

示例数据

  1. [
  2. {
  3. "type": "text",
  4. "text": "The response of flow duration curves to afforestation ",
  5. "text_level": 1,
  6. "bbox": [
  7. 62,
  8. 480,
  9. 946,
  10. 904
  11. ],
  12. "page_idx": 0
  13. },
  14. {
  15. "type": "image",
  16. "img_path": "images/a8ecda1c69b27e4f79fce1589175a9d721cbdc1cf78b4cc06a015f3746f6b9d8.jpg",
  17. "image_caption": [
  18. "Fig. 1. Annual flow duration curves of daily flows from Pine Creek, Australia, 1989–2000. "
  19. ],
  20. "image_footnote": [],
  21. "bbox": [
  22. 62,
  23. 480,
  24. 946,
  25. 904
  26. ],
  27. "page_idx": 1
  28. },
  29. {
  30. "type": "equation",
  31. "img_path": "images/181ea56ef185060d04bf4e274685f3e072e922e7b839f093d482c29bf89b71e8.jpg",
  32. "text": "$$\nQ _ { \\% } = f ( P ) + g ( T )\n$$",
  33. "text_format": "latex",
  34. "bbox": [
  35. 62,
  36. 480,
  37. 946,
  38. 904
  39. ],
  40. "page_idx": 2
  41. },
  42. {
  43. "type": "table",
  44. "img_path": "images/e3cb413394a475e555807ffdad913435940ec637873d673ee1b039e3bc3496d0.jpg",
  45. "table_caption": [
  46. "Table 2 Significance of the rainfall and time terms "
  47. ],
  48. "table_footnote": [
  49. "indicates that the rainfall term was significant at the $5 \\%$ level, $T$ indicates that the time term was significant at the $5 \\%$ level, \\* represents significance at the $10 \\%$ level, and na denotes too few data points for meaningful analysis. "
  50. ],
  51. "table_body": "<html><body><table><tr><td rowspan=\"2\">Site</td><td colspan=\"10\">Percentile</td></tr><tr><td>10</td><td>20</td><td>30</td><td>40</td><td>50</td><td>60</td><td>70</td><td>80</td><td>90</td><td>100</td></tr><tr><td>Traralgon Ck</td><td>P</td><td>P,*</td><td>P</td><td>P</td><td>P,</td><td>P,</td><td>P,</td><td>P,</td><td>P</td><td>P</td></tr><tr><td>Redhill</td><td>P,T</td><td>P,T</td><td>,*</td><td>**</td><td>P.T</td><td>P,*</td><td>P*</td><td>P*</td><td>*</td><td>,*</td></tr><tr><td>Pine Ck</td><td></td><td>P,T</td><td>P,T</td><td>P,T</td><td>P,T</td><td>T</td><td>T</td><td>T</td><td>na</td><td>na</td></tr><tr><td>Stewarts Ck 5</td><td>P,T</td><td>P,T</td><td>P,T</td><td>P,T</td><td>P.T</td><td>P.T</td><td>P,T</td><td>na</td><td>na</td><td>na</td></tr><tr><td>Glendhu 2</td><td>P</td><td>P,T</td><td>P,*</td><td>P,T</td><td>P.T</td><td>P,ns</td><td>P,T</td><td>P,T</td><td>P,T</td><td>P,T</td></tr><tr><td>Cathedral Peak 2</td><td>P,T</td><td>P,T</td><td>P,T</td><td>P,T</td><td>P,T</td><td>*,T</td><td>P,T</td><td>P,T</td><td>P,T</td><td>T</td></tr><tr><td>Cathedral Peak 3</td><td>P.T</td><td>P.T</td><td>P,T</td><td>P,T</td><td>P,T</td><td>T</td><td>P,T</td><td>P,T</td><td>P,T</td><td>T</td></tr><tr><td>Lambrechtsbos A</td><td>P,T</td><td>P</td><td>P</td><td>P,T</td><td>*,T</td><td>*,T</td><td>*,T</td><td>*,T</td><td>*,T</td><td>T</td></tr><tr><td>Lambrechtsbos B</td><td>P,T</td><td>P,T</td><td>P,T</td><td>P,T</td><td>P,T</td><td>P,T</td><td>P,T</td><td>P,T</td><td>T</td><td>T</td></tr><tr><td>Biesievlei</td><td>P,T</td><td>P.T</td><td>P,T</td><td>P,T</td><td>*,T</td><td>*,T</td><td>T</td><td>T</td><td>P,T</td><td>P,T</td></tr></table></body></html>",
  52. "bbox": [
  53. 62,
  54. 480,
  55. 946,
  56. 904
  57. ],
  58. "page_idx": 5
  59. }
  60. ]

VLM 后端 输出结果

模型推理结果 (model.json)

文件命名格式:{原文件名}_model.json

文件格式说明

  • 该文件为 VLM 模型的原始输出结果,包含两层嵌套list,外层表示页面,内层表示该页的内容块
  • 每个内容块都是一个dict,包含 type、bbox、angle、content 字段

支持的内容类型

  1. {
  2. "text": "文本",
  3. "title": "标题",
  4. "equation": "行间公式",
  5. "image": "图片",
  6. "image_caption": "图片描述",
  7. "image_footnote": "图片脚注",
  8. "table": "表格",
  9. "table_caption": "表格描述",
  10. "table_footnote": "表格脚注",
  11. "phonetic": "拼音",
  12. "code": "代码块",
  13. "code_caption": "代码描述",
  14. "ref_text": "参考文献",
  15. "algorithm": "算法块",
  16. "list": "列表",
  17. "header": "页眉",
  18. "footer": "页脚",
  19. "page_number": "页码",
  20. "aside_text": "装订线旁注",
  21. "page_footnote": "页面脚注"
  22. }

坐标系统说明

bbox 坐标格式:[x0, y0, x1, y1]

  • 分别表示左上、右下两点的坐标
  • 坐标原点在页面左上角
  • 坐标为相对于原始页面尺寸的百分比,范围在0-1之间

示例数据

  1. [
  2. [
  3. {
  4. "type": "header",
  5. "bbox": [
  6. 0.077,
  7. 0.095,
  8. 0.18,
  9. 0.181
  10. ],
  11. "angle": 0,
  12. "score": null,
  13. "block_tags": null,
  14. "content": "ELSEVIER",
  15. "format": null,
  16. "content_tags": null
  17. },
  18. {
  19. "type": "title",
  20. "bbox": [
  21. 0.157,
  22. 0.228,
  23. 0.833,
  24. 0.253
  25. ],
  26. "angle": 0,
  27. "score": null,
  28. "block_tags": null,
  29. "content": "The response of flow duration curves to afforestation",
  30. "format": null,
  31. "content_tags": null
  32. }
  33. ]
  34. ]

中间处理结果 (middle.json)

文件命名格式:{原文件名}_middle.json

文件格式说明

vlm 后端的 middle.json 文件结构与 pipeline 后端类似,但存在以下差异:

  • list变成二级block,增加sub_type字段区分list类型:
    • text(文本类型)
    • ref_text(引用类型)
  • 增加code类型blockcode类型包含两种"sub_type":
    • 分别是code和algorithm
    • 至少有code_body, 可选code_caption
  • discarded_blocks内元素type增加以下类型:
    • header(页眉)
    • footer(页脚)
    • page_number(页码)
    • aside_text(装订线文本)
    • page_footnote(脚注)
  • 所有block增加angle字段,用来表示旋转角度,0,90,180,270

示例数据

  • list block 示例

    1. {
    2. "bbox": [
    3. 174,
    4. 155,
    5. 818,
    6. 333
    7. ],
    8. "type": "list",
    9. "angle": 0,
    10. "index": 11,
    11. "blocks": [
    12. {
    13. "bbox": [
    14. 174,
    15. 157,
    16. 311,
    17. 175
    18. ],
    19. "type": "text",
    20. "angle": 0,
    21. "lines": [
    22. {
    23. "bbox": [
    24. 174,
    25. 157,
    26. 311,
    27. 175
    28. ],
    29. "spans": [
    30. {
    31. "bbox": [
    32. 174,
    33. 157,
    34. 311,
    35. 175
    36. ],
    37. "type": "text",
    38. "content": "H.1 Introduction"
    39. }
    40. ]
    41. }
    42. ],
    43. "index": 3
    44. },
    45. {
    46. "bbox": [
    47. 175,
    48. 182,
    49. 464,
    50. 229
    51. ],
    52. "type": "text",
    53. "angle": 0,
    54. "lines": [
    55. {
    56. "bbox": [
    57. 175,
    58. 182,
    59. 464,
    60. 229
    61. ],
    62. "spans": [
    63. {
    64. "bbox": [
    65. 175,
    66. 182,
    67. 464,
    68. 229
    69. ],
    70. "type": "text",
    71. "content": "H.2 Example: Divide by Zero without Exception Handling"
    72. }
    73. ]
    74. }
    75. ],
    76. "index": 4
    77. }
    78. ],
    79. "sub_type": "text"
    80. }
  • code block 示例

    1. {
    2. "type": "code",
    3. "bbox": [
    4. 114,
    5. 780,
    6. 885,
    7. 1231
    8. ],
    9. "blocks": [
    10. {
    11. "bbox": [
    12. 114,
    13. 780,
    14. 885,
    15. 1231
    16. ],
    17. "lines": [
    18. {
    19. "bbox": [
    20. 114,
    21. 780,
    22. 885,
    23. 1231
    24. ],
    25. "spans": [
    26. {
    27. "bbox": [
    28. 114,
    29. 780,
    30. 885,
    31. 1231
    32. ],
    33. "type": "text",
    34. "content": "1 // Fig. H.1: DivideByZeroNoExceptionHandling.java \n2 // Integer division without exception handling. \n3 import java.util.Scanner; \n4 \n5 public class DivideByZeroNoExceptionHandling \n6 { \n7 // demonstrates throwing an exception when a divide-by-zero occurs \n8 public static int quotient( int numerator, int denominator ) \n9 { \n10 return numerator / denominator; // possible division by zero \n11 } // end method quotient \n12 \n13 public static void main(String[] args) \n14 { \n15 Scanner scanner = new Scanner(http://System.in); // scanner for input \n16 \n17 System.out.print(\"Please enter an integer numerator: \"); \n18 int numerator = scanner.nextInt(); \n19 System.out.print(\"Please enter an integer denominator: \"); \n20 int denominator = scanner.nextInt(); \n21"
    35. }
    36. ]
    37. }
    38. ],
    39. "index": 17,
    40. "angle": 0,
    41. "type": "code_body"
    42. },
    43. {
    44. "bbox": [
    45. 867,
    46. 160,
    47. 1280,
    48. 189
    49. ],
    50. "lines": [
    51. {
    52. "bbox": [
    53. 867,
    54. 160,
    55. 1280,
    56. 189
    57. ],
    58. "spans": [
    59. {
    60. "bbox": [
    61. 867,
    62. 160,
    63. 1280,
    64. 189
    65. ],
    66. "type": "text",
    67. "content": "Algorithm 1 Modules for MCTSteg"
    68. }
    69. ]
    70. }
    71. ],
    72. "index": 19,
    73. "angle": 0,
    74. "type": "code_caption"
    75. }
    76. ],
    77. "index": 17,
    78. "sub_type": "code"
    79. }

内容列表 (content_list.json)

文件命名格式:{原文件名}_content_list.json

文件格式说明

vlm 后端的 content_list.json 文件结构与 pipeline 后端类似,伴随本次middle.json的变化,做了以下调整:

  • 新增code类型,code类型包含两种”sub_type”:
    • 分别是code和algorithm
    • 至少有code_body, 可选code_caption
  • 新增list类型,list类型包含两种”sub_type”:
    • text
    • ref_text
  • 增加所有所有discarded_blocks的输出内容
    • header
    • footer
    • page_number
    • aside_text
    • page_footnote

示例数据

  • code 类型 content

    1. {
    2. "type": "code",
    3. "sub_type": "algorithm",
    4. "code_caption": [
    5. "Algorithm 1 Modules for MCTSteg"
    6. ],
    7. "code_body": "1: function GETCOORDINATE(d) \n2: $x \\gets d / l$ , $y \\gets d$ mod $l$ \n3: return $(x, y)$ \n4: end function \n5: function BESTCHILD(v) \n6: $C \\gets$ child set of $v$ \n7: $v' \\gets \\arg \\max_{c \\in C} \\mathrm{UCTScore}(c)$ \n8: $v'.n \\gets v'.n + 1$ \n9: return $v'$ \n10: end function \n11: function BACK PROPAGATE(v) \n12: Calculate $R$ using Equation 11 \n13: while $v$ is not a root node do \n14: $v.r \\gets v.r + R$ , $v \\gets v.p$ \n15: end while \n16: end function \n17: function RANDOMSEARCH(v) \n18: while $v$ is not a leaf node do \n19: Randomly select an untried action $a \\in A(v)$ \n20: Create a new node $v'$ \n21: $(x, y) \\gets \\mathrm{GETCOORDINATE}(v'.d)$ \n22: $v'.p \\gets v$ , $v'.d \\gets v.d + 1$ , $v'.\\Gamma \\gets v.\\Gamma$ \n23: $v'.\\gamma_{x,y} \\gets a$ \n24: if $a = -1$ then \n25: $v.lc \\gets v'$ \n26: else if $a = 0$ then \n27: $v.mc \\gets v'$ \n28: else \n29: $v.rc \\gets v'$ \n30: end if \n31: $v \\gets v'$ \n32: end while \n33: return $v$ \n34: end function \n35: function SEARCH(v) \n36: while $v$ is fully expanded do \n37: $v \\gets$ BESTCHILD(v) \n38: end while \n39: if $v$ is not a leaf node then \n40: $v \\gets$ RANDOMSEARCH(v) \n41: end if \n42: return $v$ \n43: end function",
    8. "bbox": [
    9. 510,
    10. 87,
    11. 881,
    12. 740
    13. ],
    14. "page_idx": 0
    15. }
  • list 类型 content

    1. {
    2. "type": "list",
    3. "sub_type": "text",
    4. "list_items": [
    5. "H.1 Introduction",
    6. "H.2 Example: Divide by Zero without Exception Handling",
    7. "H.3 Example: Divide by Zero with Exception Handling",
    8. "H.4 Summary"
    9. ],
    10. "bbox": [
    11. 174,
    12. 155,
    13. 818,
    14. 333
    15. ],
    16. "page_idx": 0
    17. }
  • discarded 类型 content

    1. [
    2. {
    3. "type": "header",
    4. "text": "Journal of Hydrology 310 (2005) 253-265",
    5. "bbox": [
    6. 363,
    7. 164,
    8. 623,
    9. 177
    10. ],
    11. "page_idx": 0
    12. },
    13. {
    14. "type": "page_footnote",
    15. "text": "* Corresponding author. Address: Forest Science Centre, Department of Sustainability and Environment, P.O. Box 137, Heidelberg, Vic. 3084, Australia. Tel.: +61 3 9450 8719; fax: +61 3 9450 8644.",
    16. "bbox": [
    17. 71,
    18. 815,
    19. 915,
    20. 841
    21. ],
    22. "page_idx": 0
    23. }
    24. ]

参考