featuresHOGorig.cc 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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. double vv[9] = {0.0000,
  16. 0.3420,
  17. 0.6428,
  18. 0.8660,
  19. 0.9848,
  20. 0.9848,
  21. 0.8660,
  22. 0.6428,
  23. 0.3420};
  24. static inline double min(double x, double y) { return (x <= y ? x : y); }
  25. static inline double max(double x, double y) { return (x <= y ? y : x); }
  26. static inline int min(int x, int y) { return (x <= y ? x : y); }
  27. static inline int max(int x, int y) { return (x <= y ? y : x); }
  28. // main function:
  29. // takes a double color image and a bin size
  30. // returns HOG features
  31. mxArray *process(const mxArray *mximage, const mxArray *mxsbin) {
  32. double *im = (double *)mxGetPr(mximage);
  33. const int *dims = mxGetDimensions(mximage);
  34. if (mxGetNumberOfDimensions(mximage) != 3 ||
  35. dims[2] != 3 ||
  36. mxGetClassID(mximage) != mxDOUBLE_CLASS)
  37. mexErrMsgTxt("Invalid input");
  38. int sbin = (int)mxGetScalar(mxsbin);
  39. // memory for caching orientation histograms & their norms
  40. int blocks[2];
  41. blocks[0] = (int)round((double)dims[0]/(double)sbin);
  42. blocks[1] = (int)round((double)dims[1]/(double)sbin);
  43. double *hist = (double *)mxCalloc(blocks[0]*blocks[1]*18, sizeof(double));
  44. double *norm = (double *)mxCalloc(blocks[0]*blocks[1], sizeof(double));
  45. // memory for HOG features
  46. int out[3];
  47. out[0] = max(blocks[0]-2, 0);
  48. out[1] = max(blocks[1]-2, 0);
  49. out[2] = 27+4+1;
  50. mxArray *mxfeat = mxCreateNumericArray(3, out, mxDOUBLE_CLASS, mxREAL);
  51. double *feat = (double *)mxGetPr(mxfeat);
  52. int visible[2];
  53. visible[0] = blocks[0]*sbin;
  54. visible[1] = blocks[1]*sbin;
  55. for (int x = 1; x < visible[1]-1; x++) {
  56. for (int y = 1; y < visible[0]-1; y++) {
  57. // first color channel
  58. double *s = im + min(x, dims[1]-2)*dims[0] + min(y, dims[0]-2);
  59. double dy = *(s+1) - *(s-1);
  60. double dx = *(s+dims[0]) - *(s-dims[0]);
  61. double v = dx*dx + dy*dy;
  62. // second color channel
  63. s += dims[0]*dims[1];
  64. double dy2 = *(s+1) - *(s-1);
  65. double dx2 = *(s+dims[0]) - *(s-dims[0]);
  66. double v2 = dx2*dx2 + dy2*dy2;
  67. // third color channel
  68. s += dims[0]*dims[1];
  69. double dy3 = *(s+1) - *(s-1);
  70. double dx3 = *(s+dims[0]) - *(s-dims[0]);
  71. double v3 = dx3*dx3 + dy3*dy3;
  72. // pick channel with strongest gradient
  73. if (v2 > v) {
  74. v = v2;
  75. dx = dx2;
  76. dy = dy2;
  77. }
  78. if (v3 > v) {
  79. v = v3;
  80. dx = dx3;
  81. dy = dy3;
  82. }
  83. // snap to one of 18 orientations
  84. double best_dot = 0;
  85. int best_o = 0;
  86. for (int o = 0; o < 9; o++) {
  87. double dot = uu[o]*dx + vv[o]*dy;
  88. if (dot > best_dot) {
  89. best_dot = dot;
  90. best_o = o;
  91. } else if (-dot > best_dot) {
  92. best_dot = -dot;
  93. best_o = o+9;
  94. }
  95. }
  96. // add to 4 histograms around pixel using linear interpolation
  97. double xp = ((double)x+0.5)/(double)sbin - 0.5;
  98. double yp = ((double)y+0.5)/(double)sbin - 0.5;
  99. int ixp = (int)floor(xp);
  100. int iyp = (int)floor(yp);
  101. double vx0 = xp-ixp;
  102. double vy0 = yp-iyp;
  103. double vx1 = 1.0-vx0;
  104. double vy1 = 1.0-vy0;
  105. v = sqrt(v);
  106. if (ixp >= 0 && iyp >= 0) {
  107. *(hist + ixp*blocks[0] + iyp + best_o*blocks[0]*blocks[1]) +=
  108. vx1*vy1*v;
  109. }
  110. if (ixp+1 < blocks[1] && iyp >= 0) {
  111. *(hist + (ixp+1)*blocks[0] + iyp + best_o*blocks[0]*blocks[1]) +=
  112. vx0*vy1*v;
  113. }
  114. if (ixp >= 0 && iyp+1 < blocks[0]) {
  115. *(hist + ixp*blocks[0] + (iyp+1) + best_o*blocks[0]*blocks[1]) +=
  116. vx1*vy0*v;
  117. }
  118. if (ixp+1 < blocks[1] && iyp+1 < blocks[0]) {
  119. *(hist + (ixp+1)*blocks[0] + (iyp+1) + best_o*blocks[0]*blocks[1]) +=
  120. vx0*vy0*v;
  121. }
  122. }
  123. }
  124. // compute energy in each block by summing over orientations
  125. for (int o = 0; o < 9; o++) {
  126. double *src1 = hist + o*blocks[0]*blocks[1];
  127. double *src2 = hist + (o+9)*blocks[0]*blocks[1];
  128. double *dst = norm;
  129. double *end = norm + blocks[1]*blocks[0];
  130. while (dst < end) {
  131. *(dst++) += (*src1 + *src2) * (*src1 + *src2);
  132. src1++;
  133. src2++;
  134. }
  135. }
  136. // compute features
  137. for (int x = 0; x < out[1]; x++) {
  138. for (int y = 0; y < out[0]; y++) {
  139. double *dst = feat + x*out[0] + y;
  140. double *src, *p, n1, n2, n3, n4;
  141. p = norm + (x+1)*blocks[0] + y+1;
  142. n1 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps);
  143. p = norm + (x+1)*blocks[0] + y;
  144. n2 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps);
  145. p = norm + x*blocks[0] + y+1;
  146. n3 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps);
  147. p = norm + x*blocks[0] + y;
  148. n4 = 1.0 / sqrt(*p + *(p+1) + *(p+blocks[0]) + *(p+blocks[0]+1) + eps);
  149. double t1 = 0;
  150. double t2 = 0;
  151. double t3 = 0;
  152. double t4 = 0;
  153. double bh_test=0;
  154. // contrast-sensitive features
  155. src = hist + (x+1)*blocks[0] + (y+1);
  156. for (int o = 0; o < 18; o++) {
  157. bh_test=bh_test+(*src * n1)+(*src * n2)+(*src * n3)+(*src * n4);
  158. double h1 = min(*src * n1, 0.2);
  159. double h2 = min(*src * n2, 0.2);
  160. double h3 = min(*src * n3, 0.2);
  161. double h4 = min(*src * n4, 0.2);
  162. *dst = 0.5 * (h1 + h2 + h3 + h4);
  163. t1 += h1;
  164. t2 += h2;
  165. t3 += h3;
  166. t4 += h4;
  167. dst += out[0]*out[1];
  168. src += blocks[0]*blocks[1];
  169. }
  170. //printf("%f\n", bh_test);
  171. // contrast-insensitive features
  172. src = hist + (x+1)*blocks[0] + (y+1);
  173. for (int o = 0; o < 9; o++) {
  174. double sum = *src + *(src + 9*blocks[0]*blocks[1]);
  175. double h1 = min(sum * n1, 0.2);
  176. double h2 = min(sum * n2, 0.2);
  177. double h3 = min(sum * n3, 0.2);
  178. double h4 = min(sum * n4, 0.2);
  179. *dst = 0.5 * (h1 + h2 + h3 + h4);
  180. dst += out[0]*out[1];
  181. src += blocks[0]*blocks[1];
  182. }
  183. // texture features
  184. *dst = 0.2357 * t1;
  185. dst += out[0]*out[1];
  186. *dst = 0.2357 * t2;
  187. dst += out[0]*out[1];
  188. *dst = 0.2357 * t3;
  189. dst += out[0]*out[1];
  190. *dst = 0.2357 * t4;
  191. // truncation feature
  192. dst += out[0]*out[1];
  193. *dst = 0;
  194. }
  195. }
  196. mxFree(hist);
  197. mxFree(norm);
  198. return mxfeat;
  199. }
  200. // matlab entry point
  201. // F = features(image, bin)
  202. // image should be color with double values
  203. void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
  204. if (nrhs != 2)
  205. mexErrMsgTxt("Wrong number of inputs");
  206. if (nlhs != 1)
  207. mexErrMsgTxt("Wrong number of outputs");
  208. plhs[0] = process(prhs[0], prhs[1]);
  209. }