【学习OpenCV4】基于OpenCV的手写数字识别
本内容分享于课程《OpenCV入门精讲(C++/Python双语教学)》,地址:
OpenCV入门精讲(C++/Python双语教学)
如果想提升C++的编程水平,可以参考课程:
C++进阶学习
OpenCV课程中还有很多有趣且实用的案例,这些案例都以C++和Python两种语言实现,对其中的代码都有详细的讲解。
基于OpenCV的手写数字识别案例从’digits.png’加载手写数字识别的数据集,然后训练一个SVM和KNearest 分类器并评估它们的准确率。
数据集会经过如下的预处理:
- 基于图像的矩去扭曲 (参见函数deskew())
- 数字图像被分割成4个10x10的cells和16个bin,为每个bin计算定向梯度直方图
- 使用Hellinger度量将直方图转换为space(see [1] (RootSIFT))
代码如下
int main(int /* argc */, char *argv[])
{
help(argv);
vector<Mat> digits; // 训练数据
vector<int> labels; // 标签
// 加载训练数据和标签
load_digits(DIGITS_FN, digits, labels);
cout << "preprocessing..." << endl;
// 数据打乱
shuffle(digits, labels);
vector<Mat> digits2;
// 扭曲图像
for (size_t i = 0; i < digits.size(); i++)
{
Mat deskewed_digit;
deskew(digits[i], deskewed_digit);
digits2.push_back(deskewed_digit);
}
Mat samples;
// 预处理求hog算子
preprocess_hog(digits2, samples);
// imshow("samples", samples);
// waitKey(0);
// 90%的数据做训练集
int train_n = (int)(0.9 * samples.rows);
Mat test_set;
// 存储测试数据
vector<Mat> digits_test(digits2.begin() + train_n, digits2.end());
// 马赛克式将小图像拼成大图像
mosaic(25, digits_test, test_set);
imshow("test set", test_set);
imwrite("test_image.jpg", test_set);
// 训练数据和测试数据划分
Mat samples_train = samples(Rect(0, 0, samples.cols, train_n));
Mat samples_test = samples(Rect(0, train_n, samples.cols, samples.rows - train_n));
vector<int> labels_train(labels.begin(), labels.begin() + train_n);
vector<int> labels_test(labels.begin() + train_n, labels.end());
Ptr<ml::KNearest> k_nearest;
Ptr<ml::SVM> svm;
vector<float> predictions;
Mat vis;
// K最近邻算法
cout << "training KNearest..." << endl;
k_nearest = ml::KNearest::create();
// 模型训练
k_nearest->train(samples_train, ml::ROW_SAMPLE, labels_train);
// KNearest做结果预测
k_nearest->findNearest(samples_test, 4, predictions);
// 模型评估,计算错误率
evaluate_model(predictions, digits_test, labels_test, vis);
imshow("KNearest test", vis);
imwrite("KNearest-test.jpg", vis);
k_nearest.release();
// SVM算法
cout << "training SVM..." << endl;
svm = ml::SVM::create();
svm->setGamma(5.383);
svm->setC(2.67);
svm->setKernel(ml::SVM::RBF);
svm->setType(ml::SVM::C_SVC);
svm->train(samples_train, ml::ROW_SAMPLE, labels_train);
// predict digits with SVM
svm->predict(samples_test, predictions);
evaluate_model(predictions, digits_test, labels_test, vis);
imshow("SVM test", vis);
imwrite("SVM-test.jpg", vis);
cout << "Saving SVM as \"digits_svm.yml\"..." << endl;
// 训练的结果保存
svm->save("digits_svm.yml");
svm.release();
waitKey();
return 0;
}
训练结果如下:
KNearest error: 2.80 %
SVM error: 2.40 %
测试图片如下:
KNearest算法测试结果(红色标识为识别错误的结果):
SVM算法测试结果(红色标识为识别错误的结果):