1. 前言
在使用12306网站购买火车票时,我们常常会遇到图片验证码,一些涉及到用户身份识别的应用也经常使用图片验证码来保证安全性和防止机器人攻击。因此,如何识别这些验证码成为一个常见的问题。
本文将介绍如何使用Python来识别12306图片验证码上的物品。
2. 准备工作
2.1 安装Python库
本文所需的Python库有:numpy, scikit-learn, tensorflow
pip install numpy scikit-learn tensorflow
2.2 获取样本集
我们需要从12306验证码页面中获取样本图片,以进行后续的训练和测试。可以通过以下链接下载样本集:https://pan.baidu.com/s/1C1aw5XoRKwrHaB62QNIEsg,提取码为"1234"。
3. 图片处理
3.1 图片预处理
我们需要对图片进行预处理,使其适合进行机器学习。首先我们将图片转换为灰度图,并进行二值化处理。
from PIL import Image
def convert_image(image):
# 转换为灰度图片
image = image.convert("L")
# 二值化处理
threshold = 200
table = []
for i in range(256):
if i < threshold:
table.append(0)
else:
table.append(1)
image = image.point(table,"1")
return image
3.2 特征提取
我们使用笔画宽度变换(Stroke Width Transform,SWT)方法来进行特征提取。通过SWT方法,我们可以获取每个字符的特征点,并将这些特征点用于机器学习算法的分类。
import numpy as np
import cv2
def stroke_width_transform(image):
# Canny 边缘检测
edges = cv2.Canny(image,100,200)
# 膨胀
kernel = np.ones((3,3),np.uint8)
edges = cv2.dilate(edges,kernel,iterations = 1)
# 轮廓检测
contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
points = []
for cnt in contours:
x,y,w,h = cv2.boundingRect(cnt)
# 只保留大小合适的字符
if w >= 5 and w <= 30 and h >= 10 and h <= 40:
pts = []
# 计算SWT
for i in range(x, x+w):
for j in range(y, y+h):
if cv2.pointPolygonTest(cnt, (i,j), False) >= 0:
pts.append((i,j))
swts = compute_swt(image, pts)
median_swt = np.median(swts)
for p, swt in zip(pts, swts):
# 只保留SWT值符合中位数的特征点
if swt > 0 and swt < 4 * median_swt:
points.append(p)
return points
def compute_swt(image, points):
gradient_x = cv2.Sobel(image,cv2.CV_64F,1,0,ksize=3)
gradient_y = cv2.Sobel(image,cv2.CV_64F,0,1,ksize=3)
swts = []
for p in points:
gx = gradient_x[p[1], p[0]]
gy = gradient_y[p[1], p[0]]
if gx == 0:
swt = float('inf')
else:
swt = np.sqrt(gy * gy / (gx * gx))
swts.append(swt)
return swts
3.3 特征编码
将处理后的特征点编码为固定长度的特征向量,使用哈希函数映射到一维向量。
def encode_features(features):
# 取出一定数量的特征点
features = features[:30]
# 对特征点进行编码
angles = []
for i in range(len(features)):
for j in range(i+1, len(features)):
dx = features[j][0] - features[i][0]
dy = features[j][1] - features[i][1]
angle = np.arctan2(dy, dx)
angles.append(angle)
angles = np.array(angles)
# 对角度差进行排序
angles.sort()
# 计算角度差
diffs = []
for i in range(len(angles)-1):
diffs.append(angles[i+1] - angles[i])
diffs.append(2*np.pi - angles[-1] + angles[0])
diffs = np.array(diffs)
# 计算特征向量
features = []
for i in range(8):
start = i * 4
end = start + 4
feature = diffs[start:end]
feature = np.histogram(feature, bins=4, range=(0,2*np.pi))[0]
feature = feature.astype(np.float32)
feature /= feature.sum()
features.extend(feature)
features = np.array(features)
return features
4. 机器学习
4.1 数据集制作
将样本图片集处理为固定长度的特征向量,并保存到json文件中,用于后续的训练和测试。
import os
import json
data_path = "path/to/data"
sample_path = os.path.join(data_path, "sample")
features_path = os.path.join(data_path, "features")
if not os.path.exists(features_path):
os.makedirs(features_path)
for filename in os.listdir(sample_path):
if filename.endswith(".jpg"):
image_path = os.path.join(sample_path, filename)
image = Image.open(image_path)
image = convert_image(image)
points = stroke_width_transform(np.array(image))
features = encode_features(points)
features_path = os.path.join(features_path, filename.replace(".jpg", ".json"))
with open(features_path, "w") as f:
json.dump(features.tolist(), f)
4.2 分类器训练
使用线性支持向量机(Linear Support Vector Machine,LSVM)的分类器进行训练。
from sklearn.svm import LinearSVC
from sklearn.model_selection import train_test_split
import json
data_path = "path/to/data"
features_path = os.path.join(data_path, "features")
X = []
y = []
for filename in os.listdir(features_path):
if filename.endswith(".json"):
features_path = os.path.join(features_path, filename)
with open(features_path, "r") as f:
features = json.load(f)
# 将文件名转换为类别
label = filename[0]
X.append(features)
y.append(label)
X = np.array(X)
y = np.array(y)
# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 训练分类器
clf = LinearSVC()
clf.fit(X_train, y_train)
# 计算测试精度
accuracy = clf.score(X_test, y_test)
print("Test accuracy: {}".format(accuracy))
4.3 分类器应用
我们将实现对12306验证码图片进行物品分类的功能。我们只需要先进行图片处理和特征提取,然后使用训练好的分类器进行分类。
import requests
from io import BytesIO
def classify_image(url):
response = requests.get(url)
image = Image.open(BytesIO(response.content))
image = convert_image(image)
points = stroke_width_transform(np.array(image))
features = encode_features(points)
# 使用分类器进行分类
label = clf.predict([features])[0]
return label
5. 总结
本文介绍了如何使用Python来识别12306图片验证码上的物品,使用了SWT方法进行特征提取,使用LSVM分类器进行分类。通过对样本集的训练和测试,可以得到较高的分类精度。这个方法也可以应用于其他物品的识别任务中。