介绍
ocr 检测和识别模型训练完成之后,通过 api 脚本调用测试,发现文字识别的性能很差。
===== 准确率统计 ====================
类别 01: 260/273 = 95.24%
类别 02: 95/270 = 35.19%
类别 03: 280/374 = 74.87%
类别 04: 258/725 = 35.59%
类别 05: 205/343 = 59.77%
类别 06: 55/145 = 37.93%
类别 07: 97/319 = 30.41%
类别 08: 116/147 = 78.91%
类别 09: 94/130 = 72.31%
类别 10: 133/159 = 83.65%
目标类别总体准确率: 1593/2885 = 55.22%
但是用官方的 predict_system,py 脚本调用模型却没问题,准确率很高。立即想到是不是因为我在 api 调用过程中没有做数据增强预处理,于是决定对输入图像做图像增强。
一、对比度和亮度处理
cv2.convertScaleAbs()是 OpenCV 中用于图像对比度和亮度调整的函数,特别适合用于 OCR 预处理阶段增强文本的可读性。函数原型:
cv2.convertScaleAbs(src, alpha=1.0, beta=0.0)
- 输入:
src:输入图像,NumPy 数组,通常是 uint8 类型。
alpha:对比度调节系数,乘数。
beta:亮度调节系数,偏移量。 - 输出:
返回一个 uint8 类型的图像,所有像素值会被裁剪到 [0, 255] 范围内。
二、灰度化 + CLAHE
cv2.cvtColor()是 OpenCV 中用于颜色空间转换的核心函数,cv2.COLOR_BGR2GRAY是其最常用的转换,作用是将 BGR 图像转换成灰度图像。CLAHE 需要在单通道图像上操作,所以可以将灰度化后的灰度图像交给 CLAHE 处理。
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8))
enhanced = clahe.apply(gray)
image = cv2.cvtColor(enhanced, cv2.COLOR_GRAY2BGR)
cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8)) 创建了一个 CLAHE 对象,clipLimit 为对比度限制阈值,tileGridSize 为图像分块大小。enhanced = clahe.apply(gray) 将图像划分为 8x8 的网格,每个网格独立计算直方图,根据 clipLimit 裁剪并重新分配像素值,双线性插值消除块间边界。最后,image = cv2.cvtColor(enhanced, cv2.COLOR_GRAY2BGR) 转回 BGR 格式。
三、非锐化掩模 / 高斯模糊
# 非锐化掩模(Unsharp Mask)
kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]])
sharpened = cv2.filter2D(image, -1, kernel)
不同于锐化掩模使用高通滤波,原理是高频分量增强。强化中心像素,弱化周围像素,核中心值越大锐化越强。适合需要快速强锐化的场景,需注意噪声控制。
# 高斯核锐化
blurred = cv2.GaussianBlur(image, (0,0), 3)
sharpened = cv2.addWeighted(image, 1.5, blurred, -0.5, 0)
原理是用高斯核(σ=3)模糊原图,原图×1.5 - 模糊图×0.5,适合自然图像和精细调整。
四、动态二值化
# 自适应阈值(推荐高斯加权)
binary = cv2.adaptiveThreshold(
cv2.cvtColor(image, cv2.COLOR_BGR2GRAY),
255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY_INV,
blockSize=21,
C=8
)
image = cv2.cvtColor(binary, cv2.COLOR_GRAY2BGR)
用于处理光照不均图像,相比全局阈值能更好保留弱文本。
五、形态学处理
# 闭运算(先膨胀后腐蚀)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,2))
morphed = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernel)
作用:
- 连接相邻字符:如 “1” 断裂成两个部分时。
- 填充内部空隙:如 “8” 中间断裂。
- 平滑文本边缘:去除锯齿状边缘。
六、频域增强
# 傅里叶变换去噪
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
dft = cv2.dft(np.float32(gray), flags=cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
# 创建带阻滤波器(去除特定频率噪声)
rows, cols = gray.shape
crow, ccol = rows//2, cols//2
mask = np.ones((rows, cols, 2), np.uint8)
r = 30 # 阻带半径
center = [crow, ccol]
x, y = np.ogrid[:rows, :cols]
mask_area = (x - center[0])**2 + (y - center[1])**2 <= r*r
mask[mask_area] = 0
# 反变换
f_ishift = np.fft.ifftshift(dft_shift * mask)
img_back = cv2.idft(f_ishift)
img_back = cv2.magnitude(img_back[:,:,0], img_back[:,:,1])
img_back = cv2.normalize(img_back, None, 0, 255, cv2.NORM_MINMAX).astype(np.uint8)
实践
只做了对比度处理就发现效果很好了,image = cv2.convertScaleAbs(image, alpha=1.71, beta=0),发现在 alpha = 1.71 的时候准确率最高。