一. 目标网址
aHR0cHM6Ly9jcmVkaXQuYWNsYS5vcmcuY24vY3JlZGl0L2NhcHRjaGEvZ2VuP3R5cGU9Q09OQ0FU
二. 有哪些特征
- 一条横线将一张图分为上下两份,一份是固定不动的图,另一份是可以左右移动的滑动图
- 对于这个站来说,它的滑动图都在上半部分 (划重点)
写到这里,先确定几个名词:
原图
:没有经过移动还原的原始图
滑动图
: 原图上半部分可以移动的部分
三. 两个思路
3.1) yolo定位识别
其实我见到这个类型验证码的第一反应,是模型训练,也就是将滑动图
的左右分界线识别出来:
后来标了200多张图试了下,因为这个站大部分的图片分界线还是挺清晰的,所以识别的正确率大概能到85%左右,不过,这种方法不是我们今天要介绍的重点,仅仅是作为参考方案之一来提及下
3.2) 算法识别
还记得上一篇文章 拼图类验证码识别浅谈 吗?里面的余弦相似度在这里会再次被用到。
3.2.1 找到上下部分分界线
正如上面第二部分提到的,这个站的滑动验证码的下半部分是固定不动的,上半部分是被鼠标拖着从左往右移动的,那么首先,我们得知道这上下部分的分界线在哪,先来抓个包看下。
这个data参数应该就是分界线的位置了,不过看图片从最上边到分界线的距离明显小于图片高的一半了,那应该就是 (360-221) 了,我们来用代码分割下就知道了(这里图片的尺寸是590x360):
def split_img(binary, _y):
img = cv2.imdecode(np.array(bytearray(binary), dtype='uint8'), cv2.IMREAD_UNCHANGED)
upper_img = img[0:_y, 0:590]
lower_img = img[_y:360, 0:590]
return upper_img, lower_img
with open('789.jpg', 'rb') as f:
binary = f.read()
_y = 360-221
upper_img, lower_img = split_img(binary, _y)
cv2.imwrite('upper1.jpg', upper_img)
cv2.imwrite('lower1.jpg', lower_img)
原图 789.jpg:
上半部分 upper1.jpg:
下半部分 lower1.jpg:
分离的很完整,哈哈~
3.2.2 拖动的距离如何计算
将滑动图
自左向右滑动,使得上图中像素分界线一直向右移动,直到和图像的最右边重合,其上下两部分的图像能够拼接成一张完成的图片,而滑动的距离,在没有缩放的前提下,实际上就是上图两个箭头之间的距离,还原后的效果,如下图:
我们只需要把上半部分每往右移动一个步长(假设这里的步长是1),就计算一次上下两张图相邻边缘1px的余弦相似度,相似度最高的时候对应的移动距离,就是我们要识别的距离,下面是部分代码:
move_img, unmove_img = split_img(binary, y) # 把上下部分 分隔开
unmove_img_point_px = unmove_img[0:1, 0:width] # 下半部分 边缘的1像素 这个是不动的
all_deviation = []
for result in range(0, 590):
move_img=move_x(move_img) # 上半部分向右移动1px之后新的滑动图
move_img_point_px=move_img[move_height-1:move_height, 0:width] # 上半部分边缘的1像素
# 余弦相似度计算
res=image_similarity_vectors_via_numpy(Image.fromarray(move_img_point_px),
Image.fromarray(unmove_img_point_px))
all_deviation.append((result, res))
# score[0][0] 就是我们要获取的值
score = sorted(all_deviation, key=lambda x: x[1], reverse=True)
当然,这个站的图像是要缩放的,我们来看下verify时候的发包情况:
这里的高、宽、轨迹都是要缩放后的,比例是 4/9
其中,sliderImageHeight = 159 - int(data参数的值*4/9)
,这点注意下就行
四. 测试与总结
小测100次看下效果:
100次错了9次,来看下错的原因是什么,随便挑一张。我们把分界线画出来看看识别到哪了:
分界线标红,在最左边,相当于未移动的时候就已经是相似度最高了。那把原图拆分成上下部分看看:
果然,下半部分的图像被分割的有偏移,也就是说把一部分本属于滑动图
的图像切割到下半部分去了。然后看下原图
尺寸,发现是592x359的(区别于之前360的高)。草率了,一开始想当然了......这里把3.2.1
中上下图分割的代码微调下就好了:
def split_img(binary, _y):
img = cv2.imdecode(np.array(bytearray(binary), dtype='uint8'), cv2.IMREAD_UNCHANGED)
width = img.shape[1] # (修改的部分2-0)
height = img.shape[0] # (修改的部分2-1)
_y = height - _y # (修改的部分3)
upper_img = img[0:_y, 0:width]
lower_img = img[_y:height, 0:width]
return upper_img, lower_img
with open('789.jpg', 'rb') as f:
binary = f.read()
_y = 221 # (修改的部分1) 这里就填获取图片时拿到的data参数的值
upper_img, lower_img = split_img(binary, _y)
cv2.imwrite('upper1.jpg', upper_img)
cv2.imwrite('lower1.jpg', lower_img)
这次再来重新测试下,正确率就100%了。
此篇完结~