滑块验证码缺口识别技术分享

在我们抓取数据的过程中,访问频繁或者登录时,往往会弹出滑块验证码。比如下图:

python 滑块 验证码 爬虫滑块验证码_爬虫处理滑块验证

python 滑块 验证码 爬虫滑块验证码_卷积_02

那么如何准确的识别出滑块的缺口坐标则至关重要了,本文将介绍一种基于2D卷积识别缺口的技术思路。

识别处理过程

滑块:

python 滑块 验证码 爬虫滑块验证码_滑块_03


获取到小滑块及背景图

python 滑块 验证码 爬虫滑块验证码_卷积_04

python 滑块 验证码 爬虫滑块验证码_爬虫处理滑块验证_05


识别处理过程:

python 滑块 验证码 爬虫滑块验证码_python 滑块 验证码_06

正文

1. 基础知识

1. 彩色图片

一个彩色图片是有RGBA四个属性
前三个值(红绿蓝)的范围为0到255之间的整数或者0%到100%之间的百分数。这些值描述了红绿蓝三原色在预期色彩中的量。
第四个值,alpha值,制定了色彩的透明度/不透明度,它的范围为0.0到1.0之间,0为完全透明,1为完全不透明。

rgba(255,255,255,1)白色;
rgba(0,0,0,1) 黑色;

RGB颜色值与十六进制颜色码转换工具:https://www.sioe.cn/yingyong/yanse-rgb-16/

2. 灰色图片

灰度图片可以看成一个二位数组, 因为图片放大了可以看到是由一个个像素点组成,像素点的值为0~255。0 为黑色,255为白色,0~255之前为不同等级的灰色

2. 识别思路

假设我们是个图像处理的小白,我们经过一番查找图片的相关资料,了解了上面的图片基本知识。那么我们想识别缺口,该如何做呢?

仔细观察背景图的缺口,不难发现每个缺口周围都有一圈阴影,那么我们可以用一些色彩提取工具,把这个阴影的RGB提取出来

python 滑块 验证码 爬虫滑块验证码_卷积核_07

接下来我们可以读取这个图片且创建一个和图片一样大小的二维矩阵,然后遍历RGB, 当RGB等于我们提取出来的值时,我们将二维矩阵对应的位置设置成(1,1), 否则设置成(0,0), 这样处理后,我们得到了一个0,1的二维矩阵,于是便得到了如下结果:

原图

python 滑块 验证码 爬虫滑块验证码_卷积核_08


二维矩阵绘制结果(图中红色的点为1,白色的为0)

python 滑块 验证码 爬虫滑块验证码_爬虫处理滑块验证_09

可以看到效果还是可以的,我们已经把缺口标记出来了,接下来我们用同样的办法,去绘制小滑块的边界,得到如下结果:

python 滑块 验证码 爬虫滑块验证码_卷积_10

python 滑块 验证码 爬虫滑块验证码_爬虫处理滑块验证_11

接下来我们需想办法在背景图的二维矩阵中找出落在小滑块边界最多点的区域,即为缺口区域。

寻找过程:在背景图的二维矩阵中,移动小滑块的二维矩阵,用被遮罩的背景图所在区域的二维矩阵与小滑块的二维矩阵对应坐标相乘,取乘积最大的位置的中心点即为背景图的中心点。

来个形象点的动图

python 滑块 验证码 爬虫滑块验证码_python 滑块 验证码_12

真实的计算过程如下:

python 滑块 验证码 爬虫滑块验证码_卷积核_13


动图

python 滑块 验证码 爬虫滑块验证码_python 滑块 验证码_14


用到的技术就是矩阵相乘,然后把乘出来的结果赋值给一个二维矩阵对应的点,最后找到这个二维矩阵中最大值所在点的坐标

进阶

1. 图片灰度化

因为我们只识别缺口,不用考虑色彩,所以最先想到的是将彩色图片转为灰度图片,降低识别的复杂度,代码及效果如下:

python 滑块 验证码 爬虫滑块验证码_滑块_15

python 滑块 验证码 爬虫滑块验证码_滑块_16

python 滑块 验证码 爬虫滑块验证码_爬虫处理滑块验证_17

2. 锐化边缘

将上面灰度背景锐化,目的是更方便的寻找轮廓

python 滑块 验证码 爬虫滑块验证码_卷积核_18

python 滑块 验证码 爬虫滑块验证码_滑块_19

