OCR (Optical Character Recognition,光学字符识别),我们这个练习就是对OCR英文字母进行识别。得到一张OCR图片后,提取出字符相关的ROI图像,并且大小归一化,整个图像的像素值序列可以直接作为特征。但直接将整个图像作为特征数据维度太高,计算量太大,所以也可以进行一些降维处理,减少输入的数据量。

处理过程一般这样:先对原图像进行裁剪,得到字符的ROI图像,二值化。然后将图像分块,统计每个小块中非0像素的个数,这样就形成了一个较小的矩阵,这矩阵就是新的特征了。opencv为我们提供了一些这样的数据,放在

\opencv\sources\samples\data\letter-recognition.data

这个文件里,打开看看:

每一行代表一个样本。第一列大写的字母,就是标注,随后的16列就是该字母的特征向量。这个文件中总共有20000行样本,共分类26类(26个字母)。

我们将这些数据读取出来后,分成两部分,第一部分16000个样本作为训练样本,训练出分类器后,对这16000个训练数据和余下的4000个数据分别进行测试,得到训练精度和测试精度。其中adaboost比较特殊一点,训练和测试样本各为10000.

完整代码为:

  1. #include "stdafx.h"
  2. #include "opencv2\opencv.hpp"
  3. #include <iostream>
  4. using namespace std;
  5. using namespace cv;
  6. using namespace cv::ml;
  7.  
  8. // 读取文件数据
  9. bool read_num_class_data(const string& filename, int var_count,Mat* _data, Mat* _responses)
  10. {
  11. const int M = ;
  12. char buf[M + ];
  13.  
  14. Mat el_ptr(, var_count, CV_32F);
  15. int i;
  16. vector<int> responses;
  17.  
  18. _data->release();
  19. _responses->release();
  20. FILE *f;
  21. fopen_s(&f, filename.c_str(), "rt");
  22. if (!f)
  23. {
  24. cout << "Could not read the database " << filename << endl;
  25. return false;
  26. }
  27.  
  28. for (;;)
  29. {
  30. char* ptr;
  31. if (!fgets(buf, M, f) || !strchr(buf, ','))
  32. break;
  33. responses.push_back((int)buf[]);
  34. ptr = buf + ;
  35. for (i = ; i < var_count; i++)
  36. {
  37. int n = ;
  38. sscanf_s(ptr, "%f%n", &el_ptr.at<float>(i), &n);
  39. ptr += n + ;
  40. }
  41. if (i < var_count)
  42. break;
  43. _data->push_back(el_ptr);
  44. }
  45. fclose(f);
  46. Mat(responses).copyTo(*_responses);
  47. return true;
  48. }
  49.  
  50. //准备训练数据
  51. Ptr<TrainData> prepare_train_data(const Mat& data, const Mat& responses, int ntrain_samples)
  52. {
  53. Mat sample_idx = Mat::zeros(, data.rows, CV_8U);
  54. Mat train_samples = sample_idx.colRange(, ntrain_samples);
  55. train_samples.setTo(Scalar::all());
  56.  
  57. int nvars = data.cols;
  58. Mat var_type(nvars + , , CV_8U);
  59. var_type.setTo(Scalar::all(VAR_ORDERED));
  60. var_type.at<uchar>(nvars) = VAR_CATEGORICAL;
  61.  
  62. return TrainData::create(data, ROW_SAMPLE, responses,
  63. noArray(), sample_idx, noArray(), var_type);
  64. }
  65.  
  66. //设置迭代条件
  67. inline TermCriteria TC(int iters, double eps)
  68. {
  69. return TermCriteria(TermCriteria::MAX_ITER + (eps > ? TermCriteria::EPS : ), iters, eps);
  70. }
  71.  
  72. //分类预测
  73. void test_and_save_classifier(const Ptr<StatModel>& model, const Mat& data, const Mat& responses,
  74. int ntrain_samples, int rdelta)
  75. {
  76. int i, nsamples_all = data.rows;
  77. double train_hr = , test_hr = ;
  78.  
  79. // compute prediction error on train and test data
  80. for (i = ; i < nsamples_all; i++)
  81. {
  82. Mat sample = data.row(i);
  83.  
  84. float r = model->predict(sample);
  85. r = std::abs(r + rdelta - responses.at<int>(i)) <= FLT_EPSILON ? .f : .f;
  86.  
  87. if (i < ntrain_samples)
  88. train_hr += r;
  89. else
  90. test_hr += r;
  91. }
  92.  
  93. test_hr /= nsamples_all - ntrain_samples;
  94. train_hr = ntrain_samples > ? train_hr / ntrain_samples : .;
  95.  
  96. printf("Recognition rate: train = %.1f%%, test = %.1f%%\n",
  97. train_hr*., test_hr*.);
  98. }
  99.  
  100. //随机树分类
  101. bool build_rtrees_classifier(const string& data_filename)
  102. {
  103. Mat data;
  104. Mat responses;
  105. read_num_class_data(data_filename, , &data, &responses);
  106.  
  107. int nsamples_all = data.rows;
  108. int ntrain_samples = (int)(nsamples_all*0.8);
  109.  
  110. Ptr<RTrees> model;
  111. Ptr<TrainData> tdata = prepare_train_data(data, responses, ntrain_samples);
  112. model = RTrees::create();
  113. model->setMaxDepth();
  114. model->setMinSampleCount();
  115. model->setRegressionAccuracy();
  116. model->setUseSurrogates(false);
  117. model->setMaxCategories();
  118. model->setPriors(Mat());
  119. model->setCalculateVarImportance(true);
  120. model->setActiveVarCount();
  121. model->setTermCriteria(TC(, 0.01f));
  122. model->train(tdata);
  123. test_and_save_classifier(model, data, responses, ntrain_samples, );
  124. cout << "Number of trees: " << model->getRoots().size() << endl;
  125.  
  126. // Print variable importance
  127. Mat var_importance = model->getVarImportance();
  128. if (!var_importance.empty())
  129. {
  130. double rt_imp_sum = sum(var_importance)[];
  131. printf("var#\timportance (in %%):\n");
  132. int i, n = (int)var_importance.total();
  133. for (i = ; i < n; i++)
  134. printf("%-2d\t%-4.1f\n", i, .f*var_importance.at<float>(i) / rt_imp_sum);
  135. }
  136.  
  137. return true;
  138. }
  139.  
  140. //adaboost分类
  141. bool build_boost_classifier(const string& data_filename)
  142. {
  143. const int class_count = ;
  144. Mat data;
  145. Mat responses;
  146. Mat weak_responses;
  147.  
  148. read_num_class_data(data_filename, , &data, &responses);
  149. int i, j, k;
  150. Ptr<Boost> model;
  151.  
  152. int nsamples_all = data.rows;
  153. int ntrain_samples = (int)(nsamples_all*0.5);
  154. int var_count = data.cols;
  155.  
  156. Mat new_data(ntrain_samples*class_count, var_count + , CV_32F);
  157. Mat new_responses(ntrain_samples*class_count, , CV_32S);
  158.  
  159. for (i = ; i < ntrain_samples; i++)
  160. {
  161. const float* data_row = data.ptr<float>(i);
  162. for (j = ; j < class_count; j++)
  163. {
  164. float* new_data_row = (float*)new_data.ptr<float>(i*class_count + j);
  165. memcpy(new_data_row, data_row, var_count*sizeof(data_row[]));
  166. new_data_row[var_count] = (float)j;
  167. new_responses.at<int>(i*class_count + j) = responses.at<int>(i) == j + 'A';
  168. }
  169. }
  170.  
  171. Mat var_type(, var_count + , CV_8U);
  172. var_type.setTo(Scalar::all(VAR_ORDERED));
  173. var_type.at<uchar>(var_count) = var_type.at<uchar>(var_count + ) = VAR_CATEGORICAL;
  174.  
  175. Ptr<TrainData> tdata = TrainData::create(new_data, ROW_SAMPLE, new_responses,
  176. noArray(), noArray(), noArray(), var_type);
  177. vector<double> priors();
  178. priors[] = ;
  179. priors[] = ;
  180.  
  181. model = Boost::create();
  182. model->setBoostType(Boost::GENTLE);
  183. model->setWeakCount();
  184. model->setWeightTrimRate(0.95);
  185. model->setMaxDepth();
  186. model->setUseSurrogates(false);
  187. model->setPriors(Mat(priors));
  188. model->train(tdata);
  189. Mat temp_sample(, var_count + , CV_32F);
  190. float* tptr = temp_sample.ptr<float>();
  191.  
  192. // compute prediction error on train and test data
  193. double train_hr = , test_hr = ;
  194. for (i = ; i < nsamples_all; i++)
  195. {
  196. int best_class = ;
  197. double max_sum = -DBL_MAX;
  198. const float* ptr = data.ptr<float>(i);
  199. for (k = ; k < var_count; k++)
  200. tptr[k] = ptr[k];
  201.  
  202. for (j = ; j < class_count; j++)
  203. {
  204. tptr[var_count] = (float)j;
  205. float s = model->predict(temp_sample, noArray(), StatModel::RAW_OUTPUT);
  206. if (max_sum < s)
  207. {
  208. max_sum = s;
  209. best_class = j + 'A';
  210. }
  211. }
  212.  
  213. double r = std::abs(best_class - responses.at<int>(i)) < FLT_EPSILON ? : ;
  214. if (i < ntrain_samples)
  215. train_hr += r;
  216. else
  217. test_hr += r;
  218. }
  219.  
  220. test_hr /= nsamples_all - ntrain_samples;
  221. train_hr = ntrain_samples > ? train_hr / ntrain_samples : .;
  222. printf("Recognition rate: train = %.1f%%, test = %.1f%%\n",
  223. train_hr*., test_hr*.);
  224.  
  225. cout << "Number of trees: " << model->getRoots().size() << endl;
  226. return true;
  227. }
  228.  
  229. //多层感知机分类(ANN)
  230. bool build_mlp_classifier(const string& data_filename)
  231. {
  232. const int class_count = ;
  233. Mat data;
  234. Mat responses;
  235.  
  236. read_num_class_data(data_filename, , &data, &responses);
  237. Ptr<ANN_MLP> model;
  238.  
  239. int nsamples_all = data.rows;
  240. int ntrain_samples = (int)(nsamples_all*0.8);
  241. Mat train_data = data.rowRange(, ntrain_samples);
  242. Mat train_responses = Mat::zeros(ntrain_samples, class_count, CV_32F);
  243.  
  244. // 1. unroll the responses
  245. cout << "Unrolling the responses...\n";
  246. for (int i = ; i < ntrain_samples; i++)
  247. {
  248. int cls_label = responses.at<int>(i) -'A';
  249. train_responses.at<float>(i, cls_label) = .f;
  250. }
  251.  
  252. // 2. train classifier
  253. int layer_sz[] = { data.cols, , , class_count };
  254. int nlayers = (int)(sizeof(layer_sz) / sizeof(layer_sz[]));
  255. Mat layer_sizes(, nlayers, CV_32S, layer_sz);
  256.  
  257. #if 1
  258. int method = ANN_MLP::BACKPROP;
  259. double method_param = 0.001;
  260. int max_iter = ;
  261. #else
  262. int method = ANN_MLP::RPROP;
  263. double method_param = 0.1;
  264. int max_iter = ;
  265. #endif
  266.  
  267. Ptr<TrainData> tdata = TrainData::create(train_data, ROW_SAMPLE, train_responses);
  268. model = ANN_MLP::create();
  269. model->setLayerSizes(layer_sizes);
  270. model->setActivationFunction(ANN_MLP::SIGMOID_SYM, , );
  271. model->setTermCriteria(TC(max_iter, ));
  272. model->setTrainMethod(method, method_param);
  273. model->train(tdata);
  274. return true;
  275. }
  276.  
  277. //K最近邻分类
  278. bool build_knearest_classifier(const string& data_filename, int K)
  279. {
  280. Mat data;
  281. Mat responses;
  282. read_num_class_data(data_filename, , &data, &responses);
  283. int nsamples_all = data.rows;
  284. int ntrain_samples = (int)(nsamples_all*0.8);
  285.  
  286. Ptr<TrainData> tdata = prepare_train_data(data, responses, ntrain_samples);
  287. Ptr<KNearest> model = KNearest::create();
  288. model->setDefaultK(K);
  289. model->setIsClassifier(true);
  290. model->train(tdata);
  291.  
  292. test_and_save_classifier(model, data, responses, ntrain_samples, );
  293. return true;
  294. }
  295.  
  296. //贝叶斯分类
  297. bool build_nbayes_classifier(const string& data_filename)
  298. {
  299. Mat data;
  300. Mat responses;
  301. read_num_class_data(data_filename, , &data, &responses);
  302.  
  303. int nsamples_all = data.rows;
  304. int ntrain_samples = (int)(nsamples_all*0.8);
  305.  
  306. Ptr<NormalBayesClassifier> model;
  307. Ptr<TrainData> tdata = prepare_train_data(data, responses, ntrain_samples);
  308. model = NormalBayesClassifier::create();
  309. model->train(tdata);
  310.  
  311. test_and_save_classifier(model, data, responses, ntrain_samples, );
  312. return true;
  313. }
  314.  
  315. //svm分类
  316. bool build_svm_classifier(const string& data_filename)
  317. {
  318. Mat data;
  319. Mat responses;
  320. read_num_class_data(data_filename, , &data, &responses);
  321.  
  322. int nsamples_all = data.rows;
  323. int ntrain_samples = (int)(nsamples_all*0.8);
  324.  
  325. Ptr<SVM> model;
  326. Ptr<TrainData> tdata = prepare_train_data(data, responses, ntrain_samples);
  327. model = SVM::create();
  328. model->setType(SVM::C_SVC);
  329. model->setKernel(SVM::LINEAR);
  330. model->setC();
  331. model->train(tdata);
  332.  
  333. test_and_save_classifier(model, data, responses, ntrain_samples, );
  334. return true;
  335. }
  336.  
  337. int main()
  338. {
  339. string data_filename = "E:/opencv/opencv/sources/samples/data/letter-recognition.data"; //字母数据
  340.  
  341. cout << "svm分类:" << endl;
  342. build_svm_classifier(data_filename);
  343.  
  344. cout << "贝叶斯分类:" << endl;
  345. build_nbayes_classifier(data_filename);
  346.  
  347. cout << "K最近邻分类:" << endl;
  348. build_knearest_classifier(data_filename,);
  349.  
  350. cout << "随机树分类:" << endl;
  351. build_rtrees_classifier(data_filename);
  352.  
  353. //cout << "adaboost分类:" << endl;
  354. //build_boost_classifier(data_filename);
  355.  
  356. //cout << "ANN(多层感知机)分类:" << endl;
  357. //build_mlp_classifier(data_filename);
  358. }

