您的位置:首页 > 理论基础 > 计算机网络

OpenCv使用BP神经网络将图片分为三类

2017-07-31 11:06 190 查看
希望大家学会分享,可能你懂的别人不懂,让大家一起学习。

效果图:

(1)训练中



(2)训练结果





代码的实现:

首先你需要在工程下新建一个存放图片的文件夹,然后在此文件夹下再新建三个名为“0”,“1”,“2”的文件夹,为什么是0,1,2呢,因为代码是这样写的,具体的还是要你去看,去理解。如图:



上代码了(注:此代码是在别人的基础上进行修改的):

#include "stdafx.h"

#include <windows.h>  

#include <iostream>  

#include <opencv2/opencv.hpp>  

#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[le
4000
n]='\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<<"文件夹"<<i<<endl;  

        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<image_rows*image_cols; ++k)//每个图片  

            {  

                trainingData[i*sample_mun_perclass+(j-1)][k] = (float)trainImage.data[k];//将二值化数据拷贝到trainingData中  

            }  

         }  

          

    while (FindNextFile(hFile,&pNextInfo) && j<sample_mun_perclass);//如果设置读入的图片数量,则以设置的为准,如果图片不够,则读取文件夹下所有图片  

    
}  

      

    // 设置训练数据Mat输入  

    Mat trainingDataMat(class_mun*sample_mun_perclass, image_rows*image_cols, CV_32FC1, trainingData);// 设置训练数据Mat  

    cout<<"trainingDataMat——OK!"<<endl;  

  

    // 设置标签数据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<class_mun;++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:"<<endl;  

    cout<<labelsMat<<endl;  

    cout<<"labelsMat——OK!"<<endl;  

/*-------------------------------------------------------------------------------------------------*/

/*-----------------------------------训练代码-----------------------------------*/

  

    cout<<"training start...."<<endl;  

    CvANN_MLP bp;//bp神经网络  

      

    //设置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_<int>(1,5) << image_rows*image_cols,128,128,128,class_mun);//1个输入,1个输出,3个隐藏层,隐藏层数也根据内容自己调整  
bp.create(layerSizes,CvANN_MLP::SIGMOID_SYM,1.0,1.0);//CvANN_MLP::SIGMOID_SYM节点使用的函数  创建神经网络

                                                 

    cout<<"training...."<<endl;  

    bp.train(trainingDataMat, labelsMat, Mat(),Mat(), params);//trainingDataMat:输入矩阵,存储了所有训练样本的特征  神经网络的训练
 //labelsMat:输出矩阵,每个样本所属的种类每一行表示一个样本的预期输出结果,  

    bp.save("bpnet.xml"); //保存训练  

    cout<<"training finish...bpnet.xml saved "<<endl;  

/*------------------------------------------------------------------------------------*/
//CvANN_MLP bp;
//bp神经网络 

    //bp.load("bpnet.xml");//这是网络训练好后直接调用  

    //测试神经网络  

    cout<<"测试:"<<endl;  

    Mat test_image = imread("1.png",CV_LOAD_IMAGE_GRAYSCALE);//读入测试图  

    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_<float>sampleMat(1,image_rows*image_cols);   

    for(int i = 0; i<image_rows*image_cols; ++i)    

    {    

        sampleMat.at<float>(0,i) = (float)test_temp.at<uchar>(i/8,i%8);  //将test数据(unchar)copy到sampleMat(float)中图像特征  

    }    

  

    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<<"识别结果:"<<judge<<endl;  

    imshow("test_image",test_image);    

    waitKey(0);  

  

    return 0;  

}  

提示:我代码中训练的图片为637张,具体需要多少张你们进行衡量,如果训练图片太多的话会提示指针泄漏的,所以训练图片的数量从少开始依次叠加。

你们可能以为这就结束了,其实我还留了一手先看图:







是否会觉得每次测试图片都要进行训练等上几分钟,很麻烦?嘿嘿,其实不用的,图片训练完后会生成xml文件,图片的数据和特征都在里面所以你只需要会调用xml文件就可以省去训练的代码了。为了大家的方便,同时希望大家有好的东西分享出来,所以,代码如下:

一开始你应该讲xml文件放在工程下以便调用。



#include "stdafx.h"

#include <windows.h>  

#include <iostream>

#include <opencv2/opencv.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 int image_cols = 8;//图片分为列,可自行调整,这里只是随意写的  

    const int image_rows = 16;//图片分为行 
CvANN_MLP bp;
//bp神经网络 

    bp.load("bpnet.xml");//这是网络训练好后直接调用  
cout<<"测试:"<<endl;  

    Mat test_image = imread("11.png",CV_LOAD_IMAGE_GRAYSCALE);//读入测试图  

    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_<float>sampleMat(1,image_rows*image_cols);   

    for(int i = 0; i<image_rows*image_cols; ++i)    

    {    

        sampleMat.at<float>(0,i) = (float)test_temp.at<uchar>(i/8,i%8);  //将test数据(unchar)copy到sampleMat(float)中图像特征  

    }    

  

    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<<"识别结果:"<<judge<<endl;  

    imshow("test_image",test_image);    

    waitKey(0);  

  

    return 0;  

 

}

全部代码都在这了,谢谢观看。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: