日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

Python3实现机器学习经典算法(二)KNN实现简单OCR

發(fā)布時(shí)間:2023/12/19 综合教程 49 生活家
生活随笔 收集整理的這篇文章主要介紹了 Python3实现机器学习经典算法(二)KNN实现简单OCR 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

一、前言

  1、ocr概述

    OCR (Optical Character Recognition,光學(xué)字符識別)是指電子設(shè)備(例如掃描儀或數(shù)碼相機(jī))檢查紙上打印的字符,通過檢測暗、亮的模式確定其形狀,然后用字符識別方法將形狀翻譯成計(jì)算機(jī)文字的過程;即,針對印刷體字符,采用光學(xué)的方式將紙質(zhì)文檔中的文字轉(zhuǎn)換成為黑白點(diǎn)陣的圖像文件,并通過識別軟件將圖像中的文字轉(zhuǎn)換成文本格式,供文字處理軟件進(jìn)一步編輯加工的技術(shù)(摘自百度百科:光學(xué)字符識別)。

    KNN在OCR的識別過程中能發(fā)揮作用的地方在于將圖像中的文字轉(zhuǎn)換為文本格式,而OCR的其他部分,比如圖像預(yù)處理、二值化等操作將其丟給OpenCV去操作。

  2、訓(xùn)練集簡介

    由于我們采用的是KNN來轉(zhuǎn)換圖像中的文字為文本格式,需要一個(gè)龐大的手寫字符訓(xùn)練集來支撐我們的算法。這里我使用的是《機(jī)器學(xué)習(xí)實(shí)戰(zhàn)》2.3實(shí)例:手寫識別系統(tǒng)中使用的數(shù)據(jù)集,其下載地址為:https://www.manning.com/books/machine-learning-in-action,在Source CodeCh02digits rainingDigits中的兩千多個(gè)手寫字符既是我所使用的訓(xùn)練集。

    這個(gè)訓(xùn)練集配合上它所提供的測試集,提供了一個(gè)準(zhǔn)確度非常高的分類器:

    

    訓(xùn)練集是由0~9十個(gè)數(shù)字組成的,每個(gè)數(shù)字有兩百個(gè)左右的訓(xùn)練樣本。所有的訓(xùn)練樣本統(tǒng)一被處理為一個(gè)32*32的0/1矩陣,其中所有值為1的連通區(qū)域構(gòu)成了形象上的數(shù)字,如下所示:

    

    所以,在構(gòu)造我們的測試集的時(shí)候,所有的手寫數(shù)字圖片必須被處理為這樣的格式才能夠使得分類算法正確地進(jìn)行,這也是KNN的局限所在。

二、算法實(shí)現(xiàn)

  1、構(gòu)建測試集

    上面已經(jīng)提到,要想算法正確地進(jìn)行,測試集的樣式應(yīng)該和訓(xùn)練集相同,也就是說我們要把一張包含有手寫數(shù)字的圖像,轉(zhuǎn)換為一個(gè)32*32的0/1點(diǎn)陣。

    測試集使用我自己手寫的10個(gè)數(shù)字:

    這里存在一個(gè)非常大的問題:這個(gè)數(shù)據(jù)集的作者是土耳其人,他們書寫數(shù)字的習(xí)慣和我們有諸多不同,比如上面的數(shù)字4和數(shù)字8,下面這樣子的數(shù)字就無法識別:4/8。哈哈,也就是說它連印刷體都無法識別,這是這個(gè)訓(xùn)練集的一大缺陷之一。

    1)圖像預(yù)處理

     圖像預(yù)處理的過程是一個(gè)數(shù)字圖像處理(DIP)的過程,觀察上面的10個(gè)數(shù)字,可以發(fā)現(xiàn)每張圖像的大小/對比度的差距都非常大,所以圖像預(yù)處理應(yīng)該消除這些差距。

     第一步是進(jìn)行圖像的放大/縮小。由于我們很難產(chǎn)生一個(gè)小于32*32像素的手寫數(shù)字圖像,所以這里主要是縮小圖像:

    

