第一步: 把不需要的信息通通去除,比如背景,干扰线,干扰像素等等,只剩下需要识别的文字,让图片变成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