目标:
把hog+svm的程序中用的emgu2.4版本改写成3.0版本
本文内容:
写出emgu2.4和3.0版本在hog和svm上改了哪些,和对应功能的替换方法
网上好像对于两个版本的参考资料很少,而且都很分散;刚好因为项目要求,就借着这次机会整理一下
2.4版本到3.0版本更新了好多东西,其实改版本远不如重新写一个3.0来的省力。。。。
流程
根据hog+svm的整体思路,我们的改动是按照写的顺序一步步来:
1、根据训练样本,设计HOG描述子;
2、获取正负样本HOG值;
3、训练SVM;(改动)
4、从训练好的SVM提取出hog描述值(改动)
5、检测(改动)
详细描述
设计HOG描述子
在设计hog描述子只这一块,函数并没有因为版本而发生变化;
2.4与3.0:
public HOGDescriptor hog;
hog = new HOGDescriptor(new Size(100, 50), new Size(10, 5), new Size(5, 5), new Size(5, 5), 9, 1, -1, 0.2, true);
//检测窗口(100,50),块尺寸(10,10),块步长(5,5),cell尺寸(5,5),直方图bin个数9
有些在设计HOG的时候并没有写后面这一部分(1,-1,0.2,true);这部分可写可不写,不写就直接采取默认值,关系不大。
参数1. 检测窗口,最好与正负样本取的像素大小一致
参数2.块尺寸
参数3.块步长
参数4.细胞尺寸
参数5.直方图bin个数,一般默认都是9
获取正负样本HOG值
用NegSamNO代表负样本数,PosSamNO代表正样本数;(可以直接输入,也可以用循环得到)
以下是获取正样本的代码
class Train
{
public HOGDescriptor hog;
public int descriptorDim; //HOG描述子的维数,由图片大小、检测窗口大小、块大小、细胞单元中直方图bin个数决定
bool isInitial = true;
public Matrix<float> sampleFeatureMat; //所有训练样本的特征向量组成的矩阵,行数等于所有样本的个数,列数等于HOG描述子维数
public Matrix<int> sampleLabelMat; //训练样本的类别向量,行数等于所有样本的个数,列数等于1;1表示有人,-1表示无人
//分类的数字格式更改为int//在2.4版本中int和float均可
public Train()
{
int NegSamNO = xx;
int PosSamNO = xx;
hog = new HOGDescriptor(new Size(100, 50), new Size(10, 5), new Size(5, 5), new Size(5, 5), 9, 1, -1, 0.2, true);
for (int index = 0; index < PosSamNO; index++) //依次读取正样本图片,生成HOG描述子
{
string Path = posImgPath + "(" + (index + 1) + ").bmp";
float[] pos_descriptors;
Image<Bgr, Byte> img = isGray ? new Image<Gray, byte>(Path).Convert<Bgr, Byte>() : new Image<Bgr, byte>(Path);
pos_descriptors = hog.Compute(img, new Size(5, 5), Size.Empty, null);
if (isInitial)
{
descriptorDim = pos_descriptors.Length; //hog描述子的长度
//初始化所有训练样本的特征向量组成的矩阵,行数等于所有样本的个数,列数等于HOG描述子维数sampleFeatureMat
sampleFeatureMat = new Matrix<float>(PosSamNO + NegSamNO, descriptorDim);
sampleFeatureMat.SetZero();
//初始化训练样本的类别向量,行数等于所有样本的个数,列数等于1;1表示有人,0表示无人
sampleLabelMat = new Matrix<int>(PosSamNO + NegSamNO, 1);
sampleLabelMat.SetZero();
isInitial = false;
}
for (int i = 0; i < descriptorDim; i++)
{
sampleFeatureMat[index, i] = pos_descriptors[i];//第index个样本的特征向量中的第i个元素
}
sampleLabelMat[index, 0] = 1;
}
``
//for(xxxx)//同理可以得到负样本
//{
//}
}
}
以上代码来自 https://www.cnblogs.com/KC-Mei/p/4553024.html 博主
训练SVM(改动开始)
2.4version
Train my_train = new Train();
SVMParams p = new SVMParams();
p.SVMType = Emgu.CV.ML.MlEnum.SVM_TYPE.C_SVC;
p.KernelType = Emgu.CV.ML.MlEnum.SVM_KERNEL_TYPE.LINEAR;
p.Degree = 0;
p.Gamma = 1;
p.Coef0 = 0;
p.C = 0.01;
p.Nu = 0;
p.P = 0;
p.TermCrit = new MCvTermCriteria(1000, 0.00001);
bool trained = svm.Train(my_train.sampleFeatureMat, my_train.sampleLabelMat, null, null, p);
3.0version
Train my_train = new Train();
Emgu.CV.ML.SVM p;
p = new Emgu.CV.ML.SVM();
p.SetKernel(Emgu.CV.ML.SVM.SvmKernelType.Linear);
p.Type = SVM.SvmType.CSvc;
p.C = 0.01;
p.TermCriteria = new MCvTermCriteria(1000, 0.001);
TrainData td = new TrainData(my_train.sampleFeatureMat, Emgu.CV.ML.MlEnum.DataLayoutType.RowSample, my_train.sampleLabelMat);
//在3.0中trained设置的参数类型改变了,于是通过TrainData来设置trained
bool trained = p.TrainAuto(td);
当在改写的时候报出“尝试除以零”的错误,把分类的数字格式改为int;
从训练好的SVM提取出hog描述值
2.4version
以下是获取特征向量的代码,在3.0版本中相关函数被重写
GetData gd = new GetData();
svm.Load(LOAD_PATH);
int DescriptorDim = svm.GetVarCount(); //特征向量的维数,即HOG描述子的维数
int supportVectorNum = svm.GetSupportVectorCount(); //支持向量的个数
Matrix<float> supportVectorMat = new Matrix<float>(supportVectorNum, DescriptorDim);//支持向量矩阵
supportVectorMat.SetZero();
//将支持向量的数据复制到supportVectorMat矩阵中
for (int i = 0; i < supportVectorNum; i++)
{
float[] pSVData = svm.GetSupportVector(i);//返回第i个支持向量的数据指针
for (int j = 0; j < DescriptorDim; j++)
{
supportVectorMat[i, j] = pSVData[j];
}
}
3.0version
public void beginSVM()
{
svm =new Emgu.CV.ML.SVM();
Emgu.CV.FileStorage fsr = new Emgu.CV.FileStorage(LOAD_PATH, Emgu.CV.FileStorage.Mode.Read);
svm.Read(fsr.GetFirstTopLevelNode());
//在emgu2中svm没有load
XmlDocument xml = new XmlDocument();
xml.Load(LOAD_PATH);
GetData gd = new GetData();
int DescriptorDim = svm.GetSupportVectors().Width;//特征向量的维数,即HOG描述子的维数 没有GetVarCount()
int supportVectorNum = svm.GetSupportVectors().Height;//支持向量的个数 没有GetSupportVectorCount()
Matrix<float> alphaMat = new Matrix<float>(1, supportVectorNum);//alpha向量,长度等于支持向量个数
alphaMat.SetZero();
Matrix<float> supportVectorMat = new Matrix<float>(supportVectorNum, DescriptorDim);//支持向量矩阵
supportVectorMat.SetZero();
Matrix<float> resultMat = new Matrix<float>(1, DescriptorDim);//alpha向量乘以支持向量矩阵的结果
resultMat.SetZero();
Emgu.CV.Mat supporVectorMat1;
supporVectorMat1 = svm.GetSupportVectors();
supporVectorMat1.CopyTo(supportVectorMat); //格式转化
for (int i = 0; i < supportVectorNum; i++)
{
alphaMat[0, i] = (float)gd.alpha[i];
}
//计算-(alphaMat * supportVectorMat),结果放到resultMat中
resultMat = -1 * alphaMat * supportVectorMat;
//得到最终的setSVMDetector(const vector<float>& detector)参数中可用的检测子
float[] myDetector = new float[DescriptorDim + 1];
//将resultMat中的数据复制到数组myDetector中
for (int i = 0; i < DescriptorDim; i++)
{
//myDetector.push_back(resultMat.at<float>(0, i));
myDetector[i] = resultMat.Data[0, i];
}
//最后添加偏移量rho,得到检测子
myDetector[myDetector.Length - 1] = (float)gd.rho;
//检测窗口(100,50),块尺寸(10,10),块步长(5,5),cell尺寸(5,5),直方图bin个数9
Hog_Descriptor = new Emgu.CV.HOGDescriptor(new System.Drawing.Size(100, 50), new System.Drawing.Size(10, 5), new System.Drawing.Size(5, 5), new System.Drawing.Size(5, 5), 9, 1, -1, 0.2, false);
Hog_Descriptor.SetSVMDetector(myDetector);
}
在3.0版本中我们可以用GetSupportVectors()直接读取支持向量;但是两者的格式需要转化,GetSupportVectors()得到的是Mat,我们需要的是Matrix;所以最后加了一个转化的函数
许多的hog+svm的教程都提到了hog纬度的计算,我们可以用以下代码来代替
int DescriptorDim = svm.GetSupportVectors().Width
int supportVectorNum = svm.GetSupportVectors().Height
对于alpha向量和resultMat = -1 * alphaMat * supportVectorMat 的提取在两个版本中可以通用
相关代码可以参考 关于SVM中的alpha、rho向量 这个博主的文章
检测
2.4 version
beginSVM();
regions = Hog_Descriptor.DetectMultiScale(img, 0, new Size(2, 2), Size.Empty,0.8,1,false);
Image<Bgr,Byte> copy = img.Copy();
int num_target = 0;
foreach (Rectangle pedestrain in regions)
{
img.Draw(pedestrain, new Bgr(Color.Red), 5);
//保存出识别结果
copy.ROI = pedestrain;
Image<Bgr, Byte> copy2 = copy.Copy();
copy2.Resize(100, 50, Emgu.CV.CvEnum.INTER.CV_INTER_LINEAR).Save(@"D:\studyOFpostgraduate\信号灯识别\信号灯识别(HOG+SVM)\HOG_SVM\Results\" + num_target.ToString() + ".bmp");
num_target++;
}
3.0 version
beginSVM();
using (Emgu.CV.HOGDescriptor hog = new Emgu.CV.HOGDescriptor(new System.Drawing.Size(100, 50), new System.Drawing.Size(10, 5), new System.Drawing.Size(5, 5), new System.Drawing.Size(5, 5), 9))
{
MCvObjectDetection[] results = Hog_Descriptor.DetectMultiScale(img);
regions = new Rectangle[results.Length]; //格式转变
for (int i = 0; i < results.Length; i++)
{
regions[i] = results[i].Rect;
}
Image<Bgr, Byte> copy = img.Copy();
int num_target = 0;
foreach (Rectangle pedestrain in regions)
{
img.Draw(pedestrain, new Bgr(Color.Red), 5);
//删去保存结果的功能,Emgu.CV.CvEnum中找不到INTER类
num_target++;
}
}
问题
1.Hog_Descriptor.DetectMultiScale的参数设置问题(HOG detectMultiScale 参数分析)
2.regions的格式转化问题:将MCvObjectDetection[]的格式转变为 Rectangle[]
相关文章
本文很多代码都取自 https://www.cnblogs.com/KC-Mei/p/4553024.html 的博客作者
在EMGU3.1版本下的hog+svm的使用: https://blog.csdn.net/u011616825/article/details/72794527
HOG detectMultiScale 参数分析: https://www.cnblogs.com/klitech/p/5747895.html