Python徒手實現識別手寫數字—簡易圖片資料庫
Python徒手實現識別手寫數字—簡易圖片資料庫
寫在前面
上一篇文章Python徒手實現識別手寫數字—圖像的處理中我們講了圖片的處理,將圖片經過剪裁,拉伸等操作以後將每一個圖片變成了1x10000大小的向量。但是如果只是這樣的話,我們每一次運行的時候都需要將他們計算一遍,當圖片特別多的時候會消耗大量的時間。
所以我們需要將這些向量存入一個文件當中,每次先看看圖庫中有沒有新增的圖片,如果有新增的圖片,那麼就將新增的圖片變成1x10000向量再存入文件之中,然後從文件中讀取全部圖片向量即可。當圖庫中沒有新增圖片的時候,那麼就直接調用文件中的圖片向量進行計算就好。這樣子算是節省了大量的時間。
所以本文就是從零開始建立一個這樣的圖片存儲管理系統。
實現邏輯
第一次讀入圖片
我們的圖庫中擁有一大堆圖片,每一張圖片上面都是一個手寫的數字,圖片的名稱為[數字內容]_[序號]。比如說一個圖片的名稱為2_3,代表這一張圖片裡面的數字是2,並且是「數字是2的第3張圖片」。
存在一個csv文件作為我們的建議的圖片資料庫,名稱為Data.csv。
首先我們讀取圖庫中所有圖片的名稱,保存在fileNames中。然後讀取Data.csv中所有數據。
提取出Data.csv的最後一列(一共10002列,第10001列說明該數字是什麼數字,第10002列是圖片的名稱),也就是資料庫中存儲的所有圖片的名稱,存儲在item中。
將新加入圖庫的圖片名稱保存在newFileNames中。如果Data.csv為空,那麼就直接令newFileNames = fileNames。也就是說如果資料庫中什麼也沒有,那麼圖庫中所有圖片都是新加入的。
如果Data.csv不為空,那麼就將item裡面的內容與fileNames的內容比較,如果出現了fileNames裡面有的名稱item中沒有,那麼就將這些名稱放進newFileNames中。如果item里有的名稱fileNames中沒有,那就不管。
也就是說,我令我們的資料庫只進不出。
現在我們得到了新加入圖庫的圖片的名稱newFileNames。
將newFileNames中的名稱的圖片帶入上一文中函數GetTrainPicture進行處理,得到了一個nx10001的矩陣,每一行代表一個新加入的圖片,前10000列是圖片向量,第10001列是該圖片的數字,保存在pic中。
將這些圖片壓入到資料庫的後面。
讀取之前資料庫原有的圖片向量,並與pic合併,得到目前擁有的所有的訓練圖片向量pic。
以上就是本章寫的所有內容,下面放出代碼來詳細解釋一下。
代碼解析
主文件
import osimport numpy as npimport OperatePicture as OPimport OperateDataBase as ODimport csv##Essential vavriable 基礎變數#Standard size 標準大小N = 100#Gray threshold 灰度閾值color = 100/255#讀取原CSV文件reader = list(csv.reader(open(DataBase.csv, encoding = utf-8)))#清除讀取後的第一個空行del reader[0]#讀取num目錄下的所有文件名fileNames = os.listdir(r"./num/")#對比fileNames與reader,得到新增的圖片newFileNamesnewFileNames = OD.NewFiles(fileNames, reader)print(New pictures are: newFileNames)#得到newFilesNames對應的矩陣pic = OP.GetTrainPicture(newFileNames)#將新增圖片矩陣存入CSV中OD.SaveToCSV(pic, newFileNames)#將原資料庫矩陣與新資料庫矩陣合併pic = OD.Combination(reader, pic)
我將兩節內容分別封裝在兩個py文件裡面,上一篇文章中的圖片的切割與處理等所有內容我放在文件OperatePicture裡面了,這一節的資料庫處理放在了文件OperateDatabase裡面。
因為整個代碼的邏輯我在上面已經捋過一遍了,所以我不再解釋其中的內容,接下來針對每個函數開始講解。
OperateDatabase代碼
從上面的主文件中,我們首先用到了函數NewFiles,主要是對比fileNames和reader這兩個文件中圖片的名稱有什麼不同,返回值是新增的圖片的名稱的列表。下面是代碼
def NewFiles(fileNames, reader): 判斷是否有不同於資料庫中的新文件加入 #如果資料庫中沒有數據,則返回filenames if len(reader) == 0: return fileNames else: #從資料庫中提取所有名稱 files = [item[10001] for item in reader] #需要加入的圖片名稱 newFileNames = [] for item in fileNames: #判斷當前名稱是否存在資料庫中 #如果不存在,則加入newFileNames if item not in files: newFileNames.append(item) return newFileNames
首先判斷reader是否有內容,如果沒有內容,說明是第一次執行,那麼會直接把fileNames返回。否則才會進入下面進行比較。
返回了newFileNames之後,就會把這個列表中的所有名稱的圖片通過GetTrainPicture函數得到一個1x10001大小的矩陣,具體過程請看我上一篇文章講的內容。
之後為了把新的數據存入CSV文件中,我們利用函數SaveToCSV將pic存入文件中,具體代碼如下。
def SaveToCSV(pic, fileNames): 將pic與對應的dileNames存入CSV文件 writer = csv.writer(open(Database.csv, a, newline = ), dialect = excel) #將fileNames變為列表 f = [item for item in fileNames] #每一行依次寫入文件中 for i in range(len(pic)): #將改行圖片向量轉為list item = pic[i].tolist() #將這個圖片向量對應的名稱f放入列表最後一個 item.append(f[i]) writer.writerow(item)
當函數運行過後,會把pic矩陣對應的內容直接給續寫入CSV文件中,相當於資料庫操縱的寫入,並不會覆蓋之前原有的數據。
之後我們需要將資料庫原有的一大堆數據reader和新加進來的數據pic合併到pic裡面,所以利用Combination函數將兩個矩陣合併,代碼如下
def Combination(reader, pic): 將兩個矩陣reader與pic合併 #兩個矩陣的總行數 l = len(reader) + len(pic) #初始化新的矩陣 newPic = np.zeros(l*10001).reshape(l, 10001) #將reader最後的那個字元串名稱去掉 for item in reader: item.pop() #將reader轉化為numpy的矩陣形式 reader = np.array(reader) #新矩陣前半部分放reader,後半部分放pic if len(reader) != 0: newPic[0:len(reader), :] = reader newPic[len(reader):len(pic), :] = pic return newPic
因為reader最後一行還包括了一個圖片的名稱,所以先利用pop將其去掉,之後轉化為矩陣形式,然後再直接放入矩陣中。這個矩陣操作可能沒有見過,下面我詳細解釋一下。
假如我現在有一個2x3的矩陣和一個2x2的矩陣
m = [[1 2 3] [4 5 6]]n = [[7 8] [9 1]]
我可以進行如下操作
#操作一m[:, 0:2] = nprint(m)#操作二m[:, 1:3] = nprint(m)#以下為輸出結果#操作一[[7 8 3][9 1 6]]#操作二[[7 7 8][9 9 1]]
可以看出操作一直接把m的第一二列給替換成n,操作二把m的第二三列替換成了n。具體過程可以百度查一下numpy的矩陣的操作,也可以自己總結規律,不細講了。
以上就是這一篇的全部代碼。
小結
這一篇我相當於用CSV文件製作了一個非常簡陋的資料庫,能夠執行的操作只有識別已有內容NewFiles與添加內容SaveToCSV,並沒有插入、刪改等操作。主要是我覺得這兩個函數目前已經夠用,因此只寫了這兩個操作,所以再需求已經被滿足的情況下就不再拓展了。
所有的源代碼已經上傳到了我的GitHub上,可以前去下載,謝謝閱讀。
如果喜歡的話麻煩點一個贊哦,加關注可以得到超厲害的更新提醒。
推薦閱讀:
※爬蟲入門到精通-headers的詳細講解(If-modified-since)
※黃哥Python 所寫"windows下python學習環境設置"
※Linux運維人員如何學習python編程
※怎樣用 Matlab 寫出優雅的代碼?
※爬蟲入門到精通-環境的搭建