Advertisement

手撕Hog 特征值提取——一种通俗的Hog特征提取方法

阅读量:

Hog特征是什么。这里就不赘述了。

不,还是说下吧。

Hog特征是一种梯度特征。主要是对于图像边缘的描述。有点那种,一眼看过去是什么样的那种感觉。

严格的还是看CVPR 2005 年的那篇paper吧。用Hog特征结合SVM,做行人检测的。

这里摘抄下我觉得写得不错的一段。

HOG特征提取方法就是将一个image:

1. 灰度化(将图像看做一个x,y,z(灰度)的三维图像)

2. 划分成小cells(2*2)

3. 计算每个cell中每个pixel的gradient(即orientation)

4. 统计每个cell的梯度直方图(不同梯度的个数),即可形成每个cell的descriptor

灰度图就是 (R+G+B)/3

Cell 待会再说。先说说如何计算图像梯度。

图像梯度听起来很玄学。其实很弱鸡。

它是这样的:

x方向的梯度:其实就是某一像素相邻左右两边的差值。

y方向的梯度:就是某一像素上下相邻两像素的差值。

没错,这就是梯度了。。

然后是某一像素的梯度幅值如上公式所示。

梯度方向也是用的到的。

对于上面,我是通过矩阵来实现。

定义一个梯度矩阵如下:

0 -1 0 0 0 0

1 0 -1 0 0 0

0 -1 0 1 0 0

0 0 -1 0 1 0

0 0 0 -1 0 1

0 0 0 0 -1 0

注意观察这个矩阵。叫它A吧。

那么假设图像是一个mxn的矩阵I

那么对x的梯度计算就是 Dot(I,A) 其中A为nxn

对y的梯度计算就是Dot(A,I) 其中A 为mxm

然后如上公式计算各个值。这样Hog其实就做了一半多了。(而且,这两个矩阵的点乘其实是耗时的主要部分)

//这个是我生成梯度矩阵的代码:

复制代码
 Mat_<float> GenerateGradientMatrix(int rank)

    
 {
    
 	Mat_<float> result =Mat_<float>(rank, rank);
    
 	for (int i = 0; i < rank; i++)
    
 	{
    
 		for (int j = 0; j < rank; j++)
    
 		{
    
 			result(i, j) = 0;
    
 		}
    
 		if (i!=rank-1)
    
 		{
    
 			result(i, i + 1) = 1;
    
 		}
    
 		if (i!=0)
    
 		{
    
 			result(i, i - 1) = -1;
    
 		}
    
 	}
    
 	return result;
    
 }

//在Hog特征中,另外一个概念是Cell, Cell是什么?

Cell 其实就是组成图片的小图片。把一张图片,划分成几个块。一般这个块是nxn大小的。

这样比如你是3*3,那么就有9个像素属于这个Cell。

//这里引用一下: http://www.open-open.com/lib/view/open1440832074794.html

为每个细胞单元构建梯度方向直方图

第三步的目的是为局部图像区域提供一个编码,同时能够保持对图像中人体对象的姿势和外观的弱敏感性。

我们将图像分成若干个“单元格cell”,例如每个cell为66个像素。假设我们采用9个bin的直方图来统计这66个像素的梯度信息。也 就是将cell的梯度方向360度分成9个方向块,如图所示:例如:如果这个像素的梯度方向是20-40度,直方图第2个bin的计数就加一,这样,对 cell内每个像素用梯度方向在直方图中进行加权投影(映射到固定的角度范围),就可以得到这个cell的梯度方向直方图了,就是该cell对应的9维特 征向量(因为有9个bin)。

像素梯度方向用到了,那么梯度大小呢?梯度大小就是作为投影的权值的。例如说:这个像素的梯度方向是20-40度,然后它的梯度大小是2(假设啊),那么直方图第2个bin的计数就不是加一了,而是加二(假设啊)。
图像特征提取三大法宝:HOG特征,LBP特征,Haar特征

细胞单元可以是矩形的(rectangular),也可以是星形的(radial)。

//

这就是Cell了。

我写码的时候把Cell 写成Bin了。我的Bin 定义如下:

复制代码
 struct Bin

    
 {
    
 	Bin()
    
 	{
    
 		for (int i = 0; i < 360/Angle; i++)
    
 		{
    
 			bins[i] = 0;
    
 		}
    
 	}
    
 	float bins[360 / Angle];
    
 };

里面的那只数组就是用来统计每个梯度方向上的权值大小。

这个是把梯度值和梯度方向两个矩阵填充到Cells里的代码:

