Integral histogram for fast HoG feature calculation

I recently found source for calculating HoG descriptors using Integral Histogram.
http://smsoftdev-solutions.blogspot.com/2009/08/integral-histogram-for-fast-calculation.html

It is based on the C version of opencv, and I ported it to use cv::Mat format. The results from both the results are similar but not exactly the same.
For now, I am going to go with this, but if anyone find out why they are not the same, please let me know.


#ifndef HOGDETECTOR_HPP_INCLUDED
#define HOGDETECTOR_HPP_INCLUDED

//#include <opencv/cv.h>
#include <opencv2/opencv.hpp>
#include <vector>

#define ELEM(type,start,step,size,xpos,ypos,ichannel) \
*((type*)(start+step*(ypos)+(xpos)*size+ichannel))

#define PI 3.142

//IplImage** calculateIntegralHOG(IplImage* in);
//void calculateHOG_rect(CvRect cell, CvMat* hog_cell,
//                        IplImage** integrals, int normalization);

std::vector<cv::Mat> calculateIntegralHOG(const cv::Mat& _in, int& _nbins);
void calculateHOG_rect(cv::Mat& _hogCell, std::vector<cv::Mat> _integrals,
cv::Rect _cell, int _nbins, int _normalization=cv::NORM_MINMAX);

#endif // HOGDETECTOR_HPP_INCLUDED

#include <hogdetector.hpp>
#include <iostream>

/*Function to calculate the integral histogram*/
std::vector<cv::Mat> calculateIntegralHOG(const cv::Mat& _in, int& _nbins)
{
 /*Convert the input image to grayscale*/

 cv::Mat img_gray;// = cv::Mat(in.size(),CV_8UC1);// cvCreateImage(cvGetSize(in), IPL_DEPTH_8U,1);

 cv::cvtColor(_in,img_gray,CV_BGR2GRAY);
 cv::equalizeHist(img_gray, img_gray);

 /* Calculate the derivates of the grayscale image in the x and y
 directions using a sobel operator and obtain 2 gradient images
 for the x and y directions*/

 cv::Mat xsobel, ysobel;
 cv::Sobel(img_gray,xsobel,CV_32FC1,1,0);
 cv::Sobel(img_gray,ysobel,CV_32FC1,0,1);

 img_gray.release();

 /* Create an array of 9 images (9 because I assume bin size 20 degrees
 and unsigned gradient ( 180/20 = 9), one for each bin which will have
 zeroes for all pixels, except for the pixels in the original image
 for which the gradient values correspond to the particular bin.
 These will be referred to as bin images. These bin images will be then
 used to calculate the integral histogram, which will quicken
 the calculation of HOG descriptors */

 std::vector<cv::Mat> bins(_nbins);
 for (int i = 0; i < _nbins ; i++)
 {
 bins[i] = cv::Mat::zeros(_in.size(), CV_32FC1);
 }

 /* Create an array of 9 images ( note the dimensions of the image,
 the cvIntegral() function requires the size to be that), to store
 the integral images calculated from the above bin images.
 These 9 integral images together constitute the integral histogram */

 std::vector<cv::Mat> integrals(_nbins);
 //IplImage** integrals = (IplImage**) malloc(9 * sizeof(IplImage*));
 for (int i = 0; i < _nbins ; i++)
 {
 integrals[i] = cv::Mat(cv::Size(_in.size().width + 1, _in.size().height + 1), CV_64FC1);
 }

 /* Calculate the bin images. The magnitude and orientation of the gradient
 at each pixel is calculated using the xsobel and ysobel images.
 {Magnitude = sqrt(sq(xsobel) + sq(ysobel) ), gradient = itan (ysobel/xsobel) }.
 Then according to the orientation of the gradient, the value of the
 corresponding pixel in the corresponding image is set */

 int x, y;
 float temp_gradient, temp_magnitude;
 for (y = 0; y <_in.size().height; y++)
 {
 /* ptr1 and ptr2 point to beginning of the current row in the xsobel and ysobel images
 respectively.
 ptrs[i] point to the beginning of the current rows in the bin images */

 float* xsobelRowPtr = (float*) (xsobel.row(y).data);
 float* ysobelRowPtr = (float*) (ysobel.row(y).data);
 float** binsRowPtrs = new float *[_nbins];
 for (int i = 0; i < _nbins ;i++)
 {
 binsRowPtrs[i] = (float*) (bins[i].row(y).data);
 }

 /*For every pixel in a row gradient orientation and magnitude
 are calculated and corresponding values set for the bin images. */
 for (x = 0; x <_in.size().width; x++)
 {
 /* if the xsobel derivative is zero for a pixel, a small value is
 added to it, to avoid division by zero. atan returns values in radians,
 which on being converted to degrees, correspond to values between -90 and 90 degrees.
 90 is added to each orientation, to shift the orientation values range from {-90-90} to {0-180}.
 This is just a matter of convention. {-90-90} values can also be used for the calculation. */
 if (xsobelRowPtr[x] == 0)
 {
 temp_gradient = ((atan(ysobelRowPtr[x] / (xsobelRowPtr[x] + 0.00001))) * (180/ PI)) + 90;
 }
 else
 {
 temp_gradient = ((atan(ysobelRowPtr[x] / xsobelRowPtr[x])) * (180 / PI)) + 90;
 }
 temp_magnitude = sqrt((xsobelRowPtr[x] * xsobelRowPtr[x]) + (ysobelRowPtr[x] * ysobelRowPtr[x]));

 /*The bin image is selected according to the gradient values.
 The corresponding pixel value is made equal to the gradient
 magnitude at that pixel in the corresponding bin image */
 float binStep = 180/_nbins;

 for (int i=1 ; i<=_nbins ; i++)
 {
 if (temp_gradient <= binStep*i)
 {
 binsRowPtrs[i-1][x] = temp_magnitude;
 break;
 }
 }
 }
 }

 //cvReleaseImage(&xsobel);
 //cvReleaseImage(&ysobel);

 xsobel.release();
 ysobel.release();

 /*Integral images for each of the bin images are calculated*/

 for (int i = 0; i <_nbins ; i++)
 {
 cv::integral(bins[i], integrals[i]);
 }

 for (int i = 0; i <_nbins ; i++)
 {
 bins[i].release();
 }

 /*The function returns an array of 9 images which consitute the integral histogram*/
 return (integrals);
}

