因为opencv有2.0 和 3.0 的版本区别,所以网上搜到的函数或类型都是两种格式,建议用新版的,什么impImage* 类型的都是2.0版本的写法,我全部使用的是Mat。一定要统一好,不要一会新的一会旧的,会报错的。
读图片imread,显示imshow,等待waitKey等等,这些要先熟悉
opencv的强大之处在于几乎所有的图像 *** 作它都有现成的函数可供调用,非常方便。多谷歌,一定会有函数已经实现了你想完成的功能。
二值化:不论是原图还是有划痕或噪点的图,背景都不干净,这对识别的影响还是挺不好的,所以要先二值化,把黑白像素点区分的开一些。但是图片右侧明显要比左侧更暗,所以在阈值选取的时候比较难办,很难用一个固定的值将两部分图像都二值化得很理想,所以就用到了逼格更高的自适应二值化(adapTIveThreshold),TIps:二值化前先直方图均衡一下效果会更好。
中值滤波:针对有噪点和有划痕的图像,中值滤波是非常好的处理方案,中值的参数可调,可以很好的消除噪音的影响。缺点就是参数不好调啊,调的想死。。
模板匹配:模板的来源可以是自己从待识别的图片中抠图,不过我们作业提供了模板图片,所以这一步就可以省掉了。opencv提供了非常强大的matchTemplate函数,可以将给定图片与模板按照你规定的计算方法计算一个相似度的值,并将对应的坐标存储下来,你需要做的只是将值比较大(或小,与你规定计算相似度的函数有关)的图像框出来即可
窗口扫描:为了提高识别率,我设定了一个窗口对原图进行扫描,扫描窗口的移动设定了一点规则,就是如果前一个窗口没有匹配到数字就微调窗口位置,如果匹配到数字就将窗口左轴移动到匹配到的数字的右侧,再重复扫描。
以下是基于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);
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)