@TOC
一、引言
1.1 实现目标
要达到的效果是使用算法预测中间圆形的角度,返回给服务器,实现自动完成验证码的问题。要实现的内容如下图所示。
1.2 实现思路
思路1(效果较差):以RotNet要实现的验证码识别为灵感,先利用YOLO算法检测出中间圆,再把圆形图像输入给RotNet,让其预测角度,进而返回给服务器。
但是实际应用的过程中,笔者发现这种算法逻辑执行效果较差。
因为RotNet要完成的是独立的圆预测角度,实现的是如下图所示的圆的角度预测,单独的圆就已经是独立出来的一张图像,因此直接输入进网络,预测效果会很好
但是我们要实现的相当于中间圆和外部图形的拼图操作,而不仅仅是简单的预测角度,所以直接把中间的圆拿出来进行角度预测,显然脱离了背景,而且有些题把中间独立的圆抠出来之后,很难对其角度进行定义,所以效果很差
思路2:有了思路1的教训,我们要做的第一步就是把外部的背景图像引入进来进行训练
二、数据集制备
网络上没有此类开源的数据集,因此笔者自行进行了制备,具体分为以下两种:
- 一种是以下这种圆完全归位的一整张图,都是用美工P图的方法进行制备的
- 第二种是直接截取的这种没有P图过的没有修正过的图像
此类数据集制备完成之后再用笔者编写的脚本使其归位,部分代码如下
大致思路为:
- 先使用YOLO算法检测出图像中的圆
- 再利用算法使用a、d两个按键进行角度偏转,使用z、c两个按键进行切图
- 观察到图像回正之后,按s键保存到指定文件夹下
效果如下:
3eb50dabc910b6
import math
import numpy as np
import cv2
from ultralytics import YOLO
import os
yolo_model = YOLO(r"D:\kb\rotate-captcha-crack-master_my\yolo.pt")
def code_dect(folder_path, output_path):
files = []
current_index = 0
while True:
if not files:
print("文件夹中没有图像文件。")
break
img_path = os.path.join(folder_path, files[current_index])
img = cv2.imread(img_path)
imgDoub = img
# 检测出的box
center_box = []
results = yolo_model.predict(img, stream=True)
boxAll = []
for r in results:
boxes = r.boxes
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0] # 获取边界框的坐标
x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
w, h = x2 - x1, y2 - y1
bbox = (x1, y1, w, h)
boxAll.append(bbox)
# 计算图像中心坐标
width, height = img.shape[0], img.shape[1]
image_center_x = width / 2
image_center_y = height / 2
# 找出距离图像中心最近的矩形框
min_distance = float('inf')
for rectangle in boxAll:
center_x, center_y = calculate_center(rectangle)
distance = math.sqrt((center_x - image_center_x) ** 2 + (center_y - image_center_y) ** 2)
if distance < min_distance:
min_distance = distance
center_box = rectangle
if center_box == []:
print(f"没有检测到目标:{img_path}")
else:
# 创建与图像相同大小的黑色背景
mask = np.zeros_like(imgDoub[:, :, 0])
# 定义圆的外接矩形坐标
x, y, w, h = center_box
# 在掩码上绘制白色的圆形
cv2.circle(mask, (x + w // 2, y + h // 2), min(w, h) // 2, (255, 255, 255), -1)
# 将掩码应用到白色背景上,保留圆形区域
onlyCircle = cv2.bitwise_and(imgDoub, imgDoub, mask=mask)
mask2 = np.zeros_like(img, dtype=np.uint8)
# 在掩码上绘制圆形区域
cv2.circle(mask2, (x + w // 2, y + h // 2), min(w, h) // 2, (255, 255, 255), -1)
rotate = True
reverse = False
angle = 0
while rotate:
# 获取图像的中心点坐标
height, width = onlyCircle.shape[:2]
center = (width // 2, height // 2)
# 定义旋转矩阵
rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
# 进行旋转变换
rotated_image = cv2.warpAffine(onlyCircle, rotation_matrix, (width, height))
# 将圆形区域置为0
imgDoub[mask2 != 0] = 0
result = rotated_image + imgDoub
cv2.imshow("result", result)
# 添加按键监听
key = cv2.waitKey(1)
# z、c分别是切换上一张或者下一张图
if key == ord('z'):
current_index = (current_index - 1) % len(files)
break
# 按下 'c' 键逆时针旋转
elif key == ord('c'):
current_index = (current_index + 1) % len(files)
break
# 按下 's' 键保存图像
elif key == ord('s'):
output_img_path = os.path.join(output_path, files[current_index])
cv2.imwrite(output_img_path, result)
print(f"图像已保存到:{output_img_path}")
if key == ord('a'):
angle += 1
# 按下 'd' 键逆时针旋转
elif key == ord('d'):
angle -= 1
elif key == ord('q'):
angle += 10
elif key == ord('e'):
angle -= 10
if __name__ == '__main__':
folder_path = input("请输入文件夹路径:")
output_path = input("请输入输出路径:")
code_dect(folder_path, output_path)
三、算法逻辑
1、生成样本
使用RotNet作为本算法的预测核心预测算法,把我们上文中生成的回正数据首先利用编写的脚本给每张图像生成360张不同角度的图像,文件名的后缀代表这张图象真实的偏转角度。
2、训练算法
把生成的所有图像输入进改进的RotNet进行训练,由于这种类型的样本学习很容易出现过拟合的现象,因此笔者在网络中加了几个DropOut操作。
3、算法逻辑
我们并没有简单把单张图像输入进算法来进行角度预测,这样360个类别误差太大效果会比较差,在应用的时候我们也是先把中间的圆形图像抠出来,然后对其使用算法旋转360度,把360张图像都进行角度预测,最后取出0到3度和357到359度的图像返回它的序列值,即真实的角度值。如果没有这些范围之内的图像,那就返回-1,切下一张图像,防止错误次数太多。代码如下所示:
import math
import numpy as np
import torch
from PIL import Image
from rotate_captcha_crack.common import device
from rotate_captcha_crack.model import RotNetR
from rotate_captcha_crack.utils import process_captcha
import cv2
from ultralytics import YOLO
yolo_model = YOLO(r"D:\chenjie\rotate-captcha-crack-master_my\yolo.pt")
def calculate_center(rectangle):
x, y, w, h = rectangle
center_x = x + w / 2
center_y = y + h / 2
return center_x, center_y
model = RotNetR(train=False, cls_num=360)
model_path = r"D:\chenjie\rotate-captcha-crack-master_my\models\RotNetR\240316_17_14_23_006\best.pth"
model.load_state_dict(torch.load(str(model_path)))
model = model.to(device=device)
model.eval()
def predictAngle(img):
img = Image.fromarray(img)
img_ts = process_captcha(img)
img_ts = img_ts.to(device=device)
predict = model.predict(img_ts)
return predict
def code_dect(img):
imgDoub = img
# 检测出的box
center_box = []
results = yolo_model.predict(img, stream=True)
boxAll = []
for r in results:
boxes = r.boxes
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0] # Gives coordinates to draw bounding box
x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
w, h = x2 - x1, y2 - y1
bbox = (x1, y1, w, h)
boxAll.append(bbox)
# 计算图像中心坐标
width, height = img.shape[0], img.shape[1]
image_center_x = width / 2
image_center_y = height / 2
# 找出距离图像中心最近的矩形框
min_distance = float('inf')
for rectangle in boxAll:
center_x, center_y = calculate_center(rectangle)
distance = math.sqrt((center_x - image_center_x) ** 2 + (center_y - image_center_y) ** 2)
if distance < min_distance:
min_distance = distance
center_box = rectangle
if center_box==[]:
print("kong")
else:
# 把圆区域搞出来
# 创建与图像相同大小的黑色背景
mask = np.zeros_like(imgDoub[:, :, 0])
# 定义圆的外接矩形坐标
x, y, w, h = center_box
# 在掩码上绘制白色的圆形
cv2.circle(mask, (x + w // 2, y + h // 2), min(w, h) // 2, (255, 255, 255), -1)
# 将掩码应用到白色背景上,保留圆形区域
onlyCircle = cv2.bitwise_and(imgDoub, imgDoub, mask=mask)
# 显示结果图像
# cv2.imshow('Only Circle', onlyCircle)
# cv2.imshow('imgDoub', imgDoub)
# cv2.waitKey(0)
mask2 = np.zeros_like(img, dtype=np.uint8)
# 在掩码上绘制圆形区域
cv2.circle(mask2, (x + w // 2, y + h // 2), min(w, h) // 2, (255, 255, 255), -1)
angles = []
for angle in range(0, 360):
# 获取图像的中心点坐标
height, width = onlyCircle.shape[:2]
center = (width // 2, height // 2)
# 定义旋转矩阵
rotation_matrix = cv2.getRotationMatrix2D(center, angle, 1.0)
# 进行旋转变换
rotated_image = cv2.warpAffine(onlyCircle, rotation_matrix, (width, height))
# 将圆形区域置为0
imgDoub[mask2 != 0] = 0
result = rotated_image+imgDoub
predict = predictAngle(result)
# cv2.imshow("sdf",result)
# print(predict)
# cv2.waitKey(10)
angles.append(predict)
minAngle = min(angles)
maxAngle = max(angles)
minAngleIdx = angles.index(min(angles))
maxAngleIdx = angles.index(max(angles))
finalAngleIdx = -1
if maxAngle>356:
finalAngleIdx = maxAngleIdx
elif minAngle<4:
finalAngleIdx = minAngleIdx
print(finalAngleIdx)
return finalAngleIdx
if __name__ == '__main__':
# 测试整体
img = cv2.imread("D:\chenjie\\rotate-captcha-crack-master_my\images\e42c0939dc3bde88657a88ac07d59d6.png")
code_dect(img)
最后可以达到80-90%的通过率,效果已经很不错了
演示效果如下:
ab7c1b94c3bc27b8
三、代码、数据集获取
q:1831255794(有偿)制备数据集和写算法耗费了大量时间精力,因此收取点小费希望理解!!!
可接项目,大作业,毕设等
价格略贵,技术够硬,认真负责,保证质量