一、HOG特征
HOG(Histograms of Oriented Gradients)梯度方向直方图
二、HOG特征提取流程
1)灰度图像 转换(将图像看做一个x,y,z(灰度)的三维图像);
2)采用Gamma校正法对输入图像进行颜色空间的标准化(归一化);目的是调节图像的对比度,降低图像局部的阴影和光照变化所造成的影响,同时可以抑制噪音的干扰;
#if 1 // 图像增强算法 --gamma
int Gamma = 2;
int main(int args, char* arg)
{
Mat src = imread("C:\\Users\\19473\\Desktop\\opencv_images\\88.jpg");
if (!src.data)
{
printf("could not load image....\n");
}
imshow("原图像", src);
// 注意点 : CV_32FC3
Mat dst(src.size(), CV_32FC3);
for (int i = 0; i < src.rows; i++)
{
for (int j = 0; j < src.cols; j++)
{
// 对bgr 的每个通道都进行计算
dst.at<Vec3f>(i, j)[0] = pow(src.at<Vec3b>(i, j)[0], Gamma);
dst.at<Vec3f>(i, j)[1] = pow(src.at<Vec3b>(i, j)[1], Gamma);
dst.at<Vec3f>(i, j)[2] = pow(src.at<Vec3b>(i, j)[2], Gamma);
}
}
// 归一化
normalize(dst, dst, 0, 255, CV_MINMAX);
convertScaleAbs(dst, dst);
imshow("增强后的图像", dst);
waitKey(0);
return -1;
}
#endif
3)计算图像每个像素的梯度(包括大小和方向);主要是为了捕获轮廓信息,同时进一步弱化光照的干扰
Mat non_max_supprusion(Mat dx, Mat dy) //传进来的是两个方向上的差分矩阵 3*3 的掩膜
{
//边缘强度=sqrt(dx 的平方+dy 的平方)
Mat edge;
magnitude(dx, dy, edge);// 计算幅度值
int rows = dx.rows;
int cols = dy.cols;
//边缘强度的非极大值抑制
Mat edgemag_nonMaxSup = Mat::zeros(dx.size(), dx.type());
// 用两个循序计算出 和梯度方向 并且转换成angleMatrix
for (int row = 1; row < rows - 1; row++)
{
for (int col = 1; col < cols - 1; col++)
{
float x = dx.at<float>(row, col);
float y = dx.at<float>(row, col);
// 梯度的方向---atan2f
float angle = atan2f(y, x) / CV_PI * 180;
// 当前位置的边缘强度
float mag = edge.at<float>(row, col);
// 找到左右两个方向
if (abs(angle) < 22.5 || abs(angle) > 157.5)
{
float left = edge.at<float>(row, col - 1);
float right = edge.at<float>(row, col + 1);
// 判断两个方向上的
if (mag > left && mag > right) {
edgemag_nonMaxSup.at<float>(row, col) = mag;
}
}
// 左上和右下两个方向
if ((abs(angle) >= 22.5 && abs(angle) < 67.5) || (abs(angle) < -112.5 && abs(angle) > 157.5))
{
float lefttop = edge.at<float>(row - 1, col - 1);
float rightbottom = edge.at<float>(row + 1, col + 1);
// 判断两个方向上的
if (mag > lefttop && mag > rightbottom) {
edgemag_nonMaxSup.at<float>(row, col) = mag;
}
}
// 上 下 方向
if ((abs(angle) >= 67.5 && abs(angle) <= 112.5) || (abs(angle) >= -112.5 && abs(angle) <= -67.5))
{
float top = edge.at<float>(row - 1, col);
float down = edge.at<float>(row + 1, col);
// 判断两个方向上的
if (mag > top && mag > down) {
edgemag_nonMaxSup.at<float>(row, col) = mag;
}
}
// 右上 左下 方向
if ((abs(angle) > 122.5 && abs(angle) < 157.5) || (abs(angle) > -67.5 && abs(angle) <= -22.5))
{
float leftdown = edge.at<float>(row - 1, col + 1);
float rightup = edge.at<float>(row + 1, col - 1);
// 判断两个方向上的
if (mag > leftdown && mag > rightup) {
edgemag_nonMaxSup.at<float>(row, col) = mag;
}
}
}
}
return edgemag_nonMaxSup;
}
4)将图像划分成小cells(例如8* 8像素 / cell); 算出每个cell的梯度大小及方向.然后将每像素的梯度方向在 区间内(无向:0-180,有向:0-360)平均分为9个bins,每个cell内的像素用幅值来表示权值,为其所在的梯度直方图进行加权投票.
5)统计每个cell的梯度直方图(不同梯度的个数),即可形成每个cell的descriptor; 快描述籽
6)将每几个cell组成一个block(例如3 * 3个cell / block),一个block内所有cell的特征descriptor串联起来便得到该block的HOG特征descriptor。快描述籽归一化
7)将图像image内的所有block的HOG特征descriptor串联起来就可以得到该image(你要检测的目标)的HOG特征descriptor了。这个就是最终的可供分类使用的特征向量了 特征数据与检测窗口
对于大小为128×64大小的图像,采用8*8像素的sell,2×2个cell组成的16×16像素的block,采用8像素的block移动步长,这样检测窗口block的数量有((128-16)/8+1)×((64-16)/8+1)=15×7.则HOG特征描述符的维数为15×7×4×9.
8) 匹配方法
HOG的缺点:
速度慢,实时性差;难以处理遮挡问题
三、代码演示
int main(int args, char* arg)
{
//目标图像
src = imread("C:\\Users\\19473\\Desktop\\opencv_images\\153.jpg");
if (!src.data)
{
printf("could not load image....\n");
}
namedWindow(INPUT_TITLE, CV_WINDOW_AUTOSIZE);
//namedWindow(OUT_TITLE, CV_WINDOW_AUTOSIZE);
imshow(INPUT_TITLE, src);
/*
// 将图像重新规定大小
resize(src, dst,Size(64,128));
cvtColor(dst, src_gary, CV_BGR2GRAY);
HOGDescriptor detector(Size(64,128), Size(16,16), Size(8,8),Size(8,8),9);
vector<float> descripers;
vector<Point> locations;
detector.compute(src_gary, descripers, Size(0,0), Size(0, 0), locations);
printf("num of HOG: %d\n", descripers.size());
*/
//SVM分类器 --描述子
HOGDescriptor hog = HOGDescriptor();
hog.setSVMDetector(hog.getDefaultPeopleDetector());
vector<Rect> foundloactions;
// 多尺度检测
hog.detectMultiScale(src, foundloactions, 0, Size(8, 8), Size(32, 32), 1.05, 2, false);
//若rects有嵌套,则取最外面的矩形存入rect
for (size_t i = 0; i < foundloactions.size(); i++)
{
rectangle(src, foundloactions[i], Scalar(0, 0, 255), 2, 8.0);
}
namedWindow(OUT_TITLE, CV_WINDOW_AUTOSIZE);
imshow(OUT_TITLE, src);
waitKey(0);
return 0;
}