基于HOG特征的Adaboost行人检测
原地址:http://blog.csdn.net/van_ruin/article/details/9166591
.方向梯度直方图(Histogramof Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子。它通过计算和统计图像局部区域的梯度方向直方图来构成特征。基本知识可以参考博客:http://blog.csdn.net/zouxy09/article/details/7929348 .Adaboost的基础知识可以参考书籍:统计学习方法,第八章-提升方法adaboost。 这里利用HOG来训练Adaboost行人检测。在Haar-Adaboost算法中,弱分类器仅对一维分类。但是在Hog特征中,特征是每个block的串联。如果仅对一维分类(一个cell的其中一个方向的权值),就不能有效利用block的归一化效果。所以我们使用logistic弱分类器对每个block进行分类(实验中,每个block包含4个cell,每个cell有9个bin,即36维特征)。 本实验需要注意的地方:
. adaboost误差率需要计算权重
. logistic回归需要使用带权重的logistic分类器
. logistic分类可能与数据分布相反。需要计算两次。(相反的情况下,拟合没有意义,需要将数据反转(->,->))
发现总结与问题
. 公理1. 对于任何数据的二值分类,能够得到大于等于0.5的线性分类器。
. 推论. 对于任何带权重数据的二值分类,能够得到大于等于0.5的线性分类器。
. 推论? 对于任何带权重数据的n值分类,能够得到大于等于1/n的线性分类器。
. 对于与logistic函数分布相反的数据,应该如何处理?(本实验的处理方式如前面所述)。
实验结果后的猜想
ß猜想1:Adaboost弱分类器所选取的特征仍然要保持一定的颗粒度。像素级的特征是无效的。
实验结果与分析
训练集: /;测试集: /(200个弱分类器)
测试数据较少,但是训练集的高正确率至少证明其能够由弱分类器(错误率普遍在0.25左右)提高样本数据集的精度。
17张图片中,有部分图片较模糊,行人影像较小,可能导致难以分辨。 下面给出代码,希望各位能够指正错误。说明:本代码全部由自己编写,所用函数未调用OpenCV实用库函数,及机器学习库函数(基本数据除外)。 /***********************************************************/
/** Copyright by Weidi Xu, S.C.U.T in Guangzhou, Guangdong**/
/***********************************************************/ #include <opencv2\opencv.hpp>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <ctime> using std::clock_t;
using std::clock;
using namespace cv; //const parameters for image
const int NUM_NEGIMAGE = ;
const int NUM_POSIMAGE = ;
const int NUM_IMAGE = ;
const int NUM_TESTIMAGE = ;
const int MAX_DIMENSION = ;
const int IMAGE_ROWS = ;
const int IMAGE_COLS = ;
const int CELLSIZE = ;
const int BLOCKSIZE = ;
const int MOVELENGTH = ;
const int BINSIZE = ;
const double PI = *acos(0.0);
const double eps = 1e-; //mediate parameter
const int NUM_BLOCK_ROWS = (IMAGE_ROWS-BLOCKSIZE)/MOVELENGTH+;
const int NUM_BLOCK_COLS = (IMAGE_COLS-BLOCKSIZE)/MOVELENGTH+;
const int NUM_BLOCK_FEATURES = (BLOCKSIZE/CELLSIZE)*(BLOCKSIZE/CELLSIZE)*BINSIZE+;//zero for theta[0] //data from image
//since the features in the adaboost should contain a single block, it's better to define the feature of 3-dimension;
double features[NUM_IMAGE][NUM_BLOCK_ROWS][NUM_BLOCK_COLS][NUM_BLOCK_FEATURES];
double type[NUM_IMAGE]; //1 - pos, 0 - neg
double y[NUM_IMAGE]; //1 - pos, -1 - neg //number of weak classifier(changing in experiment)
const int NUM_WEAKCLASSIFIER = ; //data for adaboost
double weight[NUM_IMAGE]; //logistic function(dimension is given by NUM_BLOCK_FEATURES(37 in this setting))
double logistic(double theta[], double x[])
{
double ans = ;
for(int i = ; i < NUM_BLOCK_FEATURES; i++)
{
ans += theta[i]*x[i];
}
return /(+std::exp(-ans));
} struct WeakClassifier
{
double _theta[NUM_BLOCK_FEATURES]; //threshold classifier
int _index_row; //classify by the features in block[_block_row][_block_y]
int _index_col;
int _isreverse; //1 for (> := pos, < := neg); 0 for (< := pos, >:= neg)
double _alpha;
double _error;
void clear()
{
memset(_theta, 0.0, NUM_BLOCK_FEATURES*sizeof(double));
_alpha = 0.0;
_error = ;
_index_row = -;
_index_col = -;
_isreverse = true;
} //return 1 or -1
int cal(double x[NUM_BLOCK_ROWS][NUM_BLOCK_COLS][NUM_BLOCK_FEATURES])
{
int ans = logistic(_theta, x[_index_row][_index_col]);
if(ans > 0.5)
{
if(_isreverse)
return -;
else
return ;
}
else
{
if(_isreverse)
return ;
else
return -;
}
} void print()
{
//theta
for(int i = ; i < NUM_BLOCK_FEATURES; i++)
printf("%lf ", _theta[i]);
printf("\n"); //int _index_row;
printf("%d ",_index_row); //int _index_col;
printf("%d ",_index_col); //int _isreverse; //1 for (> := pos, < := neg); 0 for (< := pos, >:= neg)
printf("%d ",_isreverse); //double _alpha;
printf("%lf ",_alpha); //double _error;
printf("%lf \n",_error);
}
}weakClassifier[NUM_WEAKCLASSIFIER]; //Util Function
double arc2angle(double arc)
{
return arc/PI*180.0;
} double angle2arc(double angle)
{
return angle/180.0*PI;
} void posfilename(int i, char* filename)
{
sprintf(filename, "pos/pos (%d).png", i);
return;
} void negfilename(int i, char* filename)
{
sprintf(filename, "neg/neg (%d).png", i);
return;
} void testfilename(int i, char* filename)
{
sprintf(filename, "test_pos/test (%d).png", i);
return ;
} //I(x,y) = sqrt(I(x,y))
void normalizeImage(Mat& inputImage)
{
// accept only char type matrices
CV_Assert(inputImage.depth() != sizeof(uchar));
int channels = inputImage.channels();
int nRows = inputImage.rows ;
int nCols = inputImage.cols* channels;
if (inputImage.isContinuous())
{
nCols *= nRows;
nRows = ;
}
int i,j;
uchar* p;
for( i = ; i < nRows; ++i)
{
p = inputImage.ptr<uchar>(i);
for ( j = ; j < nCols; ++j)
{
p[j] = int(sqrt(p[j]*1.0));
}
}
return;
} //I(x,y) 第一维的梯度为xGradient
void calGredient(const Mat& inputImage, double xGradient[IMAGE_ROWS][IMAGE_COLS], double yGradient[IMAGE_ROWS][IMAGE_COLS])
{
uchar* dataptr = inputImage.data;
int nrows = inputImage.rows;
int ncols = inputImage.cols; //cal xgradient
for(int i = ; i < nrows - ; i++)
{
for(int j = ; j < ncols; j++)
{
xGradient[i][j] = inputImage.at<uchar>(i+,j) - inputImage.at<uchar>(i-,j);
}
} //cal margin
for(int i = ; i < ncols; i++)
{
xGradient[][i] = (inputImage.at<uchar>(,i) - inputImage.at<uchar>(,i))*;
xGradient[nrows-][i] = (inputImage.at<uchar>(nrows-,i) - inputImage.at<uchar>(nrows-,i))*;
} //cal ygradient
for(int i = ; i < nrows ; i++)
{
for(int j = ; j < ncols - ; j++)
{
yGradient[i][j] = inputImage.at<uchar>(i,j+) - inputImage.at<uchar>(i,j-);
}
} //cal margin
for(int i = ; i < nrows; i++)
{
xGradient[i][] = (inputImage.at<uchar>(i,) - inputImage.at<uchar>(i,))*;
xGradient[i][ncols-] = (inputImage.at<uchar>(i,ncols-) - inputImage.at<uchar>(i,ncols-))*;
}
} //cal the HogFeatures by block
void calHogFeatures(Mat& inputImage, double outputFeature[NUM_BLOCK_ROWS][NUM_BLOCK_COLS][NUM_BLOCK_FEATURES])
{
int nrows = inputImage.rows;
int ncols = inputImage.cols;
int type = inputImage.type(); if(nrows != IMAGE_ROWS || ncols != IMAGE_COLS)
abort(); //cal x,yGradient
double xGradient[IMAGE_ROWS][IMAGE_COLS];
double yGradient[IMAGE_ROWS][IMAGE_COLS];
calGredient(inputImage, xGradient, yGradient); //computation median
double gradient[IMAGE_ROWS][IMAGE_COLS];
double direction[IMAGE_ROWS][IMAGE_COLS]; for(int i = ; i < nrows; i++)
{
for(int j = ; j < ncols; j++)
{
double gx = xGradient[i][j];
double gy = yGradient[i][j];
gradient[i][j] = sqrt(gx*gx + gy*gy);
direction[i][j] = arc2angle(atan2(gy, gx));
}
} //compute cellinfo 8*8
double cellinfo[IMAGE_ROWS/CELLSIZE][IMAGE_COLS/CELLSIZE][BINSIZE];
memset(cellinfo, , sizeof(cellinfo)); for(int i = ; i < IMAGE_ROWS/CELLSIZE; i++)
{
for(int j = ; j < IMAGE_COLS/CELLSIZE; j++)
{
double* cell = cellinfo[i][j]; //cal single cellinfo of 8*8
for(int ci = ; ci < CELLSIZE; ci++)
{
for(int cj = ; cj < CELLSIZE; cj++)
{
//find org pix;
int px = i*CELLSIZE + ci;
int py = j*CELLSIZE + cj; int binindex = int((direction[px][py]+180.0)/(360.0/BINSIZE));
//handle bound
if(fabs(direction[px][py]-) < eps)
{
binindex = BINSIZE-;
}
if(fabs(direction[px][py]+) < eps)
{
binindex = ;
}
if(binindex < || binindex >= BINSIZE)
{
printf("Wrong binindex: %d %lf %lf %lf", binindex, xGradient[px][py], yGradient[px][py], direction[px][py]);
abort();
} cell[binindex] += gradient[px][py];
}
}
}
} /*double blockinfo[(IMAGE_ROWS-BLOCKSIZE)/MOVELENGTH+1][(IMAGE_COLS-BLOCKSIZE)/MOVELENGTH+1][(BLOCKSIZE/CELLSIZE)*(BLOCKSIZE/CELLSIZE)*BINSIZE];*/ if(MOVELENGTH%CELLSIZE != )
{
printf("MOVELENGTH%CELLSIZE != 0");
abort();
} //cal blockinfo
for(int i = ; i < (IMAGE_ROWS-BLOCKSIZE)/MOVELENGTH + ; i++)
{
for(int j = ; j < (IMAGE_COLS-BLOCKSIZE)/MOVELENGTH + ; j++)
{
int bfindex = ; outputFeature[i][j][bfindex++] = ; //cal the position of this block
for(int c1 = ; c1 < BLOCKSIZE/CELLSIZE; c1++)
{
for(int c2 = ; c2 < BLOCKSIZE/CELLSIZE; c2++)
{
//cal the index of cell
int cx = i*MOVELENGTH/CELLSIZE+c1;
int cy = j*MOVELENGTH/CELLSIZE+c2; for(int binindex = ; binindex < BINSIZE; binindex++)
{
outputFeature[i][j][bfindex++] = cellinfo[cx][cy][binindex];
}
}
}
}
}
return;
} //use global variables
void trainLogisticRegression(int block_row,int block_col, double theta[], double& errorrate, int& isreverse)
{
double theta1[NUM_BLOCK_FEATURES], theta2[NUM_BLOCK_FEATURES];
memset(theta1, , NUM_BLOCK_FEATURES*sizeof(double));
memset(theta2, , NUM_BLOCK_FEATURES*sizeof(double));
double errorrate1 = ;
double errorrate2 = ;
double rightnum1 = ;
double rightnum2 = ;
isreverse = ; //cal parameter thetas
for(int k = ; k < ; k++)
{
int i = rand()%NUM_IMAGE;
int j = rand()%NUM_BLOCK_FEATURES;
theta1[j] = theta1[j] + weight[i]*0.01*(type[i] - logistic(theta1, features[i][block_row][block_col]))*features[i][block_row][block_col][j];
} for(int i = ; i < NUM_IMAGE; i++)
{
double tmp = logistic(theta1, features[i][block_row][block_col]);
if(tmp > 0.5 && fabs(type[i] - ) < eps)
rightnum1 += 1.0*weight[i];
if(tmp < 0.5 && fabs(type[i] - ) < eps)
rightnum1 += 1.0*weight[i];
}
errorrate1 = - rightnum1; //calreverse
for(int k = ; k < ; k++)
{
int i = rand()%NUM_IMAGE;
int j = rand()%NUM_BLOCK_FEATURES;
theta2[j] = theta2[j] + weight[i]*0.01*(- type[i] - logistic(theta2, features[i][block_row][block_col]))*features[i][block_row][block_col][j];
} for(int i = ; i < NUM_IMAGE; i++)
{
double tmp = logistic(theta2, features[i][block_row][block_col]);
if(tmp > 0.5 && fabs(type[i] - ) < eps)
rightnum2 += 1.0*weight[i];
if(tmp < 0.5 && fabs(type[i] - ) < eps)
rightnum2 += 1.0*weight[i];
}
errorrate2 = - rightnum2; if(errorrate1 < errorrate2)
{
for(int i = ; i < NUM_BLOCK_FEATURES; i++)
{
theta[i] = theta1[i];
}
isreverse = ;
errorrate = errorrate1 + eps;
}
else
{
for(int i = ; i < NUM_BLOCK_FEATURES; i++)
{
theta[i] = theta2[i];
}
isreverse = ;
errorrate = errorrate2 + eps;
}
return;
} WeakClassifier trainClassifier()
{
WeakClassifier ansclassifier;
double theta[NUM_BLOCK_FEATURES];
double errorrate = ;
int isreverse = ;
double best_theta[NUM_BLOCK_FEATURES];
double best_errorrate = ;
int best_row = -;
int best_col = -;
int best_isreverse = ; //select best weak classifier
for(int i = ; i < NUM_BLOCK_ROWS; i++)
{
for(int j = ; j < NUM_BLOCK_COLS; j++)
{
trainLogisticRegression(i,j,theta,errorrate, isreverse); if(errorrate < )
{
printf("Wrong errorrate < 0 : %lf", errorrate);
abort();
} if(errorrate < best_errorrate)
{
for(int tempi = ; tempi < NUM_BLOCK_FEATURES; tempi++)
{
best_theta[tempi] = theta[tempi];
}
best_errorrate = errorrate;
best_row = i;
best_col = j;
best_isreverse = isreverse;
}
}
} if(best_errorrate > 0.5)
{
printf("The best_errorrate is greater than 0.5.\n");
abort();
} //set parameters;
ansclassifier._alpha = 1.0/*std::log((-best_errorrate)/best_errorrate);
ansclassifier._error = best_errorrate;
ansclassifier._index_col = best_col;
ansclassifier._index_row = best_row;
ansclassifier._isreverse = best_isreverse;
for(int i = ; i < NUM_BLOCK_FEATURES; i++) ansclassifier._theta[i] = best_theta[i]; return ansclassifier;
} int calByStrongClassifier(double x[NUM_BLOCK_ROWS][NUM_BLOCK_COLS][NUM_BLOCK_FEATURES])
{
double ans = ;
for(int i = ; i < NUM_WEAKCLASSIFIER; i++)
{
ans += weakClassifier[i]._alpha * weakClassifier[i].cal(x);
}
if(ans > )
return ;
else
return -;
} /*
size: 128*64;
type: CV_8UC1;
Block大小为18*18;
Cell大小为6*6;
Block在检测窗口中上下移动尺寸为6*6;
1个cell的梯度直方图化成9个bin;
滑动窗口在检测图片中滑动的尺寸为6*6;
*/ int main()
{
char filename[];
IplImage* inputImage = NULL;
clock_t timecount = clock(); //load posimage
for(int i = ; i < NUM_POSIMAGE; i++)
{
posfilename(i+ ,filename); //load grey image: set the parameter to 0;
inputImage = cvLoadImage(filename, ); //cal features;
Mat inputMat(inputImage);
calHogFeatures(inputMat, features[i]);
type[i] = ;
y[i] = ;
//printf("%d \n", inputMat.cols); //release memory
inputMat.release();
cvReleaseImage(&inputImage);
inputImage = NULL;
} printf("The feature process of pos-image have done in %d second.\n", (clock()-timecount)/);
timecount = clock(); //load neg images
for(int i = ; i < NUM_NEGIMAGE; i++)
{
negfilename(i+, filename); //load grey image: set the parameter to 0;
inputImage = cvLoadImage(filename, );
type[NUM_POSIMAGE+i] = ;
y[NUM_POSIMAGE+i] = -; Mat inputMat(inputImage);
calHogFeatures(inputMat, features[NUM_POSIMAGE+i]); //release memory
inputMat.release();
cvReleaseImage(&inputImage);
inputImage = NULL;
} printf("The feature process of neg-image have done in %d second.\n", (clock()-timecount)/);
timecount = clock(); //init weight array
for(int i = ; i < NUM_IMAGE; i++)
{
weight[i] = 1.0/NUM_IMAGE;
} //freopen
freopen("HOG_CLASSIFIER.txt", "w", stdout); //print number of weakclassifiers;
printf("%d\n", NUM_WEAKCLASSIFIER); //adaboost framework
for(int classifierindex = ; classifierindex < NUM_WEAKCLASSIFIER; classifierindex++)
{
weakClassifier[classifierindex] = trainClassifier(); double error = weakClassifier[classifierindex]._error;
double alpha = weakClassifier[classifierindex]._alpha; //printf("%d classifier: %lf ====\n",classifierindex, error);
//printf("_index_row %d _index_col %d\n", weakClassifier[classifierindex]._index_row, weakClassifier[classifierindex]._index_col); double identitysum = ;
for(int sampleindex = ; sampleindex < NUM_IMAGE; sampleindex++)
{
weight[sampleindex] *= std::exp(-alpha*y[sampleindex]*weakClassifier[classifierindex].cal(features[sampleindex]));
identitysum += weight[sampleindex];
} //reweight
for(int sampleindex = ; sampleindex < NUM_IMAGE; sampleindex++)
{
weight[sampleindex] /= identitysum;
} weakClassifier[classifierindex].print();
} freopen("CON", "w", stdout);
int rightnum = ;
for(int testindex = ;testindex < NUM_TESTIMAGE; testindex ++)
{
//posfilename(testindex+1, filename);
testfilename(testindex+, filename);
inputImage = cvLoadImage(filename, ); double testfeatures[NUM_BLOCK_ROWS][NUM_BLOCK_COLS][NUM_BLOCK_FEATURES];
memset(testfeatures, , sizeof(testfeatures)); Mat inputMat(inputImage);
calHogFeatures(inputMat, testfeatures); if(calByStrongClassifier(testfeatures) == )
{
rightnum++;
//printf("Yes\n");
}
else
//printf("No\n"); inputMat.release();
}
printf("Accuracy: %d\n", rightnum);
}
//测试数据是网上流行的128*64灰度行人图像数据。
基于HOG特征的Adaboost行人检测的更多相关文章
- OpenCV中基于HOG特征的行人检测
目前基于机器学习方法的行人检测的主流特征描述子之一是HOG(Histogram of Oriented Gradient, 方向梯度直方图).HOG特征是用于目标检测的特征描述子,它通过计算和统计图像 ...
- 基于Haar特征的Adaboost级联人脸检测分类器
基于Haar特征的Adaboost级联人脸检测分类器基于Haar特征的Adaboost级联人脸检测分类器,简称haar分类器.通过这个算法的名字,我们可以看到这个算法其实包含了几个关键点:Haar特征 ...
- 照片美妆---基于Haar特征的Adaboost级联人脸检测分类器
原文:照片美妆---基于Haar特征的Adaboost级联人脸检测分类器 本文转载自张雨石http://blog.csdn.net/stdcoutzyx/article/details/3484223 ...
- opencv+树莓PI的基于HOG特征的行人检测
树莓PI远程控制摄像头请参考前文:http://www.cnblogs.com/yuliyang/p/3561209.html 参考:http://answers.opencv.org/questio ...
- Opencv学习之路—Opencv下基于HOG特征的KNN算法分类训练
在计算机视觉研究当中,HOG算法和LBP算法算是基础算法,但是却十分重要.后期很多图像特征提取的算法都是基于HOG和LBP,所以了解和掌握HOG,是学习计算机视觉的前提和基础. HOG算法的原理很多资 ...
- 基于Haar特征Adaboost人脸检测级联分类
基于Haar特征Adaboost人脸检测级联分类 基于Haar特征Adaboost人脸检测级联分类,称haar分类器. 通过这个算法的名字,我们能够看到这个算法事实上包括了几个关键点:Haar特征.A ...
- 目标检测之人头检测(HaarLike Adaboost)---高密度环境下行人检测和统计
实验程序视频 下载 1 问题描述 高密度环境下的行人统计一直没有得到很好的解决,主要原因是对高密度人群中的行人检测和跟踪是一个很难的问题,如下图所示环境,存在的困难包括: 检测方面: 由于人群整体处于 ...
- 行人检测4(LBP特征)
参考原文: http://blog.csdn.net/zouxy09/article/details/7929531 http://www.cnblogs.com/dwdxdy/archive/201 ...
- paper 87:行人检测资源(下)代码数据【转载,以后使用】
这是行人检测相关资源的第二部分:源码和数据集.考虑到实际应用的实时性要求,源码主要是C/C++的.源码和数据集的网址,经过测试都可访问,并注明了这些网址最后更新的日期,供学习和研究进行参考.(欢迎补充 ...
随机推荐
- android ADT 设置编辑字体
新配置的android ADT 设置编辑字体的时候 可能里面没有我们想要的Courier new 这种舒服的字体 那么就在 字体选项窗口的 做下端 有个显示更多字体的链接 然后就显示微软的所有字 ...
- Java基础06 组合
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 我们已经尝试去定义类.定义类,就是新建了一种类型(type).有了类,我们接着构造 ...
- Solr4.2迁移到新项目下异常:java.lang.NoSuchMethodError: org.apache.http.conn.scheme.Scheme.<init>
由于业务调整,需要将solr搜索项目集成到另一个项目下成为一个模块,原项目运行异常,但是迁移到新项目后出现异常如下: 原因:引入的httpclient.jar冲突 解决方法:删除冲突的jar
- EasyUI - Messager消息框
全局设定: JavaScript代码: //设置按钮中的文字,默认是-ok/cancel ,可以任意设置文字,比如现在的-确认/取消 $.messager.defaults = { ok: '确认', ...
- 讲解下for循环的用法,加深记忆
引子 这是一段很简单的代码,但是即便是这么简单的东西,这里我们还是需要说一下. 关于for循环整个执行流程就是,先执行var i=10,然后到了第二个语句,判断10是否大于0,很明显为true,所以此 ...
- Spring Session - Spring Boot
The completed guide can be found in the boot sample application. Updating Dependencies Before you us ...
- python模块介绍- HTMLParser 简单的HTML和XHTML解析器
python模块介绍- HTMLParser 简单的HTML和XHTML解析器 2013-09-11 磁针石 #承接软件自动化实施与培训等gtalk:ouyangchongwu#gmail.comqq ...
- 远光软件ASP.NET笔试题小汇总
ASP.NET笔试题是ASP.NET程序员面试必须经历的,一般会叫你填两个表 1个是你的详细信息表 1个是面试题答卷 两个都要注意反正面是否都有内容不要遗漏,如果考你机试一般也有两种,就是程序连接数据 ...
- Lazy Math Instructor
Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 3721 Accepted: 1290 Description A m ...
- python发送各类邮件的主要方法
更多详见: http://www.w3cschool.cc/python/python-email.html python中email模块使得处理邮件变得比较简单,今天着重学习了一下发送邮件的具体做法 ...