ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

OCR+LLM:让大模型读取图片与扫描件中的文本

OCR+LLM:让大模型读取图片与扫描件中的文本 在开发大模型应用时很多人会遇到一个相似的尴尬场景客户给了一份扫描版合同、一页拍歪的纸质表格或者是一张截图里的报错日志——这些内容明明“看得到”却没办法直接复制粘贴给大模型。文本被锁死在图片、PDF 扫描件和不可复制的文档里大模型只能“干瞪眼”。这个问题的解决路径就是 OCR光学字符识别。把 OCR 和大语言模型LLM串起来让文档先变成可检索、可喂给模型的文本再去做问答、摘要、信息抽取和知识库构建才能打通“纸质/图片数据 → 结构化文本 → 智能分析”的完整链路。本文将围绕“为 LLM 从不可复制文档中提取文本”这一主题讲解 OCR 的核心原理、Tesseract 与 PaddleOCR 两套实战方案、文本分块与 LLM 接口接入的完整代码以及实际项目中常见的坑和工程建议。如果你正在做 RAG 知识库、智能文档分析或者自动化流程这篇文章可以直接作为一套落地参考。1. 为什么 LLM 需要 OCR—— 一个被低估的前置环节1.1 OCR 是什么它解决什么问题OCR 的全称是 Optical Character Recognition也就是光学字符识别。它的目标很简单把图片、扫描件、PDF 里的文字区域识别出来转换成计算机可以编辑、检索和处理的纯文本。很多人会误以为 OCR 只是“截图提取文字”但它在企业级场景中的价值要大得多。想象一个知识库项目老合同、纸质档案、业务截图、手写备注都是非结构化数据。大模型再聪明也读不了没有文本层的 PDF 或一张 JPG 图片。OCR 就是那一层“翻译官”把视觉信息翻译成语言模型的输入。所以OCR 之于 LLM就像一个“眼睛”。没有这双眼睛大模型只能处理已经数字化、可复制的文本有了这双眼睛所有历史文档、纸质资料、异构图片都能变成可消费的文本数据。1.2 大模型场景下的 OCR 需求有了大模型之后OCR 的需求不仅没有消失反而被放大了。主要体现在四个场景RAG 知识库构建企业知识库里大量 PDF 扫描件需要用 OCR 提取文本后分块、向量化才能被检索并送进大模型回答。文档结构化抽取发票、身份证、合同、票据这类版式固定的文档需要先定位关键字段再抽取OCR 是前置步骤。Agent 自动化流程大模型 Agent 需要读取截图、自动处理表单、识别验证码同样依赖 OCR 能力。研究与信息整合从论文扫描件、历史文献、网页截图中提取引文和证据供 LLM 生成综述或分析。以前 OCR 的用途主要是“把文字提取出来让人看”现在 OCR 的用途变成了“把文字提取出来让机器读”。这意味着识别准确率、文本结构保留、批量处理能力、接口可编程性比以往任何时候都重要。1.3 常见 OCR 技术路线对比在选型前先了解当前主流的 OCR 技术路线方案代表工具优点缺点适用场景传统开源引擎Tesseract OCR离线可用、轻量、支持多语言、部署简单复杂版式中文字准确率一般中文需要训练语料支撑英文文档、简单排版、快速落地深度学习开源框架PaddleOCR、EasyOCR中文效果好、检测识别流程完善、支持版面分析模型体积大推理需要一定算力中文文档、复杂版面、批量场景云服务 API百度 OCR、腾讯 OCR精度高、持续迭代、开箱即用数据出域有隐私风险按量收费非敏感数据、快速原型商业私有化部署部分厂商私有化包数据安全精度较高成本高闭源银行、政务等敏感场景从上表可以看出没有“最好”的方案只有“最合适当下场景”的方案。本文的实战项目会以 Tesseract 和 PaddleOCR 两条路为例分别演示如何接入 LLM。2. 环境准备与版本说明本章给出一个可复现的环境准备过程。版本需要根据你的项目实际情况调整本文示例以常见环境为例重点演示配置思路。2.1 Python 环境准备建议使用 Python 3.9 及以上版本创建独立虚拟环境避免依赖冲突。mkdir ocr_llm_demo cd ocr_llm_demo python -m venv venv # Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate确认 Python 版本python --version2.2 安装 Tesseract OCR 引擎Tesseract 是一个开源的 OCR 引擎最初由 HP 实验室开发后来由 Google 维护。它支持超过 100 种语言适合英文和简单版式的文档。Windows 用户需要下载安装包并添加系统环境变量。macOS 用户可以直接使用 Homebrew# macOS brew install tesseract brew install tesseract-langLinux 用户使用 aptsudo apt update sudo apt install tesseract-ocr sudo apt install tesseract-ocr-chi-sim安装后用命令行验证tesseract --version如果希望识别简体中文需要确认中文语言包已安装tesseract --list-langs输出中应包含chi_sim如果没有需要单独下载语言包。接着安装 Python 调用库pip install pytesseract pillow openai langchain-text-splitters2.3 安装 PaddleOCR 依赖PaddleOCR 是百度开源的一个深度学习 OCR 工具库中文识别效果在开源方案中属于第一梯队。它的安装稍微复杂一点建议参考以下步骤pip install paddlepaddle pip install paddleocr如果你有 NVIDIA GPU 并且安装了 CUDA可以安装 GPU 版 PaddlePaddle推理速度会快很多。没有 GPU 也能跑只是速度慢一些。PaddleOCR 2.x 和 3.x 的接口有差异。下面示例以 PaddleOCR 3.x 为准如果你使用的是 2.x需要根据官方文档调整参数。3. 核心原理从图片到文本再到 LLM 的完整链路3.1 一个传统 OCR 引擎的识别流程以 Tesseract 为例传统 OCR 的流程通常包含四步图像预处理、版面分析、字符识别、后处理。图像预处理是关键中的关键。OCR 引擎对输入图像质量非常敏感倾斜、模糊、光照不均都会导致识别率大幅下降。常见的预处理操作包括灰度化、二值化、去噪、矫正倾斜、放大分辨率。这也是为什么我们会在实战代码里先做 OpenCV 预处理而不是直接调用 OCR。版面分析是把页面切分成不同区域——标题、段落、表格、图片等。Tesseract 有自己的版面分析算法但对于复杂表格和双栏论文它往往力不从心。字符识别则是在文本行级别做模式匹配。最后的后处理包括字典纠错、候选字符校验、输出格式化。这一步会直接影响 LLM 拿到的文本质量。3.2 深度学习 OCR检测与识别分离PaddleOCR 采用的是当前主流的两阶段深度学习方案文本检测Detection 文本识别Recognition。文本检测阶段模型定位图片中所有文本区域的位置输出文本框坐标。文本识别阶段对每个文本框内的图像内容进行字符识别转换成字符串。这种“先检测、后识别”的设计比传统的“整图识别”在处理复杂背景、倾斜文字、多语言混排时更有优势。PaddleOCR 还有一个独特的方向分类器Angle Classify会在检测之后自动判断文本方向解决图片旋转问题。这个细节让它处理手机拍照文档时的稳定性明显好于 Tesseract。3.3 把 OCR 文本喂给 LLM分块与 Prompt 设计OCR 提取出来的文本通常很粗糙直接丢给 LLM 会带来三个问题上下文太长超过模型限制、文本噪声干扰理解、无关区域页眉页脚、页码消耗 token。因此在 OCR 与 LLM 之间需要一层“中间处理”清洗去掉 OCR 产生的乱码、重复空格、页眉页脚。分块按照段落自然边界切分成 500-1000 字左右的 chunk便于向量化或按上下文窗口输入。构造 Prompt明确告诉 LLM 这是 OCR 识别结果可能包含识别误差要求它忽略噪声并完成指定任务。这一步直接决定了喂给 LLM 的数据质量。宁可多花一点代码做清洗也不要让模型“硬读”脏文本。4. 实战一Tesseract Python LLM API 提取不可复制文档4.1 项目结构我们做一个最小可运行的完整项目项目结构如下ocr_llm_demo/ ├── input/ │ └── document.png # 待识别的图片 ├── output/ │ └── extracted_text.txt ├── ocr_tesseract.py # Tesseract 识别脚本 ├── split_text.py # 文本分块脚本 ├── query_llm.py # 调用 LLM API 的脚本 └── requirements.txt4.2 图像预处理与 OCR先写一个函数读取图片做预处理然后调用 Tesseract 识别。# 文件路径ocr_tesseract.py import cv2 import pytesseract from PIL import Image def preprocess_image(image_path: str) - Image.Image: 图像预处理 1. 灰度化 2. 二值化 3. 降噪 适当的预处理可以显著提升 OCR 精度。 img cv2.imread(image_path) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 提高对比度 gray cv2.resize(gray, None, fx2, fy2, interpolationcv2.INTER_CUBIC) # 二值化 _, binary cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU) # 降噪 denoised cv2.medianBlur(binary, 3) return Image.fromarray(denoised) def ocr_image(image_path: str, lang: str chi_simeng) - str: 识别图片中的文字默认同时识别中英文。 image preprocess_image(image_path) text pytesseract.image_to_string(image, langlang) return text if __name__ __main__: text ocr_image(input/document.png) print(text) with open(output/extracted_text.txt, w, encodingutf-8) as f: f.write(text) print(识别完成文本已保存到 output/extracted_text.txt)代码解释preprocess_image把图片放大 2 倍是因为很多手机截图或扫描件的原始分辨率不够放大后字符边缘更清晰能明显提升识别率。二值化把灰度图转成黑白图减少背景干扰。image_to_string传入langchi_simeng表示同时启用中文简体与英文识别。4.3 文本清洗与分块OCR 识别结果往往会有多余的空行、空格和识别噪声。需要先清洗再按语义块切分。# 文件路径split_text.py import re def clean_ocr_text(raw_text: str) - str: 清洗 OCR 原始文本 - 去掉空行 - 合并多余空格 - 去掉页眉页脚中的常见噪声 lines raw_text.splitlines() cleaned_lines [] for line in lines: line line.strip() if not line: continue # OCR 常见的孤立字符噪声例如 |, _ 等 if len(line) 1 and not line.isalnum(): continue cleaned_lines.append(line) text \n.join(cleaned_lines) # 合并中文之间的空格OCR 有时会在中文字符间误加空格 text re.sub(r([\u4e00-\u9fa5])\s([\u4e00-\u9fa5]), r\1\2, text) return text def split_text_into_chunks(text: str, chunk_size: int 800, overlap: int 100) - list[str]: 按长度把文本切成多个 chunkchunk 之间保留重叠 避免语义信息在切分边界被切断。 text text.replace(\n, ) chunks [] start 0 while start len(text): end start chunk_size chunk text[start:end] if len(chunk) chunk_size // 2: break chunks.append(chunk) start end - overlap return chunks if __name__ __main__: with open(output/extracted_text.txt, r, encodingutf-8) as f: raw f.read() cleaned clean_ocr_text(raw) chunks split_text_into_chunks(cleaned) for i, chunk in enumerate(chunks): print(f Chunk {i} ) print(chunk) print()这里的分块逻辑虽然简单但足够用于演示。实际项目中建议使用 LangChain 的RecursiveCharacterTextSplitter它会更智能地按段落和句子边界切分。4.4 调用 LLM 接口分析文档清洗和分块之后就可以把文本块发送给大模型。下面是一个通用示例适配 OpenAI 兼容接口的各类大模型服务。# 文件路径query_llm.py import os from openai import OpenAI # 从环境变量读取配置避免把密钥写死在代码里 client OpenAI( api_keyos.getenv(LLM_API_KEY), base_urlos.getenv(LLM_BASE_URL, https://api.openai.com/v1), ) def query_llm(content: str, task_prompt: str ) - str: 把 OCR 文本块发送给大模型执行摘要或信息抽取。 system_prompt ( 你是文档分析助手。以下是 OCR 识别出的文本可能存在少量识别误差。 请忽略噪声根据用户要求完成分析任务。 ) user_prompt f{task_prompt}\n\n文档内容\n{content} response client.chat.completions.create( modelos.getenv(LLM_MODEL, gpt-4o-mini), messages[ {role: system, content: system_prompt}, {role: user, content: user_prompt}, ], temperature0.2, ) return response.choices[0].message.content if __name__ __main__: import split_text with open(output/extracted_text.txt, r, encodingutf-8) as f: raw f.read() cleaned split_text.clean_ocr_text(raw) # 只取前 800 字作为示例避免上下文超长 chunk cleaned[:800] result query_llm( chunk, task_prompt请提取这段文档中的核心信息包括日期、金额、参与方输出为 JSON 格式。, ) print(result)这个方案的关键点在于 Prompt 中明确告诉模型“这是 OCR 识别出的文本可能存在误差”。这个小小的提示能显著降低模型对识别噪声的“强行解释”提高输出可靠性。5. 实战二PaddleOCR 中文识别 本地 RAG 知识库5.1 为什么要用 PaddleOCR上一节用 Tesseract 完成了一个“最小闭环”但在真实业务中中文文档的识别精度往往是刚需。Tesseract 对排版清晰、字体标准的英文文档表现尚可遇到复杂排版的中文文档准确率会明显下降。PaddleOCR 的优势在于预训练模型对中文场景做了大量优化简体中文识别效果好。自带文本检测、方向分类、文本识别完整管线。支持版面分析能区分标题、正文、表格区域。可以离线运行数据不出内网。下面我们用 PaddleOCR 构建一个更适合中文场景的 RAG 知识库雏形。5.2 文档批量 OCR 入库# 文件路径paddle_ocr_rag.py import os from paddleocr import PaddleOCR # 第一次运行会自动下载模型建议提前确认网络连通性 ocr PaddleOCR(use_angle_clsTrue, langch) def ocr_image_to_text(image_path: str) - str: 使用 PaddleOCR 识别图片返回带换行的纯文本。 result ocr.ocr(image_path, clsTrue) lines [] for line in result: if line is None: continue # line 结构[ [box], (text, confidence) ] for item in line: text item[1][0] lines.append(text) return \n.join(lines) def batch_ocr_folder(folder_path: str, output_dir: str) - dict: 批量识别文件夹内所有图片结果写入 output_dir 下的 txt 文件。 os.makedirs(output_dir, exist_okTrue) mapping {} for filename in os.listdir(folder_path): if filename.lower().endswith((.png, .jpg, .jpeg)): image_path os.path.join(folder_path, filename) print(f正在识别{filename}) text ocr_image_to_text(image_path) save_path os.path.join(output_dir, filename .txt) with open(save_path, w, encodingutf-8) as f: f.write(text) mapping[filename] save_path print(f完成{save_path}) return mapping if __name__ __main__: mapping batch_ocr_folder(input, output) print(批量识别完成) print(mapping)运行示例python paddle_ocr_rag.py5.3 构建 RAG 检索与问答在 RAG 流程中OCR 只是第一步。后面的步骤是文本分块 → 向量化 → 存储向量 → 检索 → 构造 Prompt → LLM 回答。为了控制示例复杂度下面用一个轻量级方案演示——使用 TF-IDF 做本地检索不依赖外部向量数据库。# 文件路径local_rag.py import os import json import jieba from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity def load_documents(output_dir: str) - list[str]: 读取 output_dir 下所有 txt 文件返回文档列表。 docs [] for filename in os.listdir(output_dir): if filename.endswith(.txt): with open(os.path.join(output_dir, filename), r, encodingutf-8) as f: docs.append(f.read()) return docs def split_chunks(documents: list[str], chunk_size: int 500) - tuple[list[str], list[int]]: 对每个文档按长度切块返回 chunks 以及该 chunk 所属的文档索引。 chunks [] doc_ids [] for doc_idx, doc in enumerate(documents): for start in range(0, len(doc), chunk_size): chunk doc[start:start chunk_size] if len(chunk) 50: continue chunks.append(chunk) doc_ids.append(doc_idx) return chunks, doc_ids def build_retriever(chunks: list[str]): 使用 TF-IDF 构建检索器返回 vectorizer 和 tfidf 矩阵。 vectorizer TfidfVectorizer(tokenizerlambda text: jieba.lcut(text)) tfidf_matrix vectorizer.fit_transform(chunks) return vectorizer, tfidf_matrix def search(query: str, vectorizer, tfidf_matrix, chunks: list[str], top_k: int 3): 检索最相关的 chunk。 query_vec vectorizer.transform([query]) scores cosine_similarity(query_vec, tfidf_matrix).flatten() top_indices scores.argsort()[-top_k:][::-1] results [] for idx in top_indices: results.append({chunk: chunks[idx], score: float(scores[idx])}) return results if __name__ __main__: docs load_documents(output) chunks, doc_ids split_chunks(docs) print(f共加载 {len(docs)} 个文档切分为 {len(chunks)} 个片段) vectorizer, tfidf_matrix build_retriever(chunks) query 合同的签订日期是什么时候 results search(query, vectorizer, tfidf_matrix, chunks) for r in results: print(相关片段) print(r[chunk][:300]) print(相关度, r[score]) print(---)到这里你已经有了一个“文档不可复制 → OCR 提取文本 → 切块检索 → 输入 LLM 回答”的完整本地知识库雏形。6. 常见问题与排查思路在实际使用 OCR LLM 方案时最容易出问题的地方往往不是模型本身而是前面的数据和环境。这里整理一份高频问题排查表。问题现象常见原因解决思路tesseract 命令找不到Windows 未添加环境变量重新安装并配置 PATH重启终端中文识别全部是乱码未安装中文语言包安装 tesseract-ocr-chi-sim并确认tesseract --list-langs有 chi_sim识别精度低、错字多图片分辨率不足、光照不均提高图片分辨率预处理时放大图像必要时校正倾斜PaddleOCR 首次运行下载模型失败网络问题或模型源不可达手动下载模型并放置到指定目录或配置镜像源OCR 结果包含大量页眉页脚噪声文档排版复杂版面分析未生效增加清洗规则通过正则去掉页码、页眉重复行LLM API 调用超时文本太长或网络波动缩小 chunk_size增加超时设置或改用流式接口喂给 LLM 的内容包含乱码文件编码不统一统一使用 UTF-8 编码读写文件读取时指定 encodingutf-8中文分块后语义被切断按固定长度硬切使用按段落/句子边界切分的递归分块器GPU 环境下 PaddleOCR 报错CUDA 版本与 PaddlePaddle 不匹配检查 CUDA 版本安装对应版本的 PaddlePaddle一个重要的排查原则先用最简单的方式定位问题出在哪个环节。比如识别结果错了优先查看原始图片是否清晰、是否倾斜检索不到内容先确认 OCR 文本是否完整、chunk 切分是否合理LLM 回答质量差再检查 Prompt 是否明确。不要一上来就调模型参数。7. 最佳实践与工程建议7.1 OCR 管线设计在生产项目中建议把 OCR 过程拆成可独立升级的阶段而不是一个大函数图像采集与规范化统一输入格式做方向纠正、去畸变、清晰度过滤。文本检测定位文本区域。文本识别将文本区域转为字符串。结构化后处理字段对齐、表格还原、去重、纠错。输出与入库按业务需要输出 JSON、Markdown 或纯文本写入数据库或向量库。这样设计的好处是未来某个环节换成更好的模型时不会影响整条链路。比如从 Tesseract 换成 PaddleOCR只需要替换识别阶段不需要改动下游的清洗和入库逻辑。7.2 精度评估与数据标注如果只是 “跑通” 而不评估效果OCR 项目在落地时会充满不确定性。建议项目早期就建立评估集和标注流程。准备一批真实业务样本至少几十到几百张覆盖不同排版、不同清晰度。对每张图片人工标注正确的文本内容。使用 CER字符错误率或字段级精确率/召回率作为评估指标。字符错误率的计算方式def cer(reference: str, hypothesis: str) - float: 简单计算字符级编辑距离错误率。 实际生产环境建议使用编辑距离库如 python-Levenshtein。 import Levenshtein distance Levenshtein.distance(reference, hypothesis) return distance / max(len(reference), 1)有了评估集你在替换模型、调整预处理参数时才能用数据说话而不是靠感觉。7.3 安全合规与性能优化OCR 与 LLM 结合时数据安全是最容易被忽视的问题。一定要明确涉及个人隐私、商业机密、敏感合同的内容不要在未经授权的云服务上识别。优先选择本地部署的 OCR 方案本地 OCR 处理完后再决定是否将文本发送给大模型。安全边界建议图片不去重就上云先做文件哈希去重避免重复上传敏感数据。API 密钥硬编码使用环境变量或密钥管理服务。缺少审计日志记录 OCR 任务的文件名、操作人、时间、识别文本摘要便于追溯。文件路径拼接未校验用户上传的文件名需要做安全过滤避免路径穿越。性能优化方面以下几点在规模化场景中非常关键批量处理PaddleOCR 支持批量推理不要逐张图启动新进程。数据库缓存同一文件哈希出现时直接返回缓存结果避免重复 OCR。异步处理长文档识别可能耗时较长用消息队列如 Redis Queue做异步任务。模型量化如果部署在边缘设备如 RK3588 等 ARM 平台可以考虑把 OCR 模型做 INT8 量化减少内存占用提升推理速度。8. 总结与下一步建议本文从“文档不可复制无法直接喂给大模型”这个痛点出发完整演示了两条 OCR LLM 的落地路径第一条路径Tesseract Python LLM API适合英文文档和快速原型代码简单、依赖轻量。第二条路径PaddleOCR 本地 TF-IDF 检索适合中文文档和 RAG 知识库场景识别精度更高且可以离线运行。通过这两条路径你可以掌握图像预处理、OCR 调用、文本清洗、文本分块、LLM Prompt 构造、检索增强生成这几个核心环节。无论是做智能文档分析、企业知识库还是辅助 Agent 读取截图这套思路都可以复用。如果继续深入建议下一步关注三个方向一是用真实业务数据建立 OCR 评估集量化识别精度二是把文本检索从 TF-IDF 升级为向量检索如 FAISS 或 Milvus并结合 Embedding 模型提升召回效果三是在长文档场景中引入结构化版面分析让表格和标题层级在喂给 LLM 前得到更完整的还原。动手跑一遍上面的代码再拿自己手边的扫描件试试你会对“OCR LLM”这条链路有更直观的感受。
返回列表