第一步: 把不需要的信息通通去除,比如背景,干扰线,干扰像素等等,只剩下需要识别的文字,让图片变成2进制点阵.对于一些和文字颜色相同但是较为分散和单一的干扰像素点,我们可以用判断相邻像素的方法,对于每个点判断该点和相邻8个点的色差,若色差大于某个值,则+1,如果周围有超过6个点的色差都比较大,说明这个点是噪点.对于图像边界的一圈像素,周围没有8个像素,则统统清除,反正文字都在图片的中间位置.
第二步:为了能识别出字符,需要对要识别的文字图图片进行分割,把每个字符作为单独的一个图片看待.
第三步:对于部分特殊的验证码,需要对分割后的图片进行标准化处理,也就是说尽量把每个相同的字符都变成一样的格式,减少随机的程度,最简单的比如旋转还原,复杂点的比如扭曲还原等等.
第四步:这一步可以用很多种方法,最简单的就是模板对比,对每个出现过的字符进行处理后把点阵变成字符串,标明是什么字符后,通过字符串对比来判断相似度.
参考代码[Python]
#!/usr/bin/env python
#coding:utf-8
import Image,ImageFont,ImageDraw,ImageFilter
import os,sys
from math import atan2,pi,sqrt
import cPickle as pickle
#import psyco
#psyco.full()
DURATION = 1000
DIAMETER = 20
COLORDIFF = 10
TEXTCOLOR = 128
BACKGROUND = 255
DBFILE='sina.pk'
MODE = 'sample'
samples = None
def pixelcount(region):
frame=region.load()
(w,h)=region.size
count=0
for i in xrange(w):
for j in xrange(h):
if frame[i,j]!=BACKGROUND:
count+=1
return count
def printregion(region):
frame = region.getdata()
(w,h)=region.size
for i in xrange(h):
for j in xrange(w):
if frame[i*w+j] != BACKGROUND:
print '*',
else:
print ' ',
print
def purify(region):
frame = region.getdata()
(w,h)=region.size
for i in xrange(h):
for j in xrange(w):
if frame[i*w+j] != BACKGROUND:
region.putpixel( (j,i), TEXTCOLOR )
else:
region.putpixel( (j,i), BACKGROUND )
return region
def isdot(frame,pos):
(x,y)=pos
color=frame[x,y]
count=0
try:
if dist3d(frame[x,y-1],color)>5:
count+=1
except:
pass
try:
if dist3d(frame[x,y+1],color)>5:
count+=1
except:
pass
try:
if dist3d(frame[x-1,y],color)>5:
count+=1
except:
pass
try:
if dist3d(frame[x+1,y],color)>5:
count+=1
except:
pass
try:
if dist3d(frame[x-1,y-1],color)>5:
count+=1
except:
pass
try:
if dist3d(frame[x+1,y-1],color)>5:
count+=1
except:
pass
try:
if dist3d(frame[x+1,y+1],color)>5:
count+=1
except:
pass
try:
if dist3d(frame[x-1,y+1],color)>5:
count+=1
except:
pass
if count<3:
return False
else:
return True
def floodfillfilter(im,way=4,mincount=8):
frame=im.load()
(w,h)=im.size
points=[]
processedpoints=[]
global goodpoints
goodpoints=[]
for x in xrange(w):
for y in xrange(h):
color=frame[x,y]
if color==BACKGROUND or ((x,y) in processedpoints):
continue
processedpoints.append((x,y))
points.append((x,y))
#points.remove((x,y))
for (x,y) in points:
try:
if frame[x,y-1] ==color and (x,y-1) not in points:
points.append((x,y-1))
except:
pass
try:
if frame[x,y+1] ==color and (x,y+1) not in points:
points.append((x,y+1))
except:
pass
try:
if frame[x-1,y] ==color and (x-1,y) not in points:
points.append((x-1,y))
except:
pass
try:
if frame[x+1,y] ==color and (x+1,y) not in points:
points.append((x+1,y))
except:
pass
if way==8:
try:
if frame[x-1,y-1]==color and (x-1,y-1) not in points:
points.append((x-1,y-1))
except:
pass
try:
if frame[x+1,y-1]==color and (x+1,y-1) not in points:
points.append((x+1,y-1))
except:
pass
try:
if frame[x-1,y+1]==color and (x-1,y+1) not in points:
points.append((x-1,y+1))
except:
pass
try:
if frame[x+1,y+1]==color and (x+1,y+1) not in points:
points.append((x+1,y+1))
except:
pass
processedpoints.extend(points)
#print color,len(points)
#print points
if 116:
goodpoints.extend(points)
points=[]
#im.show()
return im
def removedot(im):
#global goodpoints
frame=im.load()
(w,h)=im.size
for i in xrange(w):
for j in xrange(h):
if frame[i,j]!=255:
count=0
try:
if frame[i,j-1]==255:
count+=1
except IndexError:
pass
try:
if frame[i,j+1]==255:
count+=1
except IndexError:
pass
try:
if frame[i-1,j-1]==255:
count+=1
except IndexError:
pass
try:
if frame[i-1,j]==255:
count+=1
except IndexError:
pass
try:
if frame[i-1,j+1]==255:
count+=1
except IndexError:
pass
try:
if frame[i+1,j-1]==255:
count+=1
except IndexError:
pass
try:
if frame[i+1,j]==255:
count+=1
except IndexError:
pass
try:
if frame[i+1,j+1]==255:
count+=1
except IndexError:
pass
if count>=6:
frame[i,j]=BACKGROUND
return im
def distance(r1,r2):
den1 = density(r1)
den2 = density(r2)
if 1.0*den1/den2>1:
(den1,den2) = (den2,den1)
r1 = r1.resize(r2.size)
d1 = r1.getdata()
d2 = r2.getdata()
same = [0,0]
total = [0,0]
for i in xrange(len(d1)):
if d1[i] != BACKGROUND:
total[0] += 1
if d1[i] == d2[i]:
same[0] += 1
if d2[i] != BACKGROUND:
total[1] += 1
if d1[i] == d2[i]:
same[1] += 1
try:
return 1 - 1.0*same[0]/total[0] * 1.0*same[1]/total[1] * 1.0*den1/den2
except:
return 1
def getframe(fname,strict=1):
'''return w*h key frame without noise'''
#open image
try:
im = Image.open(fname)
except:
return "File error!"
frame = im.load()
(w,h)=im.size
for i in xrange(w):
frame[i,0]=(255,255,255)
frame[i,h-1]=(255,255,255)
for i in xrange(h):
frame[0,i]=(255,255,255)
frame[w-1,i]=(255,255,255)
for i in xrange(w):
for j in xrange(h):
if frame[i,j]>(200,200,200):
frame[i,j]=(255,255,255)
if frame[i,j]!=(255,255,255):
frame[i,j]=(0,0,0)
im=im.convert('L')
im=removedot(im)
im=floodfillfilter(im,4,20)
#im.show()
return im
def loadsamples():
pks = pickle.load(open(DBFILE,'rb'))
samples = {}
for (pk,v) in pks.items():
im = Image.new('L',pk[0])
r = im.crop((0,0,pk[0][0],pk[0][1]))
r.fromstring(pk[1])
samples[r] = v
return samples
def match(region,samples):
# printregion( region )
if samples == {}:
return None
dists = []
for (k,v) in samples.items():
dists.append( (distance(region,k),v) )
dists.sort()
if MODE == 'sample':
return dists[0][1]
else:
i = 0
while dists[i][1] in ['H','I']:
i += 1
return dists[i][1]
# printregion( region )
# printregion( samples[ dists[0][1] ] )
def trainmatch(region,samples):
if samples == {}:
return None
dists = []
for (k,v) in samples.items():
dists.append( (distance(region,k),v) )
dists.sort()
confidence=1-dists[0][0]
first=dists[0][1]
for i in xrange(1,4):
if dists[i][1]==first:
confidence+=0.05
if confidence<0.75:
for i in xrange(0,10):
print dists[i]
if MODE == 'sample':
return [dists[0][1],confidence]
else:
i = 0
while dists[i][1] in ['H','I']:
i += 1
return [dists[i][1],confidence]
# printregion( region )
# printregion( samples[ dists[0][1] ] )
def imdiv(im):
'''div and return pieces of pics'''
frame = im.load()
(w,h) = im.size
horis = []
for i in xrange(w):
for j in xrange(h):
if frame[i,j] != BACKGROUND:
horis.append(i)
break
horis2 = [max(horis[0]-2,0)]
for i in xrange(1,len(horis)-1):
if horis[i]!=horis[i+1]-1:
horis2.append((horis[i]+horis[i+1])/2)
horis2.append(min(horis[-1]+3,w))
boxes=[]
for i in xrange(len(horis2)-1):
boxes.append( [horis2[i],0,horis2[i+1],h] )
for k in xrange(len(boxes)):
verts = []
for j in xrange(h):
for i in xrange(boxes[k][0],boxes[k][2]):
if frame[i,j] != BACKGROUND:
verts.append(j)
boxes[k][1] = max(verts[0]-2,0)
boxes[k][3] = min(verts[-1]+3,h)
if boxes == []:
return None
regions = []
for box in boxes:
regions.append( im.crop(box) )
return regions
def getcrop(region):
frame = region.getdata()
(w,h)=region.size
pts = []
ptsi = []
for i in xrange(h):
for j in xrange(w):
if frame[i*w+j] != BACKGROUND:
pts.append((i,j))
ptsi.append((j,i))
if pts == []:
return [0,0,1,1]
pp1 = min(pts)
pp2 = max(pts)
pp3 = min(ptsi)
pp4 = max(ptsi)
return [pp3[0],pp1[0],pp4[0]+1,pp2[0]+1]
def density(region):
frame = region.getdata()
(w,h) = region.size
area_all = w*h
area = 0
for i in xrange(h):
for j in xrange(w):
if frame[i*w+j] != BACKGROUND:
area += 1
return 1.0*area/area_all
def docrop(region):
croppos = getcrop(region)
newregion = region.crop(croppos)
#newregion.show()
return newregion
def dorotate(region):
# printregion(region)
deg = 0
maxdens = 0
for i in xrange(-15,15):
dens = density( docrop( region.rotate(i) ) )
if dens > maxdens:
deg = i
maxdens = dens
# printregion( docrop( region.rotate(deg) ) )
return region.rotate(deg)
def normalize(im):
# divide im
regions = imdiv(im)
for k in xrange(len(regions)):
regions[k] = dorotate(regions[k])
regions[k] = purify( docrop(regions[k]) )
return regions
def train(im):
global samples
try:
samples = pickle.load(open(DBFILE,'rb'))
except:
samples = {}
pickle.dump(samples,open(DBFILE,'wb'))
regions = normalize(im)
for region in regions:
if pixelcount(region)<=20:
continue
(w,h)=region.size
# region = region.resize((w*.8,h*.8))
printregion(region)
#''' Comment this section when no data to begin with
smps = loadsamples()
matchresult=trainmatch(region,smps)
if matchresult!=None:
result=matchresult[0]
confidence=matchresult[1]
print "Confidence:"+str(confidence)
print result.upper()
if confidence>=0.75:
continue
else:
print "No match data, please input"
#'''
#im.show()
print 'enter [0-9a-z] to add to library: '
ans = raw_input()
if len(ans) == 1:
key = (region.size,region.tostring())
samples[key] = ans[0]
pickle.dump(samples,open(DBFILE,'wb'))
def crackcode(im):
global samples
if not samples:
samples = loadsamples()
regions = normalize(im)
s = []
ans = []
for r in regions:
if pixelcount(r)<=20:
continue
value=match(r,samples).upper()
if value!=' ':
s.append(value)
if len(s) != 5:
return ['failed']
else:
for s1 in s[0]:
for s2 in s[1]:
for s3 in s[2]:
for s4 in s[3]:
for s5 in s[4]:
t = s1+s2+s3+s4+s5
ans.append(t)
return ans
if __name__ == '__main__':
if len(sys.argv)==1:
exit('Usage: sina.py filename.png')
if len(sys.argv) == 2:
if sys.argv[1].startswith('train'):
trainfiles = os.listdir(sys.argv[1])
#trainfiles.sort()
for trainfile in trainfiles:
trainfile = sys.argv[1]+'/'+trainfile
print trainfile
im = getframe(trainfile)
train(im)
os.remove(trainfile)
else:
filename=sys.argv[1]
extension=os.path.splitext(filename)[1]
#print extension
if MODE == 'sample':
samples = loadsamples()
else:
samples = loadttf()
results = {}
im = getframe(filename)
#im.show()
#printframe(im)
ans = crackcode(im)
#print ans
if 'failed' in ans:
im = getframe(filename,1)
ans = crackcode(im)
for result in ans:
print result