手寫數字digits分類,這可是深度學習算法的入門練習。而且還有專門的手寫數字MINIST庫。opencv提供了一張手寫數字圖片給我們,先來看看
這是一張密密麻麻的手寫數字圖:圖片大小為1000*2000,有0-9的10個數字,每5行為一個數字,總共50行,共有5000個手寫數字。在opencv3.0版本中,圖片存放位置為
/opencv/sources/samples/data/digits.png
我們首先要做的,就是把這5000個手寫數字,一個個截取出來,每個數字塊大小為20*20。直接將每個小圖塊進行序列化,因此最終得到一個5000*400的特征矩陣。樣本數為5000,維度為400維。取其中前3000個樣本進行訓練。
注意:截取的時候,是按列截取。不然取前3000個樣本進行訓練就會出現后幾個數字訓練不到。
具體代碼:
#include "stdafx.h" #include "opencv2\opencv.hpp" #include <iostream> using namespace std; using namespace cv; using namespace cv::ml; int main() { Mat img = imread("E:/opencv/opencv/sources/samples/data/digits.png"); Mat gray; cvtColor(img, gray, CV_BGR2GRAY); int b = 20; int m = gray.rows / b; //原圖為1000*2000 int n = gray.cols / b; //裁剪為5000個20*20的小圖塊 Mat data,labels; //特征矩陣 for (int i = 0; i < n; i++) { int offsetCol = i*b; //列上的偏移量 for (int j = 0; j < m; j++) { int offsetRow = j*b; //行上的偏移量 //截取20*20的小塊 Mat tmp; gray(Range(offsetRow, offsetRow + b), Range(offsetCol, offsetCol + b)).copyTo(tmp); data.push_back(tmp.reshape(0,1)); //序列化后放入特征矩陣 labels.push_back((int)j / 5); //對應的標注 } } data.convertTo(data, CV_32F); //uchar型轉換為cv_32f int samplesNum = data.rows; int trainNum = 3000; Mat trainData, trainLabels; trainData = data(Range(0, trainNum), Range::all()); //前3000個樣本為訓練數據 trainLabels = labels(Range(0, trainNum), Range::all()); //使用KNN算法 int K = 5; Ptr<TrainData> tData = TrainData::create(trainData, ROW_SAMPLE, trainLabels); Ptr<KNearest> model = KNearest::create(); model->setDefaultK(K); model->setIsClassifier(true); model->train(tData); //預測分類 double train_hr = 0, test_hr = 0; Mat response; // compute prediction error on train and test data for (int i = 0; i < samplesNum; i++) { Mat sample = data.row(i); float r = model->predict(sample); //對所有行進行預測 //預測結果與原結果相比,相等為1,不等為0 r = std::abs(r - labels.at<int>(i)) <= FLT_EPSILON ? 1.f : 0.f; if (i < trainNum) train_hr += r; //累積正確數 else test_hr += r; } test_hr /= samplesNum - trainNum; train_hr = trainNum > 0 ? train_hr / trainNum : 1.; printf("accuracy: train = %.1f%%, test = %.1f%%\n", train_hr*100., test_hr*100.); waitKey(0); return 0; }
根據經驗,利用最近鄰算法對手寫數字進行分類,會有很高的精度,因此在本文中我們采用的是knn算法。
最終結果:
訓練精度為95.9%, 測試精度為92.6%。如果對手寫數字識別准確率達不到90%以上,就沒有什么實際作用了。如果調整訓練樣本數,這個精度應該會有所改變。