Python图像处理识别滑动验证码缺口
参考文章
网易易盾
模板匹配
近期在破解网易易盾的滑动验证码,涉及到缺口识别的问题时,由于没有经验,上网查了一些资料,发现一个比较火的方式是利用opencv
的模板匹配。
模板匹配是一种在较大图像中搜索和查找模板图像位置的方法。OpenCV
提供了一个函数cv2.matchTemplate()
。它只是在输入图像上滑动模板图像(如在2D
卷积中),并比较模板图像下的输入图像的模板和补丁。在OpenCV
中实现了几种比较方法。它返回一个灰度图像,其中每个像素表示该像素的邻域与模板匹配的程度。
假设输入图像的大小(W*H
)且模板图像的大小(w*h
),则输出图像的大小为(W-w + 1,H-h + 1)。获得结果后,可以使用cv2.minMaxLoc()
函数查找最大/最小值的位置。将其作为矩形的左上角,并将(w,h)作为矩形的宽度和高度。那个矩形是你的模板区域匹配后得到的区域。
- 函数使用方式:
1 | import cv2 |
- 参数method
method | introduce |
---|---|
cv2.TM_SQDIFF 平方差匹配法 |
该方法采用平方差来进行匹配;最好的匹配值为0;匹配越差,匹配值越大 |
cv2.TM_CCORR 相关匹配法 |
该方法采用乘法操作;数值越大表明匹配程度越好。 |
cv2.TM_CCOEFF 相关系数匹配法 |
1表示完美的匹配;-1表示最差的匹配。 |
cv2.TM_SQDIFF_NORMED |
归一化平方差匹配法 |
cv2.TM_CCORR_NORMED |
归一化相关匹配法 |
cv2.TM_CCOEFF_NORMED |
归一化相关系数匹配法 |
TM_SQDIFF,TM_SQDIFF_NORMED匹配数值越低表示匹配效果越好,其它四种反之。
假设我们现有易盾验证码的背景图片和滑块图片,尝试去调用此方法来解决问题:
1 | import cv2 |
这里最后是在背景图上做了矩形然后显示出来,这样我们一下就可以看出找的对不对。
这里因为用的是cv2.TM_CCOEFF_NORMED
,匹配的越好,匹配值越大,所以我们以最大值的位置作为矩阵的左上角,如果用cv2.TM_SQDIFF
就要选最小值的位置作为矩阵的左上角。
具体在检测的时候,method参数到底使用哪一种效果更好,要实际测试,在我遇到过的一些滑动验证码,貌似都是TM_CCOEFF_NORMED参数效果好一些。
这里是直接使用黑白图片进行检测,效果还可以,但是针对具体不同的图片,有可能彩色的效果更好一些。
后来有遇到过另一个网站的图片,发现用上面的方法无法正常检测到滑块的位置,检测结果是这样的:
后来从大佬那里学习到,在模板匹配之前,将图片进行一个边缘检测处理,能够提高检测的效果
edges = cv2.Canny( image, threshold1, threshold2[, apertureSize[, L2gradient]])
edges 为计算得到的边缘图像。
- image 为 8 位输入图像。
- threshold1 表示处理过程中的第一个阈值。
- threshold2 表示处理过程中的第二个阈值。
- apertureSize 表示 Sobel 算子的孔径大小。
- L2gradient 为计算图像梯度幅度(gradient magnitude)的标识。其默认值为 False。如果为 True,则使用更精确的 L2 范数进行计算(即两个方向的导数的平方和再开方),否则使用 L1 范数(直接将两个方向导数的绝对值相加)。
1 | # 边缘检测 |
两个阈值的设置要根据情况而定。
检测效果:
此外,因为网易易盾的滑块图片是有一个纯白色或者纯黑色底色的背景的,我有尝试过转换过背景底色再去匹配,但是没啥没效果,底色转换代码:
1 | # 白色底变为黑色底 |
传统算法
因为网易易盾的验证码图片的内部色彩变化较大,而不像极验绿色、蓝色灯底色,图一张易盾的背景的色彩变化就很大,很难直接利用像素变化定位到缺口位置,我也写了一个尝试性的算法,但是并没有成功,边界如果定的宽松些,就会返回多个满足条件的位置,如果将边界定的严格些,又会无返回值。
1 | def judge_pixel(gray: Image, col: int, row: int, value: int): |
因为这里直接利用图像处理的方法还无法破解极验滑块的缺口位置,我后面打算利用深度学习的目标检测方法来实现,感兴趣的可以关注后面的文章。
极验
图片还原
对于极验需要先介绍下极验滑动验证码的图片还原的方式。
直接从极验服务器获取的背景图是乱的,根本看不出来缺口在哪里,大概是这样的:
想要将其还原为原来的样子,首先我们要知道完整的验证码图片由52个10*58像素的小图片组成,共2行26列,其次只要获取使用极验服务的相关网站上的css
坐标,类似这样的:
然后按照这里的坐标顺序,从乱序的图中将一个个的小图片扣出来,粘贴到一个空白的图片中重组就能复原,下面的我实现的Python代码:
1 | # 从css中获取 |
这里的css
左边一般各个使用极验服务的网站不一样,不是通用的,根据你要破解的网站寻找其css
坐标即可,而且据我观察一般是固定不变的,若动态变化,只要每次将获取到的html
代码中的这部分解析一下就可以了。
模板匹配
按照我实际实践的顺序,我是先破解的极验验证码,用的是传统算法(下面会介绍),没有用过模板匹配,然后又去破解的网易易盾的,但是利用模板匹配和传统算法都没有解决。后来我想了下是不是因为网易易盾的背景图片只有带缺口的,没有完整的原图,所以匹配不上,正好极验的滑动验证码是可以获取原图的,所以我对极验的也尝试了一下,但是发现依然不行,这里就不详细展示叙述了。
传统算法
方法一:仅根据带缺口图片的像素变化
因为之前做过一个很简单的滑动验证码的识别,当时那个网站的图片色彩风格很单一,仅根据像素变化就找出来了缺口位置,观察极验这里感觉应该也可以用类似的方式实现,所以就尝试了一下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18def get_picture_gap(name, path):
image = Image.open(path+name)
gray = image.convert('L')
# gray.save('gray.jpg')
width, height = gray.size
for col in range(width - 55):
for row in range(height - 55):
if gray.getpixel((col, row)) < 80:
count_i = 0
count_j = 0
for i in range(row + 1, row + 50):
if gray.getpixel((col, i)) < 80:
count_i += 1
for j in range(col + 1, col + 50):
if gray.getpixel((j, row)) < 80:
count_j += 1
if count_i > 20 and count_j>20:
return col算法的大概思路在之前的海关进出口破解的那里已经讲过了,不再详细说了,然后我随机测试了五张图片(我自己利用上面的方式还原后的图片):
只有第三张图片定位错误,其他四张都准确定位了;因为我这里没有实际破解需求,只是兴趣使然,所以没有再去优化算法,应该还有进一步优化的空间。
方法二:对比带缺口的和不带缺口的图片
对于极验的滑动验证码比较特殊的一点是可以获取完整地图片和待缺口的图片,由此就产生一种大家都很容易想到的方法,逐列扫描对比这两个图片,发现连续较多行都有一定的像素差,一般就是缺口的位置,我这里简单写了一个算法,同样是测试了上面的五张图片,更巧的是,也是第三张图片没有获取到缺口位置,因为这个算法是我凭感觉直接写的,没有去认真观察图片像素变化,应该也是存在优化空间的,我这里仅仅判断这一列若干行,其实可以再继续判断后续的列和行,只不过会增加时间复杂度。
1
2
3
4
5
6
7
8
9
10
11
12
13
14def get_picture_gap1(self, name1, name2, path1, path2):
image1 = Image.open(path1 + name1)
image2 = Image.open(path2 + name2)
gray1 = image1.convert('L')
gray2 = image2.convert('L')
# gray.save('gray.jpg')
width, height = gray1.size
for col in range(width - 55):
count_i = 0
for row in range(height - 55):
if abs(gray1.getpixel((col, row)) - gray2.getpixel((col, row))) > 20:
count_i += 1
if count_i > 25:
return col
至于这里识别出来的缺口位置是不是实际滑动的距离,他们之间的关系,不属于这里的研究范围……做出来这一步,再去计算实际滑动距离基本就没什么困难了。