这是一个非常棒的决定。经过刚才的一系列“踩坑”,我们已经完全摸清了在 Windows 上部署 PaddleOCR 最稳妥的路径。
这份教程基于**“稳定性优先”**原则:放弃不稳定的开发版(V3),采用官方经过验证的 PaddleOCR 2.9.1 (PP-StructureV2),配合 Python 3.10 和 CUDA 11.8。这是目前工业界最通用的“黄金组合”。
核心目标:部署一个图形化工具,支持 PDF/图片转 Markdown/JSON,支持长文档流式处理,不卡顿。
前置工具:请确保已安装 Scoop 和 uv。
第一阶段:底层环境配置 (最容易出错的地方)
很多安装失败都是因为显卡驱动版本不对,或者系统里装了多个 Python 打架。我们先清理门户。
1. 清理旧环境 (重要)
打开 PowerShell,执行以下命令,把可能冲突的旧版全部卸载:
PowerShell
scoop uninstall cuda cudnn
# 检查是否有残留文件夹 D:\Scoop\apps\cuda...,有的话手动删掉
2. 安装 CUDA 11.8 (黄金版本)
PaddlePaddle 对 CUDA 11.8 的支持最成熟。不要装最新的 13.x。
PowerShell
# 1. 添加软件库
scoop bucket add versions
scoop bucket add extras
# 2. 搜索确切包名 (防止盲猜)
scoop search cuda
# 找到 versions bucket 下的 cuda11 (通常是 cuda11 或 cuda11-8)
# 3. 安装 CUDA 11.8
scoop install versions/cuda11
# 4. 验证驱动
# 关闭终端重新打开,输入:
nvcc -V
# 必须看到 "Release 11.8" 才算成功
3. 安装 cuDNN (手动挡最稳)
Scoop 的 cuDNN 自动安装脚本经常因为路径问题报错,强烈建议手动安装。
去 NVIDIA 官网 或找你之前 Scoop 缓存的 zip 包(
cudnn-windows-x86_64-8.x.x...zip)。解压后,你会看到
bin,include,lib三个文件夹。把这三个文件夹直接复制并覆盖到 CUDA 安装目录:
默认路径通常是:
C:\Users\用户名\scoop\apps\cuda11\current或者:
D:\Scoop\apps\cuda11\current
4. 安装 Poppler (PDF 必装)
PaddleOCR 不直接读 PDF,需要这个工具把 PDF 转图片。
PowerShell
scoop install poppler
第二阶段:Python 项目环境搭建 (uv)
为了避免 Python 3.14 等预览版捣乱,我们强制锁定 Python 3.10。
1. 初始化项目
找个地方新建文件夹,比如 D:\PaddleGUI,在里面打开终端:
PowerShell
uv init
2. 📝 修改“说明书” (关键避坑点)
用记事本打开生成的 pyproject.toml 文件。
把 requires-python = ">=3.14" (或类似的高版本)
修改为:
requires-python = ">=3.10"
保存关闭。如果不改这个,uv 会一直试图用最新版 Python 搞崩你的环境。
3. 创建虚拟环境
PowerShell
uv venv --python 3.10
第三阶段:安装核心依赖
这里我们要精准控制版本,防止自动升级到不兼容的版本。
PowerShell
# 1. 安装 PaddlePaddle GPU 版 (指定 11.8 源)
uv pip install paddlepaddle-gpu==3.0.0b1 -i https://www.paddlepaddle.org.cn/packages/stable/cu118/
# 2. 安装 PaddleOCR 稳定版 (锁定 2.9.1)
# 同时锁定 numpy 版本,防止 numpy 2.0 报错
uv pip install "paddleocr==2.9.1" "numpy<2.0"
# 3. 安装图形化界面和其他工具
uv pip install gradio opencv-python-headless pdf2image layoutparser
验证环境:
PowerShell
uv run --python 3.10 python -c "import paddle; print(paddle.utils.run_check())"
必须看到 PaddlePaddle is installed successfully! 且包含 device: 0 (GPU) 字样。
第四阶段:编写图形化代码 (App.py)
这是集大成之作。它包含了:
流式处理:300 页 PDF 也不会爆内存。
进度条:实时看到处理到第几页。
权限修复:防止 Gradio 报错
InvalidPathError。自定义路径:结果保存到你想要的地方。
请在文件夹下新建 app.py,粘贴以下内容:
Python
import gradio as gr
import os
import cv2
import json
import numpy as np
from paddleocr import PPStructure
from pdf2image import convert_from_path, pdfinfo_from_path
# --- 初始化引擎 (稳定版 V2) ---
# table=False: 除非你需要把表格还原成 Excel html,否则建议 False 提高速度
# recovery=True: 开启版面恢复,这是转 Markdown 的关键
engine = PPStructure(table=False, ocr=True, show_log=False, recovery=True, structure_version='PP-StructureV2')
def process_doc(file_obj, custom_output_dir, progress=gr.Progress()):
if file_obj is None:
return None, None, "❌ 请先上传文件"
# 1. 路径处理
if not custom_output_dir or custom_output_dir.strip() == "":
save_folder = "./output"
else:
save_folder = custom_output_dir.strip()
try:
os.makedirs(save_folder, exist_ok=True)
except Exception as e:
return None, None, f"❌ 路径无法创建: {str(e)}"
filename = file_obj.name
base_name = os.path.basename(filename).split('.')[0]
all_results = []
# 2. 核心逻辑:区分 PDF 和 图片
if filename.lower().endswith('.pdf'):
try:
# 获取 PDF 页数
info = pdfinfo_from_path(filename)
total_pages = int(info["Pages"])
print(f"检测到 PDF,共 {total_pages} 页")
# 逐页流式处理 (防爆内存)
for i in range(1, total_pages + 1):
progress((i / total_pages), desc=f"🚀 正在解析第 {i}/{total_pages} 页...")
# 只转换当前这一页
page_images = convert_from_path(filename, first_page=i, last_page=i)
if not page_images: continue
# 格式转换
img_np = np.array(page_images[0])
img_cv = cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR)
# 推理
result = engine(img_cv)
# 收集结果
for region in result:
region['page_id'] = i
if 'img' in region: del region['img']
all_results.append(region)
# 释放内存
del img_cv, img_np, page_images
except Exception as e:
return None, None, f"❌ PDF 处理失败: {str(e)}\n请检查是否安装了 poppler"
else:
# 单张图片处理
progress(0.1, desc="正在识别图片...")
img = cv2.imread(filename)
result = engine(img)
for region in result:
region['page_id'] = 1
if 'img' in region: del region['img']
all_results.append(region)
# 3. 导出文件
progress(0.95, desc="💾 正在保存...")
json_path = os.path.join(save_folder, f"{base_name}.json")
md_path = os.path.join(save_folder, f"{base_name}.md")
# JSON
serializable_result = []
for region in all_results:
text_lines = []
res = region.get('res', [])
if isinstance(res, list):
for line in res:
if isinstance(line, dict) and 'text' in line:
text_lines.append(line['text'])
serializable_result.append({
'page': region.get('page_id'),
'type': region.get('type'),
'bbox': region.get('bbox'),
'text': text_lines
})
with open(json_path, 'w', encoding='utf-8') as f:
json.dump(serializable_result, f, ensure_ascii=False, indent=2)
# Markdown
md_content = f"# {base_name}\n\n"
current_page = 0
for region in serializable_result:
if region['page'] != current_page:
current_page = region['page']
md_content += f"\n\n---\n**Page {current_page}**\n\n"
r_type = region['type']
texts = region['text']
if r_type == 'header':
md_content += f"## {' '.join(texts)}\n\n"
elif r_type == 'figure':
md_content += f"> [插图区域]\n\n"
elif r_type == 'table':
md_content += f"\n[表格区域]\n\n"
else:
md_content += f"{''.join(texts)}\n\n"
with open(md_path, 'w', encoding='utf-8') as f:
f.write(md_content)
return json_path, md_path, f"✅ 完成!\n📂 输出目录: {os.path.abspath(save_folder)}"
# --- 界面启动 ---
with gr.Blocks(title="PaddleOCR (稳定版)", theme=gr.themes.Soft()) as demo:
gr.Markdown("## 🚀 PaddleOCR 本地转换工具 (稳定版)")
with gr.Row():
with gr.Column():
input_file = gr.File(label="📄 上传文件", type="filepath")
output_dir = gr.Textbox(label="📂 输出路径 (留空默认 ./output)", placeholder="D:\\Result")
btn = gr.Button("开始转换", variant="primary")
with gr.Column():
log = gr.Textbox(label="日志")
out_json = gr.File(label="JSON")
out_md = gr.File(label="Markdown")
btn.click(process_doc, inputs=[input_file, output_dir], outputs=[out_json, out_md, log])
if __name__ == "__main__":
# 允许访问所有盘符,防止权限报错
allowed_paths = [f"{d}:\\" for d in "ABCDEFGHIJKLMNOPQRSTUVWXYZ"] + ["."]
demo.launch(inbrowser=True, allowed_paths=allowed_paths)
第五阶段:启动与运行
以后每次使用,只需要打开 PowerShell 进入该目录:
PowerShell
cd D:\PaddleGUI
uv run --python 3.10 app.py
🚨 常见避坑总结 (Retrospective)
CUDA 版本:千万别装 13.x,必须是 11.8。如果装错了,用
scoop uninstall cuda删干净再重装。cuDNN 缺失:Scoop 有时候只会装 CUDA 不装 cuDNN,导致报错
cudnn64_8.dll not found。手动复制是最稳的解法。Python 版本:uv 默认喜欢用最新的 Python (如 3.14),这会导致 Paddle 无法安装。必须在
pyproject.toml里锁死>=3.10并且运行命令时加上--python 3.10。PDF 报错:如果提示
PDF parsing failed,99% 是因为没装poppler。ImportError: PPStructure:如果报错找不到模块,通常是因为装了不稳定的开发版 (dev)。退回 2.9.1 稳定版即可解决。