环境:opencv3.3 + C++ +win10 64位
利用KNN进行手写数字识别,在opencv的文件夹中提供了一个可以用来训练的照片,一共有5000个小样本每个数字对应的有500个图片 。对应的文件夹应该是 opencv/sources/samples/data/digits.png
其中每个小图片的样本是20*20 作为训练集和预测集的图片大小必须一致,所以用程序把每一个数字都切出来,所切的图片大小为20*20放入矩阵,原图的大小为1000*2000。
Mat gray; cvtColor(img, gray, CV_BGR2GRAY); int b = 20; int m = gray.rows / b; //每一行图片的个数 int n = gray.cols / b; //每一列图片的个数 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,把对应的标注加入向量labels。然后调用opencv中的KNN算法对样本进行训练。
int K = 7; Ptr<TrainData> tData = TrainData::create(trainData, ROW_SAMPLE, trainLabels); Ptr<KNearest> model = KNearest::create(); model->setDefaultK(K); model->setIsClassifier(true); model->train(tData); model->save("KnnTest.xml");
用4000的样本作为训练集,用1000的样本作为验证集。训练完毕后调用训练好的模型对数据进验证,发现对本身的验证准确度能达到90%
double train_hr = 0, test_hr = 0; //Mat response; //// compute prediction error on train and test data //for (int i = 3000; 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;
后来用自己手写的数据进行测试发现准准确度较低,所以对样本的数据加入自己写的数据在进行测试发现效果比较差-,-
于是我对手写的样本又加入了周围人的字体样式,每个样本大约增加20个数据后进行测试,准确度明显提高(如下图所示)。其中对加入的样本一定要保证都为CV_32F格式。
下面是代码
//加入自己写的数据集 Mat src = imread("E:/picture/shuziceshi.png"); //imshow("Input img", src); Mat gray_frame, thres_img, blur_img; cvtColor(src, gray_frame, COLOR_BGR2GRAY);//对图像进行预处理(图像去燥二值化) //medianBlur(gray_frame, gray_frame, 3); GaussianBlur(gray_frame, blur_img, Size(3, 3), 3, 3); //threshold(gray_frame, thres_img, 0, 255, THRESH_BINARY | THRESH_OTSU); adaptiveThreshold(blur_img, thres_img, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV, 151, 10); Mat morph_img, tmp2,tmp3; Mat kernerl = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1)); morphologyEx(thres_img, morph_img, MORPH_OPEN, kernerl, Point(-1, -1)); //imshow("Bin", morph_img); vector<vector<Point>> contours; vector<Vec4i> hiearachy; int k = 0;//显示想要的轮廓数这样就可以方便把新数据压入数据集 不用单独保存 findContours(morph_img, contours, hiearachy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); for (int i = 0; i < contours.size(); ++i) { Rect minrect = boundingRect(contours[i]); float area = contourArea(contours[i]); float ckbi = minrect.width / minrect.height; //cout << ckbi << endl; if(ckbi<4&& area>50) { //cout << minrect.height << endl << minrect.width; rectangle(src, minrect, Scalar(0, 255, 0), 1, 8); Rect ROI =minrect; Mat ROI_img = morph_img(ROI); resize(ROI_img, ROI_img, Size(20, 20)); //ROI_img.convertTo(ROI_img, CV_32F); ROI_img.copyTo(tmp2); /*predict_mat.push_back(tmp2.reshape(0, 1)); Mat predict_simple = predict_mat.row(i);*/ //float r = model->predict(predict_simple); stringstream stream; stream << k; string str; stream >> str; //string_temp = stream.str(); putText(src, str, ROI.tl(), FONT_HERSHEY_PLAIN, 1, Scalar(255, 0, 0), 1, 8); data.push_back(tmp2.reshape(0, 1)); //序列化后放入特征矩阵 labels.push_back((int)k / 15); //对应的标注 k++; } } //imshow("Out", src); data.convertTo(data, CV_32F); //uchar型转换为cv_32f int samplesNum = data.rows; int trainNum = 5000; Mat trainData, trainLabels; trainData = data(Range(0, samplesNum), Range::all()); //前3000个样本为训练数据 trainLabels = labels(Range(0, samplesNum), Range::all());//前三千个训练标签 //使用KNN算法 int K = 7; Ptr<TrainData> tData = TrainData::create(trainData, ROW_SAMPLE, trainLabels); Ptr<KNearest> model = KNearest::create(); model->setDefaultK(K); model->setIsClassifier(true); model->train(tData); model->save("KnnTest.xml"); //用样本进行测试 Mat src_test = imread("E:/picture/shuzi4.png"); imshow("Input img", src_test); Mat gray_test, thres_test, blur_test; cvtColor(src_test, gray_test, COLOR_BGR2GRAY); GaussianBlur(gray_test, blur_test, Size(3, 3), 3, 3); adaptiveThreshold(blur_test, thres_test, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV, 151, 10); Mat morph_test, predict_mat; morphologyEx(thres_test, morph_test, MORPH_OPEN, kernerl, Point(-1, -1)); imshow("Bin", morph_test); vector<vector<Point>> contours_test; vector<Vec4i> hiearachy_test; findContours(morph_test, contours_test, hiearachy_test, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); for (int i = 0; i < contours_test.size(); ++i) { Rect minrect_test = boundingRect(contours_test[i]); float area_test = contourArea(contours_test[i]); float ckbi_test = minrect_test.width / minrect_test.height; if (ckbi_test<4 && area_test>50) { rectangle(src_test, minrect_test, Scalar(0, 255, 0), 1, 8); Rect ROI_test = minrect_test; Mat ROI_img_test = morph_test(ROI_test); resize(ROI_img_test, ROI_img_test, Size(20, 20)); ROI_img_test.convertTo(ROI_img_test, CV_32F); ROI_img_test.copyTo(tmp3); predict_mat.push_back(tmp3.reshape(0, 1)); Mat predict_simple = predict_mat.row(i); float r = model->predict(predict_simple); stringstream stream; stream << r; string str; stream >> str; putText(src_test, str, ROI_test.tl(), FONT_HERSHEY_PLAIN, 1, Scalar(255, 0, 0), 1, 8); } } imshow("img_test", src_test)
有什么改进和不足欢迎批评指正。
参考资料:http://www.cnblogs.com/denny402/p/5033898.html