复制代码
 Bin** FillInBin(Mat_<float>& length,Mat_<float>& direction,int& binRows,int& binCols)

    
 {
    
 	binRows = 1 + length.rows / CellSize;
    
 	binCols = 1 + length.cols / CellSize;
    
 	Bin **result = new Bin*[1 + length.rows / CellSize];
    
 	for (int i = 0;i<1 + length.rows / CellSize;i++)
    
 		result[i] = new Bin[1 + length.cols / CellSize];
    
  
    
 	for (int i = 0; i < length.rows; i++)
    
 	{
    
 		for (int j = 0; j < length.cols; j++)
    
 		{
    
 			int tempDir = direction(i, j);
    
 			if (tempDir<0)
    
 			{	
    
 				if (tempDir>-999)
    
 				{
    
 					tempDir = 360.0f + tempDir;
    
 				}
    
 				else
    
 				{
    
 					tempDir = 0;
    
 				}
    
 			}
    
 			result[i / CellSize][ j / CellSize].bins[ (int)(tempDir / 90)] += length(i, j);
    
 		}
    
 	}
    
  
    
 	for (int i = 0; i < binRows; i++)
    
 	{
    
 		for (int j = 0; j < binCols; j++)
    
 		{
    
 			for (int k = 0; k < 360/Angle; k++)
    
 			{
    
 				result[i][j].bins[k] /= (0.0007*(360 * CellSize*CellSize / (360.0 / Angle)));
    
 			}
    
 		}
    
 	}
    
  
    
 	return result;
    
 }

//这时,每个Cell里已经充满了每个像素的贡献。

此时对这个Cell做一个正规化。那么其实Hog,已经讲完了。。

等等,那么Block呢》

Block就是一个滑动窗口,比Cell更大的一个块。而且允许重叠。每个Block由一堆Cell构成。更多的就是再对Block做一个正规化。进一步消除光照等噪声。

//最后贴上全部代码:

全部写在一个cpp 里便于copy and paste

