希望大家学会分享,可能你懂的别人不懂,让大家一起学习。
效果图:
(1)训练中
(2)训练结果
代码的实现:
首先你需要在工程下新建一个存放图片的文件夹,然后在此文件夹下再新建三个名为“0”,“1”,“2”的文件夹,为什么是0,1,2呢,因为代码是这样写的,具体的还是要你去看,去理解。如图:
上代码了(注:此代码是在别人的基础上进行修改的):
#include "stdafx.h"
#include
#include
#include
#include "opencv2/core/core.hpp"
using namespace cv;
using namespace std;
//----------------------------------字符串之间的相互转换----------------------------------
char* WcharToChar(const wchar_t* wp)
{
char *m_char;
int len= WideCharToMultiByte(CP_ACP,0,wp,wcslen(wp),NULL,0,NULL,NULL);
m_char=new char[len+1];
WideCharToMultiByte(CP_ACP,0,wp,wcslen(wp),m_char,len,NULL,NULL);
m_char[len]='\0';
return m_char;
}
wchar_t* CharToWchar(const char* c)
{
wchar_t *m_wchar;
int len = MultiByteToWideChar(CP_ACP,0,c,strlen(c),NULL,0);
m_wchar=new wchar_t[len+1];
MultiByteToWideChar(CP_ACP,0,c,strlen(c),m_wchar,len);
m_wchar[len]='\0';
return m_wchar;
}
wchar_t* StringToWchar(const string& s)
{
const char* p=s.c_str();
return CharToWchar(p);
}
//----------------------------------------------------------------------------------------
int main()
{
const string fileform = "*.png";//文件格式
const string perfileReadPath = "charSamples"; //文件前置路径
const int sample_mun_perclass = 637;//训练字符每类数量
const int class_mun = 3;//训练字符类数
const int image_cols = 8;//图片分为列,可自行调整,这里只是随意写的
const int image_rows = 16;//图片分为行
string fileReadName,fileReadPath;
char temp[256];
float trainingData[class_mun*sample_mun_perclass] [image_cols*image_rows] = {{0}};//每一行一个训练样本
float labels[class_mun*sample_mun_perclass][class_mun] = {{0}};//训练样本标签
/*---------------------------------------------读取图片-------------------------------------------------------------*/
for(int i=0;i<=class_mun-1;++i)//不同类 class_mun-1
{
//读取每个类文件夹下所有图像
int j = 0; //每一类下读取图像计数个数
sprintf(temp,"%d",i); //按顺序读图
fileReadPath = perfileReadPath + "/" + temp + "/" + fileform; //文件读取路径
cout<<"文件夹"< HANDLE hFile;
LPCTSTR lpFileName = StringToWchar(fileReadPath); //指定搜索目录和文件类型
WIN32_FIND_DATA pNextInfo; //搜索得到的文件信息将储存在pNextInfo中;
hFile = FindFirstFile(lpFileName,&pNextInfo);
if(hFile == INVALID_HANDLE_VALUE) //搜索失败
{
exit(-1);
}
//循环读取
do
{
if(pNextInfo.cFileName[0] == '.')//过滤.和..
continue;
j++;//读取一张图
printf("%s\n",WcharToChar(pNextInfo.cFileName));
//对读入的图片进行处理
/////////////////////////////////
Mat srcImage = imread( perfileReadPath + "/" + temp + "/" + WcharToChar(pNextInfo.cFileName),CV_LOAD_IMAGE_GRAYSCALE);//读入图像
//////////////////////////////////
Mat resizeImage;
Mat trainImage;
resize(srcImage,resizeImage,Size(image_cols,image_rows),(0,0),(0,0),CV_INTER_AREA);//使用象素关系重采样。当图像缩小时候,该方法可以避免波纹出现
threshold(resizeImage,trainImage,0,255,CV_THRESH_BINARY|CV_THRESH_OTSU);//对灰度图像进行阈值操作得到二值图像
for(int k = 0; k
trainingData[i*sample_mun_perclass+(j-1)][k] = (float)trainImage.data[k];//将二值化数据拷贝到trainingData中
}
}
while (FindNextFile(hFile,&pNextInfo) && j
}
// 设置训练数据Mat输入
Mat trainingDataMat(class_mun*sample_mun_perclass, image_rows*image_cols, CV_32FC1, trainingData);// 设置训练数据Mat
cout<<"trainingDataMat——OK!"<
// 设置标签数据Mat输出
for(int i=0;i<=class_mun-1;++i) //class_mun-1
{
for(int j=0;j<=sample_mun_perclass-1;++j)
{
for(int k = 0;k
if(k==i)
labels[i*sample_mun_perclass + j][k] = 1;
else labels[i*sample_mun_perclass + j][k] = 0;
}
}
}
Mat labelsMat(class_mun*sample_mun_perclass, class_mun, CV_32FC1,labels);//设置标签数据Mat
cout<<"labelsMat:"<
/*-----------------------------------训练代码-----------------------------------*/
cout<<"training start...."<
//设置bp神经网络的参数
CvANN_MLP_TrainParams params; //类,参数 1
params.train_method=CvANN_MLP_TrainParams::BACKPROP; //训练方法:误差反向传播法
params.bp_dw_scale=0.001; //权值更新率 0.001
params.bp_moment_scale=0.1; //权值更新冲量
params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS,10000,0.0001); //设置结束条件term_crit:终止条件,10000,0.0001
//它包括了两项,迭代次数(CV_TERMCRIT_ITER)和误差最小值(CV_TERMCRIT_EPS),一旦有一个达到条件就终止训练。
//设置bp神经网络
Mat layerSizes=(Mat_
bp.create(layerSizes,CvANN_MLP::SIGMOID_SYM,1.0,1.0);//CvANN_MLP::SIGMOID_SYM节点使用的函数 创建神经网络
cout<<"training...."<
//labelsMat:输出矩阵,每个样本所属的种类每一行表示一个样本的预期输出结果,
bp.save("bpnet.xml"); //保存训练
cout<<"training finish...bpnet.xml saved "<
//CvANN_MLP bp; //bp神经网络
//bp.load("bpnet.xml");//这是网络训练好后直接调用
//测试神经网络
cout<<"测试:"<
Mat test_temp;
resize(test_image,test_temp,Size(image_cols,image_rows),(0,0),(0,0),CV_INTER_AREA);//使用象素关系重采样。当图像缩小时候,该方法可以避免波纹出现
threshold(test_temp,test_temp,0,255,CV_THRESH_BINARY|CV_THRESH_OTSU);//二值化
Mat_
for(int i = 0; i
sampleMat.at
}
Mat responseMat;
bp.predict(sampleMat,responseMat); //过调用predict函数,我们得到一个输出向量,它是一个1*nClass的行向量, 识别
//其中每一列说明它与该类的相似程度(0-1之间),也可以说是置信度
Point maxLoc;
double maxVal = 0;
minMaxLoc(responseMat,NULL,&maxVal,NULL,&maxLoc);//最小最大值
string judge = "";
if(maxLoc.x == 0)
judge = "right";
else if(maxLoc.x == 1)
judge = "letf";
else
judge = "front";
cout<<"识别结果:"<
waitKey(0);
return 0;
}
提示:我代码中训练的图片为637张,具体需要多少张你们进行衡量,如果训练图片太多的话会提示指针泄漏的,所以训练图片的数量从少开始依次叠加。
你们可能以为这就结束了,其实我还留了一手先看图:
是否会觉得每次测试图片都要进行训练等上几分钟,很麻烦?嘿嘿,其实不用的,图片训练完后会生成xml文件,图片的数据和特征都在里面所以你只需要会调用xml文件就可以省去训练的代码了。为了大家的方便,同时希望大家有好的东西分享出来,所以,代码如下:
一开始你应该讲xml文件放在工程下以便调用。
#include "stdafx.h"
#include
#include
#include
using namespace cv;
using namespace std;
char* WcharToChar(const wchar_t* wp)
{
char *m_char;
int len= WideCharToMultiByte(CP_ACP,0,wp,wcslen(wp),NULL,0,NULL,NULL);
m_char=new char[len+1];
WideCharToMultiByte(CP_ACP,0,wp,wcslen(wp),m_char,len,NULL,NULL);
m_char[len]='\0';
return m_char;
}
wchar_t* CharToWchar(const char* c)
{
wchar_t *m_wchar;
int len = MultiByteToWideChar(CP_ACP,0,c,strlen(c),NULL,0);
m_wchar=new wchar_t[len+1];
MultiByteToWideChar(CP_ACP,0,c,strlen(c),m_wchar,len);
m_wchar[len]='\0';
return m_wchar;
}
wchar_t* StringToWchar(const string& s)
{
const char* p=s.c_str();
return CharToWchar(p);
}
int main()
{
const int image_cols = 8;//图片分为列,可自行调整,这里只是随意写的
const int image_rows = 16;//图片分为行
CvANN_MLP bp; //bp神经网络
bp.load("bpnet.xml");//这是网络训练好后直接调用
cout<<"测试:"<
Mat test_temp;
resize(test_image,test_temp,Size(image_cols,image_rows),(0,0),(0,0),CV_INTER_AREA);//使用象素关系重采样。当图像缩小时候,该方法可以避免波纹出现
threshold(test_temp,test_temp,0,255,CV_THRESH_BINARY|CV_THRESH_OTSU);//二值化
Mat_
for(int i = 0; i
sampleMat.at
}
Mat responseMat;
bp.predict(sampleMat,responseMat); //过调用predict函数,我们得到一个输出向量,它是一个1*nClass的行向量,
//其中每一列说明它与该类的相似程度(0-1之间),也可以说是置信度
Point maxLoc;
double maxVal = 0;
minMaxLoc(responseMat,NULL,&maxVal,NULL,&maxLoc);//最小最大值
string judge = "";
if(maxLoc.x == 0)
judge = "right";
else if(maxLoc.x == 1)
judge = "letf";
else
judge = "front";
cout<<"识别结果:"<
waitKey(0);
return 0;
}
全部代码都在这了,谢谢观看。