探索OCR的第二个方案:EasyOCR
一、EasyOCR简介
1.1 框架定位
EasyOCR是由Jaided AI团队开发的开源OCR引擎,基于PyTorch深度学习框架构建,支持80+种语言的文本识别,包含简体中文(ch_sim)、繁体中文(ch_tra)、英语(en)等主流语言。其核心优势在于:
- 多场景适应:支持自然场景文本、文档密集文本、手写体等多种类型
- 端到端流程:集成CRAFT检测模型+CRNN识别模型的完整解决方案
- 硬件加速:支持GPU加速推理(CUDA/MPS)与CPU模式
- 灵活扩展:允许用户自定义识别网络和模型存储路径
最新版本(v1.7.2)在中文识别场景下,对复杂排版和低分辨率图像的识别准确率提升至92.3%(ICDAR2019测试集)
1.2 技术架构演进
采用模块化设计,核心组件包括:
关键改进:
- 2023版引入动态分辨率适配,优化小文本检测
- 2024版新增光栅字符分割算法,提升粘连字符处理能力
- 2025版支持ONNX导出(期待狗头)
二、环境部署与基础使用
2.1 安装指南
推荐使用Python 3.8+环境:
# 安装PyTorch(根据CUDA版本选择)
pip install torch torchvision --extra-index-url https://download.pytorch.org/whl/cu121
# 安装EasyOCR核心包
pip install easyocr
# 验证安装
python -c "import easyocr; print(easyocr.__version__)"
2.2 中文识别基础示例
import easyocr
import numpy as np
import cv2
# 读取测试图像
img = cv2.imread('pic002.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 执行OCR识别
results = reader.readtext(img,
detail=1,
paragraph=True,
contrast_ths=0.3)
print(f"识别结果:{results}")
for item in results:
if len(item) < 3: # 处理置信度缺失情况
bbox, text = item[:2]
prob = 0.0
else:
bbox, text, prob = item
# 可视化(绘制多边形框)
pts = np.array(bbox, np.int32).reshape((-1, 1, 2))
cv2.polylines(img, [pts], True, (0, 255, 0), 2)
print(f"检测到文本: {text} | 置信度: {prob:.2f}")
cv2.imshow('result', img)
cv2.waitKey(0)
参数说明:
detail=1 返回完整元数据(坐标、置信度)
paragraph=True 自动合并段落文本
contrast_ths=0.3 增强低对比度文本处理
结果:
detail=0:识别结果:[‘五。中文识别’, ‘高级应用场景 5.1 结构化数据提取’]
detail=1:识别结果:[[[[0, 6], [110, 6], [110, 32], [0, 32]], ‘五。中文识别’], [[[9, 54], [176, 54], [176, 102], [9, 102]], ‘高级应用场景 5.1 结构化数据提取’]]
小结:几乎全对,就这已经比Tesseract强很多,但是速度较慢。
模型下载:
运行中如果没有模型是会自动下载的,如果网络不行,可以单独下载后,放入easyOCR的目录下。
官网下载
点我下载
Windows放置在:C:\Users\YOURUSERNAME.EasyOCR\model
三、高级功能与参数调优
3.1 分阶段处理流程
3.1.1 独立文本检测(detect方法)
仅执行文本检测(不进行识别),返回文本区域的坐标信息。
它的作用:
- 需要获取文本位置但无需识别内容
- 多阶段处理中的前置步骤(如区域过滤)
- 训练数据标注工具开发
# 仅执行文本检测
horizontal_boxes, free_boxes = reader.detect(img,
text_threshold=0.7,
low_text=0.4,
canvas_size=2560)
输出格式:
horizontal_boxes: [x_min, x_max, y_min, y_max]
free_boxes: [[x1,y1],[x2,y2],…] 多边形坐标
detect方法参数:
参数 | 类型 | 默认值 | 作用描述 |
---|---|---|---|
img | ndarray | 必填 | 输入图像(RGB格式) |
text_threshold | float | 0.7 | 文本区域置信度阈值(0-1) |
low_text | float | 0.4 | 低置信度文本保留阈值 |
canvas_size | int | 2560 | 图像缩放的最大边长(像素单位) |
格式说明:
- 表格采用标准的Markdown表格语法
- 表头与内容通过分隔线
|---|
明确区分 - 各列保持左对齐,确保可读性
- 默认值列突出显示关键数值参数
- 作用描述列使用中文说明并保持简洁
特殊符号说明:
ndarray
:表示NumPy数组类型float
/int
:标注参数数值类型- “必填”:强调该参数无默认值,必须显式传递
3.2 性能优化策略
参数 | 推荐值 | 作用 |
---|---|---|
batch_size | 8-16 | 增大批处理规模加速推理 |
workers | 4 | 多线程数据加载 |
model_storage | SSD路径 | 加速模型加载 |
optimal_num_chars | 根据场景 | 优先处理指定字符数的区域 |
典型GPU加速配置:
reader = easyocr.Reader(
['ch_sim'],
gpu=True,
model_storage_directory='/nvme_ssd/models',
download_enabled=False
)
3.3 车牌识别
1:车牌识别最简单方法
最简单做法
import cv2
import easyocr
reader = easyocr.Reader(
['ch_sim', 'en'],
gpu=False,
download_enabled=False
)
img = cv2.imread('pic005.jpg')
text_results = reader.readtext(
img,
allowlist='京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使警港澳0123456789ABCDEFGHJKLMNPQRSTUVWXYZ',
)
print("识别结果:", text_results)
2:车牌识别较复杂方案
为了让其称得上方案,增加了图片预处理、基于颜色特征检测候选区域、识别后处理清洗以及简单校验逻辑。
注意:基于颜色特征检测候选区域尚不完善,权当抛砖引玉。
import cv2
import numpy as np
import easyocr
import re
class CPUPlateRecognizer:
def __init__(self, model_dir="./models"):
self.reader = easyocr.Reader(
['ch_sim', 'en'],
gpu=False,
download_enabled=False,
model_storage_directory=model_dir,
user_network_directory=model_dir
)
# 车牌颜色阈值(HSV空间)
self.color_ranges = {
'green': ([35, 50, 50], [90, 255, 255]), # 新能源绿牌
'blue': ([100, 80, 50], [130, 255, 200]) # 传统蓝牌
}
def detect_color_region(self, img):
"""基于颜色特征检测候选区域"""
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
candidates = []
# 遍历颜色阈值
for color, (lower, upper) in self.color_ranges.items():
mask = cv2.inRange(hsv, np.array(lower), np.array(upper))
# 形态学处理
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel, iterations=2)
# 查找轮廓
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
x, y, w, h = cv2.boundingRect(cnt)
aspect_ratio = w / h
# 根据颜色筛选长宽比
if color == 'green' and 3.2 < aspect_ratio < 4.5:
# 对高做个补偿,需要动态修改
candidates.append((x, y - 50, x + w, y + h, color))
elif color == 'blue' and 3.5 < aspect_ratio < 4.8:
candidates.append((x, y, x + w, y + h, color))
return candidates
def preprocess_plate(self, plate_img):
"""车牌图像预处理"""
# 灰度化 + CLAHE增强
gray = cv2.cvtColor(plate_img, cv2.COLOR_BGR2GRAY)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
return clahe.apply(gray)
def recognize_plate(self, img_path):
"""车牌识别主流程"""
img = cv2.imread(img_path)
if img is None:
raise ValueError(f"图片加载失败: {img_path}")
# 检测候选区域
candidates = self.detect_color_region(img)
results = []
for (x1, y1, x2, y2, color) in candidates:
# 裁剪车牌区域
plate_roi = img[y1:y2, x1:x2]
# 预处理
processed = self.preprocess_plate(plate_roi)
cv2.imshow('processed', processed)
cv2.waitKey(0)
# OCR识别
text_results = self.reader.readtext(
processed,
decoder='beamsearch',
beamWidth=5,
batch_size=4,
contrast_ths=0.5,
text_threshold=0.7
)
print(text_results)
# 合并识别结果
plate_text = ' '.join([res[1] for res in text_results])
clean_text = self.post_process(plate_text, color)
if self.validate_plate(clean_text, color):
results.append({
'location': (x1, y1, x2, y2),
'color': color,
'text': clean_text
})
# 可视化标注
cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.putText(img, clean_text, (x1, y1 - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)
cv2.imwrite('result.jpg', img)
return results
def post_process(self, text, color):
"""后处理清洗"""
# 保留有效字符
cleaned = re.sub(r'[^A-Z0-9\u4e00-\u9fa5]', '', text.upper())
# 根据车牌类型调整长度
if color == 'green' and len(cleaned) > 8:
return cleaned[:8]
elif color == 'blue' and len(cleaned) > 7:
return cleaned[:7]
return cleaned
def validate_plate(self, text, color):
"""简单校验逻辑"""
patterns = {
'green': r'^[\u4e00-\u9fa5][A-Z][A-Z0-9]{6}$', # 例:粤A123456
'blue': r'^[\u4e00-\u9fa5][A-Z][A-Z0-9]{5}$' # 例:京A12345
}
return re.match(patterns.get(color, ''), text) is not None
if __name__ == "__main__":
recognizer = CPUPlateRecognizer(model_dir="./models")
# 执行识别
result = recognizer.recognize_plate("pic005.jpg")
print("识别结果:", result)