某网站验证码的识别笔记【一】

新博客地址:http://gorthon.sinaapp.com/

第一:获取验证码素材

#! /usr/bin/env python
# coding: u8
# file:get_img.py

import urllib2 

if __name__ == '__main__': 
    [file('./%d.png'%i, 'wb').write(urllib2.urlopen('http://www.呵呵.com/captcha/').read()) for i in range(100) ]


类似如下的图片:





第二:获取背景色, 背景上的表格颜色

由于所有图片的背景色相同,背景上的表格颜色相同,而且表格在背景上的位置也是固定不变的,所以操作变得更加简单.

#! /usr/bin/env python
# coding: u8
# file:getinfo.py
import Image
im = Image.open('./0.png')
w, h = im.size
for y in range(h):
    for x in range(w):
        pixel = im.getpixel((x, y))
        print pixel,
        if x == 10:
           raise


得到的结果如下:

>>> (227, 218, 237) (128, 191, 255) (227, 218, 237) (227, 218, 237) (227, 218, 237) (227, 218, 237) (128, 191, 255) (227, 218, 237) (227, 218, 237) (227, 218, 237) (227, 218, 237)

对照图片可以得到:

BG_COLOR = (227, 218, 237) # 背景颜色
GRID_COLOR = (128, 191, 255) # 背景表格颜色


第三:去背景上的表格

#! /usr/bin/env python
# coding: utf-8
import Image
# 随便找个图片先获得以下参数:
GRID_COLOR = (128, 191, 255) #背景表格颜色
BG_COLOR = (227, 218, 237) # 背景颜色
im = Image.open('./0.png')
w, h = im.size
im.show() # 去除背景表格前的图片
for y in range(h):
    for x in range(w):
        pixel = im.getpixel((x, y))
        if pixel == GRID_COLOR:
            im.putpixel((x, y), BG_COLOR) # 将表格颜色设置为背景色即可清除表格
im.show() # 去除背景表格后的图片


去除背景表格前:


去除背景表格后:


第四:获得弧线的颜色

获得弧线的颜色有点困难(当然获取像素值的时候可以用其他工具比如gcolor2等来获取就可以了,这里通过PIL来实现),不过观察一下就知道其实很简单了.在我下载的100张图片当中每张图片的弧线都是相同的规律.

截取图片的后半部分:


可以看出:每一列从后往前遍历,遇到的第一个不为背景色的像素点即为弧线的末端.

def getArcColor(im):
    global ARC_COLOR
    w, h = im.size
    for x in range(w-1, w/2, -1):
        for y in range(h):
            pixel = im.getpixel((x, y))
            if pixel != BG_COLOR:
                ARC_COLOR = pixel
                return


结果为:(128, 128, 255),多弄几个图片测试证明结果正确,故 ARC_COLOR = (128, 128, 255).

第五:去除弧线

定义全局变量

ARC_COLOR = (128, 128, 255) # 干扰线(弧线)颜色

然后在eraseGrid函数里面加上下面(有颜色的语句)即可消除弧线:

if pixel == GRID_COLOR or pixel == ARC_COLOR:
                im.putpixel((x, y), BG_COLOR) # 将表格及弧线颜色设置为背景色即可清除表格和弧线


效果如下:


明显地,仅仅像上面程序那样去除弧线是不行的,他把字符也擦除了!

所以在去除的时候还要加上判断,如果此弧线上或下的点为非背景色,那么将弧线的颜色设置为该非背景色.实现如下:

     if pixel == GRID_COLOR:
                im.putpixel((x, y), BG_COLOR) # 将表格颜色设置为背景色即可清除表格
            elif pixel == ARC_COLOR:
                up = y > 0 and im.getpixel((x, y-1)) or None
                down = y < h-1 and im.getpixel((x, y+1)) or None
                up_down = up or down
                im.putpixel((x, y), up_down and up_down or BG_COLOR)


结果:


注:上面是基于像素来处理的,只能征对本验证码有效.

下面的程序可以简单的处理:

import Image, ImageEnhance, ImageFilter
im = Image.open('./0.png')
enhancer = ImageEnhance.Contrast(im)
im = enhancer.enhance(6) # 提高对比度
im = im.convert('1') # 二值化
im = im.filter(ImageFilter.MedianFilter) # 中值去噪
im.show()


结果:


可以根据需要设定对比度的的增量,然后就是按照上面的方法将弧线去除就可以了.

还有就是用聚类分析的话也可以,但是这个地方就不用那么复杂了,硬性K均值聚类(hcm)那可是相当复杂的了,当初做字幕检测的时候(用的C++)写了好长的代码啊,现在可不想再去碰那东西了.但是如果上面用了聚类的话在字符切割的时候就很方便了,直接切就是了.

整理一下代码得到以上步骤的完整代码:

