<-Back

豆瓣验证码识别(一)

    在几年前写过一个豆瓣自动回帖的机器人程序,因为没有处理验证码,所以回帖速度和效率很差。最近正好春节放假在家比较闲,计划用给机器人开发一个验证码识别功能,来提高回帖的数量,同时用博客的形式记录下开发过程。

  在开始破解工作之前,需要先下载大量的验证码图片来观察,寻找规律,看准方向想好思路之后再开始撸起袖子写代码,比较不容易走弯路。

经过观察我发现,豆瓣的验证码初看上去比较高大上,总结起来这样有几个特点:

  • 背景图案看上去比较复杂,但是大部分图片的背景颜色与字体色差较大
  • 图案中有比较均匀的白色和黑色噪点
  • 只有英文单词,没有数字、中文、符号等
  • 字体为黑色,有稍微的旋转和扭曲
  • 字体无遮盖、粘黏
  • 每个字母周围都有白色描边

   先随便选一张验证码图片用photo shop打开后放大,用吸管工具吸取字体上的像素点可以看到,字体主体的黑色部分rgb值可大概在20以内。

因此这里可以简单得以20为阈值,将rgb值都小于该阈值的点设置为黑色,大于的设置为白色。

def pre(self):
        width, height = self.img.size
        threshold = 21
        for i in range(0, width):
            for j in range(0, height):
                p = self.frame[i, j]
                r, g, b = p
                if r > threshold or g > threshold or b > threshold:
                    self.frame[i, j] = WHITE
                else:
                    self.frame[i, j ] = BLACK

二值化后得到的图片如下:

背景已经被去掉了,接下来是处理噪点。

    常用的去噪方法有:均值滤波、中值滤波、高斯滤波以及BM3D等等。通过实验,我发现用中值滤波可以达到比较满意的效果,如图:

选择不同的滤波窗口将会得到不同的去噪效果,“十”字窗口可以保留较多的细节,但是没法处理2pix*2pix大小的噪点,因为用十字窗口对2*2像素块上的每一个像素取中值,得到的都是黑色,因此这里我选择了3*3窗口,代码如下:

def remove_noise(self, window=1):
        """ 中值滤波移除噪点
        """
        if window == 1:
            # 十字窗口
            window_x = [1, 0, 0, -1, 0]
            window_y = [0, 1, 0, 0, -1]
        elif window == 2:
            # 3*3矩形窗口
            window_x = [-1,  0,  1, -1, 0, 1, 1, -1, 0]
            window_y = [-1, -1, -1,  1, 1, 1, 0,  0, 0]

        width, height = self.img.size
        for i in xrange(width):
            for j in xrange(height):
                box = []
                black_count, white_count =  0, 0
                for k in xrange(len(window_x)):
                    d_x = i + window_x[k]
                    d_y = j + window_y[k]
                    try:
                        d_point = self.frame[d_x, d_y]
                        if d_point == BLACK:
                            box.append(1)
                        else:
                            box.append(0)
                    except IndexError:
                        self.frame[i, j] =  WHITE
                        continue

                box.sort()
                if len(box) == len(window_x):
                    mid = box[len(box)/2]
                    if mid == 1:
                        self.frame[i, j] =  BLACK
                    else:
                        self.frame[i, j] =  WHITE

到这一步,噪点已经处理完成了,接下来就是切分单个字符,我将会在下一篇博客中详细介绍。

参考资料:

“多说”已关闭,原评论数据丢失