Simple Object Classification with HOG and SVM

Do object recognition with HOG feature and SVM classifier.

[Usage]

Training Algorithm Flow

  1. manually select representive _sample_s to generate TrainSet and TestSet

  2. Extract HOG feature for each samples either in TrainSet or in TestSet

  3. Attatch label to each sample

  4. Set parameters and train Model (you can determine save Model or not. If saved, Model will be saved into a XML file.)

  5. Predict samples in TestSet with SVM Model

Training Example Code with EndToEnd

1
2
3
4
5
6
7
8
9
10
....
....

string dirPath = "..."
HOG_SVM hog_svm;
hog_svm.SetSvmParameter(....);
float result = hog_svm.EndToEnd(dirPath);

....
....

Predict with Model in in practice:

1
2
3
4
5
6
7
8
9
10
....
....

Mat src = .... ;
string modelPath = "..."
HOG_SVM hog_svm(modelPath);
float result = hog_svm.predict(src);

....
....

[Example]

White rectangle bounding boxes denote the topk (which select topk Main Region with Depth Segmentation Algorithm in the previous blog) color candidate regions to calculate HOG features, which will be classified by an SVM classifier. The recognition results of the right image show that cuboid regions filtered by the classifier are marked red, while the rest white regions are removed from candidate regions after classifying.

Color Depth
Color Depth
Regions Classification
Regions Classification

[Head File]

HOG-SVM.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/*******************************************************************************
Utilize HOG as feature, and SVM as machine learning model.
*******************************************************************************/


#include "FileOperation.hpp"
#include <string>
#include <vector>
#include <opencv2/opencv.hpp>

using cv::Mat;
using cv::Size;
using cv::Ptr;
using cv::ml::SVM;
using cv::Ptr;
using cv::Algorithm;
using cv::HOGDescriptor;

class GroundTruth{
public:
int label;
string imgname;
GroundTruth(int c, string path){
label = c;
imgname = path;
};
};

class HOG_SVM: public FileOperation
{

private:
Ptr<SVM> svm;

public:
/** @brief default constructor */
HOG_SVM();
/**
* @brief HOG_SVM: load xml file as model
* @param model_path xml loacation
*/

HOG_SVM(string model_path);
/**
* @brief LoadModel: load xml file as model
* @param model_path xml loacation
* @return success/ fail
*/

bool LoadModel(string model_path);
/**
* @brief ExtractFeature: extract HOG feature
* @param Img input source
* @param mrs scaled size
* @return feature vector
*/

Mat ExtractFeature(Mat Img, Size mrs);
/**
* @brief GetDataSet: get dataset
* @param data_path image location
* @return feature matrix
*/

Mat GetDataSet(vector<string> data_path);
Mat GetDataSet(vector<string> data_path, vector<GroundTruth>& gt, int c);
/**
* @brief SetSvmParameter: set training Parameter
* @param sv_num max support vectors number
* @param c_r_type classification/ regression
* @param kernel linear / gussian / ploy ...
* @param gamma if gussian need to set
* @return 1
*/

int SetSvmParameter(int sv_num, int c_r_type, int kernel, double gamma);
/**
* @brief Training: training with svm
* @param trainSet train data mat
* @param label label mat
* @param save save model or not
* @param dir save path
* @return 1
*/

int Training(Mat& trainSet, Mat& label, bool save, string dir);
/**
* @brief Testing: if need to test, you can use this function
* @param testSet test matirx
* @param gt groundtruth, the othe impletementation is to show which are predicted error
* @return 1
*/

int Testing(Mat& testSet, float gt);
int Testing(Mat& testSet, vector<GroundTruth> gt);
/**
* @brief EndToEnd: the whole process, training and testing
* @param data_path datapath
* @return error rate in the test data
*/

float EndToEnd(string data_path);
/**
* @brief Predict: predict label in practical application
* @param image image / region need to classify
* @return label
*/

float Predict(Mat& image);

};

FileOperation.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/*******************************************************************************
Some Files and Os Operation, such as open, read, write, etc
*******************************************************************************/


#include <vector>
#include <string>
using std::vector;
using std::string;

//Due to compile's Named Return Value Optimization, directly return vector
class FileOperation{
public:
/**
* @brief getFilename: get all files / subdirs Under dirpath
* @param dirPath
* @return subFiles
*/

virtual vector<string> getFilename(const string& dirPath) ;
/**
* @brief getFilepath: get absolute path of files / subdirs Under dirpath
* @param dirPath
* @return absolutePaths
*/

virtual vector<string> getFilepath(const string& dirPath) ;
/**
* @brief findFileName: filter out filename from absolute path
* @param path
* @return filename
*/

virtual string findFileName(const string& path) ;
};

Marco.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/*******************************************************************************
Some efficient macros
*******************************************************************************/


//Mkdir if _DIR_ exist or not
#define DIR_NOEXIST_AND_CREATE(_DIR_) \
system(("mkdir " + _DIR_).c_str())


//Calcuate time consuming
#define elapsed_time ((clock()- start)*1.0/CLOCKS_PER_SEC)

