123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382 |
- #include <math.h>
- #include "mex.h"
- // small value, used to avoid division by zero
- #define eps 0.0001
- // unit vectors used to compute gradient orientation
- double uu[9] = {1.0000,
- 0.9397,
- 0.7660,
- 0.500,
- 0.1736,
- -0.1736,
- -0.5000,
- -0.7660,
- -0.9397
- };
-
- double vv[9] = {0.0000,
- 0.3420,
- 0.6428,
- 0.8660,
- 0.9848,
- 0.9848,
- 0.8660,
- 0.6428,
- 0.3420
- };
- static inline double min(double x, double y) { return (x <= y ? x : y); }
- static inline double max(double x, double y) { return (x <= y ? y : x); }
- static inline int min(int x, int y) { return (x <= y ? x : y); }
- static inline int max(int x, int y) { return (x <= y ? y : x); }
- // main function:
- // takes a double gray scale image and a bin size
- // returns HOG features
- mxArray *process(const mxArray *mximage, const mxArray *mxsbin)
- {
- double *im = (double *)mxGetPr(mximage);
- const int *dims = mxGetDimensions(mximage);
-
-
- if (mxGetNumberOfDimensions(mximage) != 2 ||
- mxGetClassID(mximage) != mxDOUBLE_CLASS)
- mexErrMsgTxt("Invalid input, either not a gray scale img (!= 2 dim), or input not given as double matrix.");
- // size of a cell in pixel, used for both dimensions
- int sbin = (int)mxGetScalar(mxsbin);
- // memory for caching orientation histograms & their norms
- int blocks[2];
- // compute number of cells fitting into current image given speficied cell size in pixel
- // use floor to prevent running over image borders
- blocks[0] = (int)floor( (double)dims[0] / (double)sbin );
- blocks[1] = (int)floor( (double)dims[1] / (double)sbin );
-
- // pre-allocate memory
- double *hist = (double *)mxCalloc( blocks[0]*blocks[1]*18, sizeof(double) );
- double *norm = (double *)mxCalloc( blocks[0]*blocks[1], sizeof(double) );
-
- // memory for HOG features
- int out[3];
- // we compute as many blocks as possible given the specified cell size and the current image
- out[0] = max(blocks[0], 0);
- out[1] = max(blocks[1], 0);
- // note: previously, a subtraction of 2 was done to guarantee identical bilinear interpolation behaviour in all cells
- // however, are more interested in obtaining a correct number of returned cells, rather than in slightly more consistent interpolation results...
- // apart from that, the only cells affected are the ones on the top and left border of the cell array
-
- out[2] = 27+4+1;
- mxArray *mxfeat = mxCreateNumericArray(3, out, mxDOUBLE_CLASS, mxREAL);
- double *feat = (double *)mxGetPr(mxfeat);
-
-
- int visible[2];
- visible[0] = blocks[0]*sbin;
- visible[1] = blocks[1]*sbin;
-
- // If you want to be 100% save, init output values here.
- // Note that this should not be needed, since every value will be set lateron explicitely.
- //
- //double *dst = feat;
- //for ( int tmp = 0; tmp < out[0]*out[1]*out[2]; tmp++, dst++ )
- //{
- // *dst = 0.0;
- //}
-
- // start by 1 and end by -1 to ensure safe gradient calculations on boundaries
- for (int x = 1; x < visible[1]-1; x++)
- {
- for (int y = 1; y < visible[0]-1; y++)
- {
- // simple gray scale channel
- // NOTE: why minimum check? boundary safety is given by for loop ends
- // col offset current row position
- double *s = im + min(x, dims[1]-1)*dims[0] + min(y, dims[0]-1);
- //gradient in y direction
- double dy = *(s+1) - *(s-1);
- // gradient in x direction
- double dx = *(s+dims[0]) - *(s-dims[0]);
- // squared gradient strength on current pixel
- double v = dx*dx + dy*dy;
- //
- // now, discretize gradient orientation into one of 18 possible (oriented) bins
- //
-
- // strength of strongest orientation in this pixel
- double best_dot = 0;
- // index of strongest orientation in this pixel
- int best_o = 0;
-
- for (int o = 0; o < 9; o++)
- {
- double dot = uu[o]*dx + vv[o]*dy;
-
- if (dot > best_dot)
- {
- best_dot = dot;
- best_o = o;
- }
- else if (-dot > best_dot)
- {
- best_dot = -dot;
- best_o = o+9;
- }
- }
-
- // add to 4 histograms around pixel using linear interpolation
-
- // current position normalized to cell scale, e.g. xp = 1.4 -> located in the left part of second cell
- // subtraction of 0.5 to move relative to cell center
- double xp = ((double)x+0.5)/(double)sbin - 0.5;
- double yp = ((double)y+0.5)/(double)sbin - 0.5;
-
- // that's the index of the cell, whose center is directly left of current position in x direction
- int ixp = (int)floor(xp);
- // that's the index of the cell, whose center is directly on top of current position in y direction
- int iyp = (int)floor(yp);
-
- // check whether current pixel is nearer to left or right boundary of cell
-
- // distance to left, used for weighting the gradient strength by 1-distance, guaranteed to be in [0,1]
- double vx0 = xp-ixp;
- // distance to top
- double vy0 = yp-iyp;
- // distance to right
- double vx1 = 1.0-vx0;
- // distance to bottom
- double vy1 = 1.0-vy0;
-
- // normalized gradient strength on current pixel
- v = sqrt(v);
- // if left upper cell exists
- if ( (ixp >= 0) && (iyp >= 0) )
- {
- *(hist + ixp*blocks[0] + iyp + best_o*blocks[0]*blocks[1]) +=
- vx1*vy1*v; // i.e., (1-distX0)*(1-distY0)*v
- }
- // if right upper cell exists
- if ( (ixp+2 < blocks[1]) && (iyp >= 0) )
- {
- *(hist + (ixp+1)*blocks[0] + iyp + best_o*blocks[0]*blocks[1]) +=
- vx0*vy1*v;
- }
- // if left lower cell exists
- if ( (ixp >= 0) && (iyp+2 < blocks[0]) )
- {
- *(hist + ixp*blocks[0] + (iyp+1) + best_o*blocks[0]*blocks[1]) +=
- vx1*vy0*v;
- }
- // if right lower cell exists
- if ( (ixp+2 < blocks[1]) && (iyp+2 < blocks[0]) )
- {
- *(hist + (ixp+1)*blocks[0] + (iyp+1) + best_o*blocks[0]*blocks[1]) +=
- vx0*vy0*v;
- }
- }
- }
- // compute energy in each block by summing over orientations
- for (int o = 0; o < 9; o++)
- {
- double *src1 = hist + o*blocks[0]*blocks[1];
- double *src2 = hist + (o+9)*blocks[0]*blocks[1];
- double *dst = norm;
- double *end = norm + blocks[1]*blocks[0];
-
- while (dst < end)
- {
- *(dst++) += (*src1 + *src2) * (*src1 + *src2);
- src1++;
- src2++;
- }
- }
- // compute features
- for (int x = 0; x < out[1]; x++)
- {
- for (int y = 0; y < out[0]; y++)
- {
- double *dst = feat + x*out[0] + y;
- double *src, *p, n1, n2, n3, n4;
- // compute normalization factors for all 4 possible blocks of 2x2 cells
-
-
-
- // block with current, right, lower, and lower right cell
- if ( ( (x+1) < out[1] ) && ( (y+1) < out[0] ) )
- {
- p = norm + x*blocks[0] + y;
- n1 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps);
- }
- else
- n1 = 1.0 / sqrt(eps);
-
- // block with current, upper, right, and upper right cell
- if ( ( (x+1) < out[1] ) && ( (y-1) >= 0 ) )
- {
- p = norm + x*blocks[0] + (y-1);
- n2 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps);
- }
- else
- n2 = 1.0 / sqrt(eps);
-
- // block with current, lower, left, and lower left cell
- if ( ( (x-1) >= 0 ) && ( (y+1) < out[0] ) )
- {
- p = norm + (x-1)*blocks[0] + y;
- n3 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps);
- }
- else
- n3 = 1.0 / sqrt(eps);
-
- // block with current, upper, left, and upper left cell
- if ( ( (x-1) >= 0 ) && ( (y-1) >= 0 ) )
- {
- p = norm + (x-1)*blocks[0] + (y-1);
- n4 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps);
- }
- else
- n4 = 1.0 / sqrt(eps);
-
-
- // copy normalization factors for blocks on boundaries
- // -----------------
- // | n4 | | n2 |
- // -----------------
- // | | x,y | |
- // -----------------
- // | n3 | | n1 |
- // -----------------
- if ( (x) == 0 ) // left boundary
- {
- if ( (y) == 0 ) // left top corner
- {
- n4 = n1; n3 = n1; n2 = n1;
- }
- else if ( (y) == (out[0]-1) ) // left lower corner
- {
- n4 = n2; n3 = n2; n1 = n2;
- }
- else
- {
- n4 = n2; n3 = n1;
- }
- }
- else if ( (x) == (out[1]-1) ) // right boundary
- {
- if ( (y) == 0 ) // right top corner
- {
- n4 = n3; n2 = n3; n1 = n3;
- }
- else if ( (y) == (out[0]-1) ) // right lower corner
- {
- n3 = n4; n2 = n4; n1 = n4;
- }
- else
- {
- n2 = n4; n1 = n3;
- }
- }
- if ( (y) == 0 ) // upper boundary ( corners already tackled)
- {
- if ( ( x > 0 ) && ( x < (out[1]-1) ) )
- {
- n4 = n3; n2 = n1;
- }
- }
- else if ( (y) == (out[0]-1) ) // lower boundary ( corners already tackled)
- {
- if ( ( x > 0 ) && ( x < (out[1]-1) ) )
- {
- n3 = n4; n1 = n2;
- }
- }
-
-
-
- double t1 = 0;
- double t2 = 0;
- double t3 = 0;
- double t4 = 0;
-
- // contrast-sensitive features
- src = hist + (x)*blocks[0] + (y);
- for (int o = 0; o < 18; o++)
- {
- double h1 = min(*src * n1, 0.2);
- double h2 = min(*src * n2, 0.2);
- double h3 = min(*src * n3, 0.2);
- double h4 = min(*src * n4, 0.2);
- *dst = 0.5 * (h1 + h2 + h3 + h4);
- t1 += h1;
- t2 += h2;
- t3 += h3;
- t4 += h4;
- // move pointers to next position
- dst += out[0]*out[1];
- src += blocks[0]*blocks[1];
- }
-
- // contrast-insensitive features
- src = hist + (x)*blocks[0] + (y);
- for (int o = 0; o < 9; o++)
- {
- double sum = *src + *(src + 9*blocks[0]*blocks[1]);
- double h1 = min(sum * n1, 0.2);
- double h2 = min(sum * n2, 0.2);
- double h3 = min(sum * n3, 0.2);
- double h4 = min(sum * n4, 0.2);
- *dst = 0.5 * (h1 + h2 + h3 + h4);
- dst += out[0]*out[1];
- src += blocks[0]*blocks[1];
- }
- // texture features
- //to be complicable to FFLD code
- *dst = 0.2357 * t4;
- //*dst = 0.2357 * t1;
- dst += out[0]*out[1];
- *dst = 0.2357 * t2;
- dst += out[0]*out[1];
- *dst = 0.2357 * t3;
- dst += out[0]*out[1];
- //to be complicable to FFLD code
- *dst = 0.2357 * t1;
- //*dst = 0.2357 * t4;
- // truncation feature
- dst += out[0]*out[1];
- *dst = 0;
- }
- }
- mxFree(hist);
- mxFree(norm);
- return mxfeat;
- }
- // matlab entry point
- // F = featuresGrayScale(image, bin)
- // image should be gray scale with double values
- void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
- {
- if (nrhs != 2)
- mexErrMsgTxt("Wrong number of inputs");
- if (nlhs != 1)
- mexErrMsgTxt("Wrong number of outputs");
- plhs[0] = process(prhs[0], prhs[1]);
- }
|