机器视觉-手写数字识别(vc++6.0 + opencv1.0)

机器视觉实验合集:
机器视觉-模板匹配实验(vc++6.0 + opencv1.0)
机器视觉-数米粒实验(vc++6.0 + opencv1.0)
机器视觉-手写数字识别(vc++6.0 + opencv1.0)

本实验基于学校课程要求,环境采用vc++6.0 + opencv1.0,数据集为0-9每个数字10张,共100张图片(白底黑字),记得读取图片、保存图片等替换成自己的路径,文中不再赘述。总之将全文代码从头到尾粘下来即可运行,但建议仔细看看自己弄明白,好好学习,天天向上!

实验步骤
1.创建工程文件(项目),创建源文件
2.为项目配置OpenCV环境
3.实现函数:统计子图中黑色像素的个数
4.实现函数:计算特征向量间的距离
5.train()函数,每一类数字的前7张图片作为训练集
6.test()函数,每一类数字的后3张图片作为测试集
7.main()函数,调用执行即可

具体操作步骤如下
1.创建工程文件(项目),创建源文件
在这里插入图片描述在这里插入图片描述2.为该项目配置OpenCV环境,参考 机器视觉-模板匹配实验(vc++6.0 + opencv1.0)

重点检查第6、8、9步是否配置到位,在此默认读者已独立配置完成,主要向读者介绍实验后续操作!!!配置完成后,需要引入OpenCV的各种头文件,以及初始化一些后需要用到的全局变量,在上述所创建的main.cpp中添加如下代码:

#include "cv.h"
#include "highgui.h"
#include"vector"
#include "stdio.h"
using namespace std;
int block=4;	//分割比
char str[100];
vector< vector<int> >feaVVector;	//二维向量,存放所有训练图片的特征向量
vector< vector<int> >test_feaVVector;	//二维向量,存放所有测试图片的特征向量

3.实现函数:遍历每一个像素点,通过rgb值进行判断像素点是否为黑色,统计子图中黑色像素的个数

int ImageBlackCount(IplImage *inputImg)     //统计子图中黑色像素的个数
{
	int nCount = 0;		//用于计数
	char *data = inputImg->imageData;
	int  wp = inputImg->widthStep;
	for(int i = 0; i < inputImg->height; i++)
	{
		for(int j = 0; j < inputImg->width; j++)
		{
			int r = data[i * wp + 3 * j];
			int g = data[i * wp + 3 * j + 1];
			int b = data[i * wp + 3 * j + 2];
			if(r == 0 && g == 0 && b == 0)	//如果该像素点是黑色
			{
				nCount++;
			}
		}
	}
	return nCount;
}

4.实现函数:计算特征向量间的距离(不太明白特征向量的话,可以先看后面的train()和test(),再回来看)

double CalDist(vector<int> trainingFeaVector, vector<int> featureVector) //计算特征向量之间的距离
{
	double dist = 0;
	if(trainingFeaVector.size() == featureVector.size())
	{
		for(int i = 0; i < trainingFeaVector.size(); i++)
		{
			//对比每一位,将相减得到的差的平方加起来(可以理解为将两个向量的差异累加起来了)
			dist += (trainingFeaVector[i] - featureVector[i])*(trainingFeaVector[i] - featureVector[i]);
		}
		//再开方得绝对值
		dist = sqrt(dist);
	}
	return dist;
}

5.train()函数,主要实现两个功能,一个是切割子图像,一个是保存每一类训练图片的的平均特征向量
切割子图像主要根据切割比例(即划分为几块),利用opencv的内置函数reset、set改变图像大小,及时保存下来即可。然后对每一类数字的前7张图片的16幅子图进行黑色像素计数,得到一个包含16个数的特征向量,对每一类的7张图片求平均特征向量,即最后会得到对应于0-9的共10个平均特征向量,将其存入二维向量feaVVector中,并单独保存每个feaVector[w]为.txt文件。具体代码如下

