一. 目标网址

aHR0cHM6Ly9jcmVkaXQuYWNsYS5vcmcuY24vY3JlZGl0L2NhcHRjaGEvZ2VuP3R5cGU9Q09OQ0FU

二. 有哪些特征

  • 一条横线将一张图分为上下两份,一份是固定不动的图,另一份是可以左右移动的滑动图
  • 对于这个站来说,它的滑动图都在上半部分 (划重点)

写到这里,先确定几个名词:

原图:没有经过移动还原的原始图

滑动图: 原图上半部分可以移动的部分

三. 两个思路

3.1) yolo定位识别

其实我见到这个类型验证码的第一反应,是模型训练,也就是将滑动图的左右分界线识别出来:

image-20230912112022040

后来标了200多张图试了下,因为这个站大部分的图片分界线还是挺清晰的,所以识别的正确率大概能到85%左右,不过,这种方法不是我们今天要介绍的重点,仅仅是作为参考方案之一来提及下

3.2) 算法识别

还记得上一篇文章 拼图类验证码识别浅谈 吗?里面的余弦相似度在这里会再次被用到。

3.2.1 找到上下部分分界线

正如上面第二部分提到的,这个站的滑动验证码的下半部分是固定不动的,上半部分是被鼠标拖着从左往右移动的,那么首先,我们得知道这上下部分的分界线在哪,先来抓个包看下。

image-20230912101345290

这个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:

789

上半部分 upper1.jpg:

upper1

下半部分 lower1.jpg:

lower1

分离的很完整,哈哈~

3.2.2 拖动的距离如何计算

image-20230912103259412

滑动图自左向右滑动,使得上图中像素分界线一直向右移动,直到和图像的最右边重合,其上下两部分的图像能够拼接成一张完成的图片,而滑动的距离,在没有缩放的前提下,实际上就是上图两个箭头之间的距离,还原后的效果,如下图:

image-20230912111818667

我们只需要把上半部分每往右移动一个步长(假设这里的步长是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时候的发包情况:

image-20230912142643619

这里的高、宽、轨迹都是要缩放后的,比例是 4/9

其中,sliderImageHeight = 159 - int(data参数的值*4/9),这点注意下就行

四. 测试与总结

小测100次看下效果:

image-20230912115145073

100次错了9次,来看下错的原因是什么,随便挑一张。我们把分界线画出来看看识别到哪了:

image-20230912133908903

分界线标红,在最左边,相当于未移动的时候就已经是相似度最高了。那把原图拆分成上下部分看看:

lower

果然,下半部分的图像被分割的有偏移,也就是说把一部分本属于滑动图的图像切割到下半部分去了。然后看下原图尺寸,发现是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%了。

image-20230912140105785

此篇完结~

最后修改:2023 年 11 月 27 日
如果觉得我的文章对你有用,请随意赞赏