1 import cv2
2 def readImage(imagePath):
3     image = cv2.imread(imagePath,cv2.IMREAD_GRAYSCALE)
4     image = cv2.resize(image,(32,32),interpolation = cv2.INTER_AREA)
5     return image

    這里我沒有去實(shí)現(xiàn)圖像重采樣的方法(實(shí)現(xiàn)在后面的博客會寫),而是采用的OpenCV,通過area來確定取樣點(diǎn)的灰度值(推薦用bicubic interpolation,對應(yīng)的插入函數(shù)應(yīng)該是INTER_CUBIC),在讀入圖像的時(shí)候讀入方式位IMRAD_GRAYSCALE,因?yàn)槲覀冃枰氖亲R別手寫字符,灰度圖對比彩色圖能更好的突出重點(diǎn)。

    進(jìn)行圖像的縮放是不夠的,因?yàn)橛^察上面的圖片可以發(fā)現(xiàn):拍攝環(huán)境對于對比度的影響非常大,所以我們應(yīng)該突出深色區(qū)域(數(shù)字部分),來保證后面的工作順利進(jìn)行,這里采用的是伽馬變換(也可以采用對數(shù)變換):

1 def imageGamma(image):
2     for i in range(32):
3         for j in range(32):
4             image[i][j]=3*pow(image[i][j],0.8)
5     return image

    2)圖像二值化

      縮小/放大后的圖像已經(jīng)是一個(gè)32*32的圖像了,下一步則是將非數(shù)字區(qū)域填充0,數(shù)字區(qū)域填充1,這里我采用的是閾值二值化處理:

def imageThreshold(image):
    ret,image = cv2.threshold(image,150,255,cv2.THRESH_BINARY)
    return image

      經(jīng)過二值化處理,數(shù)字部分的灰度值應(yīng)該為0,而非數(shù)字部分的連通區(qū)域的灰度值應(yīng)該為255,如下所示:

    3)去噪

      圖像去噪的方式有很多種,這里建立使用自適應(yīng)中值濾波器進(jìn)行降噪,因?yàn)槲覀兊膱D像在傳輸過程中可能出現(xiàn)若干的椒鹽噪聲,這個(gè)噪聲在上述的二值化處理中有時(shí)候是非常棘手的。

      到目前為止,一副手機(jī)攝像的手寫數(shù)字圖像就可以轉(zhuǎn)換為一個(gè)32*32的二值圖像。

    4)生成訓(xùn)練樣本

      如何將這個(gè)32*32的二值圖像轉(zhuǎn)換為0/1圖像,這個(gè)處理非常簡單:

1 def imageProcess(image):
2         with open(r'F:UsersyangPycharmProjectsOCR_KNN	estDigits6_0.txt','w+') as file:
3         for i in range(32):
4             for j in range(32):
5                 if image[i][j] == 255:
6                     file.write('0')
7                 else:
8                     file.write('1')
9             file.writelines('
')

      這里我的代碼在掃描這個(gè)圖像的同時(shí),將其保存為一個(gè)訓(xùn)練樣本,命名和訓(xùn)練集的明明要求一樣為N_M.txt,其中N代表這個(gè)訓(xùn)練樣本的實(shí)際分類是什么數(shù)字,M代表這是這個(gè)數(shù)字的第幾個(gè)樣本。這里對圖像進(jìn)行灰度變換已經(jīng)是多此一舉了,我所需要的是0/1矩陣而非一個(gè)0/1圖像,所以在掃描過程中一并生成訓(xùn)練樣本更加省時(shí)直觀。

    5)形成訓(xùn)練集

      上面的示例只是生成一個(gè)圖像的訓(xùn)練樣本的,而實(shí)際上我們往往需要一次性生成一個(gè)訓(xùn)練集,這就要求這個(gè)圖像預(yù)處理、二值化并且生成0/1矩陣的過程是自動的:

 1 from os import listdir
 2 def imProcess(imagePath):
 3     testDigits = listdir(imagePath)
 4     for i in range(len(testDigits)):
 5         imageName = testDigits[i]#圖像命名格式為N_M.png,NM含義見4)生成訓(xùn)練樣本
 6         #imageClass = int((imageName.split('.')[0]).split('_')[0])#這個(gè)圖像的數(shù)字是多少
 7         image = cv2.imread(imageName,cv2.IMREAD_GRAYSCALE)  
 8         image = cv2.resize(image, (32, 32), interpolation=cv2.INTER_AREA)
 9         ret, image = cv2.threshold(image, 150, 255, cv2.THRESH_BINARY)     
