python實現(xiàn)識別手寫數(shù)字 python圖像識別算法
寫在前面
這一段的內(nèi)容可以說是最難的一部分之一了,因為是識別圖像,所以涉及到的算法會相比之前的來說比較困難,所以我盡量會講得清楚一點。
而且因為在編寫的過程中,把前面的一些邏輯也修改了一些,將其變得更完善了,所以一切以本篇的為準。當然,如果想要直接看代碼,代碼全部放在我的GitHub中,所以這篇文章主要負責(zé)講解,如需代碼請自行前往GitHub。
本次大綱
上一次寫到了數(shù)據(jù)庫的建立,我們能夠?qū)崟r的將更新的訓(xùn)練圖片存入CSV文件中。所以這次繼續(xù)往下走,該輪到識別圖片的內(nèi)容了。
首先我們需要從文件夾中提取出需要被識別的圖片test.png,并且把它經(jīng)過與訓(xùn)練圖片相同的處理得到1x10000大小的向量。因為兩者之間存在微小的差異,我也不是很想再往源代碼之中增加邏輯了,所以我就直接把增加待識別圖片的函數(shù)重新寫一個命名為GetTestPicture,內(nèi)容與GetTrainPicture類似,只不過少了“增加圖片名稱”這一個部分。
之后我們就可以開始進行正式圖片識別內(nèi)容了。
主要是計算待識別圖片與所有訓(xùn)練圖片的距離。當兩個圖片距離越近的時候,說明他們越相似,那么他們很有可能寫的就是同一個數(shù)。所以利用這個原理,我們可以找出距離待識別圖像最近的幾個訓(xùn)練圖片,并輸出他們的數(shù)字分別是幾。比如說我想輸出前三個,前三個分別是3,3,9,那就說明這個待識別圖片很有可能是3.
之后還可以對每一個位置加個權(quán)重,具體的就放在下一次再講,本節(jié)內(nèi)容已經(jīng)夠多了。
(第一篇文章之中我說過利用圖片洞數(shù)檢測。我嘗試了一下,認為有些不妥,具體原因放在本文末。)
MAIN代碼
所以直接把主要代碼放上來,邏輯相對來說還是比較清晰的
import os import OperatePicture as OP import OperateDatabase as OD import PictureAlgorithm as PA import csv ##Essential vavriable 基礎(chǔ)變量 #Standard size 標準大小 N = 100 #Gray threshold 灰度閾值 color = 200/255 n = 10 #讀取原CSV文件 reader = list(csv.reader(open('Database.csv', encoding = 'utf-8'))) #清除讀取后的第一個空行 del reader[0] #讀取num目錄下的所有文件名 fileNames = os.listdir(r"./num/") #對比fileNames與reader,得到新增的圖片newFileNames newFileNames = OD.NewFiles(fileNames, reader) print('New pictures are: ', newFileNames) #得到newFilesNames對應(yīng)的矩陣 pic = OP.GetTrainPicture(newFileNames) #將新增圖片矩陣存入CSV中 OD.SaveToCSV(pic, newFileNames) #將原數(shù)據(jù)庫矩陣與新數(shù)據(jù)庫矩陣合并 pic = OD.Combination(reader, pic) #得到待識別圖片 testFiles = os.listdir(r"./test/") testPic = OP.GetTestPicture(testFiles) #計算每一個待識別圖片的可能分類 result = PA.CalculateResult(testPic, pic) for item in result: for i in range(n): print('第'+str(i+1)+'個向量為'+str(item[i+n])+',距離為'+str(item[i]))
相比上一篇文章的內(nèi)容,本篇文章里只增加了下面的的一段代碼,即得到待識別圖片名稱、得到待識別圖片向量、計算分類。
下面我們將著重講解CalculateResult函數(shù)的內(nèi)容,即識別圖片的算法。
算法內(nèi)容
算法大致講解
我們在大綱之中已經(jīng)簡單介紹過了,所以我就直接把復(fù)制過來,并且再添加一些內(nèi)容。
假設(shè)我們在二維平面上有兩個點A=(1,1)和B=(5,5),我現(xiàn)在再放一個點C=(2,2),那么請問,C點離哪一個更近?
學(xué)過初中數(shù)學(xué)的都會知道肯定是離A點更近。所以我們換一種說法,我們現(xiàn)在有兩個類A和B,A類中包括了點(1,1),B類中包括了點(5,5),所以對于點(2,2),它可能屬于哪一類?
因為這個點離A類的點更近一點,所以它可能屬于A類。這就是結(jié)論。那么對于3維空間,A類是點(1,1,1)和B類是(5,5,5),那么對于點(2,2,2)肯定也是屬于A類。
可以看出,我們這里是將兩個點的距離來作為判斷屬于哪一類的標準。那么對于我們將圖片拉成的1xn維向量,他實際上投影到n維空間上就是一個點,所以我們將訓(xùn)練向量分成10類,分別代表十個數(shù)字,那么被識別數(shù)字靠近哪一個類,那說明它有可能屬于這一個類。
那么我們這里可以假設(shè)對于被識別向量,列出距離他最近的前十個向量分別屬于哪一類別,然后根據(jù)名次加上一個權(quán)重,并計算出一個值。該值代表了可能是屬于哪一個類,因此這就是我們得出的最終的一個結(jié)果——被識別手寫數(shù)字圖片的值。
以上是第一篇文章中的內(nèi)容,下面我著重講一下數(shù)學(xué)方面的內(nèi)容。
考慮到某些地方不能夠輸入數(shù)學(xué)公式(或不方便輸入),我還是把這一段內(nèi)容貼成圖片出來。
之后直接挑出前幾個離被識別圖片最近的向量數(shù)字,基本上這幾個數(shù)字就是被識別圖片的數(shù)字了。但這樣做未免有些簡單,所以下一篇文章我會再深入一下,這張先講計算距離的內(nèi)容。
主代碼
下面的代碼中文件夾test用來存放待識別圖片,并通過函數(shù)GetTestPicture來得到圖片向量,之后和訓(xùn)練圖片pic一起放進計算距離的函數(shù)CalculateResult中計算每一個待識別向量和其他所有圖片向量的距離。
#得到待識別圖片 testFiles = os.listdir(r"./test/") testPic = OP.GetTestPicture(testFiles) #計算每一個待識別圖片的可能分類 result = PA.CalculateResult(testPic, pic) for item in result: for i in range(n): print('第'+str(i+1)+'個向量為'+str(item[i+n])+',距離為'+str(item[i]))
函數(shù)CalculateResult在文件PictureAlgorithm.py中,這個文件里面包含了兩個函數(shù)為CalculateDistance函數(shù)和CalculateResult函數(shù),代表識別圖片所用到的算法。
函數(shù)CalculateResult
這個函數(shù)的邏輯比較簡單,也沒什么好說的,主要的聯(lián)系就是這個計算距離的CalculateDistance函數(shù)。
def CalculateResult(test, train): '''計算待識別圖片test的可能分類''' #得到每個圖片的前n相似圖片 testDis = CalculateDistance(test[:,0:N**2], train[:,0:N**2], train[:,N**2], n) #將testDis變成列表 tt = testDis.tolist() #輸出每一個待識別圖片的所有前n個 for i in tt: for j in i: print(j)
函數(shù)CalculateDistance
函數(shù)中我導(dǎo)入了四個參數(shù):被識別向量test,訓(xùn)練向量train,與訓(xùn)練向量對應(yīng)的每個向量對應(yīng)代表的數(shù)字num,想要導(dǎo)出的前n個距離最近的向量。
def CalculateDistance(test, train, num, n): '''計算每個圖片前n相似圖片''' #前n個放距離,后n個放數(shù)字 dis = np.zeros(2*n*len(test)).reshape(len(test), 2*n) for i, item in enumerate(test): #計算出每個訓(xùn)練圖片與該待識別圖片的距離 itemDis = np.sqrt(np.sum((item-train)**2, axis=1)) #對距離進行排序,找出前n個 sortDis = np.sort(itemDis) dis[i, 0:n] = sortDis[0:n] for j in range(n): #找到前幾個在原矩陣中的位置 maxPoint = list(itemDis).index(sortDis[j]) #找到num對應(yīng)位置的數(shù)字,存入dis中 dis[i, j+n] = num[maxPoint] return dis
首先建立一個行數(shù)為test內(nèi)被識別向量數(shù)量,列數(shù)為2*n的矩陣,每一行前n個放距離,后n個放數(shù)字。之后針對每一個被識別向量進行循環(huán)。
首先直接計算每個訓(xùn)練圖片與該識別圖片的距離,直接可以用一行代碼表示
itemDis = np.sqrt(np.sum((item-train)**2, axis=1))
這一行代碼就是上文中的算法過程,我個人覺得還是比較復(fù)雜的,可以詳細的拆開看一下,我這里不細講了。下面的內(nèi)容就是開始排序并且找到距離最近的前幾個向量。
這里的邏輯是:先排序,找到距離最小的前n個,存入矩陣。找到前n個在原矩陣中的位置,并找到對應(yīng)位置上num的數(shù)字,存入dis的后n個。
這樣子就相當于完成了所有內(nèi)容,返回dis即可。
實際測試
我自己動手寫了一些數(shù)字,如圖所示。所以實際上我們的數(shù)據(jù)庫還是比較小的。
所以我又寫了一個數(shù)字作為待識別圖像,通過程序運行以后,我們的以直接輸出前十個最相似的向量:
第1個向量為2.0,距離為33.62347223932534
第2個向量為2.0,距離為35.64182105224185
第3個向量為2.0,距離為38.69663119274146
第4個向量為2.0,距離為43.52904133387693
第5個向量為2.0,距離為43.69029199677604
第6個向量為1.0,距離為43.730883339256714
第7個向量為6.0,距離為44.94800943845918
第8個向量為2.0,距離為45.033283944455924
第9個向量為4.0,距離為45.43926712996951
第10個向量為7.0,距離為45.64893989116544
之后我又依次從1-9試了一遍,我自己手寫的數(shù)字全部識別正確,可以看出準確率還是挺高的。所以做到這一步相當于已經(jīng)完成度很高了。
所以我就試了一下從網(wǎng)上找的圖片,發(fā)現(xiàn)幾乎沒有正確的了。說明我們的數(shù)據(jù)庫還是太小,只認得我的字體。不過話說這樣,也可以做一個字體識別的程序。
所以如果要提高準確率,那么擴大圖庫是必須的。這一次就到這里。
總結(jié)
所有源代碼我都放在了我的GitHub中,如果有興趣的話可以去看看。
到這里就相當于算法內(nèi)容寫完了,比較簡單,只用了一個類似于K最近鄰的算法。
下一篇文章將會講一個給前n個排名加權(quán)的想法,這樣來提高準確度。
所以這一次就先到這里為止,謝謝。
如果喜歡的話,麻煩點一個喜歡和關(guān)注一下噢,謝謝~
本文已被收錄到專題《python圖片處理操作》 ,歡迎大家點擊學(xué)習(xí)更多精彩內(nèi)容。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
python中利用Future對象異步返回結(jié)果示例代碼
future是一種對象,表示異步執(zhí)行的操作。下面這篇文章主要給大家介紹了關(guān)于python中利用Future對象異步返回結(jié)果的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面來一起看看吧。2017-09-09python調(diào)用matplotlib模塊繪制柱狀圖
這篇文章主要為大家介紹了python調(diào)用matplotlib模塊繪制柱狀圖,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-10-10Python獲取excel內(nèi)容及相關(guān)操作代碼實例
這篇文章主要介紹了Python獲取excel內(nèi)容及相關(guān)操作代碼實例,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-08-08