初学机器学习,参考HOG SVM 车辆检测(https://www.cnblogs.com/louyihang-loves-baiyan/p/4658478.html)、LBP特征原理(https://blog.csdn.net/q1007729991/article/details/52995734)及LBP特征的实现及LBP+SVM分类 (https://blog.csdn.net/qianqing13579/article/details/49406563)的一些理论知识内容,实现基于LBP特征加上其他约束特征,结合SVM模型训练正负样本,实现二分类、对感兴趣特征的检测。
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace cv;
using namespace std;
#define TRAIN //开关控制是否训练还是直接载入训练好的模型
class MySVM: public CvSVM
{
public:
double * get_alpha_data()
{
return this->decision_func->alpha;
}
double get_rho_data()
{
return this->decision_func->rho;
}
};
//计算输入图片的最大灰度差、平均灰度、平均梯度
int calAverageGary(const Mat &inImg, int &maxGaryDiff, int &averageGrad_xy)
{
float averageGary;
int garySum = 0;
int i, j;
//求平均灰度值
for (i=0; i(j, i);
}
}
averageGary = (int)(garySum*1.0f/(inImg.rows*inImg.cols));
//求滑窗内的最大灰度差值
double minGary, maxGary;
minMaxLoc(inImg, &minGary, &maxGary, NULL, NULL);
maxGaryDiff = (int)(maxGary-minGary);
//求滑窗内的平均梯度值
Mat grad_x, grad_y, abs_grad_x, abs_grad_y, grad_xy;
Sobel( inImg, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT ); //求X方向梯度
convertScaleAbs( grad_x, abs_grad_x );
Sobel( inImg, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT ); //求Y方向梯度
convertScaleAbs( grad_y, abs_grad_y );
addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad_xy); //合并梯度(近似)
//cout<<"gary_xy"<(j, i);
}
}
averageGrad_xy = (int)(grad_xy_sum*1.0f/(inImg.rows*inImg.cols));
return averageGary;
}
// 计算等价模式LBP特征图
static void ComputeLBPImage_Uniform(const Mat &srcImage, Mat &LBPImage)
{
// 参数检查,内存分配
CV_Assert(srcImage.depth() == CV_8U&&srcImage.channels() == 1);
LBPImage.create(srcImage.size(), srcImage.type());
// 计算LBP图
Mat extendedImage;
copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);
// LUT(256种每一种模式对应的等价模式)
static const int table[256] = { 1, 2, 3, 4, 5, 0, 6, 7, 8, 0, 0, 0, 9, 0, 10, 11, 12, 0, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 14, 0, 15, 16, 17, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 20, 0, 21, 22, 23, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 25,
0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 27, 0, 28, 29, 30, 31, 0, 32, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 0
, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 36, 37, 38, 0, 39, 0, 0, 0, 40, 0, 0, 0, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42
, 43, 44, 0, 45, 0, 0, 0, 46, 0, 0, 0, 0, 0, 0, 0, 47, 48, 49, 0, 50, 0, 0, 0, 51, 52, 53, 0, 54, 55, 56, 57, 58 };
// 计算LBP
int heightOfExtendedImage = extendedImage.rows;
int widthOfExtendedImage = extendedImage.cols;
int widthOfLBP=LBPImage.cols;
uchar *rowOfExtendedImage = extendedImage.data+widthOfExtendedImage+1;
uchar *rowOfLBPImage = LBPImage.data;
int pixelDiff = 5;
for (int y = 1; y <= heightOfExtendedImage - 2; ++y,rowOfExtendedImage += widthOfExtendedImage, rowOfLBPImage += widthOfLBP)
{
// 列
uchar *colOfExtendedImage = rowOfExtendedImage;
uchar *colOfLBPImage = rowOfLBPImage;
for (int x = 1; x <= widthOfExtendedImage - 2; ++x, ++colOfExtendedImage, ++colOfLBPImage)
{
// 计算LBP值
int LBPValue = 0;
if (colOfExtendedImage[0 - widthOfExtendedImage - 1] >= colOfExtendedImage[0]+pixelDiff)
LBPValue += 128;
if (colOfExtendedImage[0 - widthOfExtendedImage] >= colOfExtendedImage[0]+pixelDiff)
LBPValue += 64;
if (colOfExtendedImage[0 - widthOfExtendedImage + 1] >= colOfExtendedImage[0]+pixelDiff)
LBPValue += 32;
if (colOfExtendedImage[0 + 1] >= colOfExtendedImage[0]+pixelDiff)
LBPValue += 16;
if (colOfExtendedImage[0 + widthOfExtendedImage + 1] >= colOfExtendedImage[0]+pixelDiff)
LBPValue += 8;
if (colOfExtendedImage[0 + widthOfExtendedImage] >= colOfExtendedImage[0]+pixelDiff)
LBPValue += 4;
if (colOfExtendedImage[0 + widthOfExtendedImage - 1] >= colOfExtendedImage[0]+pixelDiff)
LBPValue += 2;
if (colOfExtendedImage[0 - 1] >= colOfExtendedImage[0]+pixelDiff)
LBPValue += 1;
colOfLBPImage[0] = table[LBPValue];
}
}
}
//计算归一化的LBP特征矩阵
static void ComputeLBPFeatureVector_Uniform(const Mat &srcImage, Size cellSize, Mat &featureVector)
{
// 参数检查,内存分配
CV_Assert(srcImage.depth() == CV_8U&&srcImage.channels() == 1);
Mat LBPImage;
ComputeLBPImage_Uniform(srcImage, LBPImage);
//cout<<"LBPImage_uniform:"<(0,i) = (uchar)(LBPImage.at(0,i) * 255.0f);
}
//cout<<"LBPImage_255"< posSamples;
vector negSamples;
vector testSamples;
int posSampleNum;
int negSampleNum;
int testSampleNum;
double rho;
#ifdef TRAIN //此开关开启,使用正负样本进行训练
ifstream fInPos("..\\Inputs\\PositiveSample.txt"); //读取正样本
ifstream fInNeg("..\\Inputs\\NegtiveSample.txt"); //读取负样本
while (fInPos) //正样本读入imgPathList中
{
if(getline(fInPos, buffer))
posSamples.push_back(buffer);
}
posSampleNum = posSamples.size();
fInPos.close();
while(fInNeg) //读取负样本
{
if (getline(fInNeg, buffer))
negSamples.push_back(buffer);
}
negSampleNum = negSamples.size();
fInNeg.close();
Mat sampleFeatureMat; //样本特征向量矩阵
Mat sampleLabelMat; //样本标签
//1、处理正样本
for(int i = 0 ; i < posSampleNum; i++)
{
Mat inputImg = imread(posSamples[i]);
GaussianBlur(inputImg, inputImg, Size(3,3), 0);
cout<<"processing "<1) cvtColor(trainImg, trainImg, CV_BGR2GRAY);
Mat dstImg(trainImg.rows, trainImg.cols, CV_8UC1, Scalar(0));
vector descriptor;
//等价模式LBP特征计算
Mat sigleFeatureMat;
calExtendLBPFeature(trainImg, Size(3, 3), sigleFeatureMat);
descriptorDim = sigleFeatureMat.cols;
if(i == 0)//首次特殊处理根据检测到的维数确定特征矩阵的尺寸
{
sampleFeatureMat = Mat::zeros(posSampleNum + negSampleNum, descriptorDim, CV_32FC1);
sampleLabelMat = Mat::zeros(posSampleNum + negSampleNum, 1, CV_32SC1);
}
sigleFeatureMat.row(0).copyTo(sampleFeatureMat.row(i)); //把sigleFeatureMat的第一列赋给sampleFeatureMat的第i列
sampleLabelMat.at(i, 0) = 1;
}
cout<<"extract posSampleFeature done"<1) cvtColor(trainImg, trainImg, CV_BGR2GRAY);
Mat dstImg(trainImg.rows, trainImg.cols, CV_8UC1, Scalar(0));
Mat sigleFeatureMat; //单行矩阵
//12×30负样本,等价模式LBP特征计算(增加3维)
calExtendLBPFeature(trainImg, Size(3, 3), sigleFeatureMat);
sigleFeatureMat.row(0).copyTo(sampleFeatureMat.row(posSampleNum + i)); //把sigleFeatureMat赋给sampleFeatureMat的第i列
sampleLabelMat.at(posSampleNum + i, 0) = 0; //标签保存样本被划分的类型
}
cout<<"extract negSampleFeature done"<(i, j)<<" ";
}
foutFeature<<"\n";
}
foutFeature.close();
cout<<"output posSample and negSample Feature done"<(i,j) = pSupportVectorData[j];
}
}
double *pAlphaData = SVM.get_alpha_data();
for (int i = 0; i < supportVectorNum; i++) //复制函数中的alpha 记住决策公式Y= wx+b
{
alphaMat.at(0, i) = pAlphaData[i];
}
resultMat = -1 * alphaMat * supportVectorMat; //alphaMat就是权重向量
cout<<"描述子维数 "< myDetector;
for (int i = 0 ;i < descriptorDim; i++)
{
myDetector.push_back(resultMat.at(0, i));
}
rho = SVM.get_rho_data();
myDetector.push_back(rho);
cout<<"检测子维数 "<1) cvtColor(testImg, testImg, CV_BGR2GRAY);
imshow("原图", testImg);
GaussianBlur(testImg, testImg, Size(3,3), 0); //高斯滤波
//imshow("高斯滤波后", testImg);
//equalizeHist(testImg, testImg); //直方图均衡
//imshow("直方图均衡后", testImg);
Mat copyImg = testImg.clone();
if(copyImg.channels()<3) cvtColor(copyImg, copyImg, CV_GRAY2BGR);
Mat testImgNorm;
resize(testImg, testImgNorm, Size(300, 720));
vector found, foundFiltered;
//设计滑窗遍历测试样本:计算每个滑窗内的LBP特征,给SVM预测,把输出为1的框画出来。
int m, n;
Mat slideWinImg(30, 12, CV_8UC1, Scalar(0));
Mat dstImg(30, 12, CV_8UC1, Scalar(0)); //LBP图,可不输出
Mat lideWinImgFeatureMat;
Mat sigleFeatureMat = Mat::zeros(1, descriptorDim, CV_32FC1);
vector descriptor;
for(n=0; n<=testImgNorm.rows-30; n+=30)
{
for(m=0; m<=testImgNorm.cols-12; m+=4)
{
slideWinImg = testImgNorm(Rect(m, n, 12, 30));
if (slideWinImg.channels()>1) cvtColor(slideWinImg, slideWinImg, CV_BGR2GRAY);
Mat extendLBPFeature;
calExtendLBPFeature(slideWinImg, Size(3, 3), extendLBPFeature);
//使用训练的SVM模型预测测试样本
int predictResult = SVM.predict(extendLBPFeature);
//cout<<"SVM训练模型预测结果:"<