python怎么做一个识别率百分百的OCR
小编给大家分享一下python怎么做一个识别率百分百的OCR,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!
写在前面
当然这里说的百分百可能有点夸张,但其实想象一下,游戏里面的某个窗口的字符就是那种样子,不会变化的。而且识别的字符可能也不需要太多。中文有大几千个常用字,还有各种符号,其实都不需要。
这里针对的场景很简单,主要是有以下几点:
识别的字符不多:只要识别几十个常用字符即可,比如说26个字母,数字,还有一些中文。
背景统一,字体一致:我们不是做验证码识别,我们要识别的字符都是清晰可见的。
字符和背景易分割:一般来说就是对图片灰度化之后,黑底白字或者白底黑字这种。
技术栈
这里用到的主要就是python+opencv了。
python3
opencv-python
环境主要是以下的库:
pipinstallopencv-python pipinstallimutils pipinstallmatplotlib
实现思路
首先看下图片的灰度图。
第一步:二值化,将灰度转换为只有黑白两种颜色。
第二步:图像膨胀,因为我们要通过找轮廓算法找到每个字符的轮廓然后分割,如果是字符还好,中文有很多左右偏旁,三点水这种无法将一个整体进行分割,这里通过膨胀将中文都黏在一起。
第三步:找轮廓。
第四步:外接矩形。我们需要的字符是一个矩形框,而不是无规则的。
第五步:过滤字符,这里比如说标点符号对我来说没用,我通过矩形框大小把它过滤掉。
第六步:字符分割,根据矩形框分割字符。
第七步:构造数据集,每一类基本上放一两张图片就可以。
第八步:向量搜索+生成结果,根据数据集的图片,进行向量搜索得到识别的标签。然后根据图片分割的位置,对识别结果进行排序。
具体实现
读取图片
首先先读取待识别的图片。
importcv2 importnumpyasnp frommatplotlibimportpyplotasplt frommatplotlib.colorsimportNoNorm importimutils fromPILimportImage img_file="test.png" im=cv2.imread(img_file,0)
使用matplotlib画图结果如下:
二值化
在进行二值化之前,首先进行灰度分析。
灰度值是在0到255之间,0代表黑色,255代表白色。可以看到这里背景色偏黑的,基本集中在灰度值30,40附近。而字符偏白,大概在180灰度这里。
这里选择100作为分割的阈值。
thresh=cv2.threshold(im,100,255,cv2.THRESH_BINARY)[1]
2值化后效果如下:
图像膨胀
接下来进行一个图像的纵向膨胀,选择一个膨胀的维度,这里选择的是7。
kernel=np.ones((7,1),np.uint8) dilation=cv2.dilate(thresh,kernel,iterations=1)
找轮廓
接下来调用opencv找一下轮廓,
#找轮廓 cnts=cv2.findContours(dilation.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) cnts=imutils.grab_contours(cnts)
接下来我们再读取一下原图,绘制轮廓看下轮廓的样子。
外接矩形
对于轮廓我们可以做外接矩形,这里可以看下外接矩形的效果。
过滤字符
这里过滤字符的原理其实就是将轮廓内的颜色填充成黑色。下面的代码是将高度小于15的轮廓填充成黑色。
fori,cinenumerate(cnts): x,y,w,h=cv2.boundingRect(c) if(h<15): cv2.fillPoly(thresh,pts=[c],color=(0))
填充后可以看到标点符号就没了。
字符分割
因为图像是个矩阵,最后字符分割就是使用切片进行分割。
forcincnts: x,y,w,h=cv2.boundingRect(c) if(h<15): continue cropImg=thresh[y:y+h,x:x+w] plt.imshow(cropImg) plt.show()
构造数据集
最后我们创建数据集进行标注,就是把上面的都串起来,然后将分割后的图片保存到文件夹里,并且完成标注。
importcv2 importnumpyasnp importimutils frommatplotlibimportpyplotasplt importuuid defsplit_letters(im): #2值化 thresh=cv2.threshold(im,100,255,cv2.THRESH_BINARY)[1] #纵向膨胀 kernel=np.ones((7,1),np.uint8) dilation=cv2.dilate(thresh,kernel,iterations=1) #找轮廓 cnts=cv2.findContours(dilation.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) cnts=imutils.grab_contours(cnts) #过滤太小的 fori,cinenumerate(cnts): x,y,w,h=cv2.boundingRect(c) ifh<15: cv2.fillPoly(thresh,pts=[c],color=(0)) #分割 char_list=[] forcincnts: x,y,w,h=cv2.boundingRect(c) ifh<15: continue cropImg=thresh[y:y+h,x:x+w] char_list.append((x,cropImg)) returnchar_list foriinrange(1,10): im=cv2.imread(f"test{i}.png",0) forchinsplit_letters(im): print(ch[0]) filename=f"ocr_datas/{str(uuid.uuid4())}.png" cv2.imwrite(filename,ch[1])
向量搜索(分类)
向量搜索其实就是个最近邻搜索的问题,我们可以使用sklearn中的KNeighborsClassifier。
训练模型代码如下:
importos importnumpyasnp fromsklearn.neighborsimportKNeighborsClassifier importcv2 importpickle importjson max_height=30 max_width=30 defmake_im_template(im): template=np.zeros((max_height,max_width)) offset_height=int((max_height-im.shape[0])/2) offset_width=int((max_width-im.shape[1])/2) template[offset_height:offset_height+im.shape[0],offset_width:offset_width+im.shape[1]]=im returntemplate label2index={} index2label={} X=[] y=[] index=0 for_dirinos.listdir("ocr_datas"): new_dir="ocr_datas/"+_dir ifos.path.isdir(new_dir): label2index[_dir]=index index2label[index]=_dir forfilenameinos.listdir(new_dir): iffilename.endswith("png"): im=cv2.imread(new_dir+"/"+filename,0) tpl=make_im_template(im)#生成固定模板 tpl=tpl/255#归一化 X.append(tpl.reshape(max_height*max_width)) y.append(index) index+=1 print(label2index) print(index2label) model=KNeighborsClassifier(n_neighbors=1) model.fit(X,y) withopen("simple_ocr.pickle","wb")asf: pickle.dump(model,f) withopen("simple_index2label.json","w")asf: json.dump(index2label,f)
这里有一点值得说的是如何构建图片的向量,我们分隔的图片的长和宽是不固定的,这里首先需要使用一个模型,将分隔后的图片放置到模板的中央。然后将模型转换为一维向量,当然还可以做一个归一化。
生成结果
最后生成结果就是还是先分割一遍,然后转换为向量,调用KNeighborsClassifier模型,找到最匹配的一个作为结果。当然这是识别一个字符的结果,我们还需要根据分割的位置进行一个排序,才能得到最后的结果。
importcv2 importnumpyasnp importimutils fromsklearn.neighborsimportKNeighborsClassifier importpickle importjson withopen("simple_ocr.pickle","rb")asf: model=pickle.load(f) withopen("simple_ocr_index2label.json","r")asf: index2label=json.load(f) max_height=30 max_width=30 defmake_im_template(im): template=np.zeros((max_height,max_width)) offset_height=int((max_height-im.shape[0])/2) offset_width=int((max_width-im.shape[1])/2) template[offset_height:offset_height+im.shape[0],offset_width:offset_width+im.shape[1]]=im returntemplate.reshape(max_height*max_width) defsplit_letters(im): #2值化 thresh=cv2.threshold(im,100,255,cv2.THRESH_BINARY)[1] #纵向膨胀 kernel=np.ones((7,1),np.uint8) dilation=cv2.dilate(thresh,kernel,iterations=1) #找轮廓 cnts=cv2.findContours(dilation.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) cnts=imutils.grab_contours(cnts) #过滤太小的 fori,cinenumerate(cnts): x,y,w,h=cv2.boundingRect(c) ifh<15: cv2.fillPoly(thresh,pts=[c],color=(0)) #分割 char_list=[] forcincnts: x,y,w,h=cv2.boundingRect(c) ifh<15: continue cropImg=thresh[y:y+h,x:x+w] char_list.append((x,cropImg)) returnchar_list defocr_recognize(fname): im=cv2.imread(fname,0) char_list=split_letters(im) result=[] forchinchar_list: res=model.predict([make_im_template(ch[1])])[0]#识别单个结果 result.append({ "x":ch[0], "label":index2label[str(res)] }) result.sort(key=lambdak:(k.get('x',0)),reverse=False)#因为是单行的,所以只需要通过x坐标进行排序。 return"".join([it["label"]foritinresult]) print(ocr_recognize("test1.png"))
以上是“python怎么做一个识别率百分百的OCR”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注恰卡编程网行业资讯频道!
推荐阅读
-
Python中怎么动态声明变量赋值
这篇文章将为大家详细讲解有关Python中怎么动态声明变量赋值,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文...
-
python中变量的存储原理是什么
-
Python中怎么引用传递变量赋值
这篇文章将为大家详细讲解有关Python中怎么引用传递变量赋值,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文...
-
python中怎么获取程序执行文件路径
python中怎么获取程序执行文件路径,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的...
-
Python中如何获取文件系统的使用率
Python中如何获取文件系统的使用率,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴...
-
Python中怎么获取文件的创建和修改时间
这篇文章将为大家详细讲解有关Python中怎么获取文件的创建和修改时间,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读...
-
python中怎么获取依赖包
今天就跟大家聊聊有关python中怎么获取依赖包,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据...
-
python怎么实现批量文件加密功能
-
python中怎么实现threading线程同步
小编给大家分享一下python中怎么实现threading线程同步,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!...
-
python下thread模块创建线程的方法
本篇内容介绍了“python下thread模块创建线程的方法”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来...