opencv 数字识别详细教程
来源:互联网 发布:软件开发技术联盟 编辑:程序博客网 时间:2024/05/22 04:08
最近要做数字识别这块,但是自己又完全不懂这个,网上搜资料搜了好多,但是都没找到完整代码。只有自己慢慢搞,下面写下自己的过程以及代码有不好的地方希望大神可以指出,大家相互交流下。有需要完整代码的可以留下邮箱(尽量不要发私信,因为一般不登录账号,所以无法看到及时回复,)
我是在VS2013 和opencv 2.4.9 环境下实现的。关于环境的搭建和配置以及软件的下载可以可以参考,http://blog.csdn.net/ltg01/article/details/50433386
我要做的是把0123456789 印刷体数字识别出来。
一、首先对图片进行预处理
对图片进行灰度化二值化
Mat src = imread("D:\\b.png", CV_LOAD_IMAGE_GRAYSCALE);//读取图片并进行灰度化处理 threshold(src, src, 100 , 255, CV_THRESH_BINARY);//二值化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的宽度,高度仍为原图的高度,这样通过函数
int width = right - left;Rect rect(left, 0, width, src.rows);leftImg = src(rect);就可以把三截取出来了,但是3的上下部分没有截取,同样对图片进行行扫描,截取上下部分,如下图就这样循环截取图片就可以吧其他数字截取下来了,但是每次截取的原图是不一样的,第二次截取的时候原图上就没有3 是从6开始的如图
int cutLeft(Mat& src, Mat& leftImg, Mat& rightImg)//左右切割{int left, right;left = 0;right = src.cols;int i;for (i = 0; i < src.cols; i++){int colValue = getColSum(src, i);//cout <<i<<" th "<< colValue << endl;if (colValue>0){left = i;break;}}if (left == 0){return 1;}for (; i < src.cols; i++){int colValue = getColSum(src, i);//cout << i << " th " << colValue << endl;if (colValue == 0){right = i;break;}}int width = right - left;Rect rect(left, 0, width, src.rows);leftImg = src(rect).clone();Rect rectRight(right, 0, src.cols - right, src.rows);rightImg = src(rectRight).clone();<strong>cutTop(leftImg, leftImg);</strong>return 0;}
void cutTop(Mat& src, Mat& dstImg)//上下切割{int top, bottom;top = 0;bottom = src.rows;int i;for (i = 0; i < src.rows; i++){int colValue = getRowSum(src, i);//cout <<i<<" th "<< colValue << endl;if (colValue>0){top = i;break;}}for (; i < src.rows; i++){int colValue = getRowSum(src, i);//cout << i << " th " << colValue << endl;if (colValue == 0){bottom = i;break;}}int height = bottom - top;Rect rect(0, top, src.cols, height);dstImg = src(rect).clone();}
<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px; background-color: rgb(254, 254, 254);"></span><pre name="code" class="html">int main(){Mat src = imread("D:\\s.png", CV_LOAD_IMAGE_GRAYSCALE);threshold(src, src, 100 , 255, CV_THRESH_BINARY_INV);imshow("origin", src);Mat leftImg,rightImg;int res = cutLeft(src, leftImg, rightImg);int i = 0; while (res == 0){ char nameLeft[10];sprintf(nameLeft, "%dLeft", i);char nameRight[10];sprintf(nameRight, "%dRight", i);i++;
//<span style="white-space:pre"></span>stringstream ss;<span style="white-space:pre"></span>//<span style="white-space:pre"></span>ss << nameLeft;<span style="white-space:pre"></span>//<span style="white-space:pre"></span>imwrite("D:\\" + ss.str() + ".jpg", leftImg);<span style="white-space:pre"></span>//<span style="white-space:pre"></span>ss >> nameLeft;<span style="white-space:pre"></span>Mat srcTmp = rightImg;<span style="white-space:pre"></span>//<span style="white-space:pre"></span>getSubtract(leftImg, 10);res = cutLeft(srcTmp, leftImg, rightImg);}waitKey(0);return 0;}
<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px; background-color: rgb(254, 254, 254);">最后截取结果如下图</span>
(截取的很清楚只是拖动的时候留下的划痕)
三、模板的制作
模板的制作和步骤二完全一样,首先你要切割的图片的字体样式和大小要和模板的样式和大小一样(比如都是宋体,10号)要不然匹配的结果就不准确,而且把0123456789最好按顺序这样匹配的时候可以知道是匹配到那个数字, 比如你切割下的数字和模板匹配的时候,匹配到第三个模板则知道是匹配的数字3(模板从第0个开始)。只需要改主函数
int main(){Mat src = imread("D:\\s.png", CV_LOAD_IMAGE_GRAYSCALE);threshold(src, src, 100 , 255, CV_THRESH_BINARY_INV);imshow("origin", src);Mat leftImg,rightImg;int res = cutLeft(src, leftImg, rightImg);int i = 0; while (res == 0){ char nameLeft[10];sprintf(nameLeft, "%dLeft", i);char nameRight[10];sprintf(nameRight, "%dRight", i);i++;imshow(nameLeft, leftImg);<strong>stringstream ss;ss << nameLeft;imwrite("D:\\" + ss.str() + ".jpg", leftImg);//保存截取图片做为模板ss >> nameLeft;</strong>Mat srcTmp = rightImg;//getSubtract(leftImg, 10);res = cutLeft(srcTmp, leftImg, rightImg);}waitKey(0);return 0;}
三、数字的识别
把你切割的数字图片大小调整到和模板一样的大小,然后让需要匹配的图和分别和10个模板相减,(让两个图片对应像素点值相减)然后求返回图片的整个图片的像素点值得平方和,和哪个模板匹配时候返回图片的平方和最小则就可以得到结果。只需要改主函数
void getPXSum(Mat &src, int &a)//获取所有像素点和{ threshold(src, src, 100, 255, CV_THRESH_BINARY); a = 0;for (int i = 0; i < src.rows;i++){for (int j = 0; j < src.cols; j++){a += src.at <uchar>(i, j);}}}int getSubtract(Mat &src, int TemplateNum) //两张图片相减{Mat img_result;int min = 1000000;int serieNum = 0;for (int i = 0; i < TemplateNum; i++){char name[20];sprintf_s(name, "D:\\%dLeft.jpg", i);Mat Template = imread(name, CV_LOAD_IMAGE_GRAYSCALE);threshold(Template, Template, 100, 255, CV_THRESH_BINARY);threshold(src, src, 100, 255, CV_THRESH_BINARY);<strong>resize(src, src, Size(32, 48), 0, 0, CV_INTER_LINEAR); resize(Template, Template, Size(32, 48), 0, 0, CV_INTER_LINEAR);//调整尺寸</strong>//imshow(name, Template);<strong>absdiff(Template, src, img_result);//<span style="color: rgb(51, 51, 51); font-family: Arial;font-size:18px; line-height: 26px; text-indent: 16px; background-color: rgb(254, 254, 254);">两个图片对应像素点值相减</span></strong>int diff = 0;<strong>getPXSum(img_result, diff)</strong>;if (diff < min){min = diff;serieNum = i;}}printf("最小距离是%d ", min);printf("匹配到第%d个模板匹配的数字是%d\n", serieNum,serieNum);return serieNum;}
int main(){Mat src = imread("D:\\s.png", CV_LOAD_IMAGE_GRAYSCALE);threshold(src, src, 100 , 255, CV_THRESH_BINARY_INV);imshow("origin", src);Mat leftImg,rightImg;int res = cutLeft(src, leftImg, rightImg);int i = 0; while (res == 0){ //char nameLeft[10];//sprintf(nameLeft, "%dLeft", i);//char nameRight[10];//sprintf(nameRight, "%dRight", i);//i++;imshow(nameLeft, leftImg);//stringstream ss;//ss << nameLeft;//imwrite("D:\\" + ss.str() + ".jpg", leftImg);//ss >> nameLeft;Mat srcTmp = rightImg;<strong>getSubtract(leftImg, 10);//数字识别</strong>res = cutLeft(srcTmp, leftImg, rightImg);}waitKey(0);return 0;}
运行最终结果如下图
- opencv 数字识别详细教程
- 【opencv】神经网络识别数字
- Opencv识别单行数字
- opencv 识别印刷体数字
- opencv数字识别
- opencv数字识别
- 【opencv】神经网络识别数字
- 使用opencv进行数字识别
- 手写数字识别【QT+OpenCV】
- OpenCV 数字验证码识别
- 【opencv】神经网络识别字母+数字
- openCV IOS 工程 识别数字
- OpenCV-简单数字识别OCR
- 【opencv】神经网络识别字母+数字
- 基于opencv数码管数字识别
- opencv+KNN实现手写简单数字识别
- opencv 学习之 液晶数字识别
- 基于OpenCV的数码管数字识别
- Objective-c开发--MRC和ARC混编--(iOS开发)
- ubuntu12.0.4安装samba服务器
- linux网络编程之socket(五):tcp流协议产生的粘包问题和解决方案
- VdsClient的使用
- 简单实现 scrollview无限轮播的封装
- opencv 数字识别详细教程
- Java内存泄漏
- 从头认识java-17.2 线程中断(interrupt)
- FZUOJ Problem 1607 Greedy division (求因子个数)
- VS2013中配置OpenCV2.4.8
- 最优决策和最满意决策问题
- 【慕课笔记】第三章 常用的运算符 第1节 什么是运算符
- How to append list to second list
- JVM垃圾回收机制