//Output Message with cout
#define MESSAGE_COUT(_TYPE_,_INFO_) \
std::cout << "[" << _TYPE_ << "]\t" << _INFO_ << std::endl;


//Exchange x and y of Point
#define POINT_CVT(P) \
P.x ^= P.y; \
P.y ^= P.x; \
P.x ^= P.y;


//Get Mat element in Vector
#define DIM1TODIM2(I,J,V,ROW) V[I*ROW+J]

//Boundary detection
#define BETWEEN(VAL,LOW,UP) ((VAL)>(LOW)&&(VAL)<(UP))

//The point at the center of region
#define MiddlePoint(RECT) \
Point(RECT.x + (RECT.width >> 1), RECT.y + (RECT.height >> 1))


#define MAX_MIN(MAX, MIN, P, POS) \
if (MAX.POS < P.POS) MAX.POS = P.POS; \
else if (MIN.POS > P.POS) MIN.POS = P.POS;


#define Region_MAX_MIN(MAX, MIN, P) \
MAX_MIN(MAX, MIN, P, x) \
MAX_MIN(MAX, MIN, P, y)

Implementation

HOG-SVM.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
#include "HOG-SVM.hpp"
#include "Macro.hpp"

using namespace cv;
using namespace ml;

HOG_SVM::HOG_SVM()
{
svm = SVM::create();
}

HOG_SVM::HOG_SVM(std::string model_path)
{
svm = Algorithm::load<SVM>(model_path);
}

bool HOG_SVM::LoadModel(std::string model_path)
{
bool flag = true;
try{
svm = Algorithm::load<SVM>(model_path);
}
catch (std::exception e){
MESSAGE_COUT("ERROR", e.what())
flag = false;
}
return flag;
}

Mat HOG_SVM::ExtractFeature(Mat Img, Size mrs)
{
/**
* @brief HOG_SVM::ExtractFeature
The story behind 1764
For example
window size is 64x64, block size is 16x16 and block setp is 8x8£¬cell size is 8x8,
the block number window contained is (£¨64-16£©/8+1)*((64-16)/8+1) = 7*7 = 49,
the cell number each block contained is (16/8)*(16/8) = 4
every cell can project 9 bin, and each bin related to 9 vector
so feature_dim = B x C x N, and caulated result is 1764
(B is each window's blocks number, C is every block's cell number, n is bin number)
*/

resize(Img, Img, mrs);
HOGDescriptor *hog = new HOGDescriptor(cvSize(64, 64), cvSize(16, 16), cvSize(8, 8), cvSize(8, 8), 9);
std::vector<float> descriptors;
hog->compute(Img, descriptors, Size(1, 1), Size(0, 0));
return Mat(descriptors).t();
}

Mat HOG_SVM::GetDataSet(std::vector<std::string> data_path)
{
int nImgNum = data_path.size();
int success = 0;
Mat data_mat, src;
for (int i = 0; i < nImgNum; i++){
src = imread(data_path[i]);
if (src.cols && src.rows){
MESSAGE_COUT("PROCESS", FileOperation::findFileName(data_path[i]) << "\t" << success++)
Mat post = ExtractFeature(src, Size(64, 64));
data_mat.push_back(post);
}
}
return data_mat;
}

Mat HOG_SVM::GetDataSet(std::vector<std::string> data_path, std::vector<GroundTruth>& gt, int c)
{
int nImgNum = data_path.size();
int success = 0;
Mat data_mat; //feature matrix
Mat src;
std::string imgname;
for (int i = 0; i < nImgNum; i++){
src = imread(data_path[i]);
if (src.cols && src.rows){
imgname = FileOperation::findFileName(data_path[i]);
MESSAGE_COUT("PROCESS", imgname << "\t" << success++)
Mat post = ExtractFeature(src, Size(64, 64));
data_mat.push_back(post);
gt.push_back(GroundTruth(c, imgname));
}
}
return data_mat;
}

int HOG_SVM::SetSvmParameter(int sv_num, int c_r_type, int kernel, double gamma)
{
TermCriteria criteria = TermCriteria(CV_TERMCRIT_EPS, sv_num, FLT_EPSILON); //max support vectocr 200
svm->setType(c_r_type);
svm->setKernel(kernel);
if (kernel == SVM::RBF) svm->setGamma(gamma);
svm->setTermCriteria(criteria);
return 1;
}

int HOG_SVM::Training(Mat& trainSet, Mat& label, bool save,std::string dir)
{
SetSvmParameter(200, SVM::C_SVC, SVM::LINEAR, 0);
Ptr<TrainData> traindata = ml::TrainData::create(trainSet, ROW_SAMPLE, label);
svm->train(traindata);
if (save){
svm->save(dir+"HOG-SVM-MODEL.xml");
}
return 1;
}

