新博客地址: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.youkuaiyun.com/bh20077/article/details/7041400
第3篇:http://blog.youkuaiyun.com/bh20077/article/details/7311183