【實驗項目名稱】
手寫數字特征提取方法與實現
【實驗目的】
通過手寫數字特征的提取,了解數字的特征提取方法,掌握特征匹配准則。
【實驗原理】
讀取標准化后的數字0~9,二值化,對每個數字進行等分區域分割,統計每個區域內的黑色像素點的個數,即為特征初值。采用歐式距離的模板匹配法判斷數字。
【實驗要求】
給定數字0-9的原始樣本集合,每個數字都有100個大小為28*28的樣本圖像。要求如下:
1、將上述圖像切分成標准圖像庫,存儲為文件。
2、對每個數字進行等分區間分割。
3、給出統計結果:每個區域內的黑色像素點個數值以及占總量的百分比,顯示出計算結果。
4、采用歐氏距離模板匹配法,給出識別結果。
5、從統計意義上,給出每個數字的識別率。
1.Python庫的相關配置:OpenCV
2.實驗流程圖:
3.實驗步驟分析(包含有train圖片集、test圖片集):
1.圖片預處理,該實驗使用圖片均已做過預處理,均為二值化的圖像(0 和 255),黑底白字,28*28大小,如下圖所示;
2.對所有train圖片進行數據壓縮,設計為28*28->7*7,也就是,每一個小方塊中含有4*4=16個像素點,通過統計小方塊中的像素值,分別給小方塊標記為0或1(其中當白點超過16/2 = 8 個時,記該小方塊為1,反之,記為0。可以知道,每一張圖片可以被49個0_1字符串刻畫。
3.對train圖片集中所有圖片進行上述處理,並將其字符串數據進行存儲(可以設計為存儲在一個txt文檔,,稱為模板庫)。然后對test圖片集中的每一張圖片進行字符串轉化,同樣可以得到49個字符串的0_1數據,然后將其與模板庫中的字符串數據進行對比,其中,歐氏距離最小的即認為是和該測試圖片是同一類數字。以此分辨手寫數字圖片。
4.關於各種統計率的計算:[正確率]--(最小距離唯一,且兩個圖片數字屬於同一類)或者(存在多個最小距離,但是均屬於同一類數字);
[拒絕識別率]--(存在多個最小距離,並且不全部屬於同一類數字,無法正常分類);
[錯誤率]--(最小距離唯一,並且兩個圖片不屬於同一類數字);
5.為了更好地判斷兩個圖片是否屬於同一類數字,故一般在其49個字符數據前加上一些標記符號,用來表示該字符串屬於哪個數字。
4.Python實現(其中路徑絕對方式給出):
one_test.py文件:(該文件中代碼復雜主要在於三種識別率的計算,其中關於每類識別得統計判斷有點繁雜,需耐心)
Function.Image_Compression(root_dir) -------圖像壓縮,返回壓縮數據流
Function.Distance(test_str, train_str)--------計算樣本間距

1 # 基於模板的手寫數字識別 2 import os 3 import cv2 4 import Function # 關於該實驗中需要調用的函數 5 # 1.樣本特征,不需預處理 6 # 2.圖像壓縮表示,28*28——7*7,存儲於(E:\PatternRecognition\picture7_7.txt) 7 root_dir = "E:/train-images" # 訓練數據集 8 file7_7 = open("E:/PatternRecognition/picture7_7.txt", 'w') # 覆蓋寫模式 9 for fl in os.listdir(root_dir): # 循環處理所有train圖片 10 img_str = fl[0:-4] + ":" + Function.Image_Compression(root_dir + '/' + fl) # 圖像壓縮,返回壓縮數據流 11 file7_7.write(img_str + '\n') # 壓縮后的圖像數據寫入文本存儲 12 file7_7.close() 13 # 3.待測樣本的相似性比較,根據最近距離判斷 14 # 4.根據相似性比較給出識別結果,並給出統計意義上的正確率,錯誤率,拒絕識別率 15 file7_7 = open("E:/PatternRecognition/picture7_7.txt", 'r') # 只讀模式 16 root_dir = "E:/test-images" # 測試test-images數據集 17 Correct_rate = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 18 Error_rate = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 19 Rejection_rate = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 20 ############ 21 for fl in os.listdir(root_dir): # 循環處理所有test圖片 22 # 一些必備的基礎數據變量 23 Same_dist_number = 0 # 用於記錄最小距離時,是否存在多個標准樣本,並標記個數 24 Same_class = -1 # 當有多個相同距離時,如果他們均為同一類別,則標記為該類別;-1表示可直接判斷。 25 min_dist = 7 # 用於存儲判斷最小距離 26 dist_img = "" # 用於標記當前最小距離時,屬於哪個數字圖片 27 # 開始處理test圖片的匹配識別 28 test_img_str = Function.Image_Compression(root_dir + '/' + fl) # test圖片數據 29 while True: 30 line = file7_7.readline() 31 if not line: # 已讀完整個文檔,光標返回開頭,結束此次匹配 32 file7_7.seek(0) 33 break 34 train_str = line[-50:-1] # train圖片數據 35 temp_dist = Function.Distance(test_img_str, train_str) # 計算樣本間距 36 if temp_dist < min_dist: # 更新min_dist/dist_img/Same_dist_number 37 min_dist = temp_dist 38 dist_img = line[0:-51] # 提取最小距離的圖片名稱,即屬於哪個數字 39 Same_dist_number = 0 # 出現最小距離,個數歸零 40 Same_class = -1 # 出現最小距離,不需要類別判斷 41 elif temp_dist == min_dist: 42 Same_dist_number += 1 # 表示具有相同距離的數字 43 if dist_img[0:1] == line[0:1]: # 樣本間距相等,同時是同一類的樣本 44 Same_class = eval(line[0:1]) # 記錄同一類別 45 else: 46 Same_class = -1 # 表明,同時具有不同類的數字樣本與測試數字距離相等 47 # 識別結果 以及 識別結果的數據統計 48 # a.Same_dist_number = 0 ,表明可以直接識別出來,但不保證正確率 49 # b.Same_dist_number != 0 and Same_class = -1 ,拒絕識別,同時有不同數字與其距離相等 50 # c.Same_dist_number != 0 and Same_class ,可以識別,表示同一類數字中多個樣本與其距離相等 51 if Same_dist_number == 0: # 該數字可以識別 52 print("測試數字:", fl[0:-4], " -- 識別出來的結果:", dist_img) # 數字的識別結果 53 if fl[0] == dist_img[0]: 54 Correct_rate[eval(fl[0])] += 1 # 正確識別 55 else: 56 Error_rate[eval(fl[0])] += 1 # 識別錯誤 57 elif Same_class == -1: # 拒絕識別 58 print("測試數字:", fl[0:-4], " -- 該數字拒絕識別!") 59 Rejection_rate[eval(fl[0])] += 1 # 拒絕識別 60 else: # 同一類數字中多個樣本與其距離相等 61 print("測試數字:", fl[0:-4], " -- 識別出來的結果(類):", Same_class) 62 if eval(fl[0]) == Same_class: 63 Correct_rate[eval(fl[0])] += 1 # 正確識別 64 else: 65 Error_rate[eval(fl[0])] += 1 # 識別錯誤 66 file7_7.close() 67 print("------------------------------------------------") 68 for i in range(10): 69 print("數字 {:d} 識別的正確率 = {:.2f}% ,錯誤率 = {:.2f}% ,拒絕識別率 = {:.2f}%".format(i, Correct_rate[i]*5, Error_rate[i]*5, Rejection_rate[i]*5)) 70 print("success!")
<<----------------------數據流顯示
5.兩個函數:
Function.py文件:(包含兩個需要常調用的函數)
Image_Compression(img_path)

1 import cv2 2 3 ####################### 4 # 實現圖像壓縮,28*28——7*7,並以49數據流返回,img_path--圖片路徑 5 # 划分為4*4的像素矩陣,其中八個及以上的像素點超過127即記為1,反之為0 6 # 參數img_path,必須為完成的圖片路徑 7 ####################### 8 def Image_Compression(img_path): 9 # 數據按行存儲 10 img_str = "" # 存數據流 11 img = cv2.imread(img_path) 12 # print("圖像的形狀,返回一個圖像的(行數,列數,通道數):", img.shape) 13 x = y = 0 # img像素點坐標表示img[x][y] 14 for k in range(1, 50): # k的范圍1-49 15 totle_img = 0 # 統計滿足要求的像素點數目 16 for i in range(4): 17 for j in range(4): 18 if img[x + i - 1][y + j - 1][0] > 127: 19 totle_img += 1 20 y = (y + 4) % 28 21 # 一個矩形陣中包含16個像素點,其中像素值大於127的點超過八個記為1,反之記為0 22 if totle_img >= 8: 23 img_str += '1' 24 else: 25 img_str += '0' 26 if k % 7 == 0: # 控制x,y的變化 27 x = x + 4 28 y = 0 29 return img_str
Distance(test_str, train_str)

1 ####################### 2 # 計算測試樣本與標准樣本之間的距離,返回以距離,test_str--測試,train_str--標准 3 # 其中test_str,train_str均必須保證為49個字符串形式的數字 4 # 返回一個double類型的數據 5 ####################### 6 def Distance(test_str, train_str): 7 len_str = len(train_str) # 數據長度 8 dist = 0.0 9 for i in range(len_str): # 計算距離 10 dist += (eval(test_str[i:i+1]) - eval(train_str[i:i+1]))**2 11 dist **= 0.5 12 return dist
6.1匹配結果:(test是從train里面提取出來的若干圖片合集)
數字 0 識別的正確率 = 100.00% ,錯誤率 = 0.00% ,拒絕識別率 = 0.00%
數字 1 識別的正確率 = 45.00% ,錯誤率 = 0.00% ,拒絕識別率 = 55.00%
數字 2 識別的正確率 = 85.00% ,錯誤率 = 0.00% ,拒絕識別率 = 15.00%
數字 3 識別的正確率 = 80.00% ,錯誤率 = 0.00% ,拒絕識別率 = 20.00%
數字 4 識別的正確率 = 90.00% ,錯誤率 = 0.00% ,拒絕識別率 = 10.00%
數字 5 識別的正確率 = 80.00% ,錯誤率 = 0.00% ,拒絕識別率 = 20.00%
數字 6 識別的正確率 = 90.00% ,錯誤率 = 0.00% ,拒絕識別率 = 10.00%
數字 7 識別的正確率 = 95.00% ,錯誤率 = 0.00% ,拒絕識別率 = 5.00%
數字 8 識別的正確率 = 100.00% ,錯誤率 = 0.00% ,拒絕識別率 = 0.00%
數字 9 識別的正確率 = 85.00% ,錯誤率 = 0.00% ,拒絕識別率 = 15.00%
6.2匹配結果:(其他方式構建的圖片)
暫無
7.總結:
實驗可以通過特征壓縮的方式來適當減少特征值的數量,然后再進行模板匹配,需要注意的是,特征值得壓縮需要處於適當的范圍。特征值過多會導致計算數據多大,匹配速度較慢,反之,特征值過少,對數據的判別存在較大的誤差。總的來說,對於模板匹配識別方式,計算量較大,匹配速度較慢。
2021-04-29