opencv的hog+svm多分类识别花种类

这是一个细分的识别,打算通过svm和结合HOG特征将五类花卉进行识别,花卉分别是

daisy 小兰花,dandelion 蒲公英 ,roses玫瑰花,sunflowers 向日葵,tulips郁金香。

意在尝试通过改变setc值和gamma值查看其准确率。


名称种类 标签值 中文名称
daisy 1 兰花
2.dandelion 2 蒲公英
3.roses 3 玫瑰花
4.sunflowers 4 向日葵
5.tulips 5 郁金香


微信截图_20210420192441.png


通过  下面指令将 图像导入文本中 test.txt 

find /home/lid/share/samplesOfCorn/Pictures/flowers/test/* > test.txt


提取 HOG特证的方法如下


 for(int num = 0; num < RosesamNO && getline(Roses,ImgName); num++)
         {
            cout<<"Now processing original negative image: "<<ImgName<<endl;
            // ImgName = "../normalized_images/train/neg/" + ImgName;
            //加上负样本的路径名
           // ImgName = NegImageFile + ImgName;
            Mat src = imread(ImgName);//读取图片
          Size dsize = Size(64,128);
             Mat trainImg = Mat(dsize, CV_32S);
             resize(src, trainImg, dsize);

            vector<float> descriptors;//HOG描述子向量
            hog.compute(trainImg,descriptors,Size(8,8));//计算HOG描述子,检测窗口移动步长(8,8)

            //将计算好的HOG描述子复制到样本特征矩阵sampleFeatureMat
            for(int i=0; i<DescriptorDim; i++)
                sampleFeatureMat.at<float>(num+DaisySamNO+DandeSamNO,i) = descriptors[i];//第PosSamNO+num个样本的特征向量中的第i个元素
            sampleLabelMat.at<int>(num +DaisySamNO+DandeSamNO , 0) = 3;// 

        }


方法中我们定义 64x128的窗口,总共3180维度。通过for 循环遍历将所有的数据从文本中提取出来。一共是5分类所以要提取5次,特征数据在

sampleFeatureMat,标签数据在sampleLabelMat中。没有实现hardexample的训练。训练的惩罚因子和gamma如下表设置


测试数据如下


setC gamma 准确率
0.01 1 40%
3.61 1.383 53%
3.61 5 53%
10.6 1 53%
10.6 10 53%
100 10 53%
setc惩罚因子具有一定的饱和度,当到达饱和度的时候走,无论再怎么增大也不会增加准去率了gamma值影响不是很大,

参数Gamma( \ gamma)控制三维数据的拉伸。它有助于分类,但也会扭曲数据。与Goldilocks一样,您必须选择此参数为“恰好”。它是人们在训练SVM时选择的两个重要参数之一。

您可以想象,选择正确的SVM参数C和Gamma可能非常耗时。幸运的是,OpenCV 3.x C ++ API提供了自动为您执行此超参数优化的功能,并提供了最佳的C和Gamma值。在上面的代码中,您可以将svm-> train(td)更改为以下内容

1 svm-&gt;trainAuto(td);

这项训练可能会花费很长的时间(比svm-> train多5倍),因为它实际上是多次训练。



使用svm +hog  识别小花五分类
发现准确率只有40% ,特证没有优化,之前用hog识别minist数据集也是很低的识别率。
通过调节setc值到 0.01-2.00-3.00 /GAMAM值到3.00可使准确率有40%到53%。
细分准确率对样本的要求以及特征提取要求更高。


train.cpp的代码如下

#include<opencv/cv.h>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<opencv2/opencv.hpp>

#include<opencv2/ml/ml.hpp>
#include<opencv2/objdetect/objdetect.hpp>
#include<iostream>
#include<fstream>
#include<string>
#include<vector>
#include "train.h"

using namespace std;
using namespace cv;

using namespace cv::ml;

extern "C"
{
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
}


int main(int argc, char ** argv)
{
//检测窗口(64,128),块尺寸(16,16),块步长(8,8),cell尺寸(8,8),直方图bin个数9
    HOGDescriptor hog(Size(64,128),Size(16,16),Size(8,8),Size(8,8),9);
    int DescriptorDim;//HOG描述子的维数,由图片大小、检测窗口大小、块大小、细胞单元中直方图bin个数决定
    Ptr<SVM> svm = SVM::create();// 创建分类器

    if(TRAIN)//若TRAIN为true,重新训练分类器
    {
        string ImgName;//图片名(绝对路径)
       
        ifstream Daisy(DaisyListFile);
		ifstream Dande(DandeListFile);
		ifstream Roses(RosesListFile);
		ifstream Sun(SunListFile);
		ifstream Tulips(TulipsListFile);
        // ifstream finNeg("../sample_neg.txt");
        //负样本图片的文件名列表
       
        //HardExample负样本的文件名列表
        ifstream finHardNeg(NegHardListFile);

 

        Mat sampleFeatureMat;
        Mat sampleLabelMat;

        //loading original positive examples...
        for(int num=0; num < DaisySamNO && getline(Daisy,ImgName); num++)
        {
          //  cout <<"Now processing original positive image: " << ImgName << endl;
           // ImgName = PosImageFile + ImgName;
            Mat src = imread(ImgName);//读取图片

            if(CENTRAL_CROP)//true:训练时,对96*160的INRIA正样本图片剪裁出中间的64*128大小人体
              //  if(src.cols >= 96 && src.rows >= 160)
					
            	{
	        	Mat trainImg;
	        	resize(src, trainImg, Size(64,128));
	             
	            vector<float> descriptors;//HOG描述子向量
	            hog.compute(trainImg, descriptors, Size(8,8));//计算HOG描述子,检测窗口移动步长(8,8)

				cout<<"描述子维数:"<<descriptors.size()<<endl;
              	

            //处理第一个样本时初始化特征向量矩阵和类别矩阵,因为只有知道了特征向量的维数才能初始化特征向量矩阵
            if(num == 0 )
            {
                DescriptorDim = descriptors.size();//HOG描述子的维数
                //初始化所有训练样本的特征向量组成的矩阵,行数等于所有样本的个数,列数等于HOG描述子维数sampleFeatureMat
                sampleFeatureMat = Mat::zeros(DaisySamNO  +DandeSamNO + RosesamNO +SunamNO+TulipsSamNO, DescriptorDim, CV_32FC1);//CV_32FC1:CvMat数据结构参数
                //初始化训练样本的类别向量,行数等于所有样本的个数,列数等于1;1表示有人,0表示无人
                sampleLabelMat = Mat::zeros(DaisySamNO  +DandeSamNO + RosesamNO +SunamNO +TulipsSamNO, 1, CV_32SC1);//sampleLabelMat的数据类型必须为有符号整数型
            }

            //将计算好的HOG描述子复制到样本特征矩阵sampleFeatureMat
            for(int i=0; i<DescriptorDim; i++)
                sampleFeatureMat.at<float>(num,i) = descriptors[i];//第num个样本的特征向量中的第i个元素
            sampleLabelMat.at<int>(num,0) = 1;//正样本类别为1,有人
          }
        }
        Daisy.close();


        //依次读取负样本图片,生成HOG描述子
        for(int num = 0; num < DandeSamNO && getline(Dande,ImgName); num++)
        {
            cout<<"Now processing original negative image: "<<ImgName<<endl;
            // ImgName = "../normalized_images/train/neg/" + ImgName;
            //加上负样本的路径名
           // ImgName = NegImageFile + ImgName;
            Mat src = imread(ImgName);//读取图片
			Size dsize = Size(64,128);
			Mat trainImg = Mat(dsize, CV_32S);
			resize(src, trainImg, dsize);

            vector<float> descriptors;//HOG描述子向量
            hog.compute(trainImg,descriptors,Size(8,8));//计算HOG描述子,检测窗口移动步长(8,8)

            //将计算好的HOG描述子复制到样本特征矩阵sampleFeatureMat
            for(int i=0; i<DescriptorDim; i++)
                sampleFeatureMat.at<float>(num+DaisySamNO,i) = descriptors[i];//第PosSamNO+num个样本的特征向量中的第i个元素
            sampleLabelMat.at<int>(num +DaisySamNO , 0) = 2;// 

        }
        Dande.close();
		//3 roses samples pasrse 
        for(int num = 0; num < RosesamNO && getline(Roses,ImgName); num++)
         {
            cout<<"Now processing original negative image: "<<ImgName<<endl;
            // ImgName = "../normalized_images/train/neg/" + ImgName;
            //加上负样本的路径名
           // ImgName = NegImageFile + ImgName;
            Mat src = imread(ImgName);//读取图片
			Size dsize = Size(64,128);
			Mat trainImg = Mat(dsize, CV_32S);
			resize(src, trainImg, dsize);

            vector<float> descriptors;//HOG描述子向量
            hog.compute(trainImg,descriptors,Size(8,8));//计算HOG描述子,检测窗口移动步长(8,8)

            //将计算好的HOG描述子复制到样本特征矩阵sampleFeatureMat
            for(int i=0; i<DescriptorDim; i++)
                sampleFeatureMat.at<float>(num+DaisySamNO+DandeSamNO,i) = descriptors[i];//第PosSamNO+num个样本的特征向量中的第i个元素
            sampleLabelMat.at<int>(num +DaisySamNO+DandeSamNO , 0) = 3;// 

        }
        Roses.close();
	  // 4 sunflowers samples pasrse 
        for(int num = 0; num < SunamNO && getline(Sun,ImgName); num++)
         {
            cout<<"Now processing original negative image: "<<ImgName<<endl;
            // ImgName = "../normalized_images/train/neg/" + ImgName;
            //加上负样本的路径名
           // ImgName = NegImageFile + ImgName;
            Mat src = imread(ImgName);//读取图片
			Size dsize = Size(64,128);
			Mat trainImg = Mat(dsize, CV_32S);
			resize(src, trainImg, dsize);

            vector<float> descriptors;//HOG描述子向量
            hog.compute(trainImg,descriptors,Size(8,8));//计算HOG描述子,检测窗口移动步长(8,8)

            //将计算好的HOG描述子复制到样本特征矩阵sampleFeatureMat
            for(int i=0; i<DescriptorDim; i++)
                sampleFeatureMat.at<float>(num+DaisySamNO+DandeSamNO+RosesamNO,i) = descriptors[i];//第PosSamNO+num个样本的特征向量中的第i个元素
            sampleLabelMat.at<int>(num +DaisySamNO+DandeSamNO +RosesamNO, 0) = 4;// 

        }
        Sun.close();
	    // 5 the last tulips samples pasrse 
        for(int num = 0; num < TulipsSamNO && getline(Tulips,ImgName); num++)
         {
            cout<<"Now processing original negative image: "<<ImgName<<endl;
            // ImgName = "../normalized_images/train/neg/" + ImgName;
            //加上负样本的路径名
           // ImgName = NegImageFile + ImgName;
            Mat src = imread(ImgName);//读取图片
			Size dsize = Size(64,128);
			Mat trainImg = Mat(dsize, CV_32S);
			resize(src, trainImg, dsize);

            vector<float> descriptors;//HOG描述子向量
            hog.compute(trainImg,descriptors,Size(8,8));//计算HOG描述子,检测窗口移动步长(8,8)

            //将计算好的HOG描述子复制到样本特征矩阵sampleFeatureMat
            for(int i=0; i<DescriptorDim; i++)
                sampleFeatureMat.at<float>(num+DaisySamNO+DandeSamNO+RosesamNO+SunamNO,i) = descriptors[i];//第PosSamNO+num个样本的特征向量中的第i个元素
            sampleLabelMat.at<int>(num +DaisySamNO+DandeSamNO +RosesamNO+SunamNO, 0) = 5;// 

        }
        Tulips.close();
        //依次读取HardExample负样本图片,生成HOG描述子
        if(HARDNEG){
            for(int num = 0; num < HardExampleNO && getline(finHardNeg,ImgName); num++)
	        {
	            cout<<"Now processing original hard negative image: "<<ImgName<<endl;
	            // ImgName = "../normalized_images/train/neg/" + ImgName;
	            //加上负样本的路径名
	            ImgName = HardNegImageFile + ImgName;
	            Mat src = imread(ImgName);//读取图片

	            vector<float> descriptors;//HOG描述子向量
	            hog.compute(src,descriptors,Size(8,8));//计算HOG描述子,检测窗口移动步长(8,8)
	            //cout<<"描述子维数:"<<descriptors.size()<<endl;

	            //将计算好的HOG描述子复制到样本特征矩阵sampleFeatureMat
	            for(int i=0; i<DescriptorDim; i++)
	                sampleFeatureMat.at<float>(num+ PosSamNO + NegSamNO + AugPosSamNO,i) = descriptors[i];//第PosSamNO+num个样本的特征向量中的第i个元素
	            sampleLabelMat.at<int>(num + PosSamNO + NegSamNO + AugPosSamNO, 0) = -1;//负样本类别为-1,无人

	        }
        }
        finHardNeg.close();

        //指定svm种类
        svm ->setType(SVM::C_SVC);
        //CvSVM::C_SVC : C类支持向量分类机。 n类分组  (n≥2),允许用异常值惩罚因子C进行不完全分类。

        //CvSVM::NU_SVC : 类支持向量分类机。n类似然不完全分类的分类器。
        // 参数为取代C(其值在区间【0,1】中,nu越大,决策边界越平滑)

        //CvSVM::ONE_CLASS : 单分类器,所有的训练数据提取自同一个类里,
        // 然后SVM建立了一个分界线以分割该类在特征空间中所占区域和其它类在特征空间中所占区域。

        //CvSVM::EPS_SVR : 类支持向量回归机。
        // 训练集中的特征向量和拟合出来的超平面的距离需要小于p。异常值惩罚因子C被采用

        //CvSVM::NU_SVR : 类支持向量回归机。 代替了p。

        svm ->setC(100.0);//惩罚因子0.01 2.67

        svm ->setGamma(10.0);//gamma参数  1.0 5.383

        //C是惩罚系数,即对误差的宽容度。
        // c越高,说明越不能容忍出现误差,容易过拟合。
        // C越小,容易欠拟合。C过大或过小,泛化能力变差

        svm ->setKernel(SVM::LINEAR);//设置核函数
        // LINEAR:线性核函数;

        // POLY:多项式核函数;
        /// -d用来设置多项式核函数的最高此项次数;默认值是3
        /// -r用来设置核函数中的coef0,也就是公式中的第二个r,默认值是0。
        // 一般选择1-11:1 3 5 7 9 11,也可以选择2,4,6…

        // RBF:径向机核函数【高斯核函数】;
        /// -g用来设置核函数中的gamma参数设置,默认值是1/k(k是类别数)
        //gamma是选择RBF函数作为kernel后,该函数自带的一个参数。
        // 隐含地决定了数据映射到新的特征空间后的分布,
        // gamma越大,支持向量越少,gamma值越小,支持向量越多。
        // 支持向量的个数影响训练与预测的速度。

        // SIGMOID:神经元的非线性作用函数核函数;
        /// -g用来设置核函数中的gamma参数设置,默认值是1/k(k是类别数)
        /// -r用来设置核函数中的coef0,也就是公式中的第二个r,默认值是0

        // PRECOMPUTED:用户自定义核函数

        //SVM的迭代训练过程的中止条件
        svm ->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 10000, FLT_EPSILON));

        cout<<"Starting training..."<<endl;
        //svm->train(trainingDataMat, cv::ml::SampleTypes::ROW_SAMPLE, labelsMat);
        svm ->train(sampleFeatureMat, ROW_SAMPLE, sampleLabelMat);

        // svm ->trainAuto(); //svm自动优化参数
        cout<<"Finishing training..."<<endl;

        //储存 SVM 分类器
        svm ->save(SvmListFile);

        //SVM的改进算法
        //J.Platt的SMO算法、
        //T.Joachims的SVM、
        //C.J.C.Burges等的PCGC、
        //张学工的CSVM
        //以及O.L.Mangasarian等的SOR算法

    }
}


sitemap