最近一直在关注视频中人体识别、训练及跟踪这块的实现,想在opencv上实现基本操作,因此也看了很多大牛的博客和相关的论文,现在对出现的一些问题进行简单的记录。
首先对图片中的人体进行识别,目前主要的是HOG+级联SVM分类器进行,但是目前的代码误差还是很大,漏检错检还是频出,正在进行调整:
(1)一开始在网上找了一些代码,代码是使用之前说的HOG+级联SVM分类器,但是在编译的过程中会发现,它使用的是代码文件中已经生成好的.xml文件,所以就想着能不能自己训练一个分类器,并将其保存在自己的.xml文件中,所以进行了一些实验。
1、先上原始代码:
#include <iostream>
#include <fstream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/ml/ml.hpp>
using namespace std;
using namespace cv;
#define PosSamNO 2400
#define NegSamNO 12000
#define TRAIN false
#define CENTRAL_CROP true
#define HardExampleNO 4435
class MySVM :
public CvSVM
{
public:
double * get_alpha_vector()
{
return this->decision_func->alpha;
}
float get_rho()
{
return this->decision_func->rho;
}
};
int main()
{
HOGDescriptor hog(Size(
64,
128),Size(
16,
16),Size(
8,
8),Size(
8,
8),
9);
int DescriptorDim;
MySVM svm;
if(TRAIN)
{
string ImgName;
ifstream finPos(
"INRIAPerson96X160PosList.txt");
ifstream finNeg(
"NoPersonFromINRIAList.txt");
Mat sampleFeatureMat;
Mat sampleLabelMat;
for(
int num=
0; num<PosSamNO && getline(finPos,ImgName); num++)
{
cout<<
"处理:"<<ImgName<<endl;
ImgName =
"D:\\DataSet\\INRIAPerson\\INRIAPerson\\96X160H96\\Train\\pos\\" + ImgName;
Mat src = imread(ImgName);
if(CENTRAL_CROP)
src = src(Rect(
16,
16,
64,
128));
vector<float> descriptors;
hog.compute(src,descriptors,Size(
8,
8));
if(
0 == num )
{
DescriptorDim = descriptors.size();
sampleFeatureMat = Mat::zeros(PosSamNO+NegSamNO+HardExampleNO, DescriptorDim, CV_32FC1);
sampleLabelMat = Mat::zeros(PosSamNO+NegSamNO+HardExampleNO,
1, CV_32FC1);
}
for(
int i=
0; i<DescriptorDim; i++)
sampleFeatureMat.at<
float>(num,i) = descriptors[i];
sampleLabelMat.at<
float>(num,
0) =
1;
}
for(
int num=
0; num<NegSamNO && getline(finNeg,ImgName); num++)
{
cout<<
"处理:"<<ImgName<<endl;
ImgName =
"D:\\DataSet\\NoPersonFromINRIA\\" + ImgName;
Mat src = imread(ImgName);
vector<float> descriptors;
hog.compute(src,descriptors,Size(
8,
8));
for(
int i=
0; i<DescriptorDim; i++)
sampleFeatureMat.at<
float>(num+PosSamNO,i) = descriptors[i];
sampleLabelMat.at<
float>(num+PosSamNO,
0) = -
1;
}
if(HardExampleNO >
0)
{
ifstream finHardExample(
"HardExample_2400PosINRIA_12000NegList.txt");
for(
int num=
0; num<HardExampleNO && getline(finHardExample,ImgName); num++)
{
cout<<
"处理:"<<ImgName<<endl;
ImgName =
"D:\\DataSet\\HardExample_2400PosINRIA_12000Neg\\" + ImgName;
Mat src = imread(ImgName);
vector<float> descriptors;
hog.compute(src,descriptors,Size(
8,
8));
for(
int i=
0; i<DescriptorDim; i++)
sampleFeatureMat.at<
float>(num+PosSamNO+NegSamNO,i) = descriptors[i];
sampleLabelMat.at<
float>(num+PosSamNO+NegSamNO,
0) = -
1;
}
}
CvTermCriteria criteria = cvTermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS,
1000, FLT_EPSILON);
CvSVMParams param(CvSVM::C_SVC, CvSVM::LINEAR,
0,
1,
0,
0.01,
0,
0,
0, criteria);
cout<<
"开始训练SVM分类器"<<endl;
svm.train(sampleFeatureMat, sampleLabelMat, Mat(), Mat(), param);
cout<<
"训练完成"<<endl;
svm.save(
"SVM_HOG.xml");
}
else
{
svm.load(
"SVM_HOG_2400PosINRIA_12000Neg_HardExample(误报少了漏检多了).xml");
}
DescriptorDim = svm.get_var_count();
int supportVectorNum = svm.get_support_vector_count();
cout<<
"支持向量个数:"<<supportVectorNum<<endl;
Mat alphaMat = Mat::zeros(
1, supportVectorNum, CV_32FC1);
Mat supportVectorMat = Mat::zeros(supportVectorNum, DescriptorDim, CV_32FC1);
Mat resultMat = Mat::zeros(
1, DescriptorDim, CV_32FC1);
for(
int i=
0; i<supportVectorNum; i++)
{
const float * pSVData = svm.get_support_vector(i);
for(
int j=
0; j<DescriptorDim; j++)
{
supportVectorMat.at<
float>(i,j) = pSVData[j];
}
}
double * pAlphaData = svm.get_alpha_vector();
for(
int i=
0; i<supportVectorNum; i++)
{
alphaMat.at<
float>(
0,i) = pAlphaData[i];
}
resultMat = -
1 * alphaMat * supportVectorMat;
vector<float> myDetector;
for(
int i=
0; i<DescriptorDim; i++)
{
myDetector.push_back(resultMat.at<
float>(
0,i));
}
myDetector.push_back(svm.get_rho());
cout<<
"检测子维数:"<<myDetector.size()<<endl;
HOGDescriptor myHOG;
myHOG.setSVMDetector(myDetector);
ofstream fout(
"HOGDetectorForOpenCV.txt");
for(
int i=
0; i<myDetector.size(); i++)
{
fout<<myDetector[i]<<endl;
}
Mat src = imread(
"1.png");
vector<Rect> found, found_filtered;
cout<<
"进行多尺度HOG人体检测"<<endl;
myHOG.detectMultiScale(src, found,
0, Size(
8,
8), Size(
32,
32),
1.05,
2);
cout<<
"找到的矩形框个数:"<<found.size()<<endl;
for(
int i=
0; i < found.size(); i++)
{
Rect r = found[i];
int j=
0;
for(; j < found.size(); j++)
if(j != i && (r & found[j]) == r)
break;
if( j == found.size())
found_filtered.push_back(r);
}
for(
int i=
0; i<found_filtered.size(); i++)
{
Rect r = found_filtered[i];
r.x += cvRound(r.width*
0.1);
r.width = cvRound(r.width*
0.8);
r.y += cvRound(r.height*
0.07);
r.height = cvRound(r.height*
0.8);
rectangle(src, r.tl(), r.br(), Scalar(
0,
255,
0),
3);
}
imwrite(
"ImgProcessed.jpg",src);
namedWindow(
"src",
0);
imshow(
"src",src);
waitKey();
system(
"pause");
}
2、分析: 在代码中大家可以看到,#define TRAIN false //是否进行训练,true表示重新训练,false表示读取xml文件中的SVM模型 ,TRAIN默认是false的,为false是代码直接进行 else //若TRAIN为false,从XML文件读取训练好的分类器 { svm.load("SVM_HOG_2400PosINRIA_12000Neg_HardExample(误报少了漏检多了).xml");//从XML文件读取训练好的SVM模型 } 为了能进行训练,将TRAIN改成true,之后发现有三个文件地址:正样本文件地址、负样本文件地址和Hard Example样例文件地址,然而网上下载的文件中并没有样例 ,在网上搜索后找到了行人检测DataSets中的INRIA数据库,地址:http://pascal.inrialpes.fr/data/human/。 下载了样本解压后,点击进去“96X160H96”这个文件夹里放的是正样本,“Train”文件夹下的“neg”放的是负样本,但是负样本需要处理一下才能成为训练分类器的负样本,处理代码如下:别忘了配置你的opencv环境,千万千万注意要更改图片文件的路径,换成你自己的。
#include <iostream>
#include <iostream>#include <fstream>
#include <stdlib.h>
#include <time.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/ml/ml.hpp>
using namespace std;
using namespace cv;
int CropImageCount =
0;
int main()
{
Mat src;
string ImgName;
char saveName[
256];
ifstream fin(
"INRIANegativeImageList.txt");
while(getline(fin,ImgName))
{
cout<<
"处理:"<<ImgName<<endl;
ImgName =
"E:\\运动目标检测\\INRIAPerson\\Train\\neg\\" + ImgName;
src = imread(ImgName,
1);
if(src.cols >=
64 && src.rows >=
128)
{
srand(time(NULL));
for(
int i=
0; i<
10; i++)
{
int x = ( rand() % (src.cols-
64) );
int y = ( rand() % (src.rows-
128) );
Mat imgROI = src(Rect(x,y,
64,
128));
sprintf(saveName,
"E:\\运动目标检测\\INRIAPerson\\negphoto\\noperson%06d.jpg",++CropImageCount);
imwrite(saveName, imgROI);
}
}
}
system(
"pause");
}
有了样本就可以训练分类器并进行识别测试了,代码如下:同样要配置自己的opencv环境,更改图片的路径。
后续在加: