http://blog.csdn.net/jinzhuojun/article/details/8579416

本文将基于OpenCV实现简单的数字识别。这里以游戏Angry Birds为例,通过以下几个主要步骤对其中右上角的分数部分进行自动识别。


1. 学习分类器

根据训练样本,选取模型训练产生数字分类器。这里的样本可以是通用的数字样本库(如NIST等),也可以是针对应用场景而制作的专门训练样本。前者优在泛化性,后者强在准确率,当然常用做法是将这两者结合,即在通用数字库基础上做修改。另外这里由于模式并不复杂,计算量也不大,所以不对样本进行特征提取,对原始样本作简单变换后直接作为训练样本。

具体地,首先是生成训练样本矩阵,一般样本是以二维矩阵的方式存在文件当中,现在要将它们读出来,进行适当的预处理,然后生成OpenCV能理解的数据结构。

train_X = cvCreateMat(sample_num * class_num, size * size, CV_32FC1);
train_Y = cvCreateMat(sample_num * class_num, 1, CV_32FC1);for(i = 0; i < class_num; i++){for(j = 0; j < sample_num; j++){ src_image = cvLoadImage(file,0);pimage = preprocessing(src_image, size, size);...cvGetRow(train_X, &row, i * sample_num + j);row_vec = cvReshape(&data, &mathdr, 0, 1);cvCopy(row_vec, &row, NULL);...cvGetRow(train_Y, &row, i * sample_num + j);cvSet(&row, cvRealScalar(i));}
}

训练样本中的数字位置形态各异,因此读入时需要进行规整化。主要方法是先找到数字的边界框,然后以宽和高中大的一边为基准进行缩放和拉伸,从而使得其可以占满整个表示单个样本的矩阵。

IplImage preprocessing(IplImage* img, int w, int h){...bb = findBoundingBox(img);cvGetSubRect(img, &data, cvRect(bb.x, bb.y, bb.width, bb.height));size = (bb.width > bb.height) ? bb.width : bb.height;res = cvCreateImage(cvSize(size, size), 8, 1);x = floor((float)(size - bb.width) / 2.0f);y = floor((float)(size - bb.height) / 2.0f);cvGetSubRect(res, &subdata, cvRect((int)x, (int)y, bb.width, bb.height));cvCopy(&data, &subdata, NULL);ret = cvCreateImage(cvSize(w, h), 8, 1);cvResize(res, ret, CV_INTER_NN);return *ret;
}

假设单个样本可表示为0/1矩阵,那findBoundingBox()只要从x和y方向分别扫描最大最小的非0值就可以了。 训练样本准备好后,在OpenCV中创建相应的分类器非常方便。这里用的是KNN,当然除了KNN外还有其它很多封装好的分类器(如NN, SVM等)。

knn = new CvKNearest(train_X, train_Y, 0, false, K);

2. 图像预处理

前面通过学习产生了分类器,但我们输入图像中的数字并不能直接作为测试输入。图像中的数字笔画有时并不规整,还可能相互重叠。因为本文例子为了简化用的是屏幕截图,所以位置形变校正,色彩亮度校正等等都省去了,但仍需要一些简单处理。下面先对输入图像进行一把简单的预处理,主要目的是将数字之间两两分开。方法很简单,首先将图像转成二值图,然后腐蚀一把,数字之间就分离得比较开了,这样便于我们下一步分割和识别。这样做还有个好处,就是把其余的噪声也顺带去掉了。

