OpenCV玩九宮格數獨(二):knn數字識別

前言

首先需要說明,這裏所說的數字識別不是手寫數字識別!

但凡對機器學習有所瞭解的人,相信看到數字識別的第一反應就是MNIST。MNIST是可以進行數字識別,但是那是手寫數字。我們現在要做的是要識別從九宮格圖片中提取出來的印刷體的數字。手寫數字集訓練出來的模型用來識別印刷體數字,顯然不太專業。而且手寫體跟印刷體相差不小,我們最看重的正確率問題不能保證。

本文從零開始做一遍數字識別,展示了數字識別的完整流程。從收集數據開始,到數據預處理,再到訓練KNN,最後進行數字識別。

我們一步一步來說。

數據收集

爲了便於處理,我百度找到了10張下面這樣按照1-9-0順序排列的圖片,作爲我們的初始數據集。

這裏寫圖片描述

有的圖片可能本來除數字區域外,周圍空白部分比較多。爲了便於處理,首先用windows自帶的畫圖軟件把圖片裁剪成上面這樣只包含數字區域的樣子。

這十張數據集基本涵蓋了印刷數字體的不同樣式、字體,而且顏色、背景甚至漸變方式都各不相同。

數據處理

顯然,我們第一步要做的就是上一節的內容,那就是把圖片中的數字分別提取出來。

訓練knn,還有其他任何有監督的機器學習模型,不光要有樣本數據,還要有知道每一個樣本對應的標籤。這也是爲什麼我要選擇上面這樣按順序排列的數字圖片。

提取數字之後,我們可以對每一個數字的位置進行排序,然後根據位置信息可以知道每一個數字是幾。標籤也就由此生成了。

這一部分的內容可以分兩部分來說,第一部分就是提取數字,第二部分是提取數字之後的數據預處理。

1.提取數字

提取數字的處理流程與上一篇內容差不多:

1.遍歷文件夾下的原始數字圖片;

2.對每一張圖片進行輪廓提取操作,只提取外圍輪廓(參考上一節講解);

img_path = gb.glob("numbers\\*") 

k = 0
labels = []
samples =  []

for path in img_path:
    img  = cv2.imread(path)       
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray,(5,5),0)
    thresh = cv2.adaptiveThreshold(blur,255,1,1,11,2)    
    image, contours, hierarchy =                      cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

3.求輪廓外包矩形,並根據矩形大小信息篩選出所有的數字輪廓;

4.然後根據位置信息對數字框排序,顯然第一排依次是12345,第二排依次是67890;

    height,width = img.shape[:2]
    w = width/5
    rect_list = []
    list1 = []
    list2 = []
    for cnt in contours:
        #if cv2.contourArea(cnt)>100:
        [x,y,w,h] = cv2.boundingRect(cnt)

        if w>30 and h > (height/4):        
            if y < (height/2):
                list1.append([x,y,w,h])
            else:
                list2.append([x,y,w,h])
    list1_sorted = sorted(list1,key = lambda t : t[0])
    list2_sorted = sorted(list2,key = lambda t : t[0])

5.提取出每一個數字所在的矩形框,作爲ROI取出。

    for i in range(5):
        [x1,y1,w1,h1] = list1_sorted[i] 
        [x2,y2,w2,h2] = list2_sorted[i]                
        number_roi1 = gray[y1:y1+h1, x1:x1+w1] #Cut the frame to size
        number_roi2 = gray[y2:y2+h2, x2:x2+w2] #Cut the frame to size  

數據預處理

爲了加快訓練速度,我們不用原圖作爲輸入,而是對每一個數字原圖做一定的處理。此處可選方案很多,提取特徵有很多經典特徵可選,也可以是自己設計的特徵。

這裏我用比較簡單的方法,把每一張數字圖片ROI轉換爲二值圖像。大致流程是這樣的:

1.把每一張ROI大小統一變換爲40 x 20。

2.閾值分割。

        resized_roi1=cv2.resize(number_roi1,(20,40))
        thresh1 = cv2.adaptiveThreshold(resized_roi1,255,1,1,11,2)

        resized_roi2=cv2.resize(number_roi2,(20,40))
        thresh2 = cv2.adaptiveThreshold(resized_roi2,255,1,1,11,2)

3.把二值圖像轉換爲0-1二值圖像。

4.把處理完的數字圖片保存到對應數字的文件夾中。(此爲中間過程,可註釋掉)

        number_path1 = "number\\%s\\%d" % (str(i+1),k) + '.jpg'
        j = i+6
        if j ==10:
            j = 0
        number_path2 = "number\\%s\\%d" % (str(j),k) + '.jpg'
        k+=1

        normalized_roi1 = thresh1/255.
        normalized_roi2 = thresh2/255.
        cv2.imwrite(number_path1,thresh1)
        cv2.imwrite(number_path2,thresh2)

處理完之後保存的文件夾如下:
這裏寫圖片描述
每一個文件夾裏面類似這樣,可以看到背景有黑有白,數字也是有黑有白:

這裏寫圖片描述

5.把處理完的二值圖像展開成一行。

6.最後把展開成的一行行樣本保存起來作爲訓練用的數據。

7.對應的,把數字標籤按照數字的保存順序對應保存成訓練用的數據。

        sample1 = normalized_roi1.reshape((1,800))
        samples.append(sample1[0])
        labels.append(float(i+1))

        sample2 = normalized_roi2.reshape((1,800))
        samples.append(sample2[0])
        labels.append(float(j))
import numpy as np
samples = np.array(samples,np.float32)
labels = np.array(labels,np.float32)
labels = labels.reshape((labels.size,1))
np.save('samples.npy',samples)
np.save('label.npy',labels)

訓練kNN識別數字

這裏用opencv自帶的knn算法實現。我同時嘗試了opencv自帶的神經網絡和SVM,發現還是kNN的效果最好。有興趣的可以自己去嘗試一下。也可能是我參數沒調好。

這裏的流程是:

1.加載上面保存的樣本和標籤數據;

2.分別用80個作爲訓練數據,20個作爲測試數據;

3.用opencv自帶的knn訓練模型;

4.用訓練好的模型識別測試數據中的數字;

5.輸出預測值和實際標籤值。

import numpy as np
import cv2

samples = np.load('samples.npy')
labels = np.load('label.npy')

k = 80
train_label = labels[:k]
train_input = samples[:k]
test_input = samples[k:]
test_label = labels[k:]

model = cv2.ml.KNearest_create()
model.train(train_input,cv2.ml.ROW_SAMPLE,train_label)

retval, results, neigh_resp, dists = model.findNearest(test_input, 1)
string = results.ravel()

print(test_label.reshape(1,len(test_label))[0])
print(string)

下面是輸出結果:

這裏寫圖片描述

可以看到,預測值和實際值簡直一模一樣!

注意

1.opencv中的knn只能訓練模型,不能保存和加載模型。所以只能用的時候訓練,訓練好直接用。

2.此次訓練樣本只有不到一百,暫時只能保證對於本系列文章自帶的九宮格圖片進行完美的數字識別。其他圖片的數字識別準確率不敢保證。如果想要得到更好的效果,請按照機器學習的方法進行優化,或進行更好的數據與處理,或加大數據集等。

3.整個項目代碼會在下一篇,也就是最終篇之後放出。


公衆號CVPy,分享OpenCV和Python的實戰內容。每一篇都會放出完整的代碼。歡迎關注。

公衆號CVPy

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章