PaddleOCR icon indicating copy to clipboard operation
PaddleOCR copied to clipboard

使用官方文档中的python api 调用报错Exception: No available model hosting platforms detected. Please check your network connection

Open gck123 opened this issue 2 months ago • 4 comments

🔎 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)

gck123 avatar Oct 24 '25 08:10 gck123

使用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)

tao-xiaoxin avatar Oct 25 '25 08:10 tao-xiaoxin

使用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", )

gck123 avatar Oct 27 '25 02:10 gck123

用PP-DocLayoutV2会导致页面排版出错,不是由上至下的顺序,不使用反而是正常的顺序(use_layout_detection=False)

gck123 avatar Oct 27 '25 02:10 gck123

用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", )

谢谢你,可以跑起来了

tao-xiaoxin avatar Nov 03 '25 05:11 tao-xiaoxin