cvtColor(input, out_img, CV_BGR2GRAY);
threshold(out_img, out_img, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
...
erode(out_img, out_img, elem);

结果:


3. 图像分割

接下来,就可以对图像进行分割了。由于我们的分类器只能对数字一个一个地识别,所以首先要把每个数字分割出来。基本思想是先用findContours()函数把基本轮廓找出来,然后通过简单验证以确认是否为数字的轮廓。对于那些通过验证的轮廓,接下去会用boundingRect()找出它们的包围盒。

vector< vector< Point> > contours;
findContours(contour_img, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);   vector<vector<Point> >::iterator it = contours.begin();
while (it!=contours.end()) {  RotatedRect rect = minAreaRect(Mat(*it));  if(verifyRect(rect)){     ++it; // A valid rectangle found      } else {  it= contours.erase(it);  }
}  ...
vector<Rect> boundRect(contours.size());
for (int i = 0; i < contours.size(); ++i) {  Scalar color = Scalar(200, 200, 200);  boundRect[i] = boundingRect(Mat(contours[i]));  rectangle(out_img, boundRect[i].tl(), boundRect[i].br(), color, 0.2, 8, 0);  CvRect roi = CvRect(boundRect[i]);  IplImage orig = out_img;  IplImage *res = cvCreateImage(cvSize(roi.width, roi.height), orig.depth, orig.nChannels);  cvSetImageROI(&orig, roi);  cvCopy(&orig, res);  cvResetImageROI(&orig);  IplImage *bininv_img;  bininv_img = cvCreateImage(cvSize(128, 128), IPL_DEPTH_8U, 1);  cvResize(res, bininv_img);  cvThreshold(bininv_img, bininv_img, 100, 255, CV_THRESH_BINARY_INV);          int ret = do_ocr(bininv_img);  res_elem elem;  elem.num = ret;  elem.xpos = boundRect[i].tl().x;  res_vec.push_back(elem);  ...
}  

结果:

4. 应用分类器

分割完后就可以应用我们前面训练好的分类器对分割结果进行识别了。当然,如果感觉结果不满意,可以将分类错误的样本加上正确的标签后放入训练样本重新生成分类器,使得分类器能够有更好的识别率。上一步中的do_ocr()函数就是利用先前训练好的分类器识别单个数字。注意训练样本进行过怎么样的预处理,这里也一样要做。

int do_ocr(IplImage *img)
{...pimage = preprocessing(img, size, size);...cvGetSubRect(pimage, &data, cvRect(0, 0, size, size));CvMat mathdr, *vec;vec = cvReshape(&data, &mathdr, 0, 1);ret = knn->find_nearest(vec, K, 0, 0, nearest, 0);return (int)ret;
}

5. 后期处理

因为分割图像时查找数字轮廓并不保证是按顺序来的,所以这儿要将识别结果按分割时输出的包围盒位置信息进行排序,最后将它们转换成数字输出。

sort(res_vec.begin(), res_vec.end(), sort_func);
int j, num = 0;
for (j = 0; j < res_vec.size(); ++j) {num = num * 10 + res_vec[j].num;
}
char resbuf[256];
sprintf(resbuf, "%d", num);
putText(show_img, resbuf, Point(OUTPUT_X, OUTPUT_Y), FONT_HERSHEY_SIMPLEX, 0.8, Scalar(0, 255, 0), 2);
imshow("show", show_img);

结果:

[机器学习]基于OpenCV实现最简单的数字识别相关推荐

  1. 基于OpenCV的 SVM算法实现数字识别(四)---代码实现

    三.基于SVM算法实现手写数字识别 作为一个工科生,而非数学专业的学生,我们研究一个算法,是要将它用于实际领域的.下面给出基于OpenCV3.0的SVM算法手写数字识别程序源码(参考http://bl ...

  2. 基于opencv实现的手写数字识别

    一.使用模板匹配算法 match.py: import os import Function root_dir = "digits/train2" file7_7 = open(& ...

  3. python opencv数字识别_基于模板匹配的手写数字识别(python+opencv)

    智能计算课第一周的实验是做基于模板匹配的手写数字识别,光听见就很感兴趣,于是决定认真做做这个实验,本实验基于python3+opencv的python版本,所用到的知识都比较简单,基本上边学边做,技术 ...

  4. python怎么建立画板_Python基于opencv实现的简单画板功能示例

    本文实例讲述了Python基于opencv实现的简单画板功能.分享给大家供大家参考,具体如下: import cv2 import numpy as np drawing = False # true ...

  5. OpenCV基于LeNet-5和连接组件分析的数字识别的实例(附完整代码)

    OpenCV基于LeNet-5和连接组件分析的数字识别的实例 OpenCV基于LeNet-5和连接组件分析的数字识别的实例 OpenCV基于LeNet-5和连接组件分析的数字识别的实例 #includ ...

  6. 基于深度学习的手写数字识别算法Python实现

    摘 要 深度学习是传统机器学习下的一个分支,得益于近些年来计算机硬件计算能力质的飞跃,使得深度学习成为了当下热门之一.手写数字识别更是深度学习入门的经典案例,学习和理解其背后的原理对于深度学习的理解有 ...

  7. OpenCV实现0到9数字识别OCR

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 使用OpenCV对0到9数字进行识别,实现简单OCR功能,基于CA ...

  8. 基于深度学习的手写数字识别、python实现

    基于深度学习的手写数字识别.python实现 一.what is 深度学习 二.加深层可以减少网络的参数数量 三.深度学习的手写数字识别 一.what is 深度学习 深度学习是加深了层的深度神经网络 ...

  9. 基于深度学习的手写数字识别Matlab实现

    基于深度学习的手写数字识别Matlab实现 1.网络设计 2. 训练方法 3.实验结果 4.实验结果分析 5.结论 1.网络设计 1.1 CNN(特征提取网络+分类网络) 随着深度学习的迅猛发展,其应 ...

最新文章

  1. notepad++及Scintilla
  2. 扩增子和宏基因组数据分析流程和可视化方案—刘永鑫(南京,2020年11月27日)
  3. 在spring cloud eureka中使用ip而不是hostname
  4. mysql正确打开方式_MySQL中MVCC的正确打开方式
  5. 在Map 3D显示管理器中更改当前地图的名字
  6. python随机数据随概率分布_概率分布及其Python实现
  7. Zabbix Server安装部署
  8. 电脑公司最新稳定win7系统下载
  9. 48种世间哲学,其中值得借鉴的有10个
  10. 男人,得有足够征服女人的本钱
  11. SQLyog安装地址
  12. 企业微信第三方服务商应用开发及上架教程
  13. mysql自增不连续的问题 ALTER TABLE `表名` AUTO_INCREMENT =1;
  14. mysql bit类型_MySQL bit类型
  15. SRAM FPGA控制,实际产品拷机测试过
  16. 新闻推荐系统:基于内容的推荐算法(Recommender System:Content-based Recommendation)
  17. 查询快递物流筛选出被拦截单号标色记号
  18. sdnu1283.山师好男友(找规律)
  19. Git LFS笔记(文档、博客、issue摘录)
  20. access数据库剔除重复项_access数据库查询时如何让查出来的数据自动去除重复项...

热门文章

  1. 在keil中使用bdata型可位寻址全局变量
  2. 分享Photoshop脚本代码_压缩,图片置灰,切图导出等
  3. 网线直接插电脑能上网 接路由器就连不上了
  4. Tutorial 05: Synching Video
  5. BJOI2019 勘破神机 新解
  6. Swift 学习总结三:数组,字典,枚举,闭包, 函数
  7. 解析explain执行计划
  8. 【Arduino学习】01.开发板选择和环境的搭建
  9. 第12届蓝桥杯国赛真题剖析-2021年5月29日Scratch编程初中级组
  10. 取色器插件 react-color ChromePicker