• SVM实现分类识别及参数调优(一)


    前言

     项目有一个模块需要将不同类别的图片进行分类,共有三个类别,使用SVM实现分类。

    实现步骤:

    1.创建训练样本库;

    2.训练、测试SVM模型;

    3.SVM的数据要求;

    实现系统:

    windows_x64、opencv2.4.10、 VS2013

    实现过程:

    1.创建训练样本库;

    1)将图片以包含类别的名称进行命名,比如0(1).jpg等等;

    2)将所有已命名正确的训练样本保存在同一个文件夹中;

    3)在训练样本库的文件夹目录下创建python源文件;

    python代码:

    import sys
    import os
    import string
    import re
    
    if __name__=='__main__':
        print('Begin generate path and label.')
        path_file=open('train_path.txt','w')
        path='E:/carriage_recognition/redplate_detection/svm_train_test/data/train/model'
        pic_type='.png'
        pat=re.compile(r'^(d+)')
        files=os.listdir(path)
        files_tmp=[]
        for i in files:
            if pic_type in i and not os.path.isdir(path+'/'+i):
                files_tmp.append(i)
        files=files_tmp
        for file in range(len(files)):
            ret=pat.match(files[file])
            path_file.write(path+'/'+files[file]+'
    ')
            if file<len(files)-1:           
                path_file.write(ret.group(1)+'
    ')
            else:           
                path_file.write(ret.group(1))
        path_file.close()
        print('finish......')
    View Code

    4)运行代码,即可,生成包含图片名称和类别的文本文件,用于SVM训练过程中读物图片获取相应的类别标签;

    E:/carriage_recognition/redplate_detection/svm_train_test/data/train/model/0 (1).png
    0
    E:/carriage_recognition/redplate_detection/svm_train_test/data/train/model/0 (10).png
    0
    

    奇数行表示训练样本图片的路径名称;偶数行表示该图片的类别标签;

    2.训练、测试SVM模型;

    1)image.h,主要实现过程的代码;

    #include <fstream>
    #include <vector>
    #include<direct.h>
    #include <opencv2corecore.hpp>     //红牌事件检测头文件 
    #include <opencv2opencv.hpp>    
    
    using namespace std;
    using namespace cv;
    
    #define ON_STUDY 0
    #define Num 3      //类别数目
    #define STANDARD_ROW 65
    #define STANDARD_COL 85
    
    #define STANDARD_ROW_CHOOSE 65
    #define STANDARD_COL_CHOOSE 85
    
    #define CHANELS 1
    class NumTrainData
    {
    public:
        NumTrainData()
        {
            memset(data, 0, sizeof(data));
            result = -1;
        }
    public:
        float data[CHANELS*STANDARD_COL_CHOOSE*STANDARD_ROW_CHOOSE];
        int result;
    };
    
    vector<string> img_path;//输入文件名变量
    vector<string> img_test_path;//输入文件名变量 
    vector<int> img_catg;
    vector<int> img_test_catg;
    int nLine = 0;
    string buf;
    
    unsigned long n;
    vector<NumTrainData> buffer;
    int featureLen = CHANELS*STANDARD_COL_CHOOSE*STANDARD_ROW_CHOOSE;
    
    char* test_path = "./test_path.txt";
    char* train_path = "./train_path.txt";
    //存放输出结果
    char* save_path = "./SVM_DATA_train_0.5_0.2.xml";
    ofstream matrix_config("./fusion_matrix_0.5_0.2.txt");   //存放混淆矩阵
    string save_wrong_results = "./wrong_0.5_0.2";         //存放识别错误的结果
    
    
    void ReadTrainData()
    {
        ifstream svm_data(train_path);//训练样本图片的路径都写在这个txt文件中,使用python可以得到这个txt文件
        while (svm_data)//将训练样本文件依次读取进来    
        {
            if (getline(svm_data, buf))
            {
                nLine++;
                if (nLine % 2 == 0)//注:奇数行是图片全路径,偶数行是标签 
                {
                    img_catg.push_back(atoi(buf.c_str()));//atoi将字符串转换成整型,标志(0,1,2,...,9),注意这里至少要有两个类别,否则会出错    
                }
                else
                {
                    img_path.push_back(buf);//图像路径    
                }
            }
        }
        svm_data.close();//关闭文件 
    }
    
    void ReadTestData()
    {
        ifstream svm_data(test_path);//训练样本图片的路径都写在这个txt文件中,使用python可以得到这个txt文件
        while (svm_data)//将训练样本文件依次读取进来    
        {
            if (getline(svm_data, buf))
            {
                nLine++;
                if (nLine % 2 == 0)//注:奇数行是图片全路径,偶数行是标签 
                {
                    img_test_catg.push_back(atoi(buf.c_str()));//atoi将字符串转换成整型,标志(0,1,2,...,9),注意这里至少要有两个类别,否则会出错    
                }
                else
                {
                    img_test_path.push_back(buf);//图像路径    
                }
            }
        }
        svm_data.close();//关闭文件 
    }
    
    void LoadTrainData()
    {
        Mat src;      //= Mat::zeros(rows, cols, CV_8UC1);
        Mat dst;
        NumTrainData rtd;
        cout << "Begin load training data...." << endl;
        for (int i = 0; i < img_path.size(); i++)
        {
            rtd.result = img_catg[i];
    
            int k = 0;
            if (CHANELS == 1) // gray image
            {
                src = imread(img_path[i].c_str(), 0);
                dst = src;
    
                Mat temp = Mat::zeros(STANDARD_ROW, STANDARD_COL, CV_8UC1); 
    
                //尺寸归一化
                resize(dst, temp, temp.size());
    
                float m[CHANELS*STANDARD_COL_CHOOSE*STANDARD_ROW_CHOOSE];
                for (int i = 0; i<STANDARD_ROW; i++)
                {
                    for (int j = 0; j<STANDARD_COL; j++)
                    {
                        rtd.data[i * STANDARD_COL + j] = temp.at<uchar>(i, j);
                    }
                }
            }
            else if (CHANELS == 3) // 3-channel image
            {
                src = imread(img_path[i].c_str(), 1);
                dst = src;
    
                Mat temp = Mat::zeros(STANDARD_ROW, STANDARD_COL, CV_8UC1);    //大小归一化
                resize(dst, temp, temp.size());
                //cout << temp.channels() << endl;
    
                for (int i = 0; i < STANDARD_ROW_CHOOSE; i++)
                {
                    for (int j = 0; j < STANDARD_COL_CHOOSE; j++)
                    {
                        Vec3b& mp = temp.at<Vec3b>(i, j);
                        float B = mp.val[0];
                        //cout << "B=" << B << endl;
                        float G = mp.val[1];
                        //cout << "G=" << B << endl;
                        float R = mp.val[2];
                        //cout << "R=" << B << endl;
    
                        rtd.data[k++] = B;  //R
                        rtd.data[k++] = G;  //G
                        rtd.data[k++] = R;  //B
                    }
                }
            }
            buffer.push_back(rtd);
            //cout << i << "th Image is loaded!" << endl;
        }
        cout << "Loading image finished!" << endl;
    }
    
    void SVMPredict()
    {
        int x = 0;
        //_mkdir(save_test_preprocess.c_str());
        _mkdir(save_wrong_results.c_str());
        int fusion_matrix[Num][Num] = { 0 };
    
        CvSVM svm;
        svm.load(save_path);
        Mat src,dst;
        Mat m = Mat::zeros(1, featureLen, CV_32FC1);
    
        NumTrainData rtd;
        int label = -1;
        int right = 0, error = 0;
        save_wrong_results += "/%d_true_%d_false_%d.png";
        //
        double ptrue_rtrue = 0;
        double ptrue = 0;
        double rtrue = 0;
        //
        for (int i = 0; i < img_test_path.size(); i++)
        {
            label = img_test_catg[i];
            rtd.result = label;
    
            if (CHANELS == 1)
            {
                src = imread(img_test_path[i].c_str(), 0);
                dst = src;
    
                Mat temp = Mat::zeros(STANDARD_ROW, STANDARD_COL, CV_8UC1);    //大小归一化
                resize(dst, temp, temp.size());
    
                for (int i = 0; i<STANDARD_ROW; i++)
                {
                    for (int j = 0; j<STANDARD_COL; j++)
                    {
                        m.at<float>(0, j + i * STANDARD_COL) = temp.at<uchar>(i, j);
                    }
                }
                normalize(m, m);
            }
            else if (CHANELS == 3) // 3-channel image
            {
                src = imread(img_test_path[i].c_str(), 1);
                dst = src;
    
                Mat temp = Mat::zeros(STANDARD_ROW, STANDARD_COL, CV_8UC1);    //大小归一化
                resize(dst, temp, temp.size());
    
                int k = 0;
                for (int i = 0; i < STANDARD_ROW_CHOOSE; i++)
                {
                    for (int j = 0; j < STANDARD_COL_CHOOSE; j++)
                    {
                        Vec3b& mp = temp.at<Vec3b>(i, j);
                        float B = mp.val[0];
                        float G = mp.val[1];
                        float R = mp.val[2];
    
                        m.at<float>(0, k++) = B;  //R
                        m.at<float>(0, k++) = G;  //G
                        m.at<float>(0, k++) = R;  //B
                    }
                }
            }
            
            int ret = svm.predict(m);
            //if (ret == 3)
            //    ret = 1;
            cout << "Picture->" << img_test_path[i].c_str() << " : 
    True label is [" << label << "] Predicted label is [" << ret << "]" << endl;
            //
            //计算FSCORE指标各个参数
            if (label == 0 && ret == 0) ptrue_rtrue++;//识别为红牌且实际为红牌;
            if (ret == 0) ptrue++;//识别为红牌的个数
            if (label == 0) rtrue++;//实际为红牌的个数
            //
            //存储错误图片
            if (label != ret)
            {
                x++;
                char filename[200];
                src = imread(img_test_path[i].c_str(), 1);
                sprintf(filename, save_wrong_results.c_str(), x, label, ret);
                imwrite(filename, src);
            }
            //计算混淆矩阵
            //fusion_matrix[label][ret] = fusion_matrix[label][ret] + 1;
        }
        //
        //FSCORE
        std::cout << "count_all: " << img_test_path.size() << std::endl;
        std::cout << "ptrue_rtrue: " << ptrue_rtrue << std::endl;
        std::cout << "ptrue: " << ptrue << std::endl;
        std::cout << "rtrue: " << rtrue << std::endl;
        //precise
        double precise = 0;
        if (ptrue != 0)
        {
            precise = ptrue_rtrue / ptrue;
            std::cout << "precise: " << precise << std::endl;
        }
        else
        {
            std::cout << "precise: " << "NA" << std::endl;
        }
        //recall
        double recall = 0;
        if (rtrue != 0)
        {
            recall = ptrue_rtrue / rtrue;
            std::cout << "recall: " << recall << std::endl;
        }
        else
        {
            std::cout << "recall: " << "NA" << std::endl;
        }
        //FSCORE
        double FScore = 0;
        if (precise + recall != 0)
        {
            FScore = 2 * (precise * recall) / (precise + recall);
            std::cout << "FScore: " << FScore << std::endl;
        }
        else
        {
            std::cout << "FScore: " << "NA" << std::endl;
        }
        //
        //for (size_t i = 0; i < Num; i++)
        //{
        //    for (size_t j = 0; j < Num; j++)
        //    {
        //        matrix_config << fusion_matrix[i][j] << " ";
        //    }
        //    matrix_config << endl;
        //}
        //matrix_config.close();
        cout << "Task finished!output_matix" << endl;
        getchar();
    }
    
    void SVMTrain(vector<NumTrainData>& trainData)
    {
        int testCount = trainData.size();
    
        Mat m = Mat::zeros(1, featureLen, CV_32FC1);
        Mat data = Mat::zeros(testCount, featureLen, CV_32FC1);
        //Mat res = Mat::zeros(testCount, 1, CV_32SC1);
        Mat res = Mat::zeros(testCount, 1, CV_32SC1);
    
        for (int i = 0; i< testCount; i++)
        {
    
            NumTrainData td = trainData.at(i);
            memcpy(m.data, td.data, featureLen * sizeof(float));
            normalize(m, m);
            memcpy(data.data + i*featureLen * sizeof(float), m.data, featureLen * sizeof(float));
            cout << td.result << endl;
            res.at<int>(i, 0) = td.result;
            
        }
    
        /////////////START SVM TRAINNING//////////////////
        //CvSVM svm = CvSVM();
        CvSVM svm;
        CvSVMParams param;
        CvTermCriteria criteria;
    
        criteria = cvTermCriteria(CV_TERMCRIT_EPS, 1000, FLT_EPSILON);
        param = CvSVMParams(CvSVM::C_SVC, CvSVM::RBF, 10.0, 0.5, 1.0, 0.2, 0.5, 0.1, NULL, criteria);  //gamma=2;C=3
        cout << "Begin to train model using given train data.....
     Total training sample count is " << testCount << endl;
        svm.train(data, res, Mat(), Mat(), param);
        svm.save(save_path);
        cout << "Finish" << endl;
    }
    

      

    2)主要函数说明;

    2.1)SVMTrain函数主要实现模型的训练,其中训练参数使用RBF核,主要调整gamma和C这两个参数,固定一个参数调整另一个参数,最后确定模型参数分别为0.5/0.2;

    2.2)SVMPredict函数主要实现对测试样本库的测试,并使用FScore指标测试SVM模型的性能;也可以使用混淆矩阵测试性能;

    2.3)ReadTrainData/ ReadTestData函数分别用于获取训练和测试样本库图片的名称和类别标签;

    2.4)LoadTrainData函数用于读取训练数据,并进行图像处理;

    2.5)代码中使用整张图片的信息进行归一化之后作为特征;

    3)主函数入口

    #include "image.h"
    
    int main(int argc, char *argv[])
    {
    #if (ON_STUDY)
        ReadTrainData();
        LoadTrainData();
        SVMTrain(buffer);
    #else
        ReadTestData();
        SVMPredict();
    #endif
    
        getchar();
    }

    参数ON_STUDY表示选择进行训练或者测试的标志位;

    3.SVM的数据要求;

    需要说明的是就是SVM对于输入的数据类型是有要求的,即mTrainData(训练数据矩阵)以及mFlagPosNeg(标签矩阵)都必须为CV_32FC1类型(我的环境标签矩阵是CV_32SC1类型的),因此需要进行类型转换,而且必须保证转换完之后数值都不能大于1,这就给我们了两点启示:1)不能直接用下采样后的图像像素作为训练数据的输入,需要进行类型的归一化。2)类型转换时要使用normlize函数,保证其数值范围不大于1,而不能简单的使用Mat的成员函数coverto,只变类型不变数值范围。( 需要注意!)

    问题:

    该实现过程需要人工调整参数,比较繁琐,可以思考一下,是否还存在其他问题;

    参考:

    1.http://blog.csdn.net/firefight/article/details/6452188

    2.opencv中SVM的那些事儿

     

  • 相关阅读:
    mybatis 对string类型判断比较 group case when then 综合
    利用Apache shiro SimpleHash 加密字符串
    linux下用rpm 安装jdk
    运行java程序的时候出现Exception in thread "main" java.lang.UnsupportedClassVersionError:
    linux如何查看端口被哪个进程占用?
    Linux如何查看JDK的安装路径
    Linux中查看jdk版本
    linux修改/etc/profile权限
    db2,oracle,mysql ,sqlserver限制返回的行数
    什么是T-SQL
  • 原文地址:https://www.cnblogs.com/happyamyhope/p/7344404.html
Copyright © 2020-2023  润新知