在进行完预处理之后,我们可以开始用算法识别数字了。首先我们不做那么复杂的、一连串银行卡数字的识别,而是做一个数字的测试。
一、机器算法的引入-----K近邻算法
K最近邻(k-Nearest Neighbour,KNN)分类算法是一个理论上比较成熟的算法,也是最简单的机器学习算法之一。顾名思义,k最近邻实际上就是取和待分类物最相似的k个模板,然后这k个模板中所占比例最高的类别就是最后决定的类别。
思路就是这么简单,那么这个最近的距离如何计算呢?往往用欧氏距离等经典距离来衡量模板和待分类物的“距离”。
KNN很适合在有大量样本的情况下进行使用,当然,这也带来了速度较慢的问题。
于是KNN就非常简单了。
二、准备工作----模板准备
我们只做单个数字的检测,来检验我们所写的算法的正确性,所以类似单片机中串口自发自收的测试方法,我们可以用模板来识别模板,从而检测算法的正确性。
我们在画图板中写入0-9十个数字(大小并没有特殊要求,后面程序会进一步处理),并一一截图保存变成我们的样本,并放在工程目录下:
模板准备完毕。
三、整体思路
1>读取模板:我们这里有10个类别,即0-9十个数字,每个类别有一个样本,一共十个模板,我们需要读入程序中,用IplImage或者cvMat进行存储;
2>模板调整(示模板情况而定需不需要该步骤):也可以说是提取特征区域吧,因为模板是手动截图的,每个模板打大小不是严格一样的,但是作为模板匹配来说,我们需要的是统一的尺寸标准,所以我们需要将导入的模板图片调整到统一的、合适的尺寸。
在http://blog.csdn.net/wangyaninglm/article/details/17091901的博客上我看到了非常合适的解释图,在这里也分享给大家:
这里提取出来的ROI是模板的有效部分,但是大小还是不一定符合统一尺寸的要求,所以,我们要进行适当的放缩
3>训练数据:这是我认为的算法最关键的部分。这里有几个参数:
Classes:类型数量
Train_simples:每个类型的样本数量
trainData:模板的训练集,
trainClasses:训练集所对应的类别标号
接下来我想解释一下trainData,trainClasses是个啥,但是感觉说太多也并不一定能够说得清楚,所以,请见下图所示:
trainData:
trainClasses:
看了上图之后,相信大家对k最近邻算法会有自己的想法了。可以看出,得到训练集的关键就是得到样本的特征向量。有很多特征向量的提取方法,详细的会在后面阐述,这里只介绍最简单的一种方法:
当模板不大的时候,我们可以将模板(可以看做一个n*m的矩阵)展开成一个n*m的一维向量,这就是这个模板的特征。事实上,有了第二步的预处理(ROI的提取),模板几乎就只有数字那么大了,所以模板是很小的,要想得到全部的信息,该方法再好也再简单不过了。
4>得到了trainData、trainClasses后,就可以利用openCV的函数进行k最近邻的算法求解了
所有步骤结束,是不是很简单呢?
接下来看看实际的实现过程和结果------关键函数代码:
void getData()
{//得到训练数据
IplImage* src_image;
IplImage* prs_image = cvCreateImage(cvSize(new_width, new_height), 8, 1); ;
CvMat row,data;
CvMat rowb,datab;
int x,y;
int c = 0;
int i,j,k;
int m,n;
m=0;
k=0;
CvMat* srcImg = cvCreateMat(32,32,CV_32FC1);
ifstream file;
char filename[50];
char a[32][32];
string s;
for(m =0; m<classes;m++)//总共10个数字
{
c = 0;
for(n = 0; n<train_samples;n++)//每个数字50个样本
{
c++;
// 这里是读入图片样本
sprintf(file, "%d%d.bmp", i, j);
src_image = cvLoadImage(file);
if(!src_image)
{
printf("Error: Cant load image %s\n",file);
}
IplImage* gray_image = cvCreateImage(cvGetSize(src_image), 8, 1);
IplImage* binary_image = cvCreateImage(cvGetSize(src_image), 8, 1);
cvCvtColor(src_image, gray_image, CV_BGR2GRAY);
cvThreshold(gray_image, binary_image, 100, 255, CV_THRESH_BINARY);
//process file
//将模板按照尺寸大小将有效部分放缩
prs_image = preprocessing(prs_image);
//生成训练矩阵,每个图像作为一个向量
cvGetRow(trainClasses, &row,m*train_samples +n);//y
cvSet(&row,cvRealScalar(m));
//Set data
cvGetRow(trainData, &rowb,m*train_samples +n);//x
IplImage *img = cvCreateImage(cvGetSize(prs_image),IPL_DEPTH_32F, 1 );
//转换换 8 bits image to 32位浮点数图片取值区间为[0,1]
//将模板图像转换成一个一维向量作为特征向量
//scale = 0.0039215 = 1/255;
cvConvertScale(prs_image,img, 0.0039215, 0);
cvGetSubRect(img, &data,cvRect(0,0,new_width,new_height));
CvMat row_header, *row1;
//convert data matrix sizexsize to vecor
row1 =cvReshape( &data, &row_header, 0, 1 );
cvCopy(row1, &rowb,NULL);
}
}
}
IplImage *preprocessing(IplImage*imgSrc)
{//放缩模板图像的有效部分
IplImage* result;
IplImage* scaledResult;
CvMat data;
CvMat dataA;
CvRect bb;//bounding box
CvRect bba;//boundinb box maintain aspect ratio
//Find bounding box找到边界框
bb=findBB(imgSrc);
cvGetSubRect(imgSrc, &data,cvRect(bb.x,bb.y,bb.width,bb.height));
int size=(bb.width>bb.height)?bb.width:bb.height;
result=cvCreateImage( cvSize( size, size ), 8, 1 );
cvSet(result,CV_RGB(255,255,255),NULL);
//将图像放中间,大小归一化
int x=(int)floor((float)(size-bb.width)/2.0f);
int y=(int)floor((float)(size-bb.height)/2.0f);
cvGetSubRect(result, &dataA,cvRect(x,y,bb.width,bb.height));
cvCopy(&data, &dataA, NULL);
//Scale result
scaledResult=cvCreateImage( cvSize( new_width, new_height ), 8, 1 );
cvResize(result, scaledResult, CV_INTER_NN);
//Return processed data
return scaledResult;//直接返回处理后的图片
}
CvRect findBB(IplImage *img)
{//找模板图像有效图像的边框,方便抠下来
CvSeq *contour;
CvMemStorage *storage = cvCreateMemStorage(0);
IplImage* temp = cvCreateImage(cvGetSize(img), 8, 1);
cvCopy(img, temp);
int a = cvFindContours(temp, storage, &contour, sizeof(CvContour),CV_RETR_LIST, CV_CHAIN_APPROX_NONE);
CvRect rect, tprect;
rect = cvBoundingRect( contour, 0 );
return rect;
}
有了上面代码的基础,我们就得到了trainData、trainClasses训练集,那么接下来我们要抠取待测图片的有效区域,并且生成相应的特征向量,当然这里生成特征向量的方法要和前面生成训练集时用的方法保持一致。
训练完之后我们就可以利用openCV函数对训练集和待测数据进行k最近邻计算具体函数见下:
int do_ocr(IplImage *img)
{
IplImage* pimage;
CvMat data;
pimage = preprocessing(img);
IplImage *image = cvCreateImage(cvGetSize(pimage),IPL_DEPTH_32F, 1 );
//转换换 8 bits image to 32位浮点数图片取值区间为[0,1]
//scale = 0.0039215 = 1/255;
cvConvertScale(pimage,image, 0.0039215, 0);
cvGetSubRect(image, &data,cvRect(0,0,new_width,new_height));
CvMat mathdr;
CvMat *vec;
vec = cvReshape(&data, &mathdr, 0, 1);
//SumMat(*vec);
CvMat* nearest = cvCreateMat( 1, K, CV_32FC1);
float ret = knn->find_nearest(vec, K, 0, 0, nearest, 0);
return (int)ret;
}
按照我上文中说的思路很容易得到我们想要的结果:
测试图片:
测试结果:
相关推荐
利用K-最近邻算法进行高识别率的信号特征五分类
数据回归-基于回归树与K-最近邻交互模型的存储设备性能预测.pdf
以图像打开、保存、几何变换、形态学处理、阈值分割、边缘检测和滤波等基本操作为基础,对手写数字图片进行处理,提取有效特征,采用K最近邻算法识别,获得了较高的准确率。训练与测试图片位于Data文件夹中。
然后计算最近邻样本的距离权重,并用该权重描述最近邻和其他近邻对预测结果的影响;最后使用新的目标函数为待测样本分类。通过在图片、Web页面文本数据等数据集上的实验表明,所提算法得到了更好的分类结果,并且...
使用K-最近邻算法对三类样本进行分类的matlab代码
K最近邻分类算法的App推荐方法.docx一种基于协同过滤推荐算法-K最近邻分类算法的App推荐方法.docx一种基于协同过滤推荐算法-K最近邻分类算法的App推荐方法.docx一种基于协同过滤推荐算法-K最近邻分类算法的App推荐...
k-最近邻搜索(KNNS) 在高维空间中应用非常广泛,但目前很多KNNS算法是基于欧氏距离对数据进行索引和搜索,不适合采用角相似性的应用。提出一种基于角相似性的k-最近邻搜索算法(BA-KNNS)。该算法先提出基于角相似性的...
基于SVD分解和最近邻算法的高维人脸识别代码,是MATLAB系统上实现的代码,可直接运行,包含人脸数据库
k最近邻算法是一种有效快速的分类算法,包括源代码和测试数据
对基于概率论的未知数据集反向K最近邻(PRKNN)搜索问题展开研究,以足够大的概率返回以查询对象为其最近邻的未知对象。基于一种新的考虑了距离相关性的修剪机制,提出一种PRNN高效查询算法。此外,还给出了如何将该...
基于k-最近邻图的小样本KNN分类算法.pdf
基于汉明距离最近邻算法在铁路扣件识别中的应用,马姗,黄雅平,最近邻分类算法每次决策都要计算未知样本与全部训练样本的距离并加以比较,在设计最近邻算法时,需要一个衡量样本之间距离的度量
最近邻分类器代码和数据,MATLAB程序,可对数据进行计算距离,识别距离最近的数据。
哈工大的模式识别的k均值算法,当K=1时为k最近邻算法,
spark-knn, 关于 Spark,k 最近邻算法 火花 knn wip 。Apache Spark 上的k 最近邻算法( k nn ) 。 这将使用混合溢出树方法实现高精度和搜索效率。 k nn的简单性和缺乏优化参数,使它的成为许多机器学习问题的有用...
基于K最近邻的支持向量机快速训练算法.pdf基于K最近邻的支持向量机快速训练算法.pdf基于K最近邻的支持向量机快速训练算法.pdf
不确定数据上的概率相互最近邻查询具有重要的实际应用,针对目前关于这方面的研究尚少,提出了不确定数据上的概率相互最近邻的top-k查询算法。首先对问题进行描述与定义,其次总结可行的裁剪规则,从而裁剪查询对象中...
手写数字识别是一种通过不同的机器学习模型自动识别和检测手写数字数据的技术或技术。... 我们说明了各种机器学习算法,例如支持向量机、卷积神经网络、量子计算、K-最近邻算法、识别技术中使用的深度学习。
模式识别分类方法中的最近邻准则方法。内有详细说明文档。
基于K最近邻分类的无线传感器网络定位算法