很多开发者刚接手 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上,可以尝试以下“骚操作”:

  1. Canvas Size 限制 (canvas_size): EasyOCR 在检测阶段会把图片 resize。如果你的图片是 4K 的,但文字很大,没必要用默认的 2560。在 readtext 里设置 canvas_size=1280,检测速度能快 4 倍,代价是极小的文字可能漏检。
  2. 纯识别模式(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 的性能优化核心不在于“换模型”,而在于**“喂数据”的策略**:

  1. 确保 gpu=True
  2. 面对密集文本(文档),拉大 readtextbatch_size 参数。
  3. 面对大量小图(卡证字段),绕过 pipeline,直接构造 List 调 recognize