int HOG_SVM::Testing(Mat& testSet, float gt)
{
int error = 0;
int postnum = testSet.rows;
Mat res = Mat::zeros(postnum, 1, CV_32FC1);
svm->predict(testSet, res);
for (int i = 0; i < postnum; i++)
if (res.at<float>(i, 0) != gt)
error++;
std::cout << error << "/" << postnum << std::endl;
return error;
}

int HOG_SVM::Testing(Mat& testSet, std::vector<GroundTruth> gt)
{
int error = 0;
int postnum = testSet.rows;
Mat res = Mat::zeros(postnum, 1, CV_32FC1);
svm->predict(testSet, res);
for (int i = 0; i < postnum; i++)
if (res.at<float>(i, 0) != gt[i].label){
MESSAGE_COUT("ERROR", gt[i].imgname << "\t" << gt[i].label)
error++;
}
MESSAGE_COUT("RESULT", error << "/" << postnum)
return error;
}

float HOG_SVM::Predict(Mat& image)
{
if (!image.rows) return -1;
Mat gray;
cvtColor(image, gray, CV_BGR2GRAY);
Mat post = ExtractFeature(gray, Size(64, 64));
gray.release();
return svm->predict(post);
}

float HOG_SVM::EndToEnd(std::string data_path)
{
//Trainset path
std::string trainPath = data_path + "train\\";
std::vector<std::string> trainPathPositive = this->getFilepath(trainPath + "1\\");
std::vector<std::string> trainPathNegative = this->getFilepath(trainPath + "-1\\");
//Testset path
std::string testPath = data_path + "test\\";
std::vector<std::string> testPathPositive = FileOperation::getCurrentDir(testPath + "1\\");
std::vector<std::string> testPathNegative = FileOperation::getCurrentDir(testPath + "-1\\");
//Get trainset
Mat trainSet, label;
Mat trainSetP = GetDataSet(trainPathPositive);
Mat labelP = Mat::ones(trainSetP.rows, 1, CV_32SC1);
Mat trainSetN = GetDataSet(trainPathNegative);
Mat labelN = Mat::ones(trainSetN.rows, 1, CV_32SC1)*(-1);
trainSet.push_back(trainSetP);
trainSet.push_back(trainSetN);
label.push_back(labelP);
label.push_back(labelN);
//Training model
Training(trainSet, label, true, data_path);
//Testing mode
std::vector<GroundTruth> gtP, gtN;
Mat testSetP = GetDataSet(testPathPositive, gtP, 1);
Mat testSetN = GetDataSet(testPathNegative, gtN, -1);
int error = Testing(testSetP, gtP) + Testing(testSetN, gtN);
return 1.0f*error / (testSetP.rows + testSetN.rows);
}

FileOperation.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include "FileOperation.hpp"
#include <iostream>
#include <cstdlib>
#include <cstdio>

#ifdef _WIN32
#include <io.h>
#endif

#ifdef __linux
vector<string> FileOperation::getFilename(const string& dirPath)
{
char buff[1024];
vector<string> fileNames;
string cmd = "ls " + dirPath;
FILE* fstream = popen (cmd.c_str(), "r");
while (fgets(buff, sizeof(buff), fstream)) {
fileNames.push_back(string(buff));
fileNames.back().back() = 0;
}
return fileNames;
}
#endif

#ifdef _WIN32
vector<string> FileOperation::getFilepath(const string& dirPath)
{
long hFile = 0;
struct _finddata_t fileinfo;
std::string p;
std::vector<std::string> files;
if ((hFile = _findfirst(p.assign(dirPath).append("\\*").c_str(), &fileinfo)) != -1)
{
do
{
if (!(fileinfo.attrib & _A_SUBDIR))
files.push_back(p.assign(dirPath).append("\\").append(fileinfo.name));
} while (_findnext(hFile, &fileinfo) == 0);
_findclose(hFile);
}
return files;
}
#endif

vector<string> FileOperation::getFilepath(const string& dirPath)
{
char buff[1024];
vector<string> filePaths;
string cmd = "ls " + dirPath;
FILE* fstream = popen (cmd.c_str(), "r");
while (fgets(buff, sizeof(buff), fstream)) {
string filename(buff);
filename.back() = 0;
filePaths.push_back(dirPath + filename);
}
return filePaths;
}

string FileOperation::findFileName(const string& path)
{
#if 1
for (int i = path.size() - 1; i >= 0; i--)
if (path[i] == '/')
return path.substr(i + 1);
return "";
#else
int i, last;
for (i = 0; path[i]; i++)
if (path[i] == '/')
last = i;
return path.substr(last + 1);
#endif
}
Contents
  1. 1. Do object recognition with HOG feature and SVM classifier.
    1. 1.1. [Usage]
      1. 1.1.1. Training Algorithm Flow
      2. 1.1.2. Predict with Model in in practice:
    2. 1.2. [Example]
    3. 1.3. [Head File]
      1. 1.3.1. HOG-SVM.hpp
      2. 1.3.2. FileOperation.hpp
      3. 1.3.3. Marco.hpp
    4. 1.4. Implementation
      1. 1.4.1. HOG-SVM.cpp
      2. 1.4.2. FileOperation.cpp