“AI开发-python-langchain框架--EasyOCR图片文字提取 英文 OCR 图片提取 文字识别 人工智能 大模型 集成 调用 工具”这一个标题串起来的信息量其实不小AI开发是目标python是语言langchain是编排框架EasyOCR则是具体的图片文字提取工具。看起来像是一个“用LangChain封装OCR能力”的技术实践但很多人在实际做的时候容易一头扎进某个单一环节比如只研究EasyOCR怎么识别得更准或者只盯着LangChain的API怎么调结果整条链路一跑起来就到处漏气。我自己在做类似项目时最大的感触是OCR本身是一个成熟但脆弱的环节成熟在于它已经高度工程化脆弱在于它一旦脱离真实场景比如模糊照片、倾斜扫描件、表格混排识别质量会断崖式下跌。而LangChain的价值不在识别而在编排——它把OCR从“一个孤立的脚本”变成“一个能被大模型理解和调用的能力单元”。这篇文章我就围绕“LangChain框架 EasyOCR 图片文字提取”这条主线把从环境搭建、工具封装、流程编排到问题排查的完整经验都摊开来讲。如果你正打算做文档问答、票据信息抽取、手写内容结构化或者只是想在LangChain项目里塞进一个离线可用的OCR能力这篇文章应该对胃口。我会把为什么要这么设计、每一步怎么落地、踩过的坑有哪些尽量讲清楚。1. 整体设计与思路拆解1.1 为什么是 EasyOCR 而不是其他 OCR 方案做图片文字提取可选的路子其实很多。最省事的是调云服务商的OCR接口识别质量高但对联网有依赖而且图片内容要送出去很多内部项目直接卡在这一关。纯开源的方案里Tesseract 是老牌选择但对中文、手写体、复杂排版的支持一直不太理想。PaddleOCR 的识别精度很高缺点是部署依赖较重PaddlePaddle 框架本身对 Python 环境和系统库有一定要求。我自己在项目里最终选了 EasyOCR核心原因有这么几个模型体积适中支持中文简繁、英文在内的几十种语言对一个内部工具来说完全够用。基于深度学习模型检测 识别两段式对倾斜文本、模糊字体、自然场景图片的适应性明显优于传统OCR。离线可用模型下载一次后完全本地推理数据不出内网这在自己的项目里是硬指标。Python API 设计直观读入图片直接返回文本和坐标信息方便做后处理。当然它不是万能药。如果你要处理的海量高清扫描件对精度要求极其苛刻可能还是 PaddleOCR 或商用方案更合适。如果只是随手截个图认个字EasyOCR 的性价比就非常突出了。1.2 LangChain 在整条链路里扮演什么角色这个项目叫“AI开发-python-langchain框架”那 LangChain 到底在这里解决什么问题很多人一开始会想不就是封装一个函数吗为什么要引入框架我换个角度解释如果你的OCR只是“输入图片、输出文字”的一次性需求那完全不必要LangChain。但一旦你希望让大模型参与后续处理——比如让模型理解票据内容、抽取关键字段、根据识别结果自动分类——就需要有一个标准化的对接方式。LangChain 在这里做的事情是把 EasyOCR 封装成一个“工具Tool”让大模型知道这个工具存在、能做什么、需要什么参数。通过 Agent 机制让模型自主决定“什么时候调用 OCR、调用完怎么处理结果”。把“OCR识别”和“大模型理解”串成一条可复用的链式流程后面换语言模型、调识别逻辑都方便。说得直白一点EasyOCR 解决“把图里的字变成文本”LangChain 解决“让大模型像一个会用工具的人那样在合适的时候使唤这个OCR”。一个管术一个管道。1.3 方案对比为什么适合自己的场景我在最初设计这个方案的时候其实纠结过两种架构思路A把OCR结果全部预处理成文本再交给LLM做问答或抽取。优点流程简单OCR一次跑完后面多次调用LLM都用同一份识别结果适合大批量离线处理。缺点OCR结果里的版面信息、位置信息会丢失如果识别有错后续LLM无法“看图纠错”。思路B把OCR封装成LangChain Tool让LLM按需调用甚至结合图片本身多次识别。优点灵活模型可以先看看图再决定要不要OCR、OCR哪个区域。识别质量和理解能力可以联动。缺点每次都要走模型调用链耗时更长成本更高更适合交互式、即时响应的场景。最终我采用了折中方案离线批量场景用思路A一次性跑完存成结构化文本在线问答场景用思路BLLM按需决定是否调用OCR工具。这个取舍后面会细讲。2. 核心细节解析EasyOCR 的关键参数与使用要点2.1 安装与环境准备EasyOCR 底层依赖 PyTorch所以安装的时候要注意版本匹配。我的环境是 Python 3.10 PyTorch 2.x安装命令如下pip install easyocr如果没有装 PyTorch它会默认拉一个 CPU 版本的 PyTorch 下来。如果你有 NVIDIA 显卡建议先去官网装对应 CUDA 版本的 PyTorch再装 EasyOCR否则后面跑推理时会白白浪费 GPU 能力。注意EasyOCR 第一次初始化时会自动下载检测模型和识别模型模型存在用户目录下的.EasyOCR文件夹里。如果所在环境访问外网受限记得提前下载好模型文件并放到对应目录否则代码会卡在下载阶段。2.2 初始化 Reader 的参数选择Reader 是 EasyOCR 的核心对象初始化看似简单其实参数选择很有讲究import easyocr reader easyocr.Reader( [ch_sim, en], gpuTrue, model_storage_directory./models, download_enabledTrue, verboseFalse )逐个参数说[ch_sim, en]指定识别语言。中文简体 英文是最常见的组合但要注意语言列表越长模型加载的显存/内存开销越大。如果明确知道图片只含中文就只写[ch_sim]。gpuTrue有 NVIDIA 显卡就开CPU 环境填False。实测同样一张 1920x1080 的图GPU 识别大约 1 秒CPU 可能要 5 到 8 秒差距非常大。model_storage_directory模型下载位置。建议固定到一个相对路径方便项目内统一管理。verboseFalse关闭推理过程中的进度条和日志输出更清爽日志文件也不会被刷爆。2.3 readtext 方法与核心参数readtext是核心方法它的返回值是一个列表每一项包含三部分文本框坐标四个角点的坐标列表识别文本内容置信度0-1 之间的浮点数基础调用方式result reader.readtext(sample.jpg) for box, text, confidence in result: print(f识别文本: {text}置信度: {confidence:.4f})难点在于readtext的参数调节。我踩过几次坑之后总结出几个高频参数的作用result reader.readtext( image_path, detail1, # 返回详细信息0 则只返回文本列表 paragraphTrue, # 自动组合段落避免同一行文字被切割 contrast_ths0.1, # 低对比度文本过滤阈值 adjust_contrast0.7, # 对低对比度区域做增强 width_ths0.7, # 文本行合并的宽度阈值 ycenter_ths0.5, # 垂直中心距离合并阈值 )paragraphTrue是我个人很推荐打开的开关。默认情况下EasyOCR 会把一行的多个片段返回成多个独立条目比如一段标题被拆成三个。打开段落模式后它会根据位置关系自动拼接对后续交给大模型理解非常有帮助。2.4 图片预处理比模型参数更影响效果这条经验在多个项目里反复被验证预处理对OCR效果的影响往往大于模型参数的调优。同样一张图片直接送进去识别率可能是80%稍微做一下预处理可能直接到95%。我常用的预处理流程是转灰度图高斯模糊降噪自适应阈值/二值化角度矫正deskewOpenCV 的完整预处理代码import cv2 import numpy as np def preprocess_image(image_path): img cv2.imread(image_path) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 降噪 denoised cv2.fastNlMeansDenoising(gray, h30) # 自适应二值化突出文字区域 binary cv2.adaptiveThreshold( denoised, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 15 ) # 保存为临时文件再用 EasyOCR 识别 tmp_path temp_preprocess.png cv2.imwrite(tmp_path, binary) return tmp_path处理后的图片文字区域对比度更高EasyOCR 识别时的误检率和漏检率都会明显下降。这里有个选择是保留原始彩色图片信息某些场景下颜色本身是特征比如印章还是一律走灰度。我的建议是一般票据、文档走灰度没问题涉及彩色标注的图要保留颜色只做降噪和缩放。2.5 输出后处理坐标信息是宝藏EasyOCR 返回的坐标信息很多人容易忽略。实际上它非常有用——你可以根据坐标重建表格结构、判断文字所属的版面区块甚至做区域OCR。我自己做过一个后续功能先把整张扫描件识别一遍拿到所有文本和坐标后再根据坐标把文本分组到“标题”“表格”“页脚”等区域这样后续给大模型的结构化数据就更准确了。坐标本身不复杂就是四个点# 左上、右上、右下、左下 四个顶点 top_left, top_right, bottom_right, bottom_left box x_center (top_left[0] bottom_right[0]) / 2 y_center (top_left[1] bottom_right[1]) / 2 width top_right[0] - top_left[0] height bottom_right[1] - top_left[1]拿到这些信息后可以自己实现简单的版面分析而不用一上来就上目标检测模型。3. 实操过程与核心环节实现LangChain 如何集成 EasyOCR3.1 LangChain 工具封装的基本思路LangChain 从早期版本到现在的版本API 经历了比较大的变化。我在最新版本中的实践方式是用tool装饰器把 EasyOCR 封装成可被 Agent 调用的工具。这里的关键点有两个工具必须有清晰的 description让大模型能判断“什么时候该用这个工具”。输入参数必须有明确的类型和说明最好用 Pydantic 模型来约束。一个基础的封装代码from langchain_core.tools import tool from pydantic import BaseModel, Field class OCRInput(BaseModel): image_path: str Field(description需要识别的图片文件路径) detail: int Field(default1, description是否返回详细坐标信息1或0) tool(easyocr_recognize, args_schemaOCRInput, description使用EasyOCR识别图片中的文字返回识别文本列表) def easyocr_recognize(image_path: str, detail: int 1): import easyocr reader easyocr.Reader([ch_sim, en], gpuTrue, verboseFalse) result reader.readtext(image_path, detaildetail) if detail 0: return \n.join(result) # 只返回文本部分坐标信息打印到日志 texts [item[1] for item in result] return \n.join(texts)这里有个细节值得注意不要在工具内部频繁初始化 Reader。EasyOCR 初始化需要加载模型一次加载后复用可以省去大量重复耗时。正确做法是在模块级别初始化一次或者用 LRU 缓存from functools import lru_cache lru_cache(maxsize1) def get_reader(): return easyocr.Reader([ch_sim, en], gpuTrue, verboseFalse)3.2 把工具接入 LangChain Agent封装好工具之后下一步是注入 Agent。我的做法是用 LangChain 的create_react_agent让大模型通过 ReAct 模式自主决定调用OCR的工具。from langchain_community.chat_models import ChatOpenAI from langchain_core.prompts import ChatPromptTemplate from langchain.agents import create_react_agent, AgentExecutor from langchain_core.tools import Tool llm ChatOpenAI(modelgpt-4o-mini, temperature0) tools [Tool( nameEasyOCR图片文字提取, funceasyocr_recognize, description当用户要求识别图片中的文字时使用。输入参数为图片路径返回图片中包含的文字内容。 )] prompt ChatPromptTemplate.from_messages([ (system, 你是一个文档处理助手可以调用OCR工具识别图片文字。), (human, {input}), (placeholder, {agent_scratchpad}), ]) agent create_react_agent(llm, tools, prompt) executor AgentExecutor(agentagent, toolstools, verboseTrue, max_iterations3) response executor.invoke({input: 帮我识别这张图里的文字./test_01.png}) print(response[output])在真实项目中我用的是一个国内开源模型做底座同样适用。LangChain 的设计目标就是模型无关切换后端只需要改llm实例。3.3 识别结果的二次理解让大模型抽取结构化信息工具封装只是第一步真正体现 LangChain 价值的是 OCR 结果如何与后续大模型逻辑衔接。拿一个实际场景举例——提取一张发票里的开票日期、金额、税号。OCR 识别出的文本是这样的增值税电子普通发票 发票代码011002200111 开票日期2025年03月18日 购买方名称某科技有限公司 纳税识别号91330101MA27XXXXX 货物名称软件服务费 价税合计12800.00 收款人张X直接在 prompt 里要求模型提取from langchain_core.output_parsers import StrOutputParser from langchain_core.prompts import PromptTemplate extract_prompt PromptTemplate.from_template( 从以下OCR识别结果中提取字段 - 发票代码 - 开票日期 - 购买方名称 - 纳税识别号 - 价税合计 识别结果 {ocr_text} ) chain extract_prompt | llm | StrOutputParser() result chain.invoke({ocr_text: ocr_text})这个方式的优势是如果 OCR 识别出的字段不完整可以把原始图片路径也传给大模型让模型先调 OCR 工具再抽字段形成一个动态闭环。这种多轮调用是纯规则脚本较难实现的。3.4 完整流程批量识别 结构化落库我做批量处理时的完整流程是遍历文件夹下所有图片对每张图做预处理灰度 降噪EasyOCR 识别文字和坐标合并为纯文本基于规则或大模型抽取结构化字段写入 JSON / CSV / 数据库核心代码片段import os import json from pathlib import Path def batch_ocr(folder_path, output_json): entries [] for img_path in Path(folder_path).glob(*.png): texts easyocr_recognize(str(img_path)) entries.append({ image_name: img_path.name, content: texts, extract_at: datetime.now().isoformat() }) with open(output_json, w, encodingutf-8) as f: json.dump(entries, f, ensure_asciiFalse, indent2) return entries这一步跑完后原始图片的信息就变成了结构化数据。我一般还会再补一步把content字段丢给模型做摘要形成“原文 - OCR结果 - 摘要”三层数据后续做检索或问答就更顺了。3.5 与 LangChain 文档检索链的融合进阶一点的用法是把 OCR 结果接入 RAG检索增强生成链路。传统的 RAG 直接切分文档文本但如果原始资料是图片扫描件就必须先 OCR 后切分。我目前的用法是把识别结果先存成 txt 或 markdown然后走文本加载器from langchain_community.document_loaders import TextLoader from langchain.text_splitter import CharacterTextSplitter from langchain_community.vectorstores import Chroma from langchain_openai import OpenAIEmbeddings loader TextLoader(ocr_output.txt, encodingutf-8) documents loader.load() text_splitter CharacterTextSplitter(chunk_size500, chunk_overlap50) texts text_splitter.split_documents(documents) vectorstore Chroma.from_documents(texts, embeddingOpenAIEmbeddings())这样就完成了从“图片”到“可检索的知识库”的链路。整个过程中 LangChain 的 Document / Loader / Splitter / VectorStore 抽象起了很好的串联作用。4. 常见问题与排查技巧实录4.1 EasyOCR 常见问题速查在多个环境里折腾 EasyOCR比较典型的问题有这么几类问题表现原因解决方案模型下载失败初始化卡在 Downloading 阶段网络受限手动下载模型放入~/.EasyOCR/model目录GPU 显存溢出识别大图时 CUDA OOM图片过大或模型占用显存压缩图片、分块识别、改用 CPU 模式CPU 识别太慢单图 5 秒以上没有GPU开启 GPU、降低输入分辨率、批量推理中文识别乱码输出连续乱码语言参数未包含 ch_simReader([ch_sim, en])识别结果重复同一文本出现两次paragraph 参数设置不当打开paragraphTrue并调整合并阈值小字漏识别表格的小号字体没结果图片分辨率不足放大图片 2-3 倍再识别倾斜文本识别差倾斜角度大时输出错乱模型对倾斜支持有限先做旋转矫正或启用文本方向检测4.2 LangChain Agent 调用工具失败的典型场景LangChain 集成最大的坑在于 Agent 的“工具选择”逻辑。经常遇到的情况是用户问“这个图片里写了什么”大模型不调 OCR 工具而是直接编了一段文字。这其实是模型在“幻觉”作答。解决方法是把工具的 description 写得非常明确并在 system prompt 里强调“只有调用工具才能获得图片内容”。Agent 反复调用工具超过迭代次数上限。因为 OCR 结果是文本模型拿到后还要继续加工可能导致多轮调用。要在AgentExecutor里调大max_iterations或拆分简化 prompt 让模型一次完成任务。工具参数传错比如把绝对路径传成相对路径导致 OCR 找不到文件。这种情况下可以对输入路径做一次标准化处理。4.3 踩过的坑图片预处理与坐标还原我印象最深的一次失误是批量处理历史扫描件时直接原图识别准确率只有七成左右。后来发现那批扫描件底色发黄、对比度低。加了一步灰度 自适应阈值后识别率明显上升。但要注意预处理后的图片如果保存成临时文件坐标信息依然是针对原图的不需要额外换算EasyOCR 返回的坐标基于传入图片的分辨率。如果中途用 OpenCV 缩放图片那坐标就会对应缩放后的图片存储时要注意。4.4 性能优化经验当图片数量上到几百上千张性能优化就很重要了。我常用的优化手段是多进程并行EasyOCR 是 CPU/GPU 密集任务Python 的 GIL 会限制线程并发效率。用multiprocessing按文件分片并行实测处理时间能压缩到原来的 1/4。限制图片尺寸对过大的长图先按比例缩放到宽不超过 2000px识别速度大幅提升精度损失可接受。复用 Reader 实例全局只初始化一次 Reader避免重复加载模型。缓存识别结果同一图片重复请求时直接返回缓存结果用文件哈希做 key避免重复计算。一个简单的多进程并行示例import multiprocessing def process_one(image_path): return image_path, easyocr_recognize(image_path) with multiprocessing.Pool(processes4) as pool: results pool.map(process_one, image_paths)需要注意每个子进程都会复制一份 Reader 模型内存占用会翻倍。如果内存紧张可以考虑用线程池配合 GPU 推理PyTorch 的 GPU 推理不会受 GIL 影响太严重。4.5 与大模型协同时的提示策略OCR 结果直接丢给大模型时可能会因为排版混乱、夹杂坐标信息而导致理解偏差。我的经验是先做一轮文本清洗再去喂模型去掉置信度低于 0.3 的识别结果按 y 坐标从上到下排序保证阅读顺序把同一行内的文本按 x 坐标合并清洗后的文本再送入大模型抽取和总结的准确率会提升很多。5. 扩展玩法这个方案还能往哪些方向延伸5.1 从单张图片到视频帧文字提取EasyOCR 同样可以处理视频抽帧后的图片。我做过一个实验输入一段录屏视频按帧率抽帧然后对每一帧做 OCR聚合后分析屏幕上出现的文字。这在自动化操作记录、短视频字幕提取等场景里都挺有用。代码逻辑不复杂import cv2 cap cv2.VideoCapture(demo_video.mp4) frame_count 0 while cap.isOpened(): ret, frame cap.read() if not ret: break if frame_count % 30 0: # 每秒抽1帧 tmp_file fframes/frame_{frame_count}.png cv2.imwrite(tmp_file, frame) text easyocr_recognize(tmp_file) if text.strip(): print(f第{frame_count}帧: {text}) frame_count 1 cap.release()注意抽帧后的图片要先做预处理不然识别率会差很多。5.2 结合向量数据库做“图片库问答”这个方向我自己觉得潜力很大。把一批历史合同扫描件OCR后攒成文本再建向量库就能实现“基于历史合同的问答系统”。问题可以是“2024年的合同里单价超过1万的采购有几笔”模型通过检索定位到相关文本片段再结合 OCR 工具在需要时重新识别原图。整套链路用 LangChain 的组织方式就是离线阶段OCR 所有图片 → 写入向量库在线阶段用户提问 → 检索文本片段 → LLM 生成答案信息不足时可回调 OCR 工具这套链路跑通之后最大的变化是以前需要人工逐页录入和整理的工作现在丢进一个文件夹就能自动变成结构化记录。5.3 自定义模型微调的边界有些场景下通用模型识别效果确实不够比如特定领域的专业符号、非常规手写字体。EasyOCR 支持自定义模型训练但是成本不低需要准备标注数据集。如果只是个别字符识别不准我更推荐做字典映射或用规则后处理兜底。比如识别“O0O”这种易混字符可以在后处理阶段做上下文纠正。通用模型 场景后处理覆盖 90% 的需求是完全够用的。6. 一些个人的实操心法做这个项目时最深的体会是OCR 工具选型和封装都不难真正的门槛在“工程化”。也就是怎么稳定地处理各种质量参差不齐的输入怎么控制内存和耗时怎么把结果变成下游系统真正能用的数据。这里把我的几条实操经验统一列一下供参考图片进 OCR 之前统一走一遍预处理管线灰度、降噪、尺寸归一化。这比你反复调模型的参数有效得多。Reader 实例全局复用不要在每次调用时重新初始化加载模型的耗时完全能省掉。永远不要忽视置信度。大批量识别时置信度低于阈值的文本单独归档做人工复核不要直接进业务库。LangChain 的工具描述要写清楚明确“输入是什么”“输出是什么”“何时使用”这决定了 Agent 在复杂任务里会不会正确调用你的工具。大批量任务优先用多进程但注意显存和内存的平衡别把所有进程都一股脑堆到同一块 GPU 上。另外有一个容易踩的坑就是 LangChain 的版本兼容。LangChain 迭代很快不同版本里Tool、AgentExecutor的导入路径可能不同。我建议你在项目里固定版本而且看官方文档时注意看当前版本的 API 说明。回到最初的话题用 LangChain 框架 EasyOCR 做图片文字提取本质上不是在做“更准的OCR”而是在搭一个“能自动调用OCR能力并理解识别结果的智能流程”。只有理解这一层你设计出来的方案才不是玩具而是能真正放进生产环境处理真实数据的东西。最后分享一个小技巧如果你需要识别的图片有一套固定的模板比如同一类型的发票可以先用一张模板图把固定区域位置存下来后续识别时直接按坐标裁剪出目标区域再单独识别。这样既能跳过无关区域的干扰又能大幅提升关键内容的识别准确率。我试过很多次这一招在票据处理场景下比任何模型参数调整都更管用。
阅读完成 · 觉得有帮助?