在本文中,我们将介绍如何使用 TensorFlow 框架,结合 卷积神经网络(CNN) 和 递归神经网络(RNN)(具体是 CRNN,即卷积递归神经网络)实现验证码识别。验证码广泛用于防止自动化攻击,而通过深度学习技术,尤其是结合 CNN 和 RNN 的特性,我们可以构建一个高效的验证码识别系统。
- 环境准备
首先,确保已经安装了必要的 Python 库。可以使用以下命令来安装:
pip install tensorflow numpy opencv-python pillow
TensorFlow:用于构建和训练深度学习模型。
NumPy:用于数据处理。
OpenCV:用于图像处理。
Pillow:用于图像处理。
2. 数据准备与预处理
验证码图像通常由多个字符组成。为了让深度学习模型有效地处理这些字符图像,我们需要对图像进行预处理。这些步骤包括:灰度化、缩放、归一化以及字符标签编码。
(1) 数据集加载与预处理
假设验证码的每个图像文件名包含验证码的内容(如 AB12.png 对应标签 AB12)。我们将创建一个数据集类来加载这些图像并对其进行预处理。
import os
import cv2
import numpy as np
import tensorflow as tf
from tensorflow.keras.utils import Sequence
class CaptchaDataset(Sequence):
def init(self, image_dir, char_set, batch_size=32, image_size=(128, 64), sequence_length=4):
self.image_dir = image_dir
self.image_paths = [os.path.join(image_dir, fname) for fname in os.listdir(image_dir)]
self.char_set = char_set
self.batch_size = batch_size
self.image_size = image_size
self.sequence_length = sequence_length
def __len__(self):return len(self.image_paths) // self.batch_sizedef __getitem__(self, idx):batch_paths = self.image_paths[idx * self.batch_size:(idx + 1) * self.batch_size]images = []labels = []for path in batch_paths:# 读取图像并进行灰度化img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)img = cv2.resize(img, self.image_size)img = img.astype('float32') / 255.0 # 归一化处理# 获取标签:文件名作为标签label = os.path.basename(path).split('.')[0]label_encoded = [self.char_set.index(c) for c in label]# 填充标签,使其长度一致label_encoded = label_encoded + [0] * (self.sequence_length - len(label_encoded))images.append(img)labels.append(label_encoded)# 转换为numpy数组并返回return np.array(images).reshape(self.batch_size, *self.image_size, 1), np.array(labels)
字符集定义:包含大写字母和数字
char_set = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
初始化数据集
train_dataset = CaptchaDataset(image_dir="captcha_images", char_set=char_set, batch_size=32)
(2) 标签编码
验证码中的每个字符将通过索引转换为数字。例如,“A” 对应 0,“B” 对应 1,以此类推。为了统一标签长度,我们在短标签后面添加零填充。
- 构建 CRNN 模型
为了识别验证码中的字符,我们将构建一个 CRNN(卷积递归神经网络) 模型。CRNN 结合了 CNN 和 RNN(LSTM),CNN 用于提取图像的空间特征,RNN(LSTM)用于处理字符序列的时序依赖。
(1) 定义模型架构
from tensorflow.keras import layers, models
def build_crnn_model(input_shape=(64, 128, 1), num_classes=36, sequence_length=4):
input_img = layers.Input(shape=input_shape)
# 卷积层1
x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(input_img)
x = layers.MaxPooling2D((2, 2))(x)# 卷积层2
x = layers.Conv2D(64, (3, 3), activation='relu', padding='same')(x)
x = layers.MaxPooling2D((2, 2))(x)# 卷积层3
x = layers.Conv2D(128, (3, 3), activation='relu', padding='same')(x)
x = layers.MaxPooling2D((2, 2))(x)# 卷积层4
x = layers.Conv2D(256, (3, 3), activation='relu', padding='same')(x)# 转换为一维特征
x = layers.Reshape(target_shape=(-1, 256))(x)# LSTM 层:处理序列数据
x = layers.Bidirectional(layers.LSTM(128, return_sequences=True))(x)# 输出层
output = layers.Dense(num_classes, activation='softmax')(x)model = models.Model(inputs=input_img, outputs=output)return model
构建 CRNN 模型
model = build_crnn_model(input_shape=(64, 128, 1), num_classes=len(char_set), sequence_length=4)
编译模型
model.compile(optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])
model.summary()
(2) 模型架构解释
卷积层(Conv2D):提取图像的局部特征。
池化层(MaxPooling2D):减少图像尺寸,从而减小计算量。
LSTM层:用于学习字符的时序依赖,帮助识别验证码中的字符顺序。
输出层:每个时间步(字符)生成一个分类结果。
4. 模型训练
使用 交叉熵损失函数 和 Adam优化器 来训练模型。
(1) 训练代码
epochs = 10
model.fit(train_dataset, epochs=epochs, steps_per_epoch=len(train_dataset), verbose=1)
(2) 训练过程
在每个训练周期,模型会通过前向传播计算输出,之后通过反向传播更新权重。使用训练集来计算误差并优化模型参数。
- 模型评估与预测
训练完成后,我们可以使用测试集评估模型的性能,并对单个验证码图像进行预测。
(1) 评估模型
使用测试集评估模型
test_dataset = CaptchaDataset(image_dir="test_captcha_images", char_set=char_set, batch_size=32)
loss, accuracy = model.evaluate(test_dataset, steps=len(test_dataset), verbose=1)
print(f"Test Accuracy: {accuracy * 100:.2f}%")
(2) 进行预测
更多内容访问ttocr.com或联系1436423940
def predict_captcha(model, img_path, char_set, sequence_length=4):
img = cv2.imread(img_path, cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img, (128, 64))
img = img.astype('float32') / 255.0
img = np.expand_dims(img, axis=0) # 增加批次维度
img = np.expand_dims(img, axis=3) # 增加通道维度
# 预测
pred = model.predict(img)
pred_label = ''.join([char_set[np.argmax(pred[0][i])] for i in range(sequence_length)])return pred_label
测试预测
test_image_path = "captcha_images/test1.png"
predicted_label = predict_captcha(model, test_image_path, char_set)
print(f"Predicted CAPTCHA label: {predicted_label}")