复制代码
 #include <iostream>

    
 #include <opencv\cv.hpp>
    
 #include <math.h>
    
 #include <vector>
    
  
    
 #define CellSize 20
    
 #define Angle 40
    
 #define PI 3.1415926
    
  
    
 using namespace std;
    
 using namespace cv;
    
  
    
 struct Bin
    
 {
    
 	Bin()
    
 	{
    
 		for (int i = 0; i < 360/Angle; i++)
    
 		{
    
 			bins[i] = 0;
    
 		}
    
 	}
    
 	float bins[360 / Angle];
    
 };
    
  
    
 Mat_<float> DrawBin(Bin** bin,const int& binRows,const int& binCols)
    
 {
    
 	Mat_<float> result = Mat_<float>(binRows*CellSize, binCols*CellSize);
    
 	for (int i = 0; i < result.rows; i++)
    
 	{
    
 		for (int j = 0; j < result.cols; j++)
    
 		{
    
 			result(i, j) = 0;
    
 		}
    
 	}
    
 	for (int i = 0; i < binRows; i++)
    
 	{
    
 		for (int j = 0; j < binCols; j++)
    
 		{
    
 			//for each angle
    
 			int angleIndex = 0;
    
 			for (int l = 0; l < 360 / Angle; l++)
    
 			{
    
 				if (bin[i][j].bins[l]>bin[i][j].bins[angleIndex])
    
 				{
    
 					angleIndex = l;
    
 				}
    
 			}
    
 			//from a cell start to end 
    
 			for (int k = 0; k < CellSize; k++)
    
 			{
    
 				int ro = k + i*CellSize;
    
 				float x = k - CellSize / 2;
    
 				int y = (int)tanf(angleIndex*(PI*Angle / 360.0))*x;
    
 				if (-CellSize/2<y&&y<CellSize/2)
    
 				{
    
 					int co = y + CellSize / 2 + j*CellSize;
    
 					if (co>0&&co<result.cols&&ro>0&&ro<result.rows)
    
 					{
    
 						int tempValue = bin[i][j].bins[angleIndex];
    
 						result(ro, co) = tempValue;
    
 					}
    
 				}
    
 			}
    
 		}
    
 	}
    
 	return result;
    
 }
    
  
    
 Bin** FillInBin(Mat_<float>& length,Mat_<float>& direction,int& binRows,int& binCols)
    
 {
    
 	binRows = 1 + length.rows / CellSize;
    
 	binCols = 1 + length.cols / CellSize;
    
 	Bin **result = new Bin*[1 + length.rows / CellSize];
    
 	for (int i = 0;i<1 + length.rows / CellSize;i++)
    
 		result[i] = new Bin[1 + length.cols / CellSize];
    
  
    
 	for (int i = 0; i < length.rows; i++)
    
 	{
    
 		for (int j = 0; j < length.cols; j++)
    
 		{
    
 			int tempDir = direction(i, j);
    
 			if (tempDir<0)
    
 			{	
    
 				if (tempDir>-999)
    
 				{
    
 					tempDir = 360.0f + tempDir;
    
 				}
    
 				else
    
 				{
    
 					tempDir = 0;
    
 				}
    
 			}
    
 			result[i / CellSize][ j / CellSize].bins[ (int)(tempDir / 90)] += length(i, j);
    
 		}
    
 	}
    
  
    
 	for (int i = 0; i < binRows; i++)
    
 	{
    
 		for (int j = 0; j < binCols; j++)
    
 		{
    
 			for (int k = 0; k < 360/Angle; k++)
    
 			{
    
 				result[i][j].bins[k] /= (0.0007*(360 * CellSize*CellSize / (360.0 / Angle)));
    
 			}
    
 		}
    
 	}
    
  
    
 	return result;
    
 }
    
  
    
 Mat_<float> GenerateGradientMatrix(int rank)
    
 {
    
 	Mat_<float> result =Mat_<float>(rank, rank);
    
 	for (int i = 0; i < rank; i++)
    
 	{
    
 		for (int j = 0; j < rank; j++)
    
 		{
    
 			result(i, j) = 0;
    
 		}
    
 		if (i!=rank-1)
    
 		{
    
 			result(i, i + 1) = 1;
    
 		}
    
 		if (i!=0)
    
 		{
    
 			result(i, i - 1) = -1;
    
 		}
    
 	}
    
 	return result;
    
 }
    
  
    
 Mat_<float> Clean(Mat_<float>& result)
    
 {
    
 	Mat_<float> source = result.clone();
    
 	for (int i = 0; i < source.cols*source.rows; i++)
    
 	{
    
 		if (source.data[i]>255)
    
 		{
    
 			source.data[i] = 255;
    
 		}
    
 		if (source.data[i]<0)
    
 		{
    
 			source.data[i] = 0;
    
 		}
    
 	}
    
 	return source;
    
 }
    
  
    
 Mat_<float> ReverseImg(Mat_<float>& source)
    
 {
    
 	Mat_<float> target = Mat_<float>(source.rows, source.cols);
    
 	for (int i = 0; i < target.rows; i++)
    
 	{
    
 		for (int j = 0; j < target.cols; j++)
    
 		{
    
 			target(i, j) = -source(i, j) + 255;
    
 		}
    
 	}
    
 	return target;
    
 }
    
  
    
 Mat_<float> GetDirection(Mat_<float>& _x, Mat_<float>& _y)
    
 {
    
 	Mat_<float> result = Mat_<float>(_x.rows, _x.cols);
    
 	for (int i = 0; i < result.rows; i++)
    
 	{
    
 		for (int j = 0; j < result.cols; j++)
    
 		{
    
 			result(i, j) = 360.0*atanf(_y(i, j) / _x(i, j)) / PI;
    
 		}
    
 	}
    
 	return result;
    
 	
    
 }
    
  
    
 Mat_<float> SquarSumSqrt(Mat_<float>& ma, Mat_<float>& mb)
    
 {
    
 	Mat_<float> result = Mat_<float>(ma.rows, ma.cols);
    
 	for (int i = 0; i < result.rows; i++)
    
 	{
    
 		for (int j = 0; j < result.cols; j++)
    
 		{
    
 			result(i, j) = sqrtf( ma(i, j)*ma(i, j)+mb(i,j)*mb(i,j));
    
 		}
    
 	}
    
 	return result;
    
 }
    
  
    
  
    
 int main(int argc, char* argv)
    
 {
    
 	Mat_<float> img = imread("E:/照片/264.jpg", 0);
    
 	Mat_<float> right = GenerateGradientMatrix(img.cols);
    
 	Mat_<float> left = GenerateGradientMatrix(img.rows);
    
 	Mat_<float> _x = img*right;
    
 	Mat_<float> _y = left*img;
    
 	Mat_<float> _length = SquarSumSqrt(_x, _y);
    
 	Mat_<float> _direction = GetDirection(_x, _y);
    
 	int binRows;
    
 	int binCols;
    
 	Bin** myBin = FillInBin(_length, _direction,binRows,binCols);
    
 	Mat_<float> draw = DrawBin(myBin,binRows,binCols);
    
 	for (int i = 0; i < binRows; i++)
    
 	{
    
 		delete[] myBin[i];
    
 		myBin[i] = NULL;
    
 	}
    
 	delete[] myBin;
    
 	imshow("Hog_CPP",(Mat_<uchar>)draw);
    
 	waitKey(0);
    
 	system("pause");
    
 	return 0;
    
 }

全部评论 (0)

还没有任何评论哟~