本地用 Ollama + 视觉模型识别 PDF 技术教程
适用场景:在一台普通电脑(无独显、仅 CPU)上,本地运行 Ollama 与视觉大模型,识别/读取无水印的扫描型(图片型)PDF 内容。
一、方案概述
所谓"本地 PDF 识别",按 PDF 内部是否含有可提取文本层,分为两类,所需技术完全不同:
| 类型 | 特征 | 是否需视觉模型 |
|---|---|---|
| 有文本层 PDF | Word 另存、LaTeX、浏览器打印、电子书等导出,文字可复制 | 否,直接提取即可 |
| 图片型/扫描型 PDF | 扫描仪、拍照转 PDF,整页是图片 | 是,需 OCR/视觉模型 |
本教程聚焦第二类:当 PDF 没有文本层时,如何用本地视觉模型完成识别。
为什么选本地方案
- 数据不出本机,隐私性好;
- 一次部署,无 API 调用费用;
- 普通办公电脑即可运行小模型(3B 级别)。
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
二、技术栈与核心概念
| 概念 | 说明 |
|---|---|
| Ollama | 本地大模型运行框架,提供模型管理命令与 /api/chat 等 REST 接口 |
| 视觉模型(VLM) | 能理解图片的多模态模型,如 qwen2.5vl、llava-phi3、moondream 等 |
| GGUF | llama.cpp 的模型权重格式,Ollama 底层使用,可通过 Modelfile 直接导入 |
| Modelfile | Ollama 的模型定义文件(类似 Dockerfile),指定权重来源 FROM、对话模板 TEMPLATE、运行参数 PARAMETER |
| 视觉投影层(mmproj) | 将图片转换为视觉特征的部分。部分模型(如 llava)有独立投影文件;Qwen2.5-VL 系列则内置在权重中 |
| PDF 栅格化 | 将 PDF 页面渲染为 PNG/JPG 图片,供视觉模型读取 |
| base64 编码 | 把图片二进制转为文本,便于通过 JSON 传给模型接口 |
三、整体流程
图片型 PDF
│
├─ ① 判断是否已有文本层(PyMuPDF 检测)
│ 有 → 直接提取文本,流程结束
│
└─ 无 → 进入视觉识别流程:
② 用 PyMuPDF 将页面渲染为 PNG(建议 dpi 150--200)
③ 图片 base64 编码
④ 调用 Ollama /api/chat,附带图片与提示词
⑤ 解析返回文本,按页/块整理
四、环境准备
4.1 安装 Ollama
- 前往 Ollama 官网下载对应系统安装包,安装后服务默认监听
http://localhost:11434。 - 验证:
ollama --version与ollama list。
4.2 获取视觉模型
获取模型有两种途径:
途径一:官方仓库直接拉取(需能访问 Ollama 官方源)
bash
ollama pull qwen2.5vl:3b
途径二:手动下载 GGUF + ollama create(适用于网络受限环境)
当官方源不可达时,可从国内模型平台(如 ModelScope)下载对应 GGUF 文件,再编写 Modelfile 导入:
bash
# Modelfile 示例
FROM /path/to/Qwen2.5-VL-3B-Instruct-Q4_K_M.gguf
TEMPLATE """{{- if .System }}<|im_start|>system
{{ .System }}<|im_end|>
{{ end }}{{ if .Prompt }}<|im_start|>user
{{ .Prompt }}
{{- if .Images }}
{{- range .Images }}
{{ . }}
{{- end }}
{{- end }}
<|im_end|>
{{ end }}<|im_start|>assistant
"""
PARAMETER stop <|im_start|>
PARAMETER stop <|im_end|>
PARAMETER num_ctx 4096
PARAMETER num_gpu 0
bash
ollama create myvlm -f Modelfile
关键注意 :视觉模型必须配置正确的多模态对话模板(
TEMPLATE)。缺少模板时,图片 token 无法被正确投影,模型只会返回占位符(如[img-0])或空内容。模板中应使用{``{ range .Images }}占位,而非手写<|image_pad|>等原生 chat template 语法------后者属于 llama.cpp 原生格式,Ollama 的 Go 模板引擎不兼容。
4.3 Python 依赖
bash
pip install pymupdf requests
五、识别脚本示例
python
import fitz, base64, json, urllib.request, os
PDF_PATH = "document.pdf" # 待识别 PDF
MODEL = "myvlm" # Ollama 中的视觉模型名
DPI = 180
doc = fitz.open(PDF_PATH)
for page_idx in range(len(doc)):
page = doc[page_idx]
# ① 先尝试提取文本层
text = page.get_text().strip()
if text:
print(f"--- 第 {page_idx+1} 页(文本层)---\n{text}")
continue
# ② 无文本层 → 栅格化
pix = page.get_pixmap(dpi=DPI)
img_b64 = base64.b64encode(pix.tobytes("png")).decode()
# ③ 调用视觉模型
payload = {
"model": MODEL,
"messages": [
{"role": "system",
"content": "你是一个OCR助手。请对图片中的文字做OCR,按原文逐行输出所有可见文字。"},
{"role": "user",
"content": "请识别这张图片中的所有文字:",
"images": [img_b64]}
],
"stream": False,
"options": {"temperature": 0.1}
}
req = urllib.request.Request(
"http://localhost:11434/api/chat",
data=json.dumps(payload).encode(),
headers={"Content-Type": "application/json"}
)
resp = json.load(urllib.request.urlopen(req, timeout=900))
out = resp["message"]["content"].strip()
print(f"--- 第 {page_idx+1} 页(视觉识别,{len(out)} 字)---\n{out}")
doc.close()
六、提升识别质量的实践建议
- 先判文本层再识别:有文本层的 PDF 直接提取,速度快、零错误,避免无谓调用模型。
- 合理设置 dpi:过低(<120)小字模糊;过高(>250)图片过大、推理慢。150--200 是平衡点。
- 分块识别:整页密集中文容易让小模型漏字或幻觉。可先裁剪出单段文字区域,逐块送模型。
- 明确提示词:用 system 角色固定"OCR 助手"身份,要求"逐行输出原文",比开放式提问更准。
- 模型规模取舍:3B 模型单页 CPU 推理约 5--20 秒,适合简单清晰内容;密集教材/扫描件建议 7B 以上模型。
- 控制台乱码 :Windows 终端默认 GBK,建议脚本中用
print(repr(text))查看原始输出,或切到 UTF-8 编码。
七、常见问题排查
模型只回 [img-0] 或空内容
- 原因 :Modelfile 缺少多模态
TEMPLATE,视觉投影未生效。 - 解决 :补充 Qwen2-VL 风格模板后重新
ollama create。
ollama pull 连接官方源超时
- 原因 :网络无法访问
registry.ollama.ai或其底层存储(Cloudflare R2)。 - 解决 :改用国内模型平台的 GGUF 文件直链下载 + 手动
ollama create;国内镜像通常只代理目录元数据,不代理实际权重存储,pull 仍可能失败。
设置镜像环境变量后 pull 仍走官方源
- 原因 :部分 Ollama 旧版本不识别
OLLAMA_REGISTRY_MIRROR类变量名,pull 仍硬编码官方地址。 - 解决:不要依赖该变量,直接走手动下载 GGUF 的路径。
用 hosts 把官方域名指向镜像 IP 失败
- 原因 :TLS 证书校验不通过------镜像证书签的是自己域名,而非
registry.ollama.ai,握手报tls: internal error。 - 解决:此路不通,改用 GGUF 直链方案。
识别结果乱码
- 原因:终端编码非 UTF-8。
- 解决 :脚本内用
repr()打印,或chcp 65001。
小模型识别不准、出现幻觉
- 原因:3B 级别模型对密集中文 OCR 能力有限。
- 缓解:提高 dpi、分块识别、升级更大模型。
八、方案对比与选型
| 方案 | 优点 | 局限 | 适用 |
|---|---|---|---|
| 本地视觉模型(本教程) | 隐私好、零调用费、可离线 | 小模型准确率低、CPU 慢 | 普通电脑、轻量识别 |
| 本地传统 OCR(PaddleOCR/Tesseract) | 快、准(清晰扫描件)、轻量 | 需 Python 环境、复杂版面弱 | 批量、结构化程度高 |
| 云端视觉 API | 准确率高、无需本地算力 | 数据出网、有费用 | 高质量、偶发需求 |
九、小结
本地用 Ollama + 视觉模型识别 PDF 的完整链路是:判断文本层 → 栅格化页面 → base64 → 调用 /api/chat → 解析文本。该方案在普通 CPU 电脑上即可运行,3B 级别模型单页推理约 5--20 秒。成功的关键在于:① 正确获取并配置视觉模型(尤其是多模态模板);② 根据内容复杂度合理选择模型规模与分块策略。瓶颈通常不在电脑配置,而在模型获取渠道与模型能力本身。