环境: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