推广 热搜:
 |  行业设备  |  机械制造  |  机器视觉  |  机械自动化  |  仪器仪表  |  工业机器人  |  编程开发  |  嵌入式  |  电子技术  |  工控自动化  |  电工电气 频道

opencv 数字识别详细教程

   日期:2018-08-22     浏览:75    评论:0    
核心提示:最近要做数字识别这块,但是自己又完全不懂这个,网上搜资料搜了好多,但是都没找到完整代码。只有自己慢慢搞,下面写下自己的过
 最近要做数字识别这块,但是自己又完全不懂这个,网上搜资料搜了好多,但是都没找到完整代码。只有自己慢慢搞,下面写下自己的过程以及代码有不好的地方希望大神可以指出,大家相互交流下。有需要完整代码的可以自行下载源码  (源码里面 是需要自己做一个图片的,没有图片,不能直接运行)


我是在VS2013 和opencv 2.4.9 环境下实现的。关于环境的搭建和配置以及软件的下载可以可以参考,http://blog.csdn.net/ltg01/article/details/50433386

我要做的是把0123456789 印刷体数字识别出来。

一、首先对图片进行预处理

对图片进行灰度化二值化

 

  1.  
    Mat src = imread("D:\\b.png", CV_LOAD_IMAGE_GRAYSCALE);//读取图片并进行灰度化处理
  2.  
    threshold(src, src, 100 , 255, CV_THRESH_BINARY);//二值化
  3.  
    imshow("origin", src);//显示图片
原图经过灰度二值化的图
Mat imread(const string& filename, int flags);
filename:文件地址 flags:标志,读取什么样(灰度,彩色)图像hdrtype:传入的为载入什么类型(enum {LOAD_CVMAT=0,LOAD_IMAGE=1, LOAD_MAT=2 };这三个中的一个。) Mat :保存图像的Mat对象了。
double threshold(InputArray src, OutputArray dst, double thresh, double maxval, int type) 
参数信息:
第一个参数,InputArray类型的src,输入数组,填单通道 , 8或32位浮点类型的Mat即可。
第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放输出结果,且和第一个参数中的Mat变量有一样的尺寸和类型。
第三个参数,double类型的thresh,阈值的具体值。
第四个参数,double类型的maxval,当第五个参数阈值类型type取 THRESH_BINARY 或THRESH_BINARY_INV阈值类型时的最大值.
第五个参数,int类型的type,阈值类型,。
其它参数很好理解,我们来看看第五个参数,第五参数有以下几种类型
0: THRESH_BINARY  当前点值大于阈值时,取Maxval,也就是第四个参数,下面再不说明,否则设置为0
1: THRESH_BINARY_INV 当前点值大于阈值时,设置为0,否则设置为Maxval
2: THRESH_TRUNC 当前点值大于阈值时,设置为阈值,否则不改变
3: THRESH_TOZERO 当前点值大于阈值时,不改变,否则设置为0
4: THRESH_TOZERO_INV  当前点值大于阈值时,设置为0,否则不改变

 


二、对图片上的数字进行切割

   图片经过二值化后每个像素点的值只有1和0两种,在这里黑色部分的像素点的值为0白色字体部分的值为1.
对图片

 

先进行列扫描求每列的和。刚开始是都是黑色所以每列的和都是0,知道扫描到3的左边缘的那列的时候因为有白色所以这列的和大于0,这时候记下当前位置left,然后接着扫描,接下来每列的和都大于0,直到3的右边缘时候这列和右等于0,记下当前位置right,则right减去left则是3的宽度,高度仍为原图的高度,这样通过函数

 

  1.  
    int width = right - left;
  2.  
    Rect rect(left, 0, width, src.rows);
  3.  
    leftImg = src(rect);

 

就可以把3截取出来了,如图

但是3的上下部分没有截取,同样对图片进行行扫描,截取上下部分,如下图