#! /usr/bin/env python
# coding: utf-8
import Image, ImageEnhance, ImageFilter
# 随便找个图片先获得以下参数:
GRID_COLOR = (128, 191, 255) #背景表格颜色
BG_COLOR = (227, 218, 237) # 背景颜色
ARC_COLOR = (128, 128, 255) # 干扰线(弧线)颜色
BLACK = (0,0,0)
WHITE = (255, 255, 255)
def eraseGridAndArc(im):
    w, h = im.size
    pixels = im.load()
    for y in range(h):
        for x in range(w):
            pixel = pixels[x, y]
            if pixel == BG_COLOR or pixel == GRID_COLOR:
                im.putpixel((x, y), WHITE)
            elif pixel == ARC_COLOR:
                up = y > 0 and pixels[x, y-1] or None
                down = y < h-1 and pixels[x, y+1] or None
                up_down = up or down
                im.putpixel((x, y), up_down and up_down or WHITE)
    enhancer = ImageEnhance.Contrast(im)
    im = enhancer.enhance(255) # 提高对比度,这个参数大点就好
    im = im.convert('1') # 二值化
    im.show()
    return im
if __name__ == '__main__':
    im = Image.open('./0.png')
    eraseGridAndArc(im)


结果如下:


第六:字符分割

字符分割的方法有很多,如垂直投影法,联通域分析法,以上二者结合等,由于本验证码的各个字符间没有粘连现象,所以可以用简单点的垂直投影法.

垂直投影法:逐列扫描二值图片,计算当列像素点为1的点个数,累加结果即为当列投影的个数,然后画成一张图,形成波峰波谷,再以波谷(本验证码波谷为0)为界分割字符.

得到垂直投影图:

def getPoint(im, end=False):
    pixels = im.load()
    w, h = im.size
    range_w = end and range(w-1, 0, -1) or range(w)
    for x in range_w:
        for y in range(h):
            if pixels[x, y] == 0:
                return end and x + 1 or x
def getVerticalProjection(im):
    '''
    得到垂直投影图, 返回投影图数据
    '''
    pixels = im.load()
    w, h = im.size
    start_x = getPoint(im)
    end_x = getPoint(im, end=True)
    graph = [0] * (end_x - start_x)
    for x in range(start_x, end_x):
        for y in range(h):
            pixel = pixels[x, y]
            if pixel == 0: # 此列有字符
                graph[x - start_x] += 1
    return start_x, end_x, graph
def showVerticalProjection(graph):
    w = len(graph)
    h = max(graph)
    img = Image.new('1', (w, h))
    for x in range(w):
        for y in range(h):
            if y <= graph[x]:
                img.putpixel((x, y), 255)
            else:
                break
    img = img.transpose(Image.FLIP_TOP_BOTTOM)
    img.show()
if __name__ == '__main__':
    im = Image.open('./0.png')
    im = eraseGridAndArc(im)
    graph = getVerticalProjection(im)
    showVerticalProjection(graph)
    print graph


结果:


graph = [18, 25, 25, 25, 13, 8, 7, 7, 8, 7, 8, 18, 16, 14, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 5, 9, 12, 14, 21, 16, 13, 10, 4, 4, 4, 3, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 5, 6, 12, 17, 19, 21, 17, 12, 9, 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 16, 17, 13, 11, 9, 3, 3, 3, 8, 14, 19, 19, 13, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 19, 23, 24, 24, 7, 6, 6, 6, 6, 6]

然后以graph中的0为界限分割即可 .

切割写的有点糟糕......如下代码:

def cut(start_x, end_x, graph, im):
    chars = [start_x]
    for i, v in enumerate(graph):
        if v !=0:
            if graph[i - 1] == 0:
                chars.append(i + start_x)
        elif graph[i - 1] !=0:
            chars.append(i + start_x)
    chars.append(end_x)
    
    result = list()
    for i in range(len(chars) - 1):
            result.append((chars[i], chars[i + 1] - 1))
    result = [v for (i, v) in enumerate(result) if not i%2] # 去除波谷的0
    w, h = im.size
    chars = list()
    for char_start_x, char_end_x  in result:
        chars.append(im.crop((char_start_x, 0, char_end_x, h)))
    # 纵向切割之后再横向切割:
    result = list()
    for char in chars:
        w, h = char.size
        pixels = char.load()
        try:
            for y in range(h):
                for x in range(w):
                    if pixels[x, y] == 0:
                        start_y = y
                        raise
        except:
            pass
        try:
            for y in range(h-1, -1, -1):
                for x in range(w):
                    if pixels[x, y] == 0:
                        end_y = y
                        raise
        except:
            pass
        result.append(char.crop((0, start_y, w, end_y)))
    return result
if __name__ == '__main__':
    im = Image.open('./0.png')
    im = eraseGridAndArc(im)
    start_x, end_x, graph = getVerticalProjection(im)
    chars = cut(start_x, end_x, graph, im)
    for char in chars:
        char.show()


效果如上图(图中最上面的黑色部分是标题栏,与字符无关----图片太小,标题栏没显示完整...)

最后将

第七:字符旋转

【未完待续……】

第2篇:http://blog.csdn.net/bh20077/article/details/7041400

第3篇:http://blog.csdn.net/bh20077/article/details/7311183

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值