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


我是在VS2013 和opencv 2.4.9 环境下实现的。关于环境的搭建和配置以及软件的下载可以可以参考,

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

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

对图片进行灰度化二值化



1.        Mat src = imread("D:\\b.png", CV_LOAD_IMAGE_GRAYSCALE);//读取图片并进行灰度化处理  
2. //二值化  
3. imshow("origin", src);//显示图片

原图

opencv 数字 识别 java opencv数字识别详细教程_i++

经过灰度二值化的图

opencv 数字 识别 java opencv数字识别详细教程_像素点_02

1. 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.

对图片

opencv 数字 识别 java opencv数字识别详细教程_像素点_03


先进行列扫描求每列的和。刚开始是都是黑色所以每列的和都是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截取出来了,如图

opencv 数字 识别 java opencv数字识别详细教程_灰度_04

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

opencv 数字 识别 java opencv数字识别详细教程_opencv 数字 识别 java_05

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

opencv 数字 识别 java opencv数字识别详细教程_i++_06




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. "D:\\s.png", CV_LOAD_IMAGE_GRAYSCALE);  
78.     threshold(src, src, 100 , 255, CV_THRESH_BINARY_INV);  
79. "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. "%dLeft", i);  
88. char nameRight[10];  
89. "%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. }


最后截取结果如下图


opencv 数字 识别 java opencv数字识别详细教程_灰度_07

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



三、制作模板


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







1. int main()  
2. {  
3. "D:\\s.png", CV_LOAD_IMAGE_GRAYSCALE);  
4.     threshold(src, src, 100 , 255, CV_THRESH_BINARY_INV);  
5. "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. "%dLeft", i);  
14. char nameRight[10];  
15. "%dRight", i);  
16.         i++;  
17.         imshow(nameLeft, leftImg);  
18.         <strong>stringstream ss;  
19.         ss << nameLeft;  
20. "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 +=  <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. "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. //调整尺寸        
27. //imshow(name, Template);  
28. //  
29.         getPXSum(img_result, diff);  
30. if (diff < min)  
31.         {  
32.             min = diff;  
33.             serieNum = i;  
34.         }  
35.     }  
36. "最小距离是%d ", min);  
37. "匹配到第%d个模板匹配的数字是%d\n", serieNum,serieNum);  
38. return serieNum;  
39. }  
40.   
41. int main()  
42. {  
43. "D:\\s.png", CV_LOAD_IMAGE_GRAYSCALE);  
44.     threshold(src, src, 100 , 255, CV_THRESH_BINARY_INV);  
45. "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. //数字识别  
64.         res = cutLeft(srcTmp, leftImg, rightImg);     
65.     }  
66.       
67.     waitKey(0);  
68. return 0;  
69. }




运行最终结果如下图

opencv 数字 识别 java opencv数字识别详细教程_opencv 数字 识别 java_08


备注: 里面有几个函数原作者没有提供,但是只要理解了作者的思路,很容易实现,便不上传我的代码。