就这样循环截取图片就可以吧其他数字截取下来了,但是每次截取的原图是不一样的,第二次截取的时候原图上就没有3 是从6开始的如图


  1.  
    int cutLeft(Mat& src, Mat& leftImg, Mat& rightImg)//左右切割
  2.  
    {
  3.  
    int left, right;
  4.  
    left = 0;
  5.  
    right = src.cols;
  6.  
     
  7.  
    int i;
  8.  
    for (i = 0; i < src.cols; i++)
  9.  
    {
  10.  
    int colValue = getColSum(src, i);
  11.  
    //cout <<i<<" th "<< colValue << endl;
  12.  
    if (colValue>0)
  13.  
    {
  14.  
    left = i;
  15.  
    break;
  16.  
    }
  17.  
    }
  18.  
    if (left == 0)
  19.  
    {
  20.  
    return 1;
  21.  
    }
  22.  
     
  23.  
     
  24.  
    for (; i < src.cols; i++)
  25.  
    {
  26.  
    int colValue = getColSum(src, i);
  27.  
    //cout << i << " th " << colValue << endl;
  28.  
    if (colValue == 0)
  29.  
    {
  30.  
    right = i;
  31.  
    break;
  32.  
    }
  33.  
    }
  34.  
    int width = right - left;
  35.  
    Rect rect(left, 0, width, src.rows);
  36.  
    leftImg = src(rect).clone();
  37.  
    Rect rectRight(right, 0, src.cols - right, src.rows);
  38.  
    rightImg = src(rectRight).clone();
  39.  
    cutTop(leftImg, leftImg);
  40.  
    return 0;
  41.  
    }
  42.  
     
  43.  
    void cutTop(Mat& src, Mat& dstImg)//上下切割
  44.  
    {
  45.  
    int top, bottom;
  46.  
    top = 0;
  47.  
    bottom = src.rows;
  48.  
     
  49.  
    int i;
  50.  
    for (i = 0; i < src.rows; i++)
  51.  
    {
  52.  
    int colValue = getRowSum(src, i);
  53.  
    //cout <<i<<" th "<< colValue << endl;
  54.  
    if (colValue>0)
  55.  
    {
  56.  
    top = i;
  57.  
    break;
  58.  
    }
  59.  
    }
  60.  
    for (; i < src.rows; i++)
  61.  
    {
  62.  
    int colValue = getRowSum(src, i);
  63.  
    //cout << i << " th " << colValue << endl;
  64.  
    if (colValue == 0)
  65.  
    {
  66.  
    bottom = i;
  67.  
    break;
  68.  
    }
  69.  
    }
  70.  
     
  71.  
    int height = bottom - top;
  72.  
    Rect rect(0, top, src.cols, height);
  73.  
    dstImg = src(rect).clone();
  74.  
    }
  75.  
    int main()
  76.  
    {
  77.  
    Mat src = imread("D:\\s.png", CV_LOAD_IMAGE_GRAYSCALE);
  78.  
    threshold(src, src, 100 , 255, CV_THRESH_BINARY_INV);
  79.  
    imshow("origin", src);
  80.  
     
  81.  
    Mat leftImg,rightImg;
  82.  
    int res = cutLeft(src, leftImg, rightImg);
  83.  
    int i = 0;
  84.  
    while (res == 0)
  85.  
    {
  86.  
    char nameLeft[10];
  87.  
    sprintf(nameLeft, "%dLeft", i);
  88.  
    char nameRight[10];
  89.  
    sprintf(nameRight, "%dRight", i);
  90.  
    i++;
  91.  
    //stringstream ss;
  92.  
    //ss << nameLeft;
  93.  
    //imwrite("D:\\" + ss.str() + ".jpg", leftImg);
  94.  
    //ss >> nameLeft;
  95.  
    Mat srcTmp = rightImg;
  96.  
    //getSubtract(leftImg, 10);
  97.  
    res = cutLeft(srcTmp, leftImg, rightImg);
  98.  
    }
  99.  
     
  100.  
    waitKey(0);
  101.  
    return 0;
  102.  
    }

最后截取结果如下图

(截取的很清楚只是拖动的时候留下的划痕)


三、制作模板


模板的制作和步骤二完全一样,首先你要切割的图片的字体样式和大小要和模板的样式和大小一样(比如都是宋体,10号)要不然匹配的结果就不准确,而且把0123456789最好按顺序这样匹配的时候可以知道是匹配到那个数字,比如你切割下的数字和模板匹配的时候,匹配到第三个模板则知道是匹配的数字3(模板从第0个开始)。只需要改主函数


 

  1.  
    int main()
  2.  
    {
  3.  
    Mat src = imread("D:\\s.png", CV_LOAD_IMAGE_GRAYSCALE);
  4.  
    threshold(src, src, 100 , 255, CV_THRESH_BINARY_INV);
  5.  
    imshow("origin", src);
  6.  
     
  7.  
    Mat leftImg,rightImg;
  8.  
    int res = cutLeft(src, leftImg, rightImg);
  9.  
    int i = 0;
  10.  
    while (res == 0)
  11.  
    {
  12.  
    char nameLeft[10];
  13.  
    sprintf(nameLeft, "%dLeft", i);
  14.  
    char nameRight[10];
  15.  
    sprintf(nameRight, "%dRight", i);
  16.  
    i++;
  17.  
    imshow(nameLeft, leftImg);
  18.  
    <strong>stringstream ss;
  19.  
    ss << nameLeft;
  20.  
    imwrite("D:\\" + ss.str() + ".jpg", leftImg);//保存截取图片做为模板
  21.  
    ss >> nameLeft;</strong>
  22.  
    Mat srcTmp = rightImg;
  23.  
    //getSubtract(leftImg, 10);
  24.  
    res = cutLeft(srcTmp, leftImg, rightImg);
  25.  
    }
  26.  
     
  27.  
    waitKey(0);
  28.  
    return 0;
  29.  
    }

