首先找到确定这次要识别的验证码
然后从某网站上下载大量同类型的验证码,人工标记上每个验证码的数值,由于此验证码识别容易就只标记了20个
图片是彩色的,我们要先让其变得简单变成灰度图像。
“灰度图像上每个像素的颜色值又称为灰度,指黑白图像中点的颜色深度,范围一般从0到255,白色为255,黑色为0。所谓灰度值是指色彩的浓淡程度,灰度直方图是指一幅数字图像中,对应每一个灰度值统计出具有该灰度值的象素数。”
这里就是用的是python的一个PIL的库 安装如下
pip install pillow
使用 from PIL import Image 加载我们下载好的图片 将图片灰度化
img=img.convert("L")
得到了数值范围为0-255的图片由于图片的白色和黄色难以分清 所以要卡一个阈值使得两种颜色可以分开来 多次尝试卡的是220。小于220的变成白色大于220的变成黑色,就完成了图片的二值化。
def ez_map(thresold): res=[] for i in range(256): if i<thresold: res.append(1) else: res.append(0) return res def pre_hd_ez(path,out_path): img=Image.open(path) img=img.convert("L") #二值 thresold=230 table=ez_map(thresold) # img=img.convert("1") img=img.point(table,'1') img.save(out_path) return img
接下来要把无关紧要的点处理了,如下图所示可以看下一个像素点周围的颜色,来对其颜色进行修正。如下图中间的黑色很可能白色更适合他。 可以通过一层来判断也可以通过多层来判断,我们管这个步骤叫做降噪。
白 | 白 | 白 |
白 | 黑 | 白 |
白 | 白 | 白 |
代码存在硬编码不规范地方日后会进行更正
def get_jz_color(img,x,y,level,layer): color_now=img.getpixel((x,y)) zero=0 one=0 all_point=0 for i in range(0-layer,0+layer+1): if x-i<0 or x+i>=img.size[0]: continue for j in range(0-layer,0+layer+1): if y-j<0 or y+j>=img.size[1]: continue if i==0 and j==0: continue if img.getpixel((i,j))==0: zero+=1 else: one+=1 all_point+=1 # return color_now # print(color_now) # return 0 # 0 黑 # return color_now if color_now==0: if one/all_point>7/8: return 1 else: return 0 if color_now==1: if zero/all_point>4/8: return 0 else: return 1 print(color_now) def pre_jz(img,out_path): img_after_table=[] for x in range(img.size[0]): img_after_table.append([]) for y in range(img.size[1]): num_color=get_jz_color(img,x,y,0,1) img_after_table[x].append(num_color) draw = ImageDraw.Draw(img) for i in range(img.size[0]): for j in range(img.size[1]): draw.point((i,j),img_after_table[i][j]) img.save(out_path) return img
此时我们得到了黑白分明的图片
分割
由于图片四个数字在图片上是等分的,我们可以直接对图片进行分割得到每个数字一个图片。
def pre_split_img(img,num,name,out_base_path): imgs=[] wide=img.size[0] # print (wide) high=img.size[1] one=int(wide/4) for i in range(num): img1=img.crop((i*one,0,(i+1)*one,high)) imgs.append(img1) if out_base_path: name1=''.join(name)+str(name[i]) img1.save(out_base_path+'/'+name[i]+'/'+name1+'.png') return imgs
将分割好的图片按照之前标记的结果分成0-9 存到0-9不同的文件夹里面
接下来我们有了各个数字图片的样本。
如何和新来的图片进行匹配?
我们要找到能代替某个数字图片的方法,比如把0这个图片分成6份每一份计算出 黑色像素点/总像素点的值然后 对多有0的图片都如此操作,分别取 分割出来的6份中第一份的平均值,这样的到了能代表0这个图片的6份数值存起来后面用。
def get_block_score(img): sum=0 black=0 for i in range(img.size[0]): for j in range(img.size[1]): if img.getpixel((i,j))==0: black+=1 sum+=1 return black,sum # 计算特征值 def get_features_vaule_by_img(img): wide=img.size[0] one_wide = int(wide/2) high=img.size[1] one_high=int(high/3) score_lsit=[] for i in range(3): for j in range(2): img_one=img.crop((j*one_wide,i*one_high,(j+1)*one_wide,(i+1)*one_high)) black,sum=get_block_score(img_one) score_lsit.append(black*1.0/sum) return score_lsit def get_features_vaule(dir_path): arry_size=6 score_lsit_res=[] for i in range(arry_size): score_lsit_res.append(0) sum_file=0 for root, dirs, files in os.walk(dir_path): for f in files: if not '.png' in f: continue img=Image.open(os.path.join(root, f)) sum_file+=1 score_lsit=get_features_vaule_by_img(img) for i in range(arry_size): score_lsit_res[i]=score_lsit[i]+score_lsit_res[i] # 求平均值 for i in range(arry_size): score_lsit_res[i]=str(score_lsit_res[i]/sum_file) value='\n'.join(score_lsit_res) fs=open(dir_path+'feture.txt','w') fs.write(value) fs.close()
此时我们拿来一个新的验证码
让这个新的验证码经过 灰度化,二值化,分割,在每个数字分割6份计算黑点/总点的占比,将计算好的 6个值与我们之前给0-9计算的这个值分别进行比较 找出和0-9最相似的数字 这个数字就是我们想要的结果
完整代码如下 获取二维码的网站已经特殊处理
import requests,os from PIL import Image from PIL import ImageDraw import math def download_image(): url='xxx' for i in range(0,20): import time time.sleep(1) res=requests.get(url).content f=open('D:\project/ocr/image/'+str(i)+'test.png','wb') f.write(res) f.close() # download_image() def makefile(): for i in range(0,10): os.makedirs('D:\project/ocr/yangben/'+str(i)) def ez_map(thresold): res=[] for i in range(256): if i<thresold: res.append(1) else: res.append(0) return res def pre_hd_ez(path,out_path): img=Image.open(path) img=img.convert("L") #二值 thresold=230 table=ez_map(thresold) # img=img.convert("1") img=img.point(table,'1') img.save(out_path) return img def get_jz_color(img,x,y,level,layer): color_now=img.getpixel((x,y)) zero=0 one=0 all_point=0 for i in range(0-layer,0+layer+1): if x-i<0 or x+i>=img.size[0]: continue for j in range(0-layer,0+layer+1): if y-j<0 or y+j>=img.size[1]: continue if i==0 and j==0: continue if img.getpixel((i,j))==0: zero+=1 else: one+=1 all_point+=1 # return color_now # print(color_now) # return 0 # 0 黑 # return color_now if color_now==0: if one/all_point>7/8: return 1 else: return 0 if color_now==1: if zero/all_point>4/8: return 0 else: return 1 print(color_now) def pre_jz(img,out_path): img_after_table=[] for x in range(img.size[0]): img_after_table.append([]) for y in range(img.size[1]): num_color=get_jz_color(img,x,y,0,1) img_after_table[x].append(num_color) draw = ImageDraw.Draw(img) for i in range(img.size[0]): for j in range(img.size[1]): draw.point((i,j),img_after_table[i][j]) img.save(out_path) return img def pre_split_img(img,num,name,out_base_path): imgs=[] wide=img.size[0] # print (wide) high=img.size[1] one=int(wide/4) for i in range(num): img1=img.crop((i*one,0,(i+1)*one,high)) imgs.append(img1) if out_base_path: name1=''.join(name)+str(name[i]) img1.save(out_base_path+'/'+name[i]+'/'+name1+'.png') return imgs # img=pre_hd_ez('D:\project/ocr/image/0/6809.png','D:\project/ocr/image/0/res.png') # pre_jz(img,'D:\project/ocr/image/0/res1.png') # makefile() def get_block_score(img): sum=0 black=0 for i in range(img.size[0]): for j in range(img.size[1]): if img.getpixel((i,j))==0: black+=1 sum+=1 return black,sum # 计算特征值 def get_features_vaule_by_img(img): wide=img.size[0] one_wide = int(wide/2) high=img.size[1] one_high=int(high/3) score_lsit=[] for i in range(3): for j in range(2): img_one=img.crop((j*one_wide,i*one_high,(j+1)*one_wide,(i+1)*one_high)) black,sum=get_block_score(img_one) score_lsit.append(black*1.0/sum) return score_lsit def get_features_vaule(dir_path): arry_size=6 score_lsit_res=[] for i in range(arry_size): score_lsit_res.append(0) sum_file=0 for root, dirs, files in os.walk(dir_path): for f in files: if not '.png' in f: continue img=Image.open(os.path.join(root, f)) sum_file+=1 score_lsit=get_features_vaule_by_img(img) for i in range(arry_size): score_lsit_res[i]=score_lsit[i]+score_lsit_res[i] # 求平均值 for i in range(arry_size): score_lsit_res[i]=str(score_lsit_res[i]/sum_file) value='\n'.join(score_lsit_res) fs=open(dir_path+'feture.txt','w') fs.write(value) fs.close() def pre_img_pipeline(): for root, dirs, files in os.walk('D:\project/ocr/image'): # 遍历文件 for f in files: img=pre_hd_ez(os.path.join(root, f),'D:\project/ocr/image1/'+f) img=pre_jz(img,'D:\project/ocr/image2/'+f) pre_split_img(img,4,f.replace('.png',''),'D:\project/ocr/yangben') for i in range(10): get_features_vaule('D:\project/ocr/yangben/'+str(i)+'/') # pre_img_pipeline() # 获取0-9的特征值 def get_base_features_vaules(path): res={} for i in range(10): fs=open(path+str(i)+'/feture.txt','r') value=fs.read() values=value.split('\n') values=[float(feature_value) for feature_value in values] res[i]=values fs.close() return res # 拉去待识别数据 def download_pending_image(): url='xxx' for i in range(1): import time time.sleep(1) res=requests.get(url).content path='D:\project/ocr/pending/'+str(i)+'test.png' f=open(path,'wb') f.write(res) f.close() img=Image.open(path) return path,img # 关联性计算 # 求向量点积 def add_vectors(a,b): res = 0 for i in range(len(a)): res += float(a[i])*float(b[i]) return res # 求向量的模 def module_vectors(a): return math.sqrt(sum([float(x)**2 for x in a])) # 求向量夹角余弦值 def get_cos(a,b): add_a, add_b = module_vectors(a),module_vectors(b) if add_a!=0 and add_b!=0: return add_vectors(a,b)/(add_a*add_b) return 0 # # 计算一个数字的值 def sb_vaule(pending_score_list,base_features_map): res=[] max_score=-10.0 max_key=None for key,values in base_features_map.items(): idx=0 scores=0 scores=get_cos(pending_score_list,values) if scores>max_score: max_score=scores max_key=key idx+=1 return max_key def run(): #取基础信息 base_features_map=get_base_features_vaules('D:\project/ocr/yangben/') path,img=download_pending_image() img=pre_hd_ez(path,'D:\project/ocr/pending/deal/first_step.png') img=pre_jz(img,'D:\project/ocr/pending/deal/second_step.png') imgs=pre_split_img(img,4,'','') res='' for img_one in imgs: pending_score_list=get_features_vaule_by_img(img_one) # 计算一个数字的值 res_one=sb_vaule(pending_score_list,base_features_map) res+=str(res_one) print(res) run()