10         with open(r'F:UsersyangPycharmProjectsOCR_KNN	estDigits\'+imageName.split('.')[0]+'.txt','w+') as file:
11             for i in range(32):
12                 for j in range(32):
13                     if image[i][j] == 255:
14                         file.write('0')
15                     else:
16                         file.write('1')
17                 file.writelines('
')

    這個(gè)函數(shù)將imagePath文件夾中所有的N_M命名的手寫數(shù)字圖像讀取并經(jīng)過預(yù)處理、二值化、最后保存為對應(yīng)的0/1矩陣,命名為N_M.txt,這就構(gòu)成一個(gè)訓(xùn)練集了。

    

  2、構(gòu)建分類器

    分類器使用上一節(jié)的分類器(classify):

    

 1 def classify(vector,dataSet,labels,k):
 2     distance = sqrt(abs(((tile(vec,(dataSet.shape[0],1)) - dataSet) ** 2).sum(axis = 1))); #計(jì)算距離
 3     sortedDistance = distance.argsort()
 4     dict={}
 5     for i in range(k):
 6         label = labels[sortedDistance[i]]
 7         if not label in dict:
 8              dict[label] = 1
 9         else:
10              dict[label]+=1
11    sortedDict = sorted(dict,key = operator.itemgetter(1),reverse = True)
12    return sortedDict[0][0]
13 
14 def dict2list(dic:dict):#將字典轉(zhuǎn)換為list類型
15     keys=dic.keys()
16     values=dic.values()
17     lst=[(key, value)for key,value in zip(keys,values)]
18     return lst

  distance的計(jì)算和dict2list函數(shù)的詳解在上一節(jié),戳上面的classify既可以跳轉(zhuǎn)過去。

  分類器已經(jīng)構(gòu)建完成,下一步是提取每一個(gè)測試樣本,提取訓(xùn)練集,提取label的過程:(這個(gè)過程大部分用的是《機(jī)器學(xué)習(xí)實(shí)戰(zhàn)》中的代碼,對于難以理解的代碼在下文中做了解釋:)

  1)讀取0/1矩陣文件:

    

1 def img2vector(filename):
2     returnvec = numpy.zeros((1,1024))
3     file = open(filename)
4     for i in range(32):
5         line = file.readline()
6         for j in range(32):
7             returnvec[0,32*i+j] = int(line[j])
8     return returnvec

  這里要注意:構(gòu)造一個(gè)32*32的全零矩陣的時(shí)候,應(yīng)該是numpy.zeros((1,1024)),雙層括號!雙層括號!雙層括號!代表構(gòu)造的是一個(gè)二維矩陣!

  2)讀取訓(xùn)練集和測試集并求解準(zhǔn)確率:

   

 1 def handWritingClassifyTest():
 2     labels=[]
 3     trainingFile = listdir(r'F:UsersyangPycharmProjectsOCR_KNN	rainingDigits')
 4     m = len(trainingFile)
 5     trainingMat = numpy.zeros((m,1024))
 6     for i in range(m):
 7         file = trainingFile[i]
 8         filestr = file.strip('.')[0]
 9         classnum = int(filestr.strip('_')[0])
