使用官方文档中的python api 调用报错Exception: No available model hosting platforms detected. Please check your network connection
🔎 Search before asking
- [x] I have searched the PaddleOCR Docs and found no similar bug report.
- [x] I have searched the PaddleOCR Issues and found no similar bug report.
- [x] I have searched the PaddleOCR Discussions and found no similar bug report.
🐛 Bug (问题描述)
api使用的是docker部署,api正常 客户端调用方法如下 pipeline = PaddleOCRVL(vl_rec_backend="vllm-server", vl_rec_server_url="http://127.0.0.1:8118/v1")
🏃♂️ Environment (运行环境)
paddlepaddle==3.2.0 paddleocr==3.3.0
🌰 Minimal Reproducible Example (最小可复现问题的Demo)
from pathlib import Path from paddleocr import PaddleOCRVL
input_file = "./your_pdf_file.pdf" output_path = Path("./output")
pipeline = PaddleOCRVL(vl_rec_backend="vllm-server", vl_rec_server_url="http://127.0.0.1:8118/v1") output = pipeline.predict(input=input_file)
markdown_list = [] markdown_images = []
for res in output: md_info = res.markdown markdown_list.append(md_info) markdown_images.append(md_info.get("markdown_images", {}))
markdown_texts = pipeline.concatenate_markdown_pages(markdown_list)
mkd_file_path = output_path / f"{Path(input_file).stem}.md" mkd_file_path.parent.mkdir(parents=True, exist_ok=True)
with open(mkd_file_path, "w", encoding="utf-8") as f: f.write(markdown_texts)
for item in markdown_images: if item: for path, image in item.items(): file_path = output_path / path file_path.parent.mkdir(parents=True, exist_ok=True) image.save(file_path)
使用PP-StructureV3 同样报错如下:
(venv) root@notebook-1761361751-vafbo-86cf9dbff6-wsts5:~/PaddleOCR# python paddleocr_server.py
No model hoster is available! Please check your network connection to one of the following model hosts:
HuggingFace (https://huggingface.co),
ModelScope (https://modelscope.cn),
AIStudio (https://aistudio.baidu.com), or
BOS (https://paddle-model-ecology.bj.bcebos.com).
Otherwise, only local models can be used.
2025-10-25 01:42:37.252 | INFO | __main__:initialize_pipeline:99 - 🚀 Initializing PaddleOCR...
2025-10-25 01:42:37.252 | INFO | 123758:140357272642688 | __main__:initialize_pipeline:99 | {} - 🚀 Initializing PaddleOCR...
Creating model: ('PP-LCNet_x1_0_doc_ori', None)
No available model hosting platforms detected. Please check your network connection.
Traceback (most recent call last):
File "/root/PaddleOCR/paddleocr_server.py", line 110, in <module>
pipeline, ocr = initialize_pipeline()
^^^^^^^^^^^^^^^^^^^^^
File "/root/PaddleOCR/paddleocr_server.py", line 101, in initialize_pipeline
pipeline = PPStructureV3(
^^^^^^^^^^^^^^
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddleocr/_pipelines/pp_structurev3.py", line 137, in __init__
super().__init__(**kwargs)
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddleocr/_pipelines/base.py", line 67, in __init__
self.paddlex_pipeline = self._create_paddlex_pipeline()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddleocr/_pipelines/base.py", line 105, in _create_paddlex_pipeline
return create_pipeline(config=self._merged_paddlex_config, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/__init__.py", line 167, in create_pipeline
pipeline = BasePipeline.get(pipeline_name)(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/utils/deps.py", line 206, in _wrapper
return old_init_func(self, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/_parallel.py", line 103, in __init__
self._pipeline = self._create_internal_pipeline(config, self.device)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/_parallel.py", line 158, in _create_internal_pipeline
return self._pipeline_cls(
^^^^^^^^^^^^^^^^^^^
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/layout_parsing/pipeline_v2.py", line 84, in __init__
self.inintial_predictor(config)
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/layout_parsing/pipeline_v2.py", line 134, in inintial_predictor
self.doc_preprocessor_pipeline = self.create_pipeline(
^^^^^^^^^^^^^^^^^^^^^
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/base.py", line 140, in create_pipeline
pipeline = create_pipeline(
^^^^^^^^^^^^^^^^
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/__init__.py", line 167, in create_pipeline
pipeline = BasePipeline.get(pipeline_name)(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/utils/deps.py", line 206, in _wrapper
return old_init_func(self, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/_parallel.py", line 103, in __init__
self._pipeline = self._create_internal_pipeline(config, self.device)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/_parallel.py", line 158, in _create_internal_pipeline
return self._pipeline_cls(
^^^^^^^^^^^^^^^^^^^
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/doc_preprocessor/pipeline.py", line 69, in __init__
self.doc_ori_classify_model = self.create_model(doc_ori_classify_config)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/base.py", line 106, in create_model
model = create_predictor(
^^^^^^^^^^^^^^^^^
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/models/__init__.py", line 76, in create_predictor
model_dir = official_models[model_name]
~~~~~~~~~~~~~~~^^^^^^^^^^^^
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/utils/official_models.py", line 613, in __getitem__
return self._get_model_local_path(model_name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/utils/official_models.py", line 589, in _get_model_local_path
raise Exception(msg)
Exception: No available model hosting platforms detected. Please check your network connection.
模型我已经下载到本地了,为什么还会请求网络呢?
版本信息如下:
(venv) root@notebook-1761361751-vafbo-86cf9dbff6-wsts5:~/PaddleOCR# pip list | grep paddle
paddleocr 3.3.0
paddlepaddle-gpu 3.2.0
paddlex 3.3.5
(venv) root@notebook-1761361751-vafbo-86cf9dbff6-wsts5:~/PaddleOCR# python3 --version
Python 3.12.9
代码如下:
import uvicorn
import os
import tempfile
import time
import json
import sys
import uuid
import base64
import requests
from io import BytesIO
import argparse
import re
from datetime import datetime
from typing import Optional, List, Union
from pathlib import Path
from loguru import logger
from fastapi import FastAPI, File, UploadFile, HTTPException, Form, Request
from fastapi.responses import JSONResponse, FileResponse, HTMLResponse
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from paddleocr import PPStructureV3, PaddleOCR
base_dir = os.path.dirname(os.path.abspath(__file__))
results_dir = os.path.join(base_dir, "results")
log_dir = os.path.join(base_dir, "logs")
# 创建必要的目录
os.makedirs(log_dir, exist_ok=True)
os.makedirs(results_dir, exist_ok=True)
# 控制台日志 - 彩色输出,包含所有级别的详细信息
logger.add(
sys.stdout,
format="<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level> | <magenta>{process}</magenta>:<magenta>{thread}</magenta> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> | <blue>{extra}</blue> - <level>{message}</level>",
level="DEBUG",
colorize=True,
backtrace=True,
diagnose=True
)
# 详细文件日志 - 包含所有级别和完整上下文信息
logger.add(
f"{log_dir}/debug_{{time:YYYY-MM-DD}}.log",
format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | PID:{process} | Thread:{thread} | {name}:{function}:{line} | Extra:{extra} | {message}",
level="DEBUG",
rotation="1 day",
retention="30 days",
compression="zip",
backtrace=True,
diagnose=True
)
# 普通文件日志 - INFO及以上级别
logger.add(
f"{log_dir}/info_{{time:YYYY-MM-DD}}.log",
format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {name}:{function}:{line} | {message}",
level="INFO",
rotation="1 day",
retention="30 days",
compression="zip"
)
# 错误日志单独文件 - 包含完整的错误追踪信息
logger.add(
f"{log_dir}/error_{{time:YYYY-MM-DD}}.log",
format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | PID:{process} | Thread:{thread} | {name}:{function}:{line} | {message}",
level="ERROR",
rotation="1 day",
retention="30 days",
compression="zip",
backtrace=True,
diagnose=True
)
app = FastAPI(
title="PaddleOCR V3 API",
description="基于 PaddleOCR V3 的文档结构化识别服务",
version="1.0.0",
docs_url="/api/v1/docs",
redoc_url="/api/v1/redoc",
openapi_url="/api/v1/openapi.json"
)
# 添加 CORS 中间件
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# 挂载 results 目录为静态资源
app.mount("/results", StaticFiles(directory=results_dir), name="results")
def initialize_pipeline():
logger.info("🚀 Initializing PaddleOCR...")
start_load = time.time()
pipeline = PPStructureV3(
device="gpu:0",
)
end_load = time.time()
logger.info(f"🚀 PaddleOCR initialized PPStructureV3 in {end_load - start_load:.2f} seconds")
logger.info("✅ PaddleOCR initialized successfully!")
return pipeline, ocr
pipeline, ocr = initialize_pipeline()
@app.get("/")
def root():
"""根路径 - 服务状态检查"""
logger.info("📊 Root endpoint accessed")
return {
"service": "PaddleOCR API",
"status": "running",
"version": "1.0.0",
"timestamp": datetime.now().isoformat(),
"current_time_utc": datetime.now(),
"documentation": {
"swagger_ui": "/api/v1/docs",
"redoc": "/api/v1/redoc",
"openapi_json": "/api/v1/openapi.json"
},
"endpoints": {
"ocr": "/api/v1/ocr",
"health": "/api/v1/health"
}
}
def process_parsing_result(res, _output_dir,use_ocr =False):
json_dir = os.path.join(_output_dir, "json")
md_dir = os.path.join(_output_dir, "markdown")
res.save_to_json(save_path=json_dir) ## 保存当前图像的结构化json结果
if not use_ocr:
res.save_to_markdown(save_path=md_dir) ## 保存当前图像的markdown结果
md_info = res.markdown
md_data = md_info.copy()
images_list = list(md_data.get("markdown_images").keys())
md_data.pop("markdown_images")
else:
md_data = ""
images_list = []
md_info = ""
logger.info(res.print())
json_data = res.json.get("res").copy()
json_data.pop("model_settings")
json_data.pop("input_path")
return json_data, md_data, images_list, md_info
def save_base64_to_file(input_str, output_dir, suffix, request_id):
"""
将base64字符串保存为文件,写入到指定目录output_dir,文件名使用request_id,返回文件路径。
"""
import os
import base64
import re
os.makedirs(output_dir, exist_ok=True)
base64_pattern = r"^data:.*?;base64,(.*)"
match = re.match(base64_pattern, input_str)
if match:
file_data = base64.b64decode(match.group(1))
else:
try:
file_data = base64.b64decode(input_str)
except Exception:
file_data = None
if file_data:
file_name = f"{request_id}.{suffix}"
file_path = os.path.join(output_dir, file_name)
with open(file_path, "wb") as f:
f.write(file_data)
return file_path
return None
@app.post("/api/v1/layout-parsing")
async def layout_parsing(request: Request):
# 先尝试解析 form
form = await request.form()
if "input_file" in form and "file_type" in form:
input_file = form["input_file"]
file_type = int(form["file_type"])
else:
# 再尝试解析 json
body = await request.json()
input_file = body.get("input_file")
file_type = int(body.get("file_type", 2))
# 后续逻辑不变
start_time = time.time()
request_id = uuid.uuid4().hex
logger.info(f"📝 layout-parsing请求 - RequestID: {request_id}")
suffix = 'pdf' if file_type == 1 else 'png'
_output_dir = os.path.join(results_dir, request_id)
data = list()
markdown_texts = ""
temp_file_path = None
try:
# 处理 base64 字符串输入
if isinstance(input_file, str):
temp_file_path = save_base64_to_file(input_file, _output_dir, suffix, request_id)
if temp_file_path:
input_file = temp_file_path
output = pipeline.predict(input_file)
if suffix == "image":
res = output[0]
json_data, md_data, images_list, _ = process_parsing_result(res, _output_dir)
markdown_texts = md_data
detail = {
"json": json_data,
"markdown": md_data,
"images": images_list,
"page_num": 0,
}
data.append(detail)
else:
markdown_list = []
for res in output:
json_data, md_data, images_list, md_info = process_parsing_result(res, _output_dir)
page_num = json_data.get("page_index")
markdown_list.append(md_info)
detail = {
"json": json_data,
"markdown": md_data,
"images": images_list,
"page_num": page_num,
}
data.append(detail)
markdown_texts = pipeline.concatenate_markdown_pages(markdown_list)
mkd_file_path = os.path.join(_output_dir, "markdown", f"{request_id}.md")
with open(mkd_file_path, "w", encoding="utf-8") as f:
f.write(markdown_texts)
total_time = time.time() - start_time
logger.info(f"✅ 版面解析请求完成 - RequestID: {request_id},总耗时: {total_time:.2f}秒")
return JSONResponse(content={
"code": 200,
"message": "解析成功!",
"request_id": request_id,
"file_type": file_type,
"markdown_texts": markdown_texts,
"data": data
})
except Exception as e:
error_time = time.time() - start_time
logger.error(f"❌ 版面解析失败 - RequestID: {request_id}, 错误: {str(e)}, 耗时: {error_time:.2f}秒")
return JSONResponse(
status_code=500,
content={
"code": 500,
"message": f"版面解析失败: {str(e)}",
"request_id": request_id,
"data": None
}
)
finally:
# 清理临时文件
if temp_file_path and os.path.exists(temp_file_path):
os.remove(temp_file_path)
@app.get("/api/v1/health")
async def health_check():
"""健康检查接口"""
return JSONResponse(content={
"code": 200,
"message": "服务运行正常",
"data": {
"status": "healthy",
"timestamp": datetime.now().isoformat(),
"services": {
"pp_structure_v3": "ready",
"paddle_ocr": "ready"
}
}
})
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="启动 PaddleOCR 服务器")
parser.add_argument("--port", type=int, default=int(os.environ.get("PADDLEOCR_PORT", 2024)), help="服务端口号")
args = parser.parse_args()
logger.info(f"🚀 启动 PaddleOCR 服务器... 端口: {args.port}")
uvicorn.run(app, host="0.0.0.0", port=args.port)
使用PP-StructureV3 同样报错如下:
(venv) root@notebook-1761361751-vafbo-86cf9dbff6-wsts5:~/PaddleOCR# python paddleocr_server.py No model hoster is available! Please check your network connection to one of the following model hosts: HuggingFace (https://huggingface.co), ModelScope (https://modelscope.cn), AIStudio (https://aistudio.baidu.com), or BOS (https://paddle-model-ecology.bj.bcebos.com). Otherwise, only local models can be used. 2025-10-25 01:42:37.252 | INFO | __main__:initialize_pipeline:99 - 🚀 Initializing PaddleOCR... 2025-10-25 01:42:37.252 | INFO | 123758:140357272642688 | __main__:initialize_pipeline:99 | {} - 🚀 Initializing PaddleOCR... Creating model: ('PP-LCNet_x1_0_doc_ori', None) No available model hosting platforms detected. Please check your network connection. Traceback (most recent call last): File "/root/PaddleOCR/paddleocr_server.py", line 110, in <module> pipeline, ocr = initialize_pipeline() ^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/paddleocr_server.py", line 101, in initialize_pipeline pipeline = PPStructureV3( ^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddleocr/_pipelines/pp_structurev3.py", line 137, in __init__ super().__init__(**kwargs) File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddleocr/_pipelines/base.py", line 67, in __init__ self.paddlex_pipeline = self._create_paddlex_pipeline() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddleocr/_pipelines/base.py", line 105, in _create_paddlex_pipeline return create_pipeline(config=self._merged_paddlex_config, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/__init__.py", line 167, in create_pipeline pipeline = BasePipeline.get(pipeline_name)( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/utils/deps.py", line 206, in _wrapper return old_init_func(self, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/_parallel.py", line 103, in __init__ self._pipeline = self._create_internal_pipeline(config, self.device) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/_parallel.py", line 158, in _create_internal_pipeline return self._pipeline_cls( ^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/layout_parsing/pipeline_v2.py", line 84, in __init__ self.inintial_predictor(config) File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/layout_parsing/pipeline_v2.py", line 134, in inintial_predictor self.doc_preprocessor_pipeline = self.create_pipeline( ^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/base.py", line 140, in create_pipeline pipeline = create_pipeline( ^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/__init__.py", line 167, in create_pipeline pipeline = BasePipeline.get(pipeline_name)( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/utils/deps.py", line 206, in _wrapper return old_init_func(self, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/_parallel.py", line 103, in __init__ self._pipeline = self._create_internal_pipeline(config, self.device) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/_parallel.py", line 158, in _create_internal_pipeline return self._pipeline_cls( ^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/doc_preprocessor/pipeline.py", line 69, in __init__ self.doc_ori_classify_model = self.create_model(doc_ori_classify_config) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/base.py", line 106, in create_model model = create_predictor( ^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/models/__init__.py", line 76, in create_predictor model_dir = official_models[model_name] ~~~~~~~~~~~~~~~^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/utils/official_models.py", line 613, in __getitem__ return self._get_model_local_path(model_name) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/utils/official_models.py", line 589, in _get_model_local_path raise Exception(msg) Exception: No available model hosting platforms detected. Please check your network connection.模型我已经下载到本地了,为什么还会请求网络呢?
版本信息如下:
(venv) root@notebook-1761361751-vafbo-86cf9dbff6-wsts5:~/PaddleOCR# pip list | grep paddle paddleocr 3.3.0 paddlepaddle-gpu 3.2.0 paddlex 3.3.5 (venv) root@notebook-1761361751-vafbo-86cf9dbff6-wsts5:~/PaddleOCR# python3 --version Python 3.12.9代码如下:
import uvicorn import os import tempfile import time import json import sys import uuid import base64 import requests from io import BytesIO import argparse import re from datetime import datetime from typing import Optional, List, Union from pathlib import Path from loguru import logger from fastapi import FastAPI, File, UploadFile, HTTPException, Form, Request from fastapi.responses import JSONResponse, FileResponse, HTMLResponse from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from paddleocr import PPStructureV3, PaddleOCR base_dir = os.path.dirname(os.path.abspath(__file__)) results_dir = os.path.join(base_dir, "results") log_dir = os.path.join(base_dir, "logs") # 创建必要的目录 os.makedirs(log_dir, exist_ok=True) os.makedirs(results_dir, exist_ok=True) # 控制台日志 - 彩色输出,包含所有级别的详细信息 logger.add( sys.stdout, format="<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level> | <magenta>{process}</magenta>:<magenta>{thread}</magenta> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> | <blue>{extra}</blue> - <level>{message}</level>", level="DEBUG", colorize=True, backtrace=True, diagnose=True ) # 详细文件日志 - 包含所有级别和完整上下文信息 logger.add( f"{log_dir}/debug_{{time:YYYY-MM-DD}}.log", format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | PID:{process} | Thread:{thread} | {name}:{function}:{line} | Extra:{extra} | {message}", level="DEBUG", rotation="1 day", retention="30 days", compression="zip", backtrace=True, diagnose=True ) # 普通文件日志 - INFO及以上级别 logger.add( f"{log_dir}/info_{{time:YYYY-MM-DD}}.log", format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {name}:{function}:{line} | {message}", level="INFO", rotation="1 day", retention="30 days", compression="zip" ) # 错误日志单独文件 - 包含完整的错误追踪信息 logger.add( f"{log_dir}/error_{{time:YYYY-MM-DD}}.log", format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | PID:{process} | Thread:{thread} | {name}:{function}:{line} | {message}", level="ERROR", rotation="1 day", retention="30 days", compression="zip", backtrace=True, diagnose=True ) app = FastAPI( title="PaddleOCR V3 API", description="基于 PaddleOCR V3 的文档结构化识别服务", version="1.0.0", docs_url="/api/v1/docs", redoc_url="/api/v1/redoc", openapi_url="/api/v1/openapi.json" ) # 添加 CORS 中间件 app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # 挂载 results 目录为静态资源 app.mount("/results", StaticFiles(directory=results_dir), name="results") def initialize_pipeline(): logger.info("🚀 Initializing PaddleOCR...") start_load = time.time() pipeline = PPStructureV3( device="gpu:0", ) end_load = time.time() logger.info(f"🚀 PaddleOCR initialized PPStructureV3 in {end_load - start_load:.2f} seconds") logger.info("✅ PaddleOCR initialized successfully!") return pipeline, ocr pipeline, ocr = initialize_pipeline() @app.get("/") def root(): """根路径 - 服务状态检查""" logger.info("📊 Root endpoint accessed") return { "service": "PaddleOCR API", "status": "running", "version": "1.0.0", "timestamp": datetime.now().isoformat(), "current_time_utc": datetime.now(), "documentation": { "swagger_ui": "/api/v1/docs", "redoc": "/api/v1/redoc", "openapi_json": "/api/v1/openapi.json" }, "endpoints": { "ocr": "/api/v1/ocr", "health": "/api/v1/health" } } def process_parsing_result(res, _output_dir,use_ocr =False): json_dir = os.path.join(_output_dir, "json") md_dir = os.path.join(_output_dir, "markdown") res.save_to_json(save_path=json_dir) ## 保存当前图像的结构化json结果 if not use_ocr: res.save_to_markdown(save_path=md_dir) ## 保存当前图像的markdown结果 md_info = res.markdown md_data = md_info.copy() images_list = list(md_data.get("markdown_images").keys()) md_data.pop("markdown_images") else: md_data = "" images_list = [] md_info = "" logger.info(res.print()) json_data = res.json.get("res").copy() json_data.pop("model_settings") json_data.pop("input_path") return json_data, md_data, images_list, md_info def save_base64_to_file(input_str, output_dir, suffix, request_id): """ 将base64字符串保存为文件,写入到指定目录output_dir,文件名使用request_id,返回文件路径。 """ import os import base64 import re os.makedirs(output_dir, exist_ok=True) base64_pattern = r"^data:.*?;base64,(.*)" match = re.match(base64_pattern, input_str) if match: file_data = base64.b64decode(match.group(1)) else: try: file_data = base64.b64decode(input_str) except Exception: file_data = None if file_data: file_name = f"{request_id}.{suffix}" file_path = os.path.join(output_dir, file_name) with open(file_path, "wb") as f: f.write(file_data) return file_path return None @app.post("/api/v1/layout-parsing") async def layout_parsing(request: Request): # 先尝试解析 form form = await request.form() if "input_file" in form and "file_type" in form: input_file = form["input_file"] file_type = int(form["file_type"]) else: # 再尝试解析 json body = await request.json() input_file = body.get("input_file") file_type = int(body.get("file_type", 2)) # 后续逻辑不变 start_time = time.time() request_id = uuid.uuid4().hex logger.info(f"📝 layout-parsing请求 - RequestID: {request_id}") suffix = 'pdf' if file_type == 1 else 'png' _output_dir = os.path.join(results_dir, request_id) data = list() markdown_texts = "" temp_file_path = None try: # 处理 base64 字符串输入 if isinstance(input_file, str): temp_file_path = save_base64_to_file(input_file, _output_dir, suffix, request_id) if temp_file_path: input_file = temp_file_path output = pipeline.predict(input_file) if suffix == "image": res = output[0] json_data, md_data, images_list, _ = process_parsing_result(res, _output_dir) markdown_texts = md_data detail = { "json": json_data, "markdown": md_data, "images": images_list, "page_num": 0, } data.append(detail) else: markdown_list = [] for res in output: json_data, md_data, images_list, md_info = process_parsing_result(res, _output_dir) page_num = json_data.get("page_index") markdown_list.append(md_info) detail = { "json": json_data, "markdown": md_data, "images": images_list, "page_num": page_num, } data.append(detail) markdown_texts = pipeline.concatenate_markdown_pages(markdown_list) mkd_file_path = os.path.join(_output_dir, "markdown", f"{request_id}.md") with open(mkd_file_path, "w", encoding="utf-8") as f: f.write(markdown_texts) total_time = time.time() - start_time logger.info(f"✅ 版面解析请求完成 - RequestID: {request_id},总耗时: {total_time:.2f}秒") return JSONResponse(content={ "code": 200, "message": "解析成功!", "request_id": request_id, "file_type": file_type, "markdown_texts": markdown_texts, "data": data }) except Exception as e: error_time = time.time() - start_time logger.error(f"❌ 版面解析失败 - RequestID: {request_id}, 错误: {str(e)}, 耗时: {error_time:.2f}秒") return JSONResponse( status_code=500, content={ "code": 500, "message": f"版面解析失败: {str(e)}", "request_id": request_id, "data": None } ) finally: # 清理临时文件 if temp_file_path and os.path.exists(temp_file_path): os.remove(temp_file_path) @app.get("/api/v1/health") async def health_check(): """健康检查接口""" return JSONResponse(content={ "code": 200, "message": "服务运行正常", "data": { "status": "healthy", "timestamp": datetime.now().isoformat(), "services": { "pp_structure_v3": "ready", "paddle_ocr": "ready" } } }) if __name__ == "__main__": parser = argparse.ArgumentParser(description="启动 PaddleOCR 服务器") parser.add_argument("--port", type=int, default=int(os.environ.get("PADDLEOCR_PORT", 2024)), help="服务端口号") args = parser.parse_args() logger.info(f"🚀 启动 PaddleOCR 服务器... 端口: {args.port}") uvicorn.run(app, host="0.0.0.0", port=args.port)
我改了下代码,可以ocr了,改动如下,你看看对你的有没有帮助 pipeline = PaddleOCRVL( vl_rec_backend="vllm-server", vl_rec_server_url="http://127.0.0.1:8118/v1", use_layout_detection=True, layout_detection_model_name="PP-DocLayoutV2", layout_detection_model_dir="/mnt2/paddle-ocr/PaddleOCR-VL/PaddleOCR-VL-0.9B/PP-DocLayoutV2", )
用PP-DocLayoutV2会导致页面排版出错,不是由上至下的顺序,不使用反而是正常的顺序(use_layout_detection=False)
用PP-DocLayoutV2会导致页面排版出错,不是由上至下的顺序,不使用反而是正常的顺序(use_layout_detection=False)
谢谢,
使用PP-StructureV3 同样报错如下:
(venv) root@notebook-1761361751-vafbo-86cf9dbff6-wsts5:~/PaddleOCR# python paddleocr_server.py No model hoster is available! Please check your network connection to one of the following model hosts: HuggingFace (https://huggingface.co), ModelScope (https://modelscope.cn), AIStudio (https://aistudio.baidu.com), or BOS (https://paddle-model-ecology.bj.bcebos.com). Otherwise, only local models can be used. 2025-10-25 01:42:37.252 | INFO | __main__:initialize_pipeline:99 - 🚀 Initializing PaddleOCR... 2025-10-25 01:42:37.252 | INFO | 123758:140357272642688 | __main__:initialize_pipeline:99 | {} - 🚀 Initializing PaddleOCR... Creating model: ('PP-LCNet_x1_0_doc_ori', None) No available model hosting platforms detected. Please check your network connection. Traceback (most recent call last): File "/root/PaddleOCR/paddleocr_server.py", line 110, in <module> pipeline, ocr = initialize_pipeline() ^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/paddleocr_server.py", line 101, in initialize_pipeline pipeline = PPStructureV3( ^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddleocr/_pipelines/pp_structurev3.py", line 137, in __init__ super().__init__(**kwargs) File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddleocr/_pipelines/base.py", line 67, in __init__ self.paddlex_pipeline = self._create_paddlex_pipeline() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddleocr/_pipelines/base.py", line 105, in _create_paddlex_pipeline return create_pipeline(config=self._merged_paddlex_config, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/__init__.py", line 167, in create_pipeline pipeline = BasePipeline.get(pipeline_name)( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/utils/deps.py", line 206, in _wrapper return old_init_func(self, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/_parallel.py", line 103, in __init__ self._pipeline = self._create_internal_pipeline(config, self.device) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/_parallel.py", line 158, in _create_internal_pipeline return self._pipeline_cls( ^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/layout_parsing/pipeline_v2.py", line 84, in __init__ self.inintial_predictor(config) File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/layout_parsing/pipeline_v2.py", line 134, in inintial_predictor self.doc_preprocessor_pipeline = self.create_pipeline( ^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/base.py", line 140, in create_pipeline pipeline = create_pipeline( ^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/__init__.py", line 167, in create_pipeline pipeline = BasePipeline.get(pipeline_name)( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/utils/deps.py", line 206, in _wrapper return old_init_func(self, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/_parallel.py", line 103, in __init__ self._pipeline = self._create_internal_pipeline(config, self.device) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/_parallel.py", line 158, in _create_internal_pipeline return self._pipeline_cls( ^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/doc_preprocessor/pipeline.py", line 69, in __init__ self.doc_ori_classify_model = self.create_model(doc_ori_classify_config) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/pipelines/base.py", line 106, in create_model model = create_predictor( ^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/models/__init__.py", line 76, in create_predictor model_dir = official_models[model_name] ~~~~~~~~~~~~~~~^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/utils/official_models.py", line 613, in __getitem__ return self._get_model_local_path(model_name) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/root/PaddleOCR/venv/lib/python3.12/site-packages/paddlex/inference/utils/official_models.py", line 589, in _get_model_local_path raise Exception(msg) Exception: No available model hosting platforms detected. Please check your network connection.模型我已经下载到本地了,为什么还会请求网络呢? 版本信息如下:
(venv) root@notebook-1761361751-vafbo-86cf9dbff6-wsts5:~/PaddleOCR# pip list | grep paddle paddleocr 3.3.0 paddlepaddle-gpu 3.2.0 paddlex 3.3.5 (venv) root@notebook-1761361751-vafbo-86cf9dbff6-wsts5:~/PaddleOCR# python3 --version Python 3.12.9代码如下:
import uvicorn import os import tempfile import time import json import sys import uuid import base64 import requests from io import BytesIO import argparse import re from datetime import datetime from typing import Optional, List, Union from pathlib import Path from loguru import logger from fastapi import FastAPI, File, UploadFile, HTTPException, Form, Request from fastapi.responses import JSONResponse, FileResponse, HTMLResponse from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from paddleocr import PPStructureV3, PaddleOCR base_dir = os.path.dirname(os.path.abspath(__file__)) results_dir = os.path.join(base_dir, "results") log_dir = os.path.join(base_dir, "logs") # 创建必要的目录 os.makedirs(log_dir, exist_ok=True) os.makedirs(results_dir, exist_ok=True) # 控制台日志 - 彩色输出,包含所有级别的详细信息 logger.add( sys.stdout, format="<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | <level>{level: <8}</level> | <magenta>{process}</magenta>:<magenta>{thread}</magenta> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> | <blue>{extra}</blue> - <level>{message}</level>", level="DEBUG", colorize=True, backtrace=True, diagnose=True ) # 详细文件日志 - 包含所有级别和完整上下文信息 logger.add( f"{log_dir}/debug_{{time:YYYY-MM-DD}}.log", format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | PID:{process} | Thread:{thread} | {name}:{function}:{line} | Extra:{extra} | {message}", level="DEBUG", rotation="1 day", retention="30 days", compression="zip", backtrace=True, diagnose=True ) # 普通文件日志 - INFO及以上级别 logger.add( f"{log_dir}/info_{{time:YYYY-MM-DD}}.log", format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {name}:{function}:{line} | {message}", level="INFO", rotation="1 day", retention="30 days", compression="zip" ) # 错误日志单独文件 - 包含完整的错误追踪信息 logger.add( f"{log_dir}/error_{{time:YYYY-MM-DD}}.log", format="{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | PID:{process} | Thread:{thread} | {name}:{function}:{line} | {message}", level="ERROR", rotation="1 day", retention="30 days", compression="zip", backtrace=True, diagnose=True ) app = FastAPI( title="PaddleOCR V3 API", description="基于 PaddleOCR V3 的文档结构化识别服务", version="1.0.0", docs_url="/api/v1/docs", redoc_url="/api/v1/redoc", openapi_url="/api/v1/openapi.json" ) # 添加 CORS 中间件 app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # 挂载 results 目录为静态资源 app.mount("/results", StaticFiles(directory=results_dir), name="results") def initialize_pipeline(): logger.info("🚀 Initializing PaddleOCR...") start_load = time.time() pipeline = PPStructureV3( device="gpu:0", ) end_load = time.time() logger.info(f"🚀 PaddleOCR initialized PPStructureV3 in {end_load - start_load:.2f} seconds") logger.info("✅ PaddleOCR initialized successfully!") return pipeline, ocr pipeline, ocr = initialize_pipeline() @app.get("/") def root(): """根路径 - 服务状态检查""" logger.info("📊 Root endpoint accessed") return { "service": "PaddleOCR API", "status": "running", "version": "1.0.0", "timestamp": datetime.now().isoformat(), "current_time_utc": datetime.now(), "documentation": { "swagger_ui": "/api/v1/docs", "redoc": "/api/v1/redoc", "openapi_json": "/api/v1/openapi.json" }, "endpoints": { "ocr": "/api/v1/ocr", "health": "/api/v1/health" } } def process_parsing_result(res, _output_dir,use_ocr =False): json_dir = os.path.join(_output_dir, "json") md_dir = os.path.join(_output_dir, "markdown") res.save_to_json(save_path=json_dir) ## 保存当前图像的结构化json结果 if not use_ocr: res.save_to_markdown(save_path=md_dir) ## 保存当前图像的markdown结果 md_info = res.markdown md_data = md_info.copy() images_list = list(md_data.get("markdown_images").keys()) md_data.pop("markdown_images") else: md_data = "" images_list = [] md_info = "" logger.info(res.print()) json_data = res.json.get("res").copy() json_data.pop("model_settings") json_data.pop("input_path") return json_data, md_data, images_list, md_info def save_base64_to_file(input_str, output_dir, suffix, request_id): """ 将base64字符串保存为文件,写入到指定目录output_dir,文件名使用request_id,返回文件路径。 """ import os import base64 import re os.makedirs(output_dir, exist_ok=True) base64_pattern = r"^data:.*?;base64,(.*)" match = re.match(base64_pattern, input_str) if match: file_data = base64.b64decode(match.group(1)) else: try: file_data = base64.b64decode(input_str) except Exception: file_data = None if file_data: file_name = f"{request_id}.{suffix}" file_path = os.path.join(output_dir, file_name) with open(file_path, "wb") as f: f.write(file_data) return file_path return None @app.post("/api/v1/layout-parsing") async def layout_parsing(request: Request): # 先尝试解析 form form = await request.form() if "input_file" in form and "file_type" in form: input_file = form["input_file"] file_type = int(form["file_type"]) else: # 再尝试解析 json body = await request.json() input_file = body.get("input_file") file_type = int(body.get("file_type", 2)) # 后续逻辑不变 start_time = time.time() request_id = uuid.uuid4().hex logger.info(f"📝 layout-parsing请求 - RequestID: {request_id}") suffix = 'pdf' if file_type == 1 else 'png' _output_dir = os.path.join(results_dir, request_id) data = list() markdown_texts = "" temp_file_path = None try: # 处理 base64 字符串输入 if isinstance(input_file, str): temp_file_path = save_base64_to_file(input_file, _output_dir, suffix, request_id) if temp_file_path: input_file = temp_file_path output = pipeline.predict(input_file) if suffix == "image": res = output[0] json_data, md_data, images_list, _ = process_parsing_result(res, _output_dir) markdown_texts = md_data detail = { "json": json_data, "markdown": md_data, "images": images_list, "page_num": 0, } data.append(detail) else: markdown_list = [] for res in output: json_data, md_data, images_list, md_info = process_parsing_result(res, _output_dir) page_num = json_data.get("page_index") markdown_list.append(md_info) detail = { "json": json_data, "markdown": md_data, "images": images_list, "page_num": page_num, } data.append(detail) markdown_texts = pipeline.concatenate_markdown_pages(markdown_list) mkd_file_path = os.path.join(_output_dir, "markdown", f"{request_id}.md") with open(mkd_file_path, "w", encoding="utf-8") as f: f.write(markdown_texts) total_time = time.time() - start_time logger.info(f"✅ 版面解析请求完成 - RequestID: {request_id},总耗时: {total_time:.2f}秒") return JSONResponse(content={ "code": 200, "message": "解析成功!", "request_id": request_id, "file_type": file_type, "markdown_texts": markdown_texts, "data": data }) except Exception as e: error_time = time.time() - start_time logger.error(f"❌ 版面解析失败 - RequestID: {request_id}, 错误: {str(e)}, 耗时: {error_time:.2f}秒") return JSONResponse( status_code=500, content={ "code": 500, "message": f"版面解析失败: {str(e)}", "request_id": request_id, "data": None } ) finally: # 清理临时文件 if temp_file_path and os.path.exists(temp_file_path): os.remove(temp_file_path) @app.get("/api/v1/health") async def health_check(): """健康检查接口""" return JSONResponse(content={ "code": 200, "message": "服务运行正常", "data": { "status": "healthy", "timestamp": datetime.now().isoformat(), "services": { "pp_structure_v3": "ready", "paddle_ocr": "ready" } } }) if __name__ == "__main__": parser = argparse.ArgumentParser(description="启动 PaddleOCR 服务器") parser.add_argument("--port", type=int, default=int(os.environ.get("PADDLEOCR_PORT", 2024)), help="服务端口号") args = parser.parse_args() logger.info(f"🚀 启动 PaddleOCR 服务器... 端口: {args.port}") uvicorn.run(app, host="0.0.0.0", port=args.port)我改了下代码,可以ocr了,改动如下,你看看对你的有没有帮助 pipeline = PaddleOCRVL( vl_rec_backend="vllm-server", vl_rec_server_url="http://127.0.0.1:8118/v1", use_layout_detection=True, layout_detection_model_name="PP-DocLayoutV2", layout_detection_model_dir="/mnt2/paddle-ocr/PaddleOCR-VL/PaddleOCR-VL-0.9B/PP-DocLayoutV2", )
谢谢你,可以跑起来了