四、数字识别

 

 

 

 

 

把你切割的数字图片大小调整到和模板一样的大小,然后让需要匹配的图和分别和10个模板相减,(让两个图片对应像素点值相减)然后求返回图片的整个图片的像素点值得平方和,和哪个模板匹配时候返回图片的平方和最小则就可以得到结果。只需要改主函数

 

 

  1.  
    void getPXSum(Mat &src, int &a)//获取所有像素点和
  2.  
    {
  3.  
    threshold(src, src, 100, 255, CV_THRESH_BINARY);
  4.  
    a = 0;
  5.  
    for (int i = 0; i < src.rows;i++)
  6.  
    {
  7.  
    for (int j = 0; j < src.cols; j++)
  8.  
    {
  9.  
    a += src.at <uchar>(i, j);
  10.  
    }
  11.  
    }
  12.  
    }
  13.  
     
  14.  
    int getSubtract(Mat &src, int TemplateNum) //两张图片相减
  15.  
    {
  16.  
    Mat img_result;
  17.  
    int min = 1000000;
  18.  
    int serieNum = 0;
  19.  
    for (int i = 0; i < TemplateNum; i++){
  20.  
    char name[20];
  21.  
    sprintf_s(name, "D:\\%dLeft.jpg", i);
  22.  
    Mat Template = imread(name, CV_LOAD_IMAGE_GRAYSCALE);
  23.  
    threshold(Template, Template, 100, 255, CV_THRESH_BINARY);
  24.  
    threshold(src, src, 100, 255, CV_THRESH_BINARY);
  25.  
    resize(src, src, Size(32, 48), 0, 0, CV_INTER_LINEAR);
  26.  
    resize(Template, Template, Size(32, 48), 0, 0, CV_INTER_LINEAR);//调整尺寸
  27.  
    //imshow(name, Template);
  28.  
    absdiff(Template, src, img_result);//
  29.  
    getPXSum(img_result, diff);
  30.  
    if (diff < min)
  31.  
    {
  32.  
    min = diff;
  33.  
    serieNum = i;
  34.  
    }
  35.  
    }
  36.  
    printf("最小距离是%d ", min);
  37.  
    printf("匹配到第%d个模板匹配的数字是%d\n", serieNum,serieNum);
  38.  
    return serieNum;
  39.  
    }
  40.  
     
  41.  
    int main()
  42.  
    {
  43.  
    Mat src = imread("D:\\s.png", CV_LOAD_IMAGE_GRAYSCALE);
  44.  
    threshold(src, src, 100 , 255, CV_THRESH_BINARY_INV);
  45.  
    imshow("origin", src);
  46.  
     
  47.  
    Mat leftImg,rightImg;
  48.  
    int res = cutLeft(src, leftImg, rightImg);
  49.  
    int i = 0;
  50.  
    while (res == 0)
  51.  
    {
  52.  
    // char nameLeft[10];
  53.  
    // sprintf(nameLeft, "%dLeft", i);
  54.  
    // char nameRight[10];
  55.  
    // sprintf(nameRight, "%dRight", i);
  56.  
    // i++;
  57.  
    imshow(nameLeft, leftImg);
  58.  
    // stringstream ss;
  59.  
    // ss << nameLeft;
  60.  
    // imwrite("D:\\" + ss.str() + ".jpg", leftImg);
  61.  
    // ss >> nameLeft;
  62.  
    Mat srcTmp = rightImg;
  63.  
    getSubtract(leftImg, 10);//数字识别
  64.  
    res = cutLeft(srcTmp, leftImg, rightImg);
  65.  
    }
  66.  
     
  67.  
    waitKey(0);
  68.  
    return 0;
  69.  
    }


 

运行最终结果如下图


 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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

本文将基于OpenCV实现简单的数字识别。这里以游戏Angry Birds为例,通过以下几个主要步骤对其中右上角的分数部分进行自动识别。
 
打赏
 
更多>同类编程
0相关评论

推荐图文
推荐编程
点击排行

网站首页  |  关于我们  |  联系方式  |  使用协议  |  版权隐私  |  网站地图  |  排名推广  |  广告服务  |  积分换礼  |  网站留言  |  RSS订阅  |  违规举报