10         labels.append(classnum)
11         trainingMat[i,:] = img2vector('trainingDigits/%s' % file)
12     testFileList = listdir(r'F:UsersyangPycharmProjectsOCR_KNN	estDigits')
13     error = 0.0
14     testnum = len(testFileList)
15     for i in range(testnum):
16         file_test = testFileList[i]
17         filestr_test = file_test.strip('.')[0]
18         classnum_test = int(filestr_test.strip('_')[0])
19         vector_test = img2vector('testDigits/%s'%file_test)
20         result = classify(vector_test,trainingMat,labels,1)
21         if(result!=classnum_test):error+=1.0
22     print("準(zhǔn)確率:%f"%(1.0-(error/float(testnum))))

    代碼其實(shí)沒有很難懂的地方,主要任務(wù)就是讀取文件,通過img2vctor函數(shù)轉(zhuǎn)換為矩陣,還有切割文件名獲取該測試樣本的類別和該訓(xùn)練樣本的類別,通過對比獲得準(zhǔn)確率。

  3、使用分類器

    現(xiàn)在為止,我們的分類器已經(jīng)構(gòu)建完成,下面就是測試和使用階段:

    1)測試《機(jī)器學(xué)習(xí)實(shí)戰(zhàn)》中給出的訓(xùn)練集:

    

    2)測試手寫訓(xùn)練集:

    

    emmm果然學(xué)不出來大佬寫字,附上幾張無法識別的0/1數(shù)字矩陣:(0,4,6無法識別的原因是比劃太細(xì)哈哈,8無法識別的原因……太端正了吧)

    

  4、完整代碼:

    

 1 from os import listdir
 2 import numpy
 3 import operator
 4 import cv2
 5 
 6 def imProcess(imagePath):
 7     testDigits = listdir(imagePath)
 8     for i in range(len(testDigits)):
 9         imageName = testDigits[i]#圖像命名格式為N_M.png,NM含義見4)生成訓(xùn)練樣本
