在针对验证码,做一个简单的自动网上签到程序(二) 中已经进行了图像二值化、降噪等处理,也得到了非常好的验证码的二值化字符串。但是在进行的试验中发现出现一些错误,尤其是在二值化之后7变成1 的情况经常出现,其原因在于前面二值化的时候选取的阈值为200,而不是根据图像动态选取的阈值,阈值的选取并不能够包括全部字符信息。
为此查阅了文献,发现设定阈值有相当多的方法,我们选取较为常见的otsu法计算图像阈值。
#! /usr/bin/env python
#coding=utf-8
import Image,ImageEnhance,ImageFilter,ImageDraw
import ImageTk
def OtsuGray(image):
# 创建Hist
#image = Image.open("4.f")
image.convert('L')
hist = image.histogram()
print len(hist)
print hist
'''大津法由大津于1979年提出,对图像Image,记t为前景与背景的分割阈值,
前景点数占图像比例为w0,平均灰度为u0;背景点数占图像比例为w1,平均灰度为u1。
图像的总平均灰度为:u=w0*u0+w1*u1。从最小灰度值到最大灰度值遍历t,
当t使得值g=w0*(u0-u)2+w1* (u1-u)2 最大时t即为分割的最佳阈值。
直接应用大津法计算量较大,因此我们在实现时采用了等价的公式g=w0*w1*(u0-u1)2。
部分计算过程如下'''
# 开始计算
# 计算总亮度
totalH = 0
for h in range(0,256):
v =hist[h]
if v == 0 : continue
totalH += v*h
print h,totalH,v*h
width = image.size[0]
height = image.size[1]
total = width*height
print width
print height
print "总像素:%d;总亮度:%d平均亮度:%0.2f"%(total,totalH,totalH/total)
# t=0和t=255的时候无法构成分割,所以从t=1开始计算一致到t=255
# 初始化v值
v = 0
gMax = 0.0
tIndex = 0
# temp
n0Acc = 0.0
n1Acc = 0.0
n0H = 0.0
n1H = 0.0
for t in range(1,255):
v = hist[t-1]
if v == 0: continue
n0Acc += v #灰度小于t的像素的数目
n1Acc = total - n0Acc #灰度大于等于t的像素的数目
n0H += (t-1)*v #灰度小于t的像素的总亮度
n1H = totalH - n0H #灰度大于等于t的像素的总亮度
if n0Acc > 0 and n1Acc > 0:
u0 = n0H/n0Acc # 灰阶小于t的平均灰度
u1 = n1H/n1Acc # 灰阶大于等于t的平均灰度
w0 = n0Acc/total # 灰阶小于t的像素比例
w1 = 1.0-w0 # 灰阶大于等于t的像素的比例
uD = u0-u1
g = w0 * w1 * uD * uD
print 'g=',g
#if debug > 2: print "t=%3d; u0=%.2f,u1=%.2f,%.2f;n0H=%d,n1H=%d; g=%.2f"\
# %(t,u0,u1,u0*w0+u1*w1,n0H,n1H,g)
print t,u0,u1,w0,w1,g
if gMax < g:
gMax = g
tIndex = t
# if debug >0 : print "gMaxValue=%.2f; t = %d ; t_inv = %d"\
# %(gMax,tIndex,255-tIndex)
print tIndex
return tIndex
#a=OtsuGray()
#print a
上述计算方法也是从javaeye摘抄来的,算法很简单。
这样计算了每幅图片的阈值之后发现仍然存在着图像缺失的问题等。因此需要考虑对图像再做进一步的处理,以便二值化后基本反映数字的轮廓又能够不丢失信息。
因此我们在二值化之前对图像进行二值化和边界增强处理。后面降噪的步骤可以去掉。
相应的部分代码改为
image = image.filter(ImageFilter.EDGE_ENHANCE())
image = image.filter(ImageFilter.MedianFilter())
image.show()
#enhancer = ImageEnhance.Contrast(im)
otsu1=otsu.OtsuGray(image)
#twoValue(image,200)
print otsu1
twoValue(image,otsu1)
这样我们的字模提取已经基本上全部完成,可以开始制作字模库。验证码的识别仅仅剩下最后一步,模板与验证码的对照步骤。现在处理的效果是这样的。
原图
处理后
虽然现在的处理效果没有前面的处理办法边缘平滑,数字清晰,但是对于每个验证码,反映的基本特征均比较清晰,稳定性也大大增强。