锐化后我们会得到个二维数组,图中黑色区域点对应的值为0,白色的点对应的值为255。

3. 制作滤波矩阵-卷积核

什么是卷积核?下图中间移动的3*3的矩阵我们称其为卷积核。

python 滑块 验证码 爬虫滑块验证码_python 滑块 验证码_14

3.1 卷积核作用

此处是为了框选我们想定位的区域,为了更好的理解,我们举出如下例子:

问题:假如有如下矩阵,我们暂且称作目标矩阵,如果我们想寻找点数为1的最多的位置,怎么寻找呢?

[
    [0, 0, 0, 0, 0, 0, 0],
    [0, 0, 1, 1, 1, 0, 0],
    [0, 0, 1, 1, 1, 0, 0],
    [0, 0, 1, 1, 1, 0, 0],
    [0, 0, 0, 0, 0, 0, 0]
]

做法:

  1. 制作一个模版,即卷积核,如下:
[
    [1, 1, 1],

    [1, 1, 1],

    [1, 1, 1]

]
  1. 以模版矩阵[1,1]的位置为中心点,在目标矩阵中的左上角开始移动至右下角,然后将被该模版覆盖的矩阵区域与该模版矩阵对应点相乘,然后取乘积之和作为目标矩阵被覆盖区域中心点的值。运算过程及结果如下:
[[0 1 2 3 2 1 0]
 [0 2 4 6 4 2 0]

 [0 3 6 9 6 3 0]

 [0 2 4 6 4 2 0]

 [0 1 2 3 2 1 0]]
  1. 观察上面计算后的结果即可看出值为9的点即为我们要寻找的点数为1的最多的位置。因为值为9的点,目标矩阵被覆盖区域必须为
[
    [1, 1, 1],

    [1, 1, 1],

    [1, 1, 1]

]

这样卷积运算后值才为9

3.2 制作缺口的卷积核

因为缺口的形状和小滑块是完全吻合的,因此我们基于小滑块来制作

3.2.1 裁剪

python 滑块 验证码 爬虫滑块验证码_卷积_21


观察小滑块,是有透明区域的,我们需要将其裁剪掉,裁剪代码及效果如下:

python 滑块 验证码 爬虫滑块验证码_滑块_22

python 滑块 验证码 爬虫滑块验证码_python 滑块 验证码_23

3.2.2 锐化边缘

参考 2.锐化边缘,将裁剪后的小滑块锐化,结果如下:

python 滑块 验证码 爬虫滑块验证码_python 滑块 验证码_24

3. 2D卷积

现在我们有了目标矩阵(背景图)及 卷积核(小滑块),下面可以做2D卷积了。计算过程参考 3.1 卷积核作用

目标矩阵(背景图):

python 滑块 验证码 爬虫滑块验证码_滑块_19


卷积核(小滑块):

python 滑块 验证码 爬虫滑块验证码_python 滑块 验证码_24

python计算2D卷积时有现成的库,不用手写了,代码如下:

python 滑块 验证码 爬虫滑块验证码_卷积核_27

经过这一步,我们会得到类似 3.1 卷积核作用 示例运算后的结果。

4. 取卷积结果最大的点

再次理解下下面这俩图

目标矩阵(背景图):

python 滑块 验证码 爬虫滑块验证码_滑块_19


卷积核(小滑块):

python 滑块 验证码 爬虫滑块验证码_python 滑块 验证码_24

图中白色区域为255,黑色区域为0. 那么做完卷积运算后,卷积结果最大的点所在区域即为与卷积核(小滑块)边缘重合度最高的区域。那么在背景图中,与小滑块重合度最高的区域应该为缺口区域。因此我们找到的卷积结果最大的点就是背景图缺口的中心点。

寻找最大点的代码如下:

python 滑块 验证码 爬虫滑块验证码_卷积核_30

总结

在做2D卷积时,有个细节一定要注意,即在做卷积之前 卷积核的矩阵要逆时针旋转180度

直接做卷积

python 滑块 验证码 爬虫滑块验证码_卷积核_31


卷积核的矩阵时针旋转180度后做卷积

python 滑块 验证码 爬虫滑块验证码_python 滑块 验证码_32

至于为什么旋转180,这个我没理解明白,卷积就是这么定义的。如果有大神理解明白了,欢迎留言交流。

另外cv2有个模版匹配的函数,我们处理好小滑块及背景图后,也可以使用cv2的模版匹配功能去寻找缺口位置,有兴趣的同学可以试试。