featuresHOGGrayScale.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. #include <math.h>
  2. #include "mex.h"
  3. // small value, used to avoid division by zero
  4. #define eps 0.0001
  5. // unit vectors used to compute gradient orientation
  6. double uu[9] = {1.0000,
  7. 0.9397,
  8. 0.7660,
  9. 0.500,
  10. 0.1736,
  11. -0.1736,
  12. -0.5000,
  13. -0.7660,
  14. -0.9397
  15. };
  16. double vv[9] = {0.0000,
  17. 0.3420,
  18. 0.6428,
  19. 0.8660,
  20. 0.9848,
  21. 0.9848,
  22. 0.8660,
  23. 0.6428,
  24. 0.3420
  25. };
  26. static inline double min(double x, double y) { return (x <= y ? x : y); }
  27. static inline double max(double x, double y) { return (x <= y ? y : x); }
  28. static inline int min(int x, int y) { return (x <= y ? x : y); }
  29. static inline int max(int x, int y) { return (x <= y ? y : x); }
  30. // main function:
  31. // takes a double gray scale image and a bin size
  32. // returns HOG features
  33. mxArray *process(const mxArray *mximage, const mxArray *mxsbin)
  34. {
  35. double *im = (double *)mxGetPr(mximage);
  36. const int *dims = mxGetDimensions(mximage);
  37. if (mxGetNumberOfDimensions(mximage) != 2 ||
  38. mxGetClassID(mximage) != mxDOUBLE_CLASS)
  39. mexErrMsgTxt("Invalid input, either not a gray scale img (!= 2 dim), or input not given as double matrix.");
  40. // size of a cell in pixel, used for both dimensions
  41. int sbin = (int)mxGetScalar(mxsbin);
  42. // memory for caching orientation histograms & their norms
  43. int blocks[2];
  44. // compute number of cells fitting into current image given speficied cell size in pixel
  45. // use floor to prevent running over image borders
  46. blocks[0] = (int)floor( (double)dims[0] / (double)sbin );
  47. blocks[1] = (int)floor( (double)dims[1] / (double)sbin );
  48. // pre-allocate memory
  49. double *hist = (double *)mxCalloc( blocks[0]*blocks[1]*18, sizeof(double) );
  50. double *norm = (double *)mxCalloc( blocks[0]*blocks[1], sizeof(double) );
  51. // memory for HOG features
  52. int out[3];
  53. // we compute as many blocks as possible given the specified cell size and the current image
  54. out[0] = max(blocks[0], 0);
  55. out[1] = max(blocks[1], 0);
  56. // note: previously, a subtraction of 2 was done to guarantee identical bilinear interpolation behaviour in all cells
  57. // however, are more interested in obtaining a correct number of returned cells, rather than in slightly more consistent interpolation results...
  58. // apart from that, the only cells affected are the ones on the top and left border of the cell array
  59. out[2] = 27+4+1;
  60. mxArray *mxfeat = mxCreateNumericArray(3, out, mxDOUBLE_CLASS, mxREAL);
  61. double *feat = (double *)mxGetPr(mxfeat);
  62. int visible[2];
  63. visible[0] = blocks[0]*sbin;
  64. visible[1] = blocks[1]*sbin;
  65. // If you want to be 100% save, init output values here.
  66. // Note that this should not be needed, since every value will be set lateron explicitely.
  67. //
  68. //double *dst = feat;
  69. //for ( int tmp = 0; tmp < out[0]*out[1]*out[2]; tmp++, dst++ )
  70. //{
  71. // *dst = 0.0;
  72. //}
  73. // start by 1 and end by -1 to ensure safe gradient calculations on boundaries
  74. for (int x = 1; x < visible[1]-1; x++)
  75. {
  76. for (int y = 1; y < visible[0]-1; y++)
  77. {
  78. // simple gray scale channel
  79. // NOTE: why minimum check? boundary safety is given by for loop ends
  80. // col offset current row position
  81. double *s = im + min(x, dims[1]-1)*dims[0] + min(y, dims[0]-1);
  82. //gradient in y direction
  83. double dy = *(s+1) - *(s-1);
  84. // gradient in x direction
  85. double dx = *(s+dims[0]) - *(s-dims[0]);
  86. // squared gradient strength on current pixel
  87. double v = dx*dx + dy*dy;
  88. //
  89. // now, discretize gradient orientation into one of 18 possible (oriented) bins
  90. //
  91. // strength of strongest orientation in this pixel
  92. double best_dot = 0;
  93. // index of strongest orientation in this pixel
  94. int best_o = 0;
  95. for (int o = 0; o < 9; o++)
  96. {
  97. double dot = uu[o]*dx + vv[o]*dy;
  98. if (dot > best_dot)
  99. {
  100. best_dot = dot;
  101. best_o = o;
  102. }
  103. else if (-dot > best_dot)
  104. {
  105. best_dot = -dot;
  106. best_o = o+9;
  107. }
  108. }
  109. // add to 4 histograms around pixel using linear interpolation
  110. // current position normalized to cell scale, e.g. xp = 1.4 -> located in the left part of second cell
  111. // subtraction of 0.5 to move relative to cell center
  112. double xp = ((double)x+0.5)/(double)sbin - 0.5;
  113. double yp = ((double)y+0.5)/(double)sbin - 0.5;
  114. // that's the index of the cell, whose center is directly left of current position in x direction
  115. int ixp = (int)floor(xp);
  116. // that's the index of the cell, whose center is directly on top of current position in y direction
  117. int iyp = (int)floor(yp);
  118. // check whether current pixel is nearer to left or right boundary of cell
  119. // distance to left, used for weighting the gradient strength by 1-distance, guaranteed to be in [0,1]
  120. double vx0 = xp-ixp;
  121. // distance to top
  122. double vy0 = yp-iyp;
  123. // distance to right
  124. double vx1 = 1.0-vx0;
  125. // distance to bottom
  126. double vy1 = 1.0-vy0;
  127. // normalized gradient strength on current pixel
  128. v = sqrt(v);
  129. // if left upper cell exists
  130. if ( (ixp >= 0) && (iyp >= 0) )
  131. {
  132. *(hist + ixp*blocks[0] + iyp + best_o*blocks[0]*blocks[1]) +=
  133. vx1*vy1*v; // i.e., (1-distX0)*(1-distY0)*v
  134. }
  135. // if right upper cell exists
  136. if ( (ixp+2 < blocks[1]) && (iyp >= 0) )
  137. {
  138. *(hist + (ixp+1)*blocks[0] + iyp + best_o*blocks[0]*blocks[1]) +=
  139. vx0*vy1*v;
  140. }
  141. // if left lower cell exists
  142. if ( (ixp >= 0) && (iyp+2 < blocks[0]) )
  143. {
  144. *(hist + ixp*blocks[0] + (iyp+1) + best_o*blocks[0]*blocks[1]) +=
  145. vx1*vy0*v;
  146. }
  147. // if right lower cell exists
  148. if ( (ixp+2 < blocks[1]) && (iyp+2 < blocks[0]) )
  149. {
  150. *(hist + (ixp+1)*blocks[0] + (iyp+1) + best_o*blocks[0]*blocks[1]) +=
  151. vx0*vy0*v;
  152. }
  153. }
  154. }
  155. // compute energy in each block by summing over orientations
  156. for (int o = 0; o < 9; o++)
  157. {
  158. double *src1 = hist + o*blocks[0]*blocks[1];
  159. double *src2 = hist + (o+9)*blocks[0]*blocks[1];
  160. double *dst = norm;
  161. double *end = norm + blocks[1]*blocks[0];
  162. while (dst < end)
  163. {
  164. *(dst++) += (*src1 + *src2) * (*src1 + *src2);
  165. src1++;
  166. src2++;
  167. }
  168. }
  169. // compute features
  170. for (int x = 0; x < out[1]; x++)
  171. {
  172. for (int y = 0; y < out[0]; y++)
  173. {
  174. double *dst = feat + x*out[0] + y;
  175. double *src, *p, n1, n2, n3, n4;
  176. // compute normalization factors for all 4 possible blocks of 2x2 cells
  177. // block with current, right, lower, and lower right cell
  178. if ( ( (x+1) < out[1] ) && ( (y+1) < out[0] ) )
  179. {
  180. p = norm + x*blocks[0] + y;
  181. n1 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps);
  182. }
  183. else
  184. n1 = 1.0 / sqrt(eps);
  185. // block with current, upper, right, and upper right cell
  186. if ( ( (x+1) < out[1] ) && ( (y-1) >= 0 ) )
  187. {
  188. p = norm + x*blocks[0] + (y-1);
  189. n2 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps);
  190. }
  191. else
  192. n2 = 1.0 / sqrt(eps);
  193. // block with current, lower, left, and lower left cell
  194. if ( ( (x-1) >= 0 ) && ( (y+1) < out[0] ) )
  195. {
  196. p = norm + (x-1)*blocks[0] + y;
  197. n3 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps);
  198. }
  199. else
  200. n3 = 1.0 / sqrt(eps);
  201. // block with current, upper, left, and upper left cell
  202. if ( ( (x-1) >= 0 ) && ( (y-1) >= 0 ) )
  203. {
  204. p = norm + (x-1)*blocks[0] + (y-1);
  205. n4 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps);
  206. }
  207. else
  208. n4 = 1.0 / sqrt(eps);
  209. // copy normalization factors for blocks on boundaries
  210. // -----------------
  211. // | n4 | | n2 |
  212. // -----------------
  213. // | | x,y | |
  214. // -----------------
  215. // | n3 | | n1 |
  216. // -----------------
  217. if ( (x) == 0 ) // left boundary
  218. {
  219. if ( (y) == 0 ) // left top corner
  220. {
  221. n4 = n1; n3 = n1; n2 = n1;
  222. }
  223. else if ( (y) == (out[0]-1) ) // left lower corner
  224. {
  225. n4 = n2; n3 = n2; n1 = n2;
  226. }
  227. else
  228. {
  229. n4 = n2; n3 = n1;
  230. }
  231. }
  232. else if ( (x) == (out[1]-1) ) // right boundary
  233. {
  234. if ( (y) == 0 ) // right top corner
  235. {
  236. n4 = n3; n2 = n3; n1 = n3;
  237. }
  238. else if ( (y) == (out[0]-1) ) // right lower corner
  239. {
  240. n3 = n4; n2 = n4; n1 = n4;
  241. }
  242. else
  243. {
  244. n2 = n4; n1 = n3;
  245. }
  246. }
  247. if ( (y) == 0 ) // upper boundary ( corners already tackled)
  248. {
  249. if ( ( x > 0 ) && ( x < (out[1]-1) ) )
  250. {
  251. n4 = n3; n2 = n1;
  252. }
  253. }
  254. else if ( (y) == (out[0]-1) ) // lower boundary ( corners already tackled)
  255. {
  256. if ( ( x > 0 ) && ( x < (out[1]-1) ) )
  257. {
  258. n3 = n4; n1 = n2;
  259. }
  260. }
  261. double t1 = 0;
  262. double t2 = 0;
  263. double t3 = 0;
  264. double t4 = 0;
  265. // contrast-sensitive features
  266. src = hist + (x)*blocks[0] + (y);
  267. for (int o = 0; o < 18; o++)
  268. {
  269. double h1 = min(*src * n1, 0.2);
  270. double h2 = min(*src * n2, 0.2);
  271. double h3 = min(*src * n3, 0.2);
  272. double h4 = min(*src * n4, 0.2);
  273. *dst = 0.5 * (h1 + h2 + h3 + h4);
  274. t1 += h1;
  275. t2 += h2;
  276. t3 += h3;
  277. t4 += h4;
  278. // move pointers to next position
  279. dst += out[0]*out[1];
  280. src += blocks[0]*blocks[1];
  281. }
  282. // contrast-insensitive features
  283. src = hist + (x)*blocks[0] + (y);
  284. for (int o = 0; o < 9; o++)
  285. {
  286. double sum = *src + *(src + 9*blocks[0]*blocks[1]);
  287. double h1 = min(sum * n1, 0.2);
  288. double h2 = min(sum * n2, 0.2);
  289. double h3 = min(sum * n3, 0.2);
  290. double h4 = min(sum * n4, 0.2);
  291. *dst = 0.5 * (h1 + h2 + h3 + h4);
  292. dst += out[0]*out[1];
  293. src += blocks[0]*blocks[1];
  294. }
  295. // texture features
  296. //to be complicable to FFLD code
  297. *dst = 0.2357 * t4;
  298. //*dst = 0.2357 * t1;
  299. dst += out[0]*out[1];
  300. *dst = 0.2357 * t2;
  301. dst += out[0]*out[1];
  302. *dst = 0.2357 * t3;
  303. dst += out[0]*out[1];
  304. //to be complicable to FFLD code
  305. *dst = 0.2357 * t1;
  306. //*dst = 0.2357 * t4;
  307. // truncation feature
  308. dst += out[0]*out[1];
  309. *dst = 0;
  310. }
  311. }
  312. mxFree(hist);
  313. mxFree(norm);
  314. return mxfeat;
  315. }
  316. // matlab entry point
  317. // F = featuresGrayScale(image, bin)
  318. // image should be gray scale with double values
  319. void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
  320. {
  321. if (nrhs != 2)
  322. mexErrMsgTxt("Wrong number of inputs");
  323. if (nlhs != 1)
  324. mexErrMsgTxt("Wrong number of outputs");
  325. plhs[0] = process(prhs[0], prhs[1]);
  326. }