很多开发者刚接手 EasyOCR 时,第一反应是:“这玩意儿怎么这么慢?”
在 CPU 上跑,一张图可能要几秒;换了 GPU,如果姿势不对,速度提升也不明显。这通常不是模型的问题,而是数据吞吐(Throughput)和显存利用率没调好。
EasyOCR 的底层是 PyTorch。作为工程师,我们要做的就是尽可能把 GPU 的 CUDA Core 喂饱,减少 CPU 和 GPU 之间的数据搬运(IO瓶颈)。
这里有三个从工程角度提升批量处理速度的实战技巧。
1. 硬件层面的“硬开关”
首先,别笑,很多人的代码跑在 GPU 服务器上,但实际还在用 CPU 跑推理。 EasyOCR 初始化时如果不显式指定 gpu=True,它有时候会默默地 fallback 到 CPU。
检查环境: 确保你装的是带 CUDA 支持的 PyTorch,而不是 CPU 只有版。
Bash
python -c "import torch; print(torch.cuda.is_available())"
# 必须输出 True,否则去 pytorch.org 重新装
2. 核心参数:batch_size 的误区与真相
这是优化的关键。 EasyOCR 的 readtext 函数有一个 batch_size 参数,默认值通常是 1。
误区:很多人以为这个 batch_size 是指“一次输入多少张图片”。 真相:EasyOCR 的工作流程是 检测(Detection)-> 裁剪(Crop)-> 识别(Recognition)。这个 batch_size 控制的是**第三步(识别)**的并发量。
如果一张图里有 100 个文本框(比如报表、合同):
batch_size=1:模型要跑 100 次推理。batch_size=64:模型可能只需要跑 2 次推理。
实战策略:对于文档类图片,直接把 batch_size 拉到 64 或 128,速度能翻倍。
3. 代码实战:构建高效的处理 Pipeline
单张处理是玩具,批量处理才是生产环境。 下面的代码展示了如何通过显式 GPU 调用 + 大 Batch Size + 内存预加载 来压榨性能。
Python
import easyocr
import os
import time
import cv2
import torch
# 1. 初始化 Reader (只做一次)
# gpu=True: 开启显卡加速
# quantize=False: 除非显存极度吃紧,否则建议关掉量化,FP16 (默认) 在现代 GPU (T4/3090) 上通常比 INT8 更快且精度更高
print("正在加载模型到 GPU...")
reader = easyocr.Reader(['ch_sim', 'en'], gpu=True, quantize=False)
def process_batch(image_folder, batch_size_recog=128):
"""
image_folder: 图片目录
batch_size_recog: 识别阶段的 batch size (关键参数)
"""
# 获取图片列表
img_files = [os.path.join(image_folder, f) for f in os.listdir(image_folder)
if f.lower().endswith(('.png', '.jpg', '.jpeg'))]
print(f"待处理图片数量: {len(img_files)}")
start_time = time.time()
results = []
# 2. 循环处理
# 注意:EasyOCR 的 readtext 目前主要支持单图入口。
# 虽然不能像 ResNet 那样直接 feed 一个 (N, C, H, W) 的 Tensor 进去做 Detection,
# 但我们可以通过优化 Recognition 的 batch 来提速。
for i, img_file in enumerate(img_files):
# 计时:单张图处理
t0 = time.time()
# 核心优化点:
# batch_size=batch_size_recog: 极大提升密集文本的识别速度
# workers=0: 在 Windows 上设为 0,Linux 上可以设为 2 或 4 (Dataloader worker)
# detail=0: 生产环境通常只需要文本,不需要坐标,减少数据处理开销
try:
res = reader.readtext(
img_file,
batch_size=batch_size_recog,
workers=4, # Linux下利用多核CPU预处理
detail=0 # 如果只需要文字,关掉detail能省点后处理时间
)
results.append((img_file, res))
except Exception as e:
print(f"Error processing {img_file}: {e}")
# 简单的进度打印
if i % 10 == 0:
print(f"Processed {i}/{len(img_files)} - Last image took {time.time()-t0:.3f}s")
total_time = time.time() - start_time
print(f"\n=== 性能统计 ===")
print(f"总耗时: {total_time:.2f}s")
print(f"平均每张耗时: {total_time/len(img_files):.3f}s")
print(f"FPS: {len(img_files)/total_time:.2f}")
if __name__ == "__main__":
# 构造一个测试目录
test_dir = "./invoice_images"
if not os.path.exists(test_dir):
print(f"请先创建 {test_dir} 并放入一些测试图片")
else:
# 建议在 T4 或 3060 以上显卡测试
# 将 batch_size 设为 64 或 128
process_batch(test_dir, batch_size_recog=128)
4. 进阶调优(给追求极致性能的人)
如果你跑完上面的代码觉得还不够快,瓶颈可能卡在显存带宽或者IO上,可以尝试以下“骚操作”:
- Canvas Size 限制 (
canvas_size): EasyOCR 在检测阶段会把图片 resize。如果你的图片是 4K 的,但文字很大,没必要用默认的2560。在readtext里设置canvas_size=1280,检测速度能快 4 倍,代价是极小的文字可能漏检。 - 纯识别模式(Recognition Only): 如果你的业务场景图片是切好的(比如只要识别验证码,或者已经切好的车牌),千万不要直接调
readtext。 直接调reader.recognize(img_list)。recognize方法允许你直接传入一个图片列表[img1, img2, img3...],这才是真正的 Batch Processing。这能完全跳过检测阶段,且完全并行。Python# 针对已切分好的小图(如车牌、验证码)的极速方案 import cv2 # 1. 读入多张图到内存 img_list = [cv2.imread(f) for f in file_paths] # 2. 一次性推几十张 # 这种方式能把 GPU 跑满 results = reader.recognize(img_list, batch_size=128)
总结
EasyOCR 的性能优化核心不在于“换模型”,而在于**“喂数据”的策略**:
- 确保
gpu=True。 - 面对密集文本(文档),拉大
readtext的batch_size参数。 - 面对大量小图(卡证字段),绕过 pipeline,直接构造 List 调
recognize。