10         #imageClass = int((imageName.split('.')[0]).split('_')[0])#這個(gè)圖像的數(shù)字是多少
11         image = cv2.imread(imageName,cv2.IMREAD_GRAYSCALE)
12         image = cv2.resize(image, (32, 32), interpolation=cv2.INTER_AREA)
13         ret, image = cv2.threshold(image, 150, 255, cv2.THRESH_BINARY)
14         with open(r'F:UsersyangPycharmProjectsOCR_KNN	estDigits\'+imageName.split('.')[0]+'.txt','w+') as file:
15             for i in range(32):
16                 for j in range(32):
17                     if image[i][j] == 255:
18                         file.write('0')
19                     else:
20                         file.write('1')
21                 file.writelines('
')
22 
23 def img2vector(filename):
24     returnvec = numpy.zeros((1,1024))
25     file = open(filename)
26     for i in range(32):
27         line = file.readline()
28         for j in range(32):
29             returnvec[0,32*i+j] = int(line[j])
30     return returnvec
31 
32 def handWritingClassifyTest():
33     labels=[]
34     trainingFile = listdir(r'F:UsersyangPycharmProjectsOCR_KNN	rainingDigits')
35     m = len(trainingFile)
36     trainingMat = numpy.zeros((m,1024))
37     for i in range(m):
38         file = trainingFile[i]
39         filestr = file.strip('.')[0]
40         classnum = int(filestr.strip('_')[0])
41         labels.append(classnum)
42         trainingMat[i,:] = img2vector('trainingDigits/%s' % file)
43     testFileList = listdir(r'F:UsersyangPycharmProjectsOCR_KNN	estDigits')
44     error = 0.0
45     testnum = len(testFileList)
46     for i in range(testnum):
47         file_test = testFileList[i]
48         filestr_test = file_test.strip('.')[0]
49         classnum_test = int(filestr_test.strip('_')[0])
50         vector_test = img2vector('testDigits/%s'%file_test)
51         result = classify(vector_test,trainingMat,labels,1)
52         if(result!=classnum_test):error+=1.0
53     print("準(zhǔn)確率:%f"%(1.0-(error/float(testnum))))
54 
55 def classify(inX,dataSet,labels,k):
56     size = dataSet.shape[0]
57     distance = (((numpy.tile(inX,(size,1))-dataSet)**2).sum(axis=1))**0.5
58     sortedDistance = distance.argsort()
59     count = {}
60     for i in range(k):
61         label = labels[sortedDistance[i]]
62         count[label]=count.get(label,0)+1
63     sortedcount = sorted(dict2list(count),key=operator.itemgetter(1),reverse=True)
64     return sortedcount[0][0]
65 
66 def dict2list(dic:dict):#將字典轉(zhuǎn)換為list類型
67     keys=dic.keys()
68     values=dic.values()
69     lst=[(key, value)for key,value in zip(keys,values)]
70     return lst
71 
72 # def imProcess(image):
73 #     image = cv2.resize(image, (32, 32), interpolation=cv2.INTER_AREA)
74 #     ret, image = cv2.threshold(image, 150, 255, cv2.THRESH_BINARY)
75 #     cv2.imshow('result',image)
76 #     cv2.waitKey(0)
77 #     with open(r'F:UsersyangPycharmProjectsOCR_KNN	estDigits6_0.txt','w+') as file:
78 #         for i in range(32):
79 #             for j in range(32):
80 #                 if image[i][j] == 255:
81 #                     file.write('0')
82 #                 else:
83 #                     file.write('1')
84 #             file.writelines('
')
85 
86 
87 
88 # iamge = cv2.imread(r'C:UsersyangDesktop6.png',cv2.IMREAD_GRAYSCALE)
89 # image = imProcess(iamge)
90 imProcess(r'F:UsersyangPycharmProjectsOCR_KNN	estDigits')
91 handWritingClassifyTest()

  5、github:https://github.com/hahahaha1997/OCR

三、總結(jié)

    KNN還是不適合用來做OCR的識別過程的,雖然《機(jī)器學(xué)習(xí)實(shí)戰(zhàn)》的作者提到這個(gè)系統(tǒng)是美國的郵件分揀系統(tǒng)實(shí)際運(yùn)行的一個(gè)系統(tǒng),但是它肯定無法高準(zhǔn)確率地識別中國人寫的手寫文字就對了,畢竟中國有些地方的“9”還會寫成“p”的樣子的。這一節(jié)主要是將KNN拓展到實(shí)際運(yùn)用中的,結(jié)合上一節(jié)的理論,KNN的執(zhí)行效率還是太低了,比如這個(gè)系統(tǒng),要識別一個(gè)手寫數(shù)字,它需要和所有的訓(xùn)練樣本做距離計(jì)算,每個(gè)距離計(jì)算又有1024個(gè)(a-b)2,還有運(yùn)行效率特別低下的sqrt(),如果是一個(gè)非常大的測試集,需要的時(shí)間就更加龐大,如果訓(xùn)練集非常龐大,在將0/1矩陣讀入內(nèi)存中的時(shí)候,內(nèi)存開銷是非常巨大的,所以整個(gè)程序可能會非常耗時(shí)費(fèi)力。不過KNN仍舊是一個(gè)精度非常高的算法,并且也是機(jī)器學(xué)習(xí)分類算法中最簡單的算法之一。下一節(jié)將帶來機(jī)器學(xué)習(xí)經(jīng)典算法——ID3決策樹。轉(zhuǎn)載注明出處哦:https://www.cnblogs.com/DawnSwallow/p/9440516.html

總結(jié)

以上是生活随笔為你收集整理的Python3实现机器学习经典算法(二)KNN实现简单OCR的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。