准备工作
首先找到确定这次要识别的验证码
然后从某网站上下载大量同类型的验证码,人工标记上每个验证码的数值,由于此验证码识别容易就只标记了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()