好久没写博客了,人到中年,有点儿犯懒。
从信息安全行业,又去了IT合规领域,与信息安全结合还是两手抓,两手都不硬。
由于工作原因,需要获取一个token来请求接口,奈何没有现成的接口,需要在web端登录才可以获取。既然如此,想要实现这个功能肯定是selenium+识别,如下图
通过对验证码的分析,得到滑块在距离背景图10px的地方开始滑动,也就是说滑动距离是缺口在图片的横坐标 X - 10。纵坐标无需考虑。
一、opencv
参考: python实现过顶象滑块_顶象滑块js_weixin_43419774的博客-CSDN博客
看完这篇文章之后,大量的下载,复原图片的前期工作都已经直接搞定,感谢作者。
按照这篇博客的识别算法
1. 先将背景图与滑块进行灰度处理
2. 将滑块进行reverse
3. 两张图进行高斯模糊,然后以标准相关系数匹配,找到坐标
对我的验证码中只有70%以上的准确率,因为顶象会有一些混淆图片例如
我们可以看到正确的缺口里面是纯色,而滑块里面是有背景图片内容的,会造成不准确。
改进方法是进行边缘检测,然后进行标准相关系数匹配
方法1:
def dXImgSlider2(origin, sliderImg, filename='labeled.webp'):
slider = cv2.imread(sliderImg)
originImg = cv2.imread(origin, cv2.IMREAD_GRAYSCALE)
sliderImg = cv2.cvtColor(slider, cv2.COLOR_BGR2GRAY)
# sliderImg 二值化
_, th2 = cv2.threshold(sliderImg, 5, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
# 梯度处理
sobelx = cv2.Sobel(th2, cv2.CV_16S, 1, 0, ksize=1)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.Sobel(th2, cv2.CV_16S, 0, 1, ksize=1)
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)
# 参数微调
th2 = cv2.Canny(sobelxy, 700, 700)
# cv2.imwrite('th2.png', th2)
dstx = cv2.Sobel(originImg, cv2.CV_16S, 1, 0, ksize=1)
dstxcmp = cv2.convertScaleAbs(dstx)
dsty = cv2.Sobel(originImg, cv2.CV_16S, 0, 1, ksize=1)
dstycmp = cv2.convertScaleAbs(dsty)
dstxy = cv2.addWeighted(dstxcmp, 0.5, dstycmp, 0.5, 0)
# 参数微调
dstxy = cv2.Canny(dstxy , 600, 800)
# cv2.imwrite('th1.png', dstxy)
w, h = sliderImg.shape[:2]
new_img = cv2.imread(origin)
res = cv2.matchTemplate(dstxy, th2, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
im = cv2.rectangle(new_img, top_left, bottom_right, (0, 0, 255), 2)
cv2.imwrite(filename, im)
resObj = [int(x * 300/400) for x in [top_left[0], top_left[1], bottom_right[0], bottom_right[1]]]
return resObj
方法2:
# 滑块识别方法3
def dXImgSlider3(origin, sliderImg, filename='labeled.webp'):
slider = cv2.imread(sliderImg)
originImg = cv2.imread(origin)
bgImg = cv2.cvtColor(originImg, cv2.COLOR_BGR2GRAY)
sliderImg = cv2.cvtColor(slider, cv2.COLOR_BGR2GRAY)
# add canny
bg_edges = cv2.Canny(bgImg, 600, 800)
slider_edges = cv2.Canny(sliderImg, 700, 700)
# _, thresh = cv2.threshold(bgImg, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
contours, hierarchy = cv2.findContours(bg_edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
res = cv2.matchTemplate(bg_edges, slider_edges, cv2.TM_CCOEFF_NORMED)
# epsilon = 0.1 * cv2.arcLength(contours[0], True)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
tl = min_loc
br = (tl[0] + slider_edges.shape[0], tl[1] + slider_edges.shape[1])
return tl[0], tl[1], br[0], br[1]
方法2比方法1代码少了梯度处理,梯度处理能够更好地可视化图像中的边缘信息。但以我自己的case中准确度相差不大。
二、目标检测
完成这个事情首先观察验证码的背景图片特点,背景图存在2个缺口,一个是正确的,一个是混淆缺口,通过对大量背景图的下载,发现正确缺口比混淆的缺口更清晰。因此,可提前论证神经网络找到缺口位置无需滑块图片。
深度学习技术方案有三种:
1. 使用2个神经网络,第1个神经网络将滑块图片输入进行识别,然后输出形状权重,作为第2个神经网络的输入,然后输出缺口位置坐标。
2. 单分类目标检测,对背景图片进行标注,然后进行训练,并输出正确缺口坐标;
3. 多分类目标检测,对背景图片进行正确缺口的标注,作为correct类别,错误缺口进行标注,作为wrong类别,然后进行训练,并输出正确缺口坐标;
选择第2种方案更简单,也更有效。
训练使用:PyTorch-YOLOv3
权重文件: darknet53.conv.74
类别: 1
标注工具: labelImg
训练集: 160张
验证集: 40张
epoch: 23
Total loss: 1.88
由于训练在M1芯片的机器上,不知道为何出现一些crash的问题。不过还好第23次的结果测试效果已经非常好了,置信度99.9%
现在的训练已经非常无脑了,饭都喂到嘴边了,没有不吃的道理。去B站或者Youtube看一看视频就完全掌握了。接下来我的目标是对一些大佬开源的训练方法进行改进。
最后,测试2种方法返回的滑动距离,结合seleium进行实现,搞定。
最后部署的时候由于云主机安装不上opencv,一直报错,还好有模型backup。