[验证码识别]某盾滑块验证码增强版的识别
本文章所有内容仅供学习和研究使用,本人不提供具体模型和源码。若有侵权,请联系我立即删除!维护网络安全,人人有责。
前言
最近,某盾新推出了增强版验证码,其中滑块的增强版比较新颖(或许是我见识少,我见过百度类似的,但不是滑块)。
与传统滑块不同的是,该滑块的移动轨迹是条弧线,且还会旋转,并且两个缺口的形状相同,这就排除了传统的cv2模板匹配识别缺口的方式。
目录
-
滑块移动曲线研究
-
还原滑块移动曲线
-
缺口识别的方案
-
匹配缺口
滑块移动曲线研究
和非增强版滑块不同的是,该滑块接口新返回了一个值attrs
,并且每次返回的值都不同
{
"attrs": [0.20475486292799452],
...
}
而且每次滑块旋转角度和偏移位置都不同,因此有理由怀疑滑块的移动轨迹和该值有关。
我们现在开始分析滑块是怎么移动和旋转的,通过控制台给滑块图片的CSS加上边框显示,就可以明显看出滑块的移动。
结合attrs
可以发现当attrs
小于0时,是以滑块图片的右上角为中心点进行旋转加向右偏移,反之则以右下角为中心点。
为了验证我们的猜想,我们可以查看它的js文件,以下是我经过反混淆后的代码
function updateJigsawRotateAndLeft() {
var E = this['$el']["offsetWidth"]
, w = this["$slider"]["offsetWidth"]
, Q = this["$jigsaw"]["offsetWidth"]
, J = this["restrict"](this['$jigsaw'], w - Q);
if (this['ratio'] = (E / 0x2 - Q) / E,
this["attrs"]) {
var g0 = this['attrs'][0x0]
, g1 = J * this['ratio'];
this['$jigsaw']['style']['left'] = g1 + 'px',
this['$jigsaw']['style']['transform'] = 'rotate(' + g0 * g1 + 'deg)',
this['$jigsaw']['style']['transformOrigin'] = g0 > 0x0 ? "bottom right" : "top right";
}
}
可以看到当g0也就是attrs
大于0时,将滑块dom的style(也就是css)的transformOrigin
设置为bottom right
,并且还可以得知滑块旋转角度rotate
由滑块偏移距离g1与attrs
的值g0相乘得到
查资料得知,transformOrigin: bottom right
正是设置中心点为右下角
还原滑块移动曲线
再次观察可以发现,滑块的偏移量和下面滑条的偏移量是不相同的,研究上面的js可以发现,滑块的偏移量经过一些算法得到J,再计算就可以得到滑块的偏移量
在控制台上打上日志点,再和还原的算法结果对比
可以看到,结果一样。
缺口识别的方案
我们的目标是获取每个缺口的位置和旋转的角度,才可以判断哪个是符合条件的缺口,例如我得到一个缺口的角度,可以反向计算出滑块的偏移量,然后和该缺口的位置匹配,那么该缺口就是目标。
1.旋转模板匹配
我刚开始采用了旋转模板匹配,也就是将滑块旋转,然后每次旋转都进行一次模板匹配,返回置信度最高的坐标和角度。
但是由于多个缺口的存在,即使取置信度最高的2个值,也不能保证能匹配成功,有时候甚至不能匹配到缺口
于是我弃用了这个方法
2.目标检测 + 旋转模板匹配
在某大佬的建议下,我使用yolov5先将滑块缺口切割出来,再对切割下来的图片进行旋转模板匹配
由于具有针对性,该识别方案的准确率远远高于第一种,但是缺陷是,识别时间较久,且计算量较大
原因很简单,因为需要目标检测再加上匹配每一个角度和多个缺口图片,所以消耗时间高于第一种方案
3.目标检测 + 滑块曲线经过位置 + 置信度比较
最后我想出了另一种识别方案,既然识别缺口角度比较麻烦,那么能不能通过滑块的移动轨迹去匹配缺口呢?
再多次观察可以发现,滑块不是每次都经过两个缺口,能不能只算出经过的缺口就行了
步骤是这样的:
- 通过
attrs
计算滑块移动曲线
- 使用yolo识别缺口位置
- 再通过三角函数计算去除空白背景的滑块轨迹(每个坐标点和旋转角度)
- 算出滑块轨迹和每个缺口的最小距离,再比较每个缺口的最小距离,距离最小的就是目标缺口
- 如果每个缺口的最小距离只差小于一定阈值,则使用距离每个缺口最小轨迹点的旋转角度去目标匹配得到置信度
- 置信度最高的缺口就是目标缺口
为了方便,我画了一张图表示怎么算出滑块轨迹点
我总结了以下内容,需要三角函数的知识
- 首先算出去除空白背景滑块的中心点,再得到
α
- 然后通过
β
和α
算出γ
- 通过
γ
算出x2
和y
- 滑块轨迹点 = (滑块宽度 + x1 + x2, y)
import cv2
def get_slide_center(image_path):
"""获取滑块中心点坐标, 不包含透明背景"""
# 读取图片
image = cv2.imread(image_path, cv2.IMREAD_UNCHANGED)
# 提取Alpha通道
alpha_channel = image[:, :, 3]
# 寻找非零Alpha值的最小边界框
_, thresh = cv2.threshold(alpha_channel, 1, 255, cv2.THRESH_BINARY)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
x, y, w, h = cv2.boundingRect(contours[0])
# 裁剪图片
return [int(w / 2 + x), int(h / 2 + y)]
得到所有轨迹点后,绘制到背景图上查看结果
接下来就是计算缺口图片中心点到轨迹线的最小距离,然后比较就行了
如果轨迹线经过了2个缺口的中心点,那么就可以根据轨迹点去旋转滑块图片再去匹配缺口图片,取置信度最高的即可
总结
训练该缺口的目标检测我使用了500张图片的数据集,不过我认为300张就够了,具体怎么训练可以参考我之前的文章。
另外,我不是什么大佬,我只是站在了巨人的肩膀上
最后,如果大家有个更好的方法欢迎大家讨论!