机器视觉实验合集:
机器视觉-模板匹配实验(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张子图),图片过度压缩导致,所以应该属于正常情况。优化的话可以划分更多子图,提取更多特征值,甚至极端点可以不划分子图,把所有像素点记录下来,用所有像素点进行计算欧氏距离,判断结果也是可以的。