OpenCV 玩九宫格数独(二):knn 数字识别
歡迎大家關(guān)注騰訊云技術(shù)社區(qū)-博客園官方主頁(yè),我們將持續(xù)在博客園為大家推薦技術(shù)精品文章哦~
作者:劉瀟龍
前言
首先需要說(shuō)明,這里所說(shuō)的數(shù)字識(shí)別不是手寫(xiě)數(shù)字識(shí)別!
但凡對(duì)機(jī)器學(xué)習(xí)有所了解的人,相信看到數(shù)字識(shí)別的第一反應(yīng)就是MNIST。MNIST是可以進(jìn)行數(shù)字識(shí)別,但是那是手寫(xiě)數(shù)字。我們現(xiàn)在要做的是要識(shí)別從九宮格圖片中提取出來(lái)的印刷體的數(shù)字。手寫(xiě)數(shù)字集訓(xùn)練出來(lái)的模型用來(lái)識(shí)別印刷體數(shù)字,顯然不太專(zhuān)業(yè)。而且手寫(xiě)體跟印刷體相差不小,我們最看重的正確率問(wèn)題不能保證。
本文從零開(kāi)始做一遍數(shù)字識(shí)別,展示了數(shù)字識(shí)別的完整流程。從收集數(shù)據(jù)開(kāi)始,到數(shù)據(jù)預(yù)處理,再到訓(xùn)練KNN,最后進(jìn)行數(shù)字識(shí)別。
我們一步一步來(lái)說(shuō)。
數(shù)據(jù)收集
為了便于處理,我百度找到了10張下面這樣按照1-9-0順序排列的圖片,作為我們的初始數(shù)據(jù)集。
有的圖片可能本來(lái)除數(shù)字區(qū)域外,周?chē)瞻撞糠直容^多。為了便于處理,首先用windows自帶的畫(huà)圖軟件把圖片裁剪成上面這樣只包含數(shù)字區(qū)域的樣子。
這十張數(shù)據(jù)集基本涵蓋了印刷數(shù)字體的不同樣式、字體,而且顏色、背景甚至漸變方式都各不相同。
數(shù)據(jù)處理
顯然,我們第一步要做的就是上一節(jié)的內(nèi)容,那就是把圖片中的數(shù)字分別提取出來(lái)。
訓(xùn)練knn,還有其他任何有監(jiān)督的機(jī)器學(xué)習(xí)模型,不光要有樣本數(shù)據(jù),還要有知道每一個(gè)樣本對(duì)應(yīng)的標(biāo)簽。這也是為什么我要選擇上面這樣按順序排列的數(shù)字圖片。
提取數(shù)字之后,我們可以對(duì)每一個(gè)數(shù)字的位置進(jìn)行排序,然后根據(jù)位置信息可以知道每一個(gè)數(shù)字是幾。標(biāo)簽也就由此生成了。
這一部分的內(nèi)容可以分兩部分來(lái)說(shuō),第一部分就是提取數(shù)字,第二部分是提取數(shù)字之后的數(shù)據(jù)預(yù)處理。
1.提取數(shù)字
提取數(shù)字的處理流程與上一篇內(nèi)容差不多:
1.遍歷文件夾下的原始數(shù)字圖片;
2.對(duì)每一張圖片進(jìn)行輪廓提取操作,只提取外圍輪廓(參考上一節(jié)講解);
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.求輪廓外包矩形,并根據(jù)矩形大小信息篩選出所有的數(shù)字輪廓;
4.然后根據(jù)位置信息對(duì)數(shù)字框排序,顯然第一排依次是12345,第二排依次是67890;
height,width = img.shape[:2]w = width/5rect_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.提取出每一個(gè)數(shù)字所在的矩形框,作為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 sizenumber_roi2 = gray[y2:y2+h2, x2:x2+w2] #Cut the frame to size數(shù)據(jù)預(yù)處理
為了加快訓(xùn)練速度,我們不用原圖作為輸入,而是對(duì)每一個(gè)數(shù)字原圖做一定的處理。此處可選方案很多,提取特征有很多經(jīng)典特征可選,也可以是自己設(shè)計(jì)的特征。
這里我用比較簡(jiǎn)單的方法,把每一張數(shù)字圖片ROI轉(zhuǎn)換為二值圖像。大致流程是這樣的:
1.把每一張ROI大小統(tǒng)一變換為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.把二值圖像轉(zhuǎn)換為0-1二值圖像。
4.把處理完的數(shù)字圖片保存到對(duì)應(yīng)數(shù)字的文件夾中。(此為中間過(guò)程,可注釋掉)
number_path1 = "number\\%s\\%d" % (str(i+1),k) + '.jpg'j = i+6if j ==10:j = 0number_path2 = "number\\%s\\%d" % (str(j),k) + '.jpg'k+=1normalized_roi1 = thresh1/255.normalized_roi2 = thresh2/255.cv2.imwrite(number_path1,thresh1)cv2.imwrite(number_path2,thresh2)處理完之后保存的文件夾如下:
每一個(gè)文件夾里面類(lèi)似這樣,可以看到背景有黑有白,數(shù)字也是有黑有白:
5.把處理完的二值圖像展開(kāi)成一行。
6.最后把展開(kāi)成的一行行樣本保存起來(lái)作為訓(xùn)練用的數(shù)據(jù)。
7.對(duì)應(yīng)的,把數(shù)字標(biāo)簽按照數(shù)字的保存順序?qū)?yīng)保存成訓(xùn)練用的數(shù)據(jù)。
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)訓(xùn)練kNN識(shí)別數(shù)字
這里用opencv自帶的knn算法實(shí)現(xiàn)。我同時(shí)嘗試了opencv自帶的神經(jīng)網(wǎng)絡(luò)和SVM,發(fā)現(xiàn)還是kNN的效果最好。有興趣的可以自己去嘗試一下。也可能是我參數(shù)沒(méi)調(diào)好。
這里的流程是:
1.加載上面保存的樣本和標(biāo)簽數(shù)據(jù);
2.分別用80個(gè)作為訓(xùn)練數(shù)據(jù),20個(gè)作為測(cè)試數(shù)據(jù);
3.用opencv自帶的knn訓(xùn)練模型;
4.用訓(xùn)練好的模型識(shí)別測(cè)試數(shù)據(jù)中的數(shù)字;
5.輸出預(yù)測(cè)值和實(shí)際標(biāo)簽值。
import numpy as np import cv2samples = 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)下面是輸出結(jié)果:
可以看到,預(yù)測(cè)值和實(shí)際值簡(jiǎn)直一模一樣!
注意
1.opencv中的knn只能訓(xùn)練模型,不能保存和加載模型。所以只能用的時(shí)候訓(xùn)練,訓(xùn)練好直接用。
2.此次訓(xùn)練樣本只有不到一百,暫時(shí)只能保證對(duì)于本系列文章自帶的九宮格圖片進(jìn)行完美的數(shù)字識(shí)別。其他圖片的數(shù)字識(shí)別準(zhǔn)確率不敢保證。如果想要得到更好的效果,請(qǐng)按照機(jī)器學(xué)習(xí)的方法進(jìn)行優(yōu)化,或進(jìn)行更好的數(shù)據(jù)與處理,或加大數(shù)據(jù)集等。
3.整個(gè)項(xiàng)目代碼會(huì)在下一篇,也就是最終篇之后放出。
相關(guān)推薦:
OpenCV玩九宮格數(shù)獨(dú)(一):九宮格圖片中提取數(shù)字
OpenCV檢測(cè)篇(一)——貓臉檢測(cè)
OpenCV檢測(cè)篇(二)——笑臉檢測(cè)
此文已由作者授權(quán)騰訊云技術(shù)社區(qū)發(fā)布,轉(zhuǎn)載請(qǐng)注明文章出處
原文鏈接:https://www.qcloud.com/community/article/901168
獲取更多騰訊海量技術(shù)實(shí)踐干貨,歡迎大家前往騰訊云技術(shù)社區(qū)
轉(zhuǎn)載于:https://www.cnblogs.com/qcloud1001/p/6764025.html
總結(jié)
以上是生活随笔為你收集整理的OpenCV 玩九宫格数独(二):knn 数字识别的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 1099 字串变换 2002年NOIP
- 下一篇: ajax传递参数给springmvc总结