void train()
{
	int sum[16]={0,0};	//初始化数组
	vector<int> feaVector;	//存放每一张训练图片的特征向量	
	for(int i = 0; i < 10; i++)	//0-9共10种数字
	{
		int kind=0;	//用于,每一类数字中重置,重新计算新的平均特征向量(其实不用也行)
		for(int j = 1; j < 8; j++)	//每一类的前7张图片作为训练集(记得替换路径,后面同理)
		{
			sprintf(str, "D:\\vc++\\Microsoft Visual Studio\\MyProjects\\机器视觉2020\\数字识别\\数字图片\\%d\\%d-%d.bmp",i,i,j);
			IplImage* inputImg = cvLoadImage(str, 1);
			
			//切割子图像
			int nWidth=inputImg->width/ block;
			int nHeight=inputImg->height/ block;
			feaVector.clear();
			//获取每张图的特征
			for(int m=0;m<block;m++){
				for(int n=0;n<block;n++){
					cvResetImageROI(inputImg);
					cvSetImageROI(inputImg, cvRect(n*nWidth, m*nHeight, nWidth, nHeight));
					IplImage *img = cvCreateImage(cvSize(nWidth, nHeight),8,3);
					cvCopy(inputImg, img); 
					//保存子图像
					sprintf(str, "D:\\vc++\\Microsoft Visual Studio\\MyProjects\\机器视觉2020\\数字识别\\数字图片\\cutImg\\%d-%d-%d-%d.bmp",i,j,m,n);
					cvSaveImage(str,img);

					//计数
					int nCount=ImageBlackCount(img);
					feaVector.push_back(nCount);
				}
			}
			kind++;
			//将平均图的特征放入feaVVector里面
			if(kind==7){
				for(int w=0;w<16;w++){
					sum[w]=sum[w]/16;
					feaVector[w]=sum[w];
				}
				feaVVector.push_back(feaVector);
				//保存训练图片平均特征向量
				sprintf(str, "D:\\vc++\\Microsoft Visual Studio\\MyProjects\\机器视觉2020\\数字识别\\数字图片\\avg_vector\\%d.txt",i);
				FILE *fp=fopen(str,"w");
				for(int k=0;k<feaVector.size();k++){
					fprintf(fp,"%d ",feaVector[k]);
				}
				fclose(fp);
			}
			else{
				for(int w=0;w<16;w++){
					sum[w]+=feaVector[w];
				}
			}			
		}
	}
}

下面是训练集图片切割保存后的子图像
在这里插入图片描述
下面是每一类数字训练集图片的平均特征向量
在这里插入图片描述

5.test()函数,主要实现4个功能,一是切割子图像,二是保存每一类训练图片的的平均特征向量,操作与train()基本完全相同,得到的内容也基本类似,也会得到二维向量test_feaVVector,可自行看后面代码。三是利用步骤4中实现的函数计算两个二维特征向量feaVVector[k], test_feaVVector[num]的欧氏距离,进行识别,判断结果。四是统计输出,代码细节如下:

void test()
{
	int num=0;	//测试图片数量
	int right = 0;	//识别正确数量
	vector<int>test_pic;	//存放每一张测试图片的特征向量

	for(int i = 0; i < 10; i++)
	{
		for(int j = 8; j < 11; j++)
		{	
			sprintf(str, "D:\\vc++\\Microsoft Visual Studio\\MyProjects\\机器视觉2020\\数字识别\\数字图片\\%d\\%d-%d.bmp",i,i,j);
			IplImage* inputImg = cvLoadImage(str, 1);

			//输出图像
			cvNamedWindow("170512_show");
			cvShowImage("170512_show",inputImg); 
			cvWaitKey(0);
			
			//分割图像
			int nWidth=inputImg->width/ block;
			int nHeight=inputImg->height/ block;
			test_pic.clear();
			//获取每张图的特征
			for(int m=0;m<block;m++){
				for(int n=0;n<block;n++){
					cvResetImageROI(inputImg);
					cvSetImageROI(inputImg, cvRect(n*nWidth, m*nHeight, nWidth, nHeight));
					IplImage *img = cvCreateImage(cvSize(nWidth, nHeight),8,3);
					cvCopy(inputImg, img); 
					sprintf(str, "D:\\vc++\\Microsoft Visual Studio\\MyProjects\\机器视觉2020\\数字识别\\数字图片\\test_cut\\%d-%d-%d-%d.bmp",i,j,m,n);
					cvSaveImage(str,img);
					int nCount=ImageBlackCount(img);
					test_pic.push_back(nCount);
				}
			}			
			test_feaVVector.push_back(test_pic);
			//保存测试图片特征向量
			sprintf(str, "D:\\vc++\\Microsoft Visual Studio\\MyProjects\\机器视觉2020\\数字识别\\数字图片\\test_cut\\%d.txt",i);
			FILE *fp=fopen(str,"w");
			for(int k=0;k<test_pic.size();k++){
				fprintf(fp,"%d ",test_pic[k]);
			}
			fclose(fp);

			//计算欧氏距离,识别结果
			double max=1000000;	//假设最大距离
			double dist=0;
			int label =0;	//定义标签		
			for(k=0;k<feaVVector.size();k++){
				dist = CalDist(feaVVector[k], test_feaVVector[num]);
				if(dist<max){
					max=dist;	//更新识别结果
					label = k;
				}
			}
			if(label == i){
				right++;
			}
			printf("%d的识别结果为:%d\t",i,label);
			num++;
		}
		printf("\n");
	}
	printf("识别成功总数为:%d\n",right);
	printf("识别失败总数为:%d\n",num-right);
	printf("总识别率为:%f\n",right*1.0/num);
}

6.main()函数,执行即可。

int main(int argc,char *argv[])
{		
	train();
	test();
    return 0;
}

总结:本文基于欧式距离的方法识别率不高,甚至可以说很低,不到70%,主要还是学习一下整个流程吧,分析原因应该是由于提取的特征数量较少(才16个,因为只划分成了16张子图),图片过度压缩导致,所以应该属于正常情况。优化的话可以划分更多子图,提取更多特征值,甚至极端点可以不划分子图,把所有像素点记录下来,用所有像素点进行计算欧氏距离,判断结果也是可以的。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值