由于adaboost分类和 ann分类速度非常慢,因此我在main函数里把这两个分类注释掉了,大家有兴趣和时间可以测试一下。

结果:

从结果显示来看,测试的四种分类算法中,KNN(最近邻)分类精度是最高的。所以说,对ocr进行识别,还是用knn最好。

在opencv3中的机器学习算法练习:对OCR进行分类的更多相关文章

  1. 在opencv3中的机器学习算法

    在opencv3.0中,提供了一个ml.cpp的文件,这里面全是机器学习的算法,共提供了这么几种: 1.正态贝叶斯:normal Bayessian classifier    我已在另外一篇博文中介 ...

  2. opencv3中的机器学习算法之:EM算法

    不同于其它的机器学习模型,EM算法是一种非监督的学习算法,它的输入数据事先不需要进行标注.相反,该算法从给定的样本集中,能计算出高斯混和参数的最大似然估计.也能得到每个样本对应的标注值,类似于kmea ...

  3. 在opencv3中实现机器学习算法之:利用最近邻算法(knn)实现手写数字分类

    手写数字digits分类,这可是深度学习算法的入门练习.而且还有专门的手写数字MINIST库.opencv提供了一张手写数字图片给我们,先来看看 这是一张密密麻麻的手写数字图:图片大小为1000*20 ...

  4. scikit-learn中的机器学习算法封装——kNN

    接前面 https://www.cnblogs.com/Liuyt-61/p/11738399.html 回过头来看这张图,什么是机器学习?就是将训练数据集喂给机器学习算法,在上面kNN算法中就是将特 ...

  5. 在opencv3中实现机器学习之:利用逻辑斯谛回归(logistic regression)分类

    logistic regression,注意这个单词logistic ,并不是逻辑(logic)的意思,音译过来应该是逻辑斯谛回归,或者直接叫logistic回归,并不是什么逻辑回归.大部分人都叫成逻 ...

  6. 在opencv3中实现机器学习之:利用svm(支持向量机)分类

    svm分类算法在opencv3中有了很大的变动,取消了CvSVMParams这个类,因此在参数设定上会有些改变. opencv中的svm分类代码,来源于libsvm. #include "s ...

  7. 在opencv3中实现机器学习之:利用正态贝叶斯分类

    opencv3.0版本中,实现正态贝叶斯分类器(Normal Bayes Classifier)分类实例 #include "stdafx.h" #include "op ...

  8. Python机器学习算法 — 朴素贝叶斯算法(Naive Bayes)

    朴素贝叶斯算法 -- 简介 朴素贝叶斯法是基于贝叶斯定理与特征条件独立假设的分类方法.最为广泛的两种分类模型是决策树模型(Decision Tree Model)和朴素贝叶斯模型(Naive Baye ...

  9. 简单易学的机器学习算法——基于密度的聚类算法DBSCAN

    一.基于密度的聚类算法的概述     最近在Science上的一篇基于密度的聚类算法<Clustering by fast search and find of density peaks> ...

随机推荐

  1. jar包双击执行程序

    源文件:MyMenuTest.java 编译结果: C:\Users\zhangbz\Desktop\demo>javac MyMenuTest.java C:\Users\zhangbz\De ...

  2. 为Xcode添加和备份快捷代码

    有IOS开发经验的,相信你一定了解快捷代码的使用以及可以自定义代码,备份到XCode右下角,供下次使用. 那么,快捷代码备份到本地什么位置呢: 位置:~/Library/Developer/Xcode ...

  3. css中font-family的中文字体

    说到css中的font-family,相信很多朋友经常用,但不知道当你遇到引用中文字体的时候你会怎么写?最近特别关注了下,发现最常用的基本有三种类型: 1.直接中文: 2.英文形式: 3.unicod ...

  4. Handler、Looper、MessageQueue、Thread源码分析

    关于这几个之间的关系以及源码分析的文章应该挺多的了,不过既然学习了,还是觉得整理下,印象更深刻点,嗯,如果有错误的地方欢迎反馈. 转载请注明出处:http://www.cnblogs.com/John ...

  5. 修复ORACLETNS-12545 因目标主机或对象不存在错误

    现象: ORACLE启动不了,输入cmd->lsnrctl后,出现如下错误, 经查资料,发现是主机名可能解析有问题,后来在D:\oracle\ora92\network\admin下打开list ...

  6. JavaScript Patterns 3.1 Object Literal

    Basic concept Values can be properties: primitives or other objects methods: functions User-defined ...

  7. 深入PHP内核之in_array

    无意中看到一段代码 1.a.php <?php $y="12"; $x = array(); for($j=0;$j<50000;$j++){ $x[]= " ...

  8. Mongodb 字段类型转换

    db.diningmembers.find({modifedDate:{$type:9}}).forEach(function(x){x.tel = String(x.tel);db.diningme ...

  9. Snowflake weakness and type2 fact table

    DimProduct DimSubcategory Dimcategory productpk subcategorypk categorypk sku subcategoryName categor ...

  10. js开发工具箱

    昨天看到一位大牛的博客,里面有一篇文章“web前端开发分享-目录”,文章中提到的一个给前端er用的一个js开发工具箱.自己使用了一下,非常好用,代码压缩,代码美化,加密,解密之类基本功能都有,生成二维 ...