验证码(CAPTCHA)广泛用于区分人类和自动化程序(如机器人),通常由扭曲的字母、数字或符号组成。为了实现验证码的自动识别,深度学习尤其是卷积神经网络(CNN)非常有效。本文将带你一起使用CNN构建一个验证码识别模型,并应用图像预处理技术来优化训练数据。
1. 预处理验证码图像
在训练模型之前,我们需要对图像进行一些预处理操作,以便让CNN能够更好地学习图像特征。以下是常见的图像预处理步骤:
- 自适应阈值化:根据图像周围的区域动态确定阈值,适合处理光照不均的图像。
- 形态学操作:如膨胀(Dilation)和腐蚀(Erosion),可以帮助去除噪声和连接断裂的字符。
- 高斯模糊:去除图像中的噪声,平滑图像。
1.1 自适应阈值化
自适应阈值化是通过计算图像中每个小区域的均值或加权均值来确定该区域的阈值。这使得我们能更好地处理亮度不均的图像。
import cv2
import matplotlib.pyplot as plt
path1 = './samples/23n88.png'
path2 = './samples/23mdg.png'
# 读取图像
img1 = cv2.imread(path1, cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread(path2, cv2.IMREAD_GRAYSCALE)
# 自适应阈值化
thresh_img1 = cv2.adaptiveThreshold(img1, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 145, 0)
thresh_img2 = cv2.adaptiveThreshold(img2, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 145, 0)
# 显示阈值化结果
plt.figure(figsize=(20,5))
plt.subplot(1, 2, 1)
plt.imshow(thresh_img1, cmap='gray')
plt.subplot(1, 2, 2)
plt.imshow(thresh_img2, cmap='gray')
plt.show()
1.2 形态学操作(闭运算)
闭运算是先进行膨胀操作,然后进行腐蚀操作,主要用于去除小的噪点并填补字符中的小空洞。
# 形态学操作:闭运算
close_img1 = cv2.morphologyEx(thresh_img1, cv2.MORPH_CLOSE, np.ones((5,2), np.uint8))
close_img2 = cv2.morphologyEx(thresh_img2, cv2.MORPH_CLOSE, np.ones((5,2), np.uint8))
# 显示闭运算后的结果
plt.figure(figsize=(20,5))
plt.subplot(1, 2, 1)
plt.imshow(close_img1, cmap='gray')
plt.subplot(1, 2, 2)
plt.imshow(close_img2, cmap='gray')
plt.show()
1.3 膨胀操作
膨胀操作通过使用一个结构化元素对图像进行扫描,选择局部区域的最大值,通常用于扩展白色区域。
# 膨胀操作
dilate_img1 = cv2.dilate(close_img1, np.ones((2,2), np.uint8), iterations=1)
dilate_img2 = cv2.dilate(close_img2, np.ones((2,2), np.uint8), iterations=1)
# 显示膨胀后的结果
plt.figure(figsize=(20,5))
plt.subplot(1, 2, 1)
plt.imshow(dilate_img1, cmap='gray')
plt.subplot(1, 2, 2)
plt.imshow(dilate_img2, cmap='gray')
plt.show()
1.4 高斯模糊
高斯模糊有助于去除图像中的噪点,使图像更加平滑。
# 高斯模糊
gauss_img1 = cv2.GaussianBlur(dilate_img1, (1,1), 0)
gauss_img2 = cv2.GaussianBlur(dilate_img2, (1,1), 0)
# 显示模糊后的结果
plt.figure(figsize=(20,5))
plt.subplot(1, 2, 1)
plt.imshow(gauss_img1, cmap='gray')
plt.subplot(1, 2, 2)
plt.imshow(gauss_img2, cmap='gray')
plt.show()
1.5 将图像切割成小块
由于验证码通常包含多个字符,我们需要将每个字符切割出来作为模型的输入。这里,我们将图像分割成多个小块。
# 切割图像
image_list1 = [gauss_img1[10:50, 30:50], gauss_img1[10:50, 50:70], gauss_img1[10:50, 70:90], gauss_img1[10:50, 90:110], gauss_img1[10:50, 110:130]]
image_list2 = [gauss_img2[10:50, 30:50], gauss_img2[10:50, 50:70], gauss_img2[10:50, 70:90], gauss_img2[10:50, 90:110], gauss_img2[10:50, 110:130]]
# 显示切割结果
plt.figure(figsize=(20,5))
for i in range(5):
plt.subplot(1, 5, i+1)
plt.imshow(image_list1[i], cmap='gray')
plt.show()
2. 构建卷积神经网络(CNN)
接下来,我们将使用卷积神经网络(CNN)进行字符识别。CNN对于图像分类任务非常有效,因为它能够自动提取图像特征。
from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, Flatten, Dense, BatchNormalization, Dropout
def cnn_model(input_shape, num_classes):
model = Sequential()
model.add(Conv2D(32, (3, 3), padding='same', activation='relu', input_shape=input_shape))
model.add(BatchNormalization())
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.2))
model.add(Conv2D(64, (3, 3), padding='same', activation='relu'))
model.add(BatchNormalization())
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.2))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))
model.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
return model
# 创建模型
model = cnn_model((40, 20, 1), 19) # 假设有19个类
model.summary()
3. 数据增强与过采样
3.1 SMOTE
SMOTE(合成少数类过采样技术)可以帮助我们增加少数类的样本量,以解决类别不平衡的问题。
from imblearn.over_sampling import SMOTE
# 假设 X_train 和 y_train 是训练集和标签
X_train = np.reshape(X_train, (4160, 40*20*1)) # 扁平化图像
X_train, y_train = SMOTE(sampling_strategy='auto', random_state=1).fit_resample(X_train, y_train)
X_train = np.reshape(X_train, (8037, 40, 20, 1)) # 恢复图像形状
3.2 使用 ImageDataGenerator 进行图像数据增强
数据增强可以生成更多样化的训练数据,从而帮助模型泛化能力的提升。
from keras.preprocessing.image import ImageDataGenerator
# 定义数据增强
traingen = ImageDataGenerator(rotation_range=5, width_shift_range=[-2, 2])
traingen.fit(X_train)
# 使用生成器
train_set = traingen.flow(X_train, y_train)
4. 模型训练与评估
我们可以使用Keras的ModelCheckpoint
和ReduceLROnPlateau
回调来保存最佳模型,并在训练过程中调整学习率。
from keras.callbacks import ModelCheckpoint, ReduceLROnPlateau
checkp = ModelCheckpoint('./captcha_model.h5', monitor='val_loss', save_best_only=True)
reduce = ReduceLROnPlateau(monitor='val_loss', patience=20, verbose=1)
history = model.fit(traingen.flow(X_train, y_train, batch_size=32
), validation_data=(X_test, y_test), epochs=10, callbacks=[checkp, reduce])
5. 测试与预测
训练完成后,我们可以用测试集进行评估,并通过model.predict()
进行预测。
# 加载最佳模型
model = load_model('./captcha_model.h5')
# 预测
pred = model.predict(X_test)
pred = np.argmax(pred, axis=1)
# 打印准确率和分类报告
from sklearn.metrics import accuracy_score, classification_report
print('Accuracy:', accuracy_score(y_test, pred))
print(classification_report(y_test, pred))
6. 测试样本
我们还可以使用训练好的模型对一些新的验证码样本进行预测。
def get_demo(img_path):
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
img = t_img(img)
img = c_img(img)
img = d_img(img)
img = b_img(img)
image_list = [img[10:50, 30:50], img[10:50, 50:70], img[10:50, 70:90], img[10:50, 90:110], img[10:50, 110:130]]
Xdemo = np.array([img_to_array(Image.fromarray(img)) for img in image_list]) / 255.0
ydemo = model.predict(Xdemo)
ydemo = np.argmax(ydemo, axis=1)
for res in ydemo:
print(info[res])
print(img_path[-9:])
在这里插入图片描述
总结
通过图像预处理技术、数据增强、以及卷积神经网络的结合,我们可以构建一个高效的验证码识别系统。在实际应用中,可以根据具体的验证码类型调整预处理和模型参数,以提高准确性和泛化能力。