/*The following demonstrates how the integral histogram calculated using
 the above function can be used to calculate the histogram of oriented
 gradients for any rectangular region in the image:*/

/* The following function takes as input the rectangular cell for which the
 histogram of oriented gradients has to be calculated, a matrix hog_cell
 of dimensions 1x9 to store the bin values for the histogram, the integral histogram,
 and the normalization scheme to be used. No normalization is done if normalization = -1 */

void calculateHOG_rect(cv::Mat& _hogCell, std::vector<cv::Mat> _integrals,
 cv::Rect _roi, int _nbins, int _normalization)
{
 if (_roi.width == 0 || _roi.height == 0)
 {
 _roi.x = 0; _roi.y = 0;
 _roi.width = _integrals[0].size().width-1;
 _roi.height = _integrals[0].size().height-1;
 }
 /* Calculate the bin values for each of the bin of the histogram one by one */
 for (int i = 0; i < _nbins ; i++)
 {
 IplImage intImgIpl = _integrals[i];

 float a = ((double*)(intImgIpl.imageData + (_roi.y)
 * (intImgIpl.widthStep)))[_roi.x];
 float b = ((double*) (intImgIpl.imageData + (_roi.y + _roi.height)
 * (intImgIpl.widthStep)))[_roi.x + _roi.width];
 float c = ((double*) (intImgIpl.imageData + (_roi.y)
 * (intImgIpl.widthStep)))[_roi.x + _roi.width];
 float d = ((double*) (intImgIpl.imageData + (_roi.y + _roi.height)
 * (intImgIpl.widthStep)))[_roi.x];

 ((float*) _hogCell.data)[i] = (a + b) - (c + d);
 }
 /*Normalize the matrix*/
 if (_normalization != -1)
 {
 cv::normalize(_hogCell, _hogCell, 0, 1, CV_MINMAX);
 }
}

Advertisements

3 Comments

Filed under opencv

3 responses to “Integral histogram for fast HoG feature calculation

  1. Hi, thanks for your code. And I know why the results of both version are similar but not exactly the same. It results from the following code:
    cv::Sobel(img_gray,xsobel,CV_32FC1,1,0);
    cv::Sobel(img_gray,ysobel,CV_32FC1,0,1);

    You can change them to be the following version to get the same result:
    cv::Sobel(img_gray, xsobel, CV_32F, 1, 0, 3, 1, 0, BORDER_REFLECT);
    cv::Sobel(img_gray, ysobel, CV_32F, 0, 1, 3, 1, 0, BORDER_REFLECT);

    It’s just the difference between selecting different border options when doing sobelling.

    Best,
    Tommy

  2. Daksitha

    hello, I am doing my project on exact what you have done in this code. the problem I have is
    CvCapture *camera=cvCaptureFromFile(“rtsp://192.168.1.19:554/0/1:1/main”);
    IplImage *img=cvQueryFrame(camera);
    this is the method I could be able to stream real time using my IP camera. (couldn’t find a way to use Mat ) so could you please tell me how to synchronize your code to my project. thank you so much. Much need one.

    • Hello, Sorry for such a late response. Are you restricted to using the c API of OpenCV. You should consider using the new c++ api. check highgui module of opencv.

      http://docs.opencv.org/modules/highgui/doc/highgui.html

      #include “opencv2/opencv.hpp”

      using namespace cv;

      int main(int, char**)
      {
      VideoCapture cap(0); // open the default camera
      if(!cap.isOpened()) // check if we succeeded
      return -1;

      Mat edges;
      namedWindow(“edges”,1);
      for(;;)
      {
      Mat frame;
      cap >> frame; // get a new frame from camera
      cvtColor(frame, edges, CV_BGR2GRAY);
      GaussianBlur(edges, edges, Size(7,7), 1.5, 1.5);
      Canny(edges, edges, 0, 30, 3);
      imshow(“edges”, edges);
      if(waitKey(30) >= 0) break;
      }
      // the camera will be deinitialized automatically in VideoCapture destructor
      return 0;
      }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s