EasyOCR实战:从安装到批量处理,离线OCR的省心之选 1. 为什么我最终把票投给了 easyocr做图像相关项目的朋友大概都有过这种经历手头有一批截图、扫描件或者手机拍的照片需要把里面的文字提取出来。第一反应可能是去找在线OCR服务传上去、等结果、复制下来偶尔还要担心文件隐私。用着用着就发现网络一卡、文件一大、格式一怪体验立刻断崖式下跌。更别提有些场景根本不允许把图片往外传比如内部资料整理、个人证件信息归档这类需求。我最早接触OCR是从一些老牌开源引擎开始的装起来倒是不难但识别中文的时候那个准确率实在让人挠头尤其是遇到倾斜、光照不均、字体花哨的图片出来的结果基本没法直接用。后来也试过一些云端API效果确实好但免费额度用完就得掏钱长期用下来成本不低而且每次调用都要走网络批量处理几百张图的时候那个等待时间相当煎熬。直到用上 easyocr才算找到了一个比较舒服的平衡点。它的定位很明确装起来简单、用起来简单、识别效果还过得去。底层是基于深度学习的文字检测和识别模型支持八十多种语言中文、英文、日文、韩文这些常见语种都在列而且不需要你手动做图像预处理扔进去一张图它自己会搞定检测框定位和文字识别两件事。对于我这种不想在环境配置上折腾太久、又希望有个稳定离线方案的人来说easyocr 基本就是那个“够用且省心”的选择。这篇文章我会从实际使用的角度出发把 easyocr 的安装、核心用法、参数调优、常见坑点以及一些进阶玩法都聊一遍。不管你是刚接触OCR的新手还是已经用过其他方案想换个工具试试的老手应该都能从中找到对自己有用的东西。文章里涉及的操作步骤和参数配置都是我反复实测之后总结出来的你可以直接照着抄作业。2. easyocr 的安装与首次运行比想象中少踩很多坑2.1 环境准备Python 版本和依赖管理easyocr 对 Python 版本的要求不算苛刻3.7 到 3.11 都能跑但我个人建议用 3.8 或 3.9这两个版本在各类深度学习库的兼容性上最稳。如果你用的是 3.11 以上某些依赖包的预编译轮子可能还没跟上装的时候容易卡在编译环节。安装方式有两种看你的使用场景来选pip 直接装适合快速上手一条命令搞定。源码安装适合需要改源码或者跟进最新特性的情况。绝大多数人用第一种就够了。打开终端执行pip install easyocr这条命令会自动把 torch、torchvision、opencv-python、Pillow 这些依赖一起拉下来。这里有个细节要注意easyocr 默认装的是 CPU 版的 torch。如果你有 NVIDIA 显卡并且想用 GPU 加速需要先去 pytorch 官网找到对应 CUDA 版本的安装命令手动装好 GPU 版 torch然后再装 easyocr。否则即使你有显卡easyocr 也只会用 CPU 跑速度差距大概在五到十倍左右。怎么判断自己装的是 CPU 版还是 GPU 版在 Python 里跑一行import torch print(torch.cuda.is_available())输出True就是 GPU 版可用False就是 CPU 版或者 CUDA 环境没配好。如果是False但你有显卡那就得重新装 torch。提示如果你在公司内网或者网络受限的环境下安装可能会遇到下载超时的问题。可以配置国内镜像源来加速比如清华源或者阿里源具体命令网上很多这里不展开。2.2 模型下载第一次运行时的“隐藏步骤”easyocr 装好之后第一次调用Reader的时候会自动下载检测模型和识别模型。检测模型负责找出图片里哪里有文字识别模型负责把文字内容读出来。这两个模型加起来大概一百多兆下载速度取决于你的网络环境。这里有个很多人会忽略的点模型下载是一次性的下载完之后会缓存在本地。默认路径在用户目录下的.EasyOCR/model文件夹里。如果你在多台机器上部署可以把下载好的模型文件拷贝过去省去重复下载的时间。具体路径可以用这段代码查import easyocr reader easyocr.Reader([ch_sim, en]) print(reader.model_storage_directory)输出就是模型存放的位置。把整个model文件夹复制到目标机器的对应位置就能跳过下载步骤。另外如果你在离线环境下工作也可以手动下载模型文件然后放到指定目录。模型文件的下载地址在 easyocr 的官方文档里有说明这里就不贴具体链接了需要的朋友可以自行查阅。2.3 跑通第一个例子从图片到文字的完整链路环境准备好之后来跑一个最简单的例子。假设你有一张名为test.png的图片里面有一段中英文混合的文字import easyocr reader easyocr.Reader([ch_sim, en]) result reader.readtext(test.png) for item in result: print(item)readtext返回的是一个列表每个元素是一个元组包含三个部分边界框坐标、识别出的文字、置信度。边界框是一个四个点的坐标列表文字就是识别结果置信度是一个 0 到 1 之间的浮点数越接近 1 表示模型越有把握。如果你只想拿文字内容可以这样写texts [item[1] for item in result] print(\n.join(texts))第一次运行的时候会看到终端里出现下载进度条等模型下载完识别结果就出来了。整个过程不需要你做任何图像预处理easyocr 内部会自动完成灰度化、尺寸调整、归一化这些操作。实测下来对于清晰的截图和扫描件easyocr 的中英文识别准确率相当不错基本能到 95% 以上。对于手机拍摄的照片如果光线均匀、文字没有明显倾斜效果也还可以。真正考验它的是那些模糊、倾斜、背景复杂的图片这个后面会专门讲怎么调优。3. Reader 对象的参数怎么调不是越多越好而是要配对3.1 语言参数一次加载多种语言的代价与收益Reader构造函数的第一个参数是语言列表。easyocr 支持的语言很多常见的有语言代码对应语言ch_sim简体中文ch_tra繁体中文en英文ja日文ko韩文fr法文de德文es西班牙文你可以同时传多个语言比如[ch_sim, en]表示同时识别简体中文和英文。这里有个重要的经验语言组合会影响识别速度和准确率。每多加载一种语言识别模型就会变大推理时间也会增加。而且某些语言之间的字符集有重叠组合不当反而会降低准确率。我的建议是只加载你实际需要的语言。如果你只处理中文文档就只传[ch_sim]不要顺手把英文也加上。中英文混合的场景确实需要同时加载两个但日文和韩文如果不同时出现就没必要一起加载。还有一个细节ch_sim和ch_tra不要同时加载。这两个模型的字符集高度重叠同时加载会导致模型在判断某个字是简体还是繁体时产生混淆实测准确率会下降。如果你的图片里简繁体都有优先选ch_sim它对繁体字的兼容性比想象中好。3.2 GPU 开关什么时候该开什么时候不该开Reader的gpu参数控制是否使用 GPU 加速。默认值是True但前提是你装了 GPU 版的 torch。如果你只有 CPU这个参数设成True也不会报错只是会自动回退到 CPU 模式但会多花一点检查时间。reader easyocr.Reader([ch_sim, en], gpuTrue)什么时候该开 GPU批量处理大量图片的时候。单张图片的识别时间在 CPU 上大概是 1 到 3 秒GPU 上可以降到 0.2 到 0.5 秒。如果你要处理几百上千张图这个差距就非常明显了。但如果你只是偶尔识别一两张图CPU 完全够用没必要折腾 GPU 环境。而且 GPU 模式下显存占用会随着图片尺寸增大而增加处理超大图的时候可能遇到显存不足的问题这时候反而要回退到 CPU 或者先把图片缩小。注意如果你在服务器上部署而服务器有多张显卡可以通过CUDA_VISIBLE_DEVICES环境变量指定用哪张卡避免和其他任务抢资源。3.3 其他实用参数verbose、model_storage_directory 等Reader还有几个参数值得关注verbose默认True会打印模型加载和下载的日志。在生产环境里建议设成False避免日志刷屏。model_storage_directory指定模型存放路径。多项目共用模型的时候很有用可以避免重复下载。download_enabled默认True如果设成False模型不存在时不会自动下载而是直接报错。离线部署的时候用得上。user_network_directory自定义识别网络的存放路径进阶用户才会用到。这些参数不需要一开始就全部搞懂用到的时候查一下就行。我的习惯是先把verboseFalse加上让输出干净一点。4. 识别效果不理想时我从哪些方向入手排查4.1 图片预处理的取舍easyocr 帮你做了哪些没做哪些easyocr 内部确实做了一些预处理比如把图片转成 RGB、调整尺寸、归一化像素值。但它不会帮你做去噪、二值化、纠偏这些操作。如果你的图片质量很差指望 easyocr 直接输出完美结果是不现实的。我遇到过几种典型的“识别效果差”场景图片分辨率太低文字只有十几个像素高模型根本看不清。这种情况再怎么调参数都没用只能换图或者放大图片。文字倾斜严重比如拍摄角度偏了三十度以上检测框会歪识别准确率骤降。背景复杂文字压在花纹、渐变、水印上面检测模型容易漏检或者框错位置。光照不均一半亮一半暗暗处的文字识别不出来。针对这些情况我的处理策略是先判断问题出在检测还是识别。如果检测框都没框对那问题在检测阶段需要做图像增强如果框对了但文字读错了那问题在识别阶段可以尝试调整识别参数或者换语言模型。一个简单的判断方法把readtext返回的边界框画到原图上看看框的位置准不准。如果框是准的那识别错误大概率是图片清晰度或者字体的问题。4.2 调整 text_threshold 和 low_text控制检测灵敏度的两个旋钮readtext方法支持传入一些检测相关的参数其中两个最常用text_threshold文字置信度阈值默认 0.7。调低会让更多区域被判定为文字适合文字比较淡的图片调高会过滤掉一些不确定的区域适合背景干扰多的图片。low_text低置信度文字的阈值默认 0.4。这个参数控制的是“边缘文字”的判定调低可以保留更多模糊文字但可能引入噪声。result reader.readtext(test.png, text_threshold0.6, low_text0.3)这两个参数需要配合使用。我的经验是如果漏检严重先把 text_threshold 降到 0.5 试试如果误检太多把它升到 0.8。low_text 一般不用动除非你处理的是手写体或者艺术字。还有一个参数叫link_threshold默认 0.4控制的是文字区域之间的连接。对于字间距很大的文本调低这个值有助于把同一行的文字连起来。但这个参数比较敏感调不好反而会把不该连的区域连在一起建议谨慎使用。4.3 处理大图时的分块策略避免显存溢出和精度下降easyocr 对输入图片的尺寸没有硬性限制但图片太大会导致两个问题显存占用飙升和小文字识别精度下降。因为模型在推理前会把图片缩放到固定尺寸如果原图很大缩放后小文字就变得模糊了。我的做法是如果图片宽度超过 2000 像素先切块再识别。切块的方式有两种按固定尺寸切比如每块 1000x1000块之间留 100 像素重叠避免文字被切断。按内容切先做一次粗略检测找到文字密集区域再针对这些区域切块。第一种方式实现简单适合大多数场景。第二种方式更精细但实现复杂度高适合对精度要求极高的场景。切块之后每块单独调用readtext最后把结果按坐标合并。合并的时候要注意去重因为重叠区域可能会被识别两次。import cv2 img cv2.imread(large_image.png) h, w img.shape[:2] block_size 1000 overlap 100 results [] for y in range(0, h, block_size - overlap): for x in range(0, w, block_size - overlap): block img[y:yblock_size, x:xblock_size] block_results reader.readtext(block) for bbox, text, conf in block_results: # 把局部坐标转成全局坐标 global_bbox [[p[0]x, p[1]y] for p in bbox] results.append((global_bbox, text, conf))这段代码只是示意实际用的时候还要处理边界情况和去重逻辑。但思路就是这样分而治之最后合并。5. 批量处理和性能优化从单张到上千张的实战经验5.1 批量识别的代码骨架循环、进度条和异常捕获单张识别跑通之后下一步就是批量处理。最朴素的写法就是一个 for 循环import os import easyocr reader easyocr.Reader([ch_sim, en], verboseFalse) image_dir ./images output_dir ./results os.makedirs(output_dir, exist_okTrue) for filename in os.listdir(image_dir): if not filename.lower().endswith((.png, .jpg, .jpeg, .bmp)): continue filepath os.path.join(image_dir, filename) try: result reader.readtext(filepath) texts [item[1] for item in result] with open(os.path.join(output_dir, filename .txt), w, encodingutf-8) as f: f.write(\n.join(texts)) except Exception as e: print(f处理 {filename} 时出错: {e})这个骨架够用但有几个地方可以优化进度显示用tqdm库加个进度条处理大量文件的时候心里有数。异常捕获单张图片出错不应该中断整个批次用 try-except 包起来记录出错的文件名事后单独处理。结果格式除了纯文本还可以把边界框和置信度一起保存成 JSON方便后续分析。5.2 多进程加速什么时候值得上什么时候反而更慢Python 的 GIL 限制了多线程的并行能力所以批量处理想加速一般用多进程。multiprocessing或者concurrent.futures.ProcessPoolExecutor都可以。但这里有个坑每个进程都会加载一份模型。如果你的模型是中文加英文一份大概占几百兆内存开四个进程就是一千多兆。内存不够的机器直接卡死。而且模型加载本身也要时间如果每个进程处理的任务很少加载模型的开销可能比识别本身还大。我的经验是只有当单批任务超过 50 张图片且机器内存充足至少 8GB 以上时才考虑多进程。否则单进程慢慢跑反而更稳。如果确实需要加速可以考虑把模型加载放在进程初始化阶段让每个进程只加载一次模型然后循环处理分配给它的任务。from concurrent.futures import ProcessPoolExecutor import easyocr def init_reader(): global reader reader easyocr.Reader([ch_sim, en], verboseFalse) def process_image(filepath): result reader.readtext(filepath) return [item[1] for item in result] # 使用 initializer 让每个进程只加载一次模型 with ProcessPoolExecutor(max_workers4, initializerinit_reader) as executor: futures [executor.submit(process_image, fp) for fp in file_list] for future in futures: print(future.result())这段代码的关键是initializer参数它确保每个子进程在开始处理任务之前先加载好模型避免重复加载的开销。5.3 内存和显存监控别让程序跑着跑着就崩了批量处理最怕的就是跑了一半程序崩了前面的结果全丢。除了做好异常捕获和断点续跑还要监控内存和显存的使用情况。在 Linux 上可以用htop或者nvidia-smi实时看。在 Python 里可以用psutil查内存用torch.cuda.memory_allocated()查显存。import psutil import torch print(f内存使用: {psutil.Process().memory_info().rss / 1024 / 1024:.1f} MB) if torch.cuda.is_available(): print(f显存使用: {torch.cuda.memory_allocated() / 1024 / 1024:.1f} MB)如果发现内存持续增长不释放可能是图片对象或者中间结果没有及时回收。可以在每处理完一批之后手动调用gc.collect()或者把处理逻辑封装成函数让局部变量自然释放。还有一个容易忽略的点OpenCV 读取的图片是 BGR 格式而 easyocr 期望的是 RGB 格式。如果你用cv2.imread读图然后直接传给readtext颜色通道是反的虽然大多数时候不影响识别但在某些颜色对比度低的图片上可能会导致检测失败。稳妥的做法是手动转换img cv2.imread(test.png) img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) result reader.readtext(img_rgb)或者直接用PIL读图它默认就是 RGB 格式。6. 那些文档里不会写的踩坑记录6.1 模型下载失败和缓存混乱的处理前面提到过模型会自动下载但实际用的时候可能会遇到下载失败的情况。常见原因有两个网络不稳定和缓存目录权限不对。如果下载到一半断了重新运行的时候 easyocr 会尝试续传但有时候缓存文件损坏了它会一直卡在那里。这时候需要手动把.EasyOCR/model目录下的临时文件删掉重新下载。还有一种情况是缓存目录混乱比如你先装了英文模型后来又装了中文模型两个模型的文件名可能有冲突。easyocr 的模型命名规则是按语言代码来的正常情况下不会冲突但如果你手动拷贝过模型文件就可能出现版本不匹配的问题。这时候最简单的办法是把整个model目录删掉让它重新下载。提示如果你在 Docker 容器里跑 easyocr记得把模型目录挂载到宿主机上否则每次重建容器都要重新下载模型浪费时间。6.2 中文识别中的标点符号和空格问题easyocr 识别中文的时候标点符号的处理有点意思。它会把中文标点识别成对应的全角符号比如逗号识别成“”句号识别成“。”这没问题。但有时候它会把英文标点也识别成全角比如把“.”识别成“。”这在处理中英文混合文本的时候会造成困扰。空格的处理也有类似问题。中文文本里通常没有空格但 easyocr 有时候会在汉字之间插入空格尤其是当字间距比较大的时候。如果你后续要做文本分析这些多余的空格需要清理掉。我的处理方式是识别结果先不做任何修改保存原始输出然后在后处理阶段根据具体需求做清洗。比如做中文分词之前先把连续空格替换成单个空格再把中文字符之间的空格去掉。这样既保留了原始信息又能得到干净的文本。6.3 竖排文字和特殊字体的识别表现easyocr 对横排文字的识别效果最好竖排文字也能识别但准确率会下降。如果你处理的是古籍或者某些特殊排版的文档竖排文字比较多可能需要额外的处理先把图片旋转 90 度让文字变成横排再识别。特殊字体方面印刷体基本没问题手写体就看情况了。工整的手写体识别率还行潦草的手写体基本没法看。艺术字、装饰性字体也是类似越接近标准印刷体识别效果越好。如果确实需要识别特殊字体可以考虑自己训练模型。easyocr 支持自定义识别网络但这属于进阶玩法需要准备标注数据、设计网络结构、训练调参投入的时间精力不小。对于大多数应用场景用预训练模型加上合理的预处理就够了。7. 把 easyocr 接入实际工作流的几种思路7.1 截图转文字日常办公场景的自动化我平时写东西的时候经常需要引用一些截图里的文字手动敲太慢用在线OCR又嫌麻烦。后来写了个小脚本监听剪贴板一旦发现新图片就自动识别并把文字写回剪贴板。这样截图之后直接粘贴就是文字效率提升很明显。实现思路是用Pillow的ImageGrab模块监听剪贴板或者用pyperclip配合系统剪贴板 API。识别部分就是调用 easyocr把结果拼接成字符串再写回剪贴板。这个脚本我用了大半年稳定性不错唯一需要注意的是识别大图的时候会有几秒延迟习惯就好。7.2 文档数字化扫描件批量转可编辑文本扫描件数字化的需求很常见比如把纸质合同、发票、报表转成可搜索的 PDF 或者纯文本。这个场景的特点是图片数量多、格式统一、对准确率要求高。我的做法是分三步走预处理、批量识别、后处理。预处理包括去噪、纠偏、二值化用 OpenCV 就能搞定。批量识别用前面讲的循环加异常捕获。后处理包括拼写检查、格式整理、关键信息提取。如果扫描件是表格识别出来的文字还需要按行列对齐。这个用 easyocr 的边界框坐标可以做根据 y 坐标判断行根据 x 坐标判断列然后重组表格结构。实现起来有点繁琐但逻辑是清晰的。7.3 和 PDF 处理库配合从 PDF 到结构化数据PDF 文件分两种文本型 PDF和扫描型 PDF。文本型 PDF 可以直接用pdfplumber或者PyPDF2提取文字不需要 OCR。扫描型 PDF 本质上是一堆图片需要先转成图片再识别。处理扫描型 PDF 的流程是用pdf2image把每页转成图片然后用 easyocr 识别最后把结果按页组织。pdf2image依赖poppler在 Linux 上需要先安装poppler-utils在 Windows 上需要下载 poppler 的二进制包并配置环境变量。from pdf2image import convert_from_path import easyocr reader easyocr.Reader([ch_sim, en], verboseFalse) pages convert_from_path(document.pdf, dpi300) all_text [] for i, page in enumerate(pages): result reader.readtext(page) page_text \n.join([item[1] for item in result]) all_text.append(f--- 第 {i1} 页 ---\n{page_text}) with open(output.txt, w, encodingutf-8) as f: f.write(\n\n.join(all_text))这里dpi300是个经验值太低会模糊太高会慢且占内存。300 对于大多数文档来说够用了。8. 关于 easyocr 的一些个人体会用 easyocr 这段时间最大的感受是它把 OCR 的门槛降到了很低。以前搞 OCR 要折腾环境、调参数、处理各种格式兼容问题现在几行代码就能跑起来而且效果还不差。对于个人项目和小型应用来说这个性价比非常高。当然它也不是万能的。如果你的场景对准确率要求极高比如金融票据识别、法律文书处理那可能还是需要商业 API 或者自己训练模型。但对于日常的截图转文字、文档数字化、图片信息提取这些需求easyocr 完全够用。还有一个我觉得很实用的点easyocr 的返回结果包含置信度和边界框这意味着你可以根据置信度做过滤只保留高置信度的结果或者根据边界框做版面分析。这个灵活性是很多在线 OCR 服务不具备的。最后分享一个小技巧如果你发现某个语言的识别效果不理想可以试试把图片放大两倍再识别。easyocr 内部会做尺寸调整但有时候原图太小放大之后反而能提升检测和识别的准确率。这个技巧在处理小字号的截图时特别管用。