1 // Copyright (C) 2013 Davis E. King (davis@dlib.net) 2 // License: Boost Software License See LICENSE.txt for the full license. 3 #ifndef DLIB_fHOG_Hh_ 4 #define DLIB_fHOG_Hh_ 5 6 #include "fhog_abstract.h" 7 #include "../matrix.h" 8 #include "../array2d.h" 9 #include "../array.h" 10 #include "../geometry.h" 11 #include "assign_image.h" 12 #include "draw.h" 13 #include "interpolation.h" 14 #include "../simd.h" 15 16 namespace dlib 17 { 18 19 // ---------------------------------------------------------------------------------------- 20 21 namespace impl_fhog 22 { 23 template <typename image_type, typename T> get_gradient(const int r,const int c,const image_type & img,matrix<T,2,1> & grad,T & len)24 inline typename dlib::enable_if_c<pixel_traits<typename image_type::pixel_type>::rgb>::type get_gradient ( 25 const int r, 26 const int c, 27 const image_type& img, 28 matrix<T,2,1>& grad, 29 T& len 30 ) 31 { 32 matrix<T, 2, 1> grad2, grad3; 33 // get the red gradient 34 grad(0) = (int)img[r][c+1].red-(int)img[r][c-1].red; 35 grad(1) = (int)img[r+1][c].red-(int)img[r-1][c].red; 36 len = length_squared(grad); 37 38 // get the green gradient 39 grad2(0) = (int)img[r][c+1].green-(int)img[r][c-1].green; 40 grad2(1) = (int)img[r+1][c].green-(int)img[r-1][c].green; 41 T v2 = length_squared(grad2); 42 43 // get the blue gradient 44 grad3(0) = (int)img[r][c+1].blue-(int)img[r][c-1].blue; 45 grad3(1) = (int)img[r+1][c].blue-(int)img[r-1][c].blue; 46 T v3 = length_squared(grad3); 47 48 // pick color with strongest gradient 49 if (v2 > len) 50 { 51 len = v2; 52 grad = grad2; 53 } 54 if (v3 > len) 55 { 56 len = v3; 57 grad = grad3; 58 } 59 } 60 61 template <typename image_type> get_gradient(const int r,const int c,const image_type & img,simd4f & grad_x,simd4f & grad_y,simd4f & len)62 inline typename dlib::enable_if_c<pixel_traits<typename image_type::pixel_type>::rgb>::type get_gradient ( 63 const int r, 64 const int c, 65 const image_type& img, 66 simd4f& grad_x, 67 simd4f& grad_y, 68 simd4f& len 69 ) 70 { 71 simd4i rleft((int)img[r][c-1].red, 72 (int)img[r][c].red, 73 (int)img[r][c+1].red, 74 (int)img[r][c+2].red); 75 simd4i rright((int)img[r][c+1].red, 76 (int)img[r][c+2].red, 77 (int)img[r][c+3].red, 78 (int)img[r][c+4].red); 79 simd4i rtop((int)img[r-1][c].red, 80 (int)img[r-1][c+1].red, 81 (int)img[r-1][c+2].red, 82 (int)img[r-1][c+3].red); 83 simd4i rbottom((int)img[r+1][c].red, 84 (int)img[r+1][c+1].red, 85 (int)img[r+1][c+2].red, 86 (int)img[r+1][c+3].red); 87 88 simd4i gleft((int)img[r][c-1].green, 89 (int)img[r][c].green, 90 (int)img[r][c+1].green, 91 (int)img[r][c+2].green); 92 simd4i gright((int)img[r][c+1].green, 93 (int)img[r][c+2].green, 94 (int)img[r][c+3].green, 95 (int)img[r][c+4].green); 96 simd4i gtop((int)img[r-1][c].green, 97 (int)img[r-1][c+1].green, 98 (int)img[r-1][c+2].green, 99 (int)img[r-1][c+3].green); 100 simd4i gbottom((int)img[r+1][c].green, 101 (int)img[r+1][c+1].green, 102 (int)img[r+1][c+2].green, 103 (int)img[r+1][c+3].green); 104 105 simd4i bleft((int)img[r][c-1].blue, 106 (int)img[r][c].blue, 107 (int)img[r][c+1].blue, 108 (int)img[r][c+2].blue); 109 simd4i bright((int)img[r][c+1].blue, 110 (int)img[r][c+2].blue, 111 (int)img[r][c+3].blue, 112 (int)img[r][c+4].blue); 113 simd4i btop((int)img[r-1][c].blue, 114 (int)img[r-1][c+1].blue, 115 (int)img[r-1][c+2].blue, 116 (int)img[r-1][c+3].blue); 117 simd4i bbottom((int)img[r+1][c].blue, 118 (int)img[r+1][c+1].blue, 119 (int)img[r+1][c+2].blue, 120 (int)img[r+1][c+3].blue); 121 122 simd4i grad_x_red = rright-rleft; 123 simd4i grad_y_red = rbottom-rtop; 124 simd4i grad_x_green = gright-gleft; 125 simd4i grad_y_green = gbottom-gtop; 126 simd4i grad_x_blue = bright-bleft; 127 simd4i grad_y_blue = bbottom-btop; 128 129 simd4i rlen = grad_x_red*grad_x_red + grad_y_red*grad_y_red; 130 simd4i glen = grad_x_green*grad_x_green + grad_y_green*grad_y_green; 131 simd4i blen = grad_x_blue*grad_x_blue + grad_y_blue*grad_y_blue; 132 133 simd4i cmp = rlen>glen; 134 simd4i tgrad_x = select(cmp,grad_x_red,grad_x_green); 135 simd4i tgrad_y = select(cmp,grad_y_red,grad_y_green); 136 simd4i tlen = select(cmp,rlen,glen); 137 138 cmp = tlen>blen; 139 grad_x = select(cmp,tgrad_x,grad_x_blue); 140 grad_y = select(cmp,tgrad_y,grad_y_blue); 141 len = select(cmp,tlen,blen); 142 } 143 144 // ------------------------------------------------------------------------------------ 145 146 template <typename image_type> get_gradient(const int r,const int c,const image_type & img,simd8f & grad_x,simd8f & grad_y,simd8f & len)147 inline typename dlib::enable_if_c<pixel_traits<typename image_type::pixel_type>::rgb>::type get_gradient( 148 const int r, 149 const int c, 150 const image_type& img, 151 simd8f& grad_x, 152 simd8f& grad_y, 153 simd8f& len 154 ) 155 { 156 simd8i rleft((int)img[r][c - 1].red, 157 (int)img[r][c].red, 158 (int)img[r][c + 1].red, 159 (int)img[r][c + 2].red, 160 (int)img[r][c + 3].red, 161 (int)img[r][c + 4].red, 162 (int)img[r][c + 5].red, 163 (int)img[r][c + 6].red); 164 simd8i rright((int)img[r][c + 1].red, 165 (int)img[r][c + 2].red, 166 (int)img[r][c + 3].red, 167 (int)img[r][c + 4].red, 168 (int)img[r][c + 5].red, 169 (int)img[r][c + 6].red, 170 (int)img[r][c + 7].red, 171 (int)img[r][c + 8].red); 172 simd8i rtop((int)img[r - 1][c].red, 173 (int)img[r - 1][c + 1].red, 174 (int)img[r - 1][c + 2].red, 175 (int)img[r - 1][c + 3].red, 176 (int)img[r - 1][c + 4].red, 177 (int)img[r - 1][c + 5].red, 178 (int)img[r - 1][c + 6].red, 179 (int)img[r - 1][c + 7].red); 180 simd8i rbottom((int)img[r + 1][c].red, 181 (int)img[r + 1][c + 1].red, 182 (int)img[r + 1][c + 2].red, 183 (int)img[r + 1][c + 3].red, 184 (int)img[r + 1][c + 4].red, 185 (int)img[r + 1][c + 5].red, 186 (int)img[r + 1][c + 6].red, 187 (int)img[r + 1][c + 7].red); 188 189 simd8i gleft((int)img[r][c - 1].green, 190 (int)img[r][c].green, 191 (int)img[r][c + 1].green, 192 (int)img[r][c + 2].green, 193 (int)img[r][c + 3].green, 194 (int)img[r][c + 4].green, 195 (int)img[r][c + 5].green, 196 (int)img[r][c + 6].green); 197 simd8i gright((int)img[r][c + 1].green, 198 (int)img[r][c + 2].green, 199 (int)img[r][c + 3].green, 200 (int)img[r][c + 4].green, 201 (int)img[r][c + 5].green, 202 (int)img[r][c + 6].green, 203 (int)img[r][c + 7].green, 204 (int)img[r][c + 8].green); 205 simd8i gtop((int)img[r - 1][c].green, 206 (int)img[r - 1][c + 1].green, 207 (int)img[r - 1][c + 2].green, 208 (int)img[r - 1][c + 3].green, 209 (int)img[r - 1][c + 4].green, 210 (int)img[r - 1][c + 5].green, 211 (int)img[r - 1][c + 6].green, 212 (int)img[r - 1][c + 7].green); 213 simd8i gbottom((int)img[r + 1][c].green, 214 (int)img[r + 1][c + 1].green, 215 (int)img[r + 1][c + 2].green, 216 (int)img[r + 1][c + 3].green, 217 (int)img[r + 1][c + 4].green, 218 (int)img[r + 1][c + 5].green, 219 (int)img[r + 1][c + 6].green, 220 (int)img[r + 1][c + 7].green); 221 222 simd8i bleft((int)img[r][c - 1].blue, 223 (int)img[r][c].blue, 224 (int)img[r][c + 1].blue, 225 (int)img[r][c + 2].blue, 226 (int)img[r][c + 3].blue, 227 (int)img[r][c + 4].blue, 228 (int)img[r][c + 5].blue, 229 (int)img[r][c + 6].blue); 230 simd8i bright((int)img[r][c + 1].blue, 231 (int)img[r][c + 2].blue, 232 (int)img[r][c + 3].blue, 233 (int)img[r][c + 4].blue, 234 (int)img[r][c + 5].blue, 235 (int)img[r][c + 6].blue, 236 (int)img[r][c + 7].blue, 237 (int)img[r][c + 8].blue); 238 simd8i btop((int)img[r - 1][c].blue, 239 (int)img[r - 1][c + 1].blue, 240 (int)img[r - 1][c + 2].blue, 241 (int)img[r - 1][c + 3].blue, 242 (int)img[r - 1][c + 4].blue, 243 (int)img[r - 1][c + 5].blue, 244 (int)img[r - 1][c + 6].blue, 245 (int)img[r - 1][c + 7].blue); 246 simd8i bbottom((int)img[r + 1][c].blue, 247 (int)img[r + 1][c + 1].blue, 248 (int)img[r + 1][c + 2].blue, 249 (int)img[r + 1][c + 3].blue, 250 (int)img[r + 1][c + 4].blue, 251 (int)img[r + 1][c + 5].blue, 252 (int)img[r + 1][c + 6].blue, 253 (int)img[r + 1][c + 7].blue); 254 255 simd8i grad_x_red = rright - rleft; 256 simd8i grad_y_red = rbottom - rtop; 257 simd8i grad_x_green = gright - gleft; 258 simd8i grad_y_green = gbottom - gtop; 259 simd8i grad_x_blue = bright - bleft; 260 simd8i grad_y_blue = bbottom - btop; 261 262 simd8i rlen = grad_x_red*grad_x_red + grad_y_red*grad_y_red; 263 simd8i glen = grad_x_green*grad_x_green + grad_y_green*grad_y_green; 264 simd8i blen = grad_x_blue*grad_x_blue + grad_y_blue*grad_y_blue; 265 266 simd8i cmp = rlen > glen; 267 simd8i tgrad_x = select(cmp, grad_x_red, grad_x_green); 268 simd8i tgrad_y = select(cmp, grad_y_red, grad_y_green); 269 simd8i tlen = select(cmp, rlen, glen); 270 271 cmp = tlen > blen; 272 grad_x = select(cmp, tgrad_x, grad_x_blue); 273 grad_y = select(cmp, tgrad_y, grad_y_blue); 274 len = select(cmp, tlen, blen); 275 } 276 277 // ------------------------------------------------------------------------------------ 278 279 template <typename image_type, typename T> get_gradient(const int r,const int c,const image_type & img,matrix<T,2,1> & grad,T & len)280 inline typename dlib::disable_if_c<pixel_traits<typename image_type::pixel_type>::rgb>::type get_gradient ( 281 const int r, 282 const int c, 283 const image_type& img, 284 matrix<T, 2, 1>& grad, 285 T& len 286 ) 287 { 288 grad(0) = (int)get_pixel_intensity(img[r][c+1])-(int)get_pixel_intensity(img[r][c-1]); 289 grad(1) = (int)get_pixel_intensity(img[r+1][c])-(int)get_pixel_intensity(img[r-1][c]); 290 len = length_squared(grad); 291 } 292 293 template <typename image_type> get_gradient(int r,int c,const image_type & img,simd4f & grad_x,simd4f & grad_y,simd4f & len)294 inline typename dlib::disable_if_c<pixel_traits<typename image_type::pixel_type>::rgb>::type get_gradient ( 295 int r, 296 int c, 297 const image_type& img, 298 simd4f& grad_x, 299 simd4f& grad_y, 300 simd4f& len 301 ) 302 { 303 simd4i left((int)get_pixel_intensity(img[r][c-1]), 304 (int)get_pixel_intensity(img[r][c]), 305 (int)get_pixel_intensity(img[r][c+1]), 306 (int)get_pixel_intensity(img[r][c+2])); 307 simd4i right((int)get_pixel_intensity(img[r][c+1]), 308 (int)get_pixel_intensity(img[r][c+2]), 309 (int)get_pixel_intensity(img[r][c+3]), 310 (int)get_pixel_intensity(img[r][c+4])); 311 312 simd4i top((int)get_pixel_intensity(img[r-1][c]), 313 (int)get_pixel_intensity(img[r-1][c+1]), 314 (int)get_pixel_intensity(img[r-1][c+2]), 315 (int)get_pixel_intensity(img[r-1][c+3])); 316 simd4i bottom((int)get_pixel_intensity(img[r+1][c]), 317 (int)get_pixel_intensity(img[r+1][c+1]), 318 (int)get_pixel_intensity(img[r+1][c+2]), 319 (int)get_pixel_intensity(img[r+1][c+3])); 320 321 grad_x = right-left; 322 grad_y = bottom-top; 323 324 len = (grad_x*grad_x + grad_y*grad_y); 325 } 326 327 // ------------------------------------------------------------------------------------ 328 329 template <typename image_type> get_gradient(int r,int c,const image_type & img,simd8f & grad_x,simd8f & grad_y,simd8f & len)330 inline typename dlib::disable_if_c<pixel_traits<typename image_type::pixel_type>::rgb>::type get_gradient( 331 int r, 332 int c, 333 const image_type& img, 334 simd8f& grad_x, 335 simd8f& grad_y, 336 simd8f& len 337 ) 338 { 339 simd8i left((int)get_pixel_intensity(img[r][c - 1]), 340 (int)get_pixel_intensity(img[r][c]), 341 (int)get_pixel_intensity(img[r][c + 1]), 342 (int)get_pixel_intensity(img[r][c + 2]), 343 (int)get_pixel_intensity(img[r][c + 3]), 344 (int)get_pixel_intensity(img[r][c + 4]), 345 (int)get_pixel_intensity(img[r][c + 5]), 346 (int)get_pixel_intensity(img[r][c + 6])); 347 simd8i right((int)get_pixel_intensity(img[r][c + 1]), 348 (int)get_pixel_intensity(img[r][c + 2]), 349 (int)get_pixel_intensity(img[r][c + 3]), 350 (int)get_pixel_intensity(img[r][c + 4]), 351 (int)get_pixel_intensity(img[r][c + 5]), 352 (int)get_pixel_intensity(img[r][c + 6]), 353 (int)get_pixel_intensity(img[r][c + 7]), 354 (int)get_pixel_intensity(img[r][c + 8])); 355 356 simd8i top((int)get_pixel_intensity(img[r - 1][c]), 357 (int)get_pixel_intensity(img[r - 1][c + 1]), 358 (int)get_pixel_intensity(img[r - 1][c + 2]), 359 (int)get_pixel_intensity(img[r - 1][c + 3]), 360 (int)get_pixel_intensity(img[r - 1][c + 4]), 361 (int)get_pixel_intensity(img[r - 1][c + 5]), 362 (int)get_pixel_intensity(img[r - 1][c + 6]), 363 (int)get_pixel_intensity(img[r - 1][c + 7])); 364 simd8i bottom((int)get_pixel_intensity(img[r + 1][c]), 365 (int)get_pixel_intensity(img[r + 1][c + 1]), 366 (int)get_pixel_intensity(img[r + 1][c + 2]), 367 (int)get_pixel_intensity(img[r + 1][c + 3]), 368 (int)get_pixel_intensity(img[r + 1][c + 4]), 369 (int)get_pixel_intensity(img[r + 1][c + 5]), 370 (int)get_pixel_intensity(img[r + 1][c + 6]), 371 (int)get_pixel_intensity(img[r + 1][c + 7])); 372 373 grad_x = right - left; 374 grad_y = bottom - top; 375 376 len = (grad_x*grad_x + grad_y*grad_y); 377 } 378 379 // ------------------------------------------------------------------------------------ 380 381 template <typename T, typename mm1, typename mm2> set_hog(dlib::array<array2d<T,mm1>,mm2> & hog,int o,int x,int y,const float & value)382 inline void set_hog ( 383 dlib::array<array2d<T,mm1>,mm2>& hog, 384 int o, 385 int x, 386 int y, 387 const float& value 388 ) 389 { 390 hog[o][y][x] = value; 391 } 392 393 template <typename T, typename mm1, typename mm2> init_hog(dlib::array<array2d<T,mm1>,mm2> & hog,int hog_nr,int hog_nc,int filter_rows_padding,int filter_cols_padding)394 void init_hog ( 395 dlib::array<array2d<T,mm1>,mm2>& hog, 396 int hog_nr, 397 int hog_nc, 398 int filter_rows_padding, 399 int filter_cols_padding 400 ) 401 { 402 const int num_hog_bands = 27+4; 403 hog.resize(num_hog_bands); 404 for (int i = 0; i < num_hog_bands; ++i) 405 { 406 hog[i].set_size(hog_nr+filter_rows_padding-1, hog_nc+filter_cols_padding-1); 407 rectangle rect = get_rect(hog[i]); 408 rect.top() += (filter_rows_padding-1)/2; 409 rect.left() += (filter_cols_padding-1)/2; 410 rect.right() -= filter_cols_padding/2; 411 rect.bottom() -= filter_rows_padding/2; 412 zero_border_pixels(hog[i],rect); 413 } 414 } 415 416 template <typename T, typename mm1, typename mm2> init_hog_zero_everything(dlib::array<array2d<T,mm1>,mm2> & hog,int hog_nr,int hog_nc,int filter_rows_padding,int filter_cols_padding)417 void init_hog_zero_everything ( 418 dlib::array<array2d<T,mm1>,mm2>& hog, 419 int hog_nr, 420 int hog_nc, 421 int filter_rows_padding, 422 int filter_cols_padding 423 ) 424 { 425 const int num_hog_bands = 27+4; 426 hog.resize(num_hog_bands); 427 for (int i = 0; i < num_hog_bands; ++i) 428 { 429 hog[i].set_size(hog_nr+filter_rows_padding-1, hog_nc+filter_cols_padding-1); 430 assign_all_pixels(hog[i], 0); 431 } 432 } 433 434 // ------------------------------------------------------------------------------------ 435 436 template <typename T, typename mm> set_hog(array2d<matrix<T,31,1>,mm> & hog,int o,int x,int y,const float & value)437 inline void set_hog ( 438 array2d<matrix<T,31,1>,mm>& hog, 439 int o, 440 int x, 441 int y, 442 const float& value 443 ) 444 { 445 hog[y][x](o) = value; 446 } 447 448 template <typename T, typename mm> init_hog(array2d<matrix<T,31,1>,mm> & hog,int hog_nr,int hog_nc,int filter_rows_padding,int filter_cols_padding)449 void init_hog ( 450 array2d<matrix<T,31,1>,mm>& hog, 451 int hog_nr, 452 int hog_nc, 453 int filter_rows_padding, 454 int filter_cols_padding 455 ) 456 { 457 hog.set_size(hog_nr+filter_rows_padding-1, hog_nc+filter_cols_padding-1); 458 459 // now zero out the border region 460 rectangle rect = get_rect(hog); 461 rect.top() += (filter_rows_padding-1)/2; 462 rect.left() += (filter_cols_padding-1)/2; 463 rect.right() -= filter_cols_padding/2; 464 rect.bottom() -= filter_rows_padding/2; 465 border_enumerator be(get_rect(hog),rect); 466 while (be.move_next()) 467 { 468 const point p = be.element(); 469 set_all_elements(hog[p.y()][p.x()], 0); 470 } 471 } 472 473 template <typename T, typename mm> init_hog_zero_everything(array2d<matrix<T,31,1>,mm> & hog,int hog_nr,int hog_nc,int filter_rows_padding,int filter_cols_padding)474 void init_hog_zero_everything ( 475 array2d<matrix<T,31,1>,mm>& hog, 476 int hog_nr, 477 int hog_nc, 478 int filter_rows_padding, 479 int filter_cols_padding 480 ) 481 { 482 hog.set_size(hog_nr+filter_rows_padding-1, hog_nc+filter_cols_padding-1); 483 484 for (long r = 0; r < hog.nr(); ++r) 485 { 486 for (long c = 0; c < hog.nc(); ++c) 487 { 488 set_all_elements(hog[r][c], 0); 489 } 490 } 491 } 492 493 // ------------------------------------------------------------------------------------ 494 495 template < 496 typename image_type, 497 typename out_type 498 > impl_extract_fhog_features_cell_size_1(const image_type & img_,out_type & hog,int filter_rows_padding,int filter_cols_padding)499 void impl_extract_fhog_features_cell_size_1( 500 const image_type& img_, 501 out_type& hog, 502 int filter_rows_padding, 503 int filter_cols_padding 504 ) 505 { 506 const_image_view<image_type> img(img_); 507 // make sure requires clause is not broken 508 DLIB_ASSERT( filter_rows_padding > 0 && 509 filter_cols_padding > 0 , 510 "\t void extract_fhog_features()" 511 << "\n\t Invalid inputs were given to this function. " 512 << "\n\t filter_rows_padding: " << filter_rows_padding 513 << "\n\t filter_cols_padding: " << filter_cols_padding 514 ); 515 516 /* 517 This function is an optimized version of impl_extract_fhog_features() for 518 the case where cell_size == 1. 519 */ 520 521 522 // unit vectors used to compute gradient orientation 523 matrix<float,2,1> directions[9]; 524 directions[0] = 1.0000, 0.0000; 525 directions[1] = 0.9397, 0.3420; 526 directions[2] = 0.7660, 0.6428; 527 directions[3] = 0.500, 0.8660; 528 directions[4] = 0.1736, 0.9848; 529 directions[5] = -0.1736, 0.9848; 530 directions[6] = -0.5000, 0.8660; 531 directions[7] = -0.7660, 0.6428; 532 directions[8] = -0.9397, 0.3420; 533 534 535 536 if (img.nr() <= 2 || img.nc() <= 2) 537 { 538 hog.clear(); 539 return; 540 } 541 542 array2d<unsigned char> angle(img.nr(), img.nc()); 543 544 array2d<float> norm(img.nr(), img.nc()); 545 zero_border_pixels(norm,1,1); 546 547 // memory for HOG features 548 const long hog_nr = img.nr()-2; 549 const long hog_nc = img.nc()-2; 550 551 const int padding_rows_offset = (filter_rows_padding-1)/2; 552 const int padding_cols_offset = (filter_cols_padding-1)/2; 553 init_hog_zero_everything(hog, hog_nr, hog_nc, filter_rows_padding, filter_cols_padding); 554 555 556 const int visible_nr = img.nr()-1; 557 const int visible_nc = img.nc()-1; 558 559 // First populate the gradient histograms 560 for (int y = 1; y < visible_nr; y++) 561 { 562 int x; 563 for (x = 1; x < visible_nc - 7; x += 8) 564 { 565 // v will be the length of the gradient vectors. 566 simd8f grad_x, grad_y, v; 567 get_gradient(y, x, img, grad_x, grad_y, v); 568 569 float _vv[8]; 570 v.store(_vv); 571 572 // Now snap the gradient to one of 18 orientations 573 simd8f best_dot = 0; 574 simd8f best_o = 0; 575 for (int o = 0; o < 9; o++) 576 { 577 simd8f dot = grad_x*directions[o](0) + grad_y*directions[o](1); 578 simd8f_bool cmp = dot>best_dot; 579 best_dot = select(cmp, dot, best_dot); 580 dot *= -1; 581 best_o = select(cmp, o, best_o); 582 583 cmp = dot > best_dot; 584 best_dot = select(cmp, dot, best_dot); 585 best_o = select(cmp, o + 9, best_o); 586 } 587 588 int32 _best_o[8]; simd8i(best_o).store(_best_o); 589 590 norm[y][x + 0] = _vv[0]; 591 norm[y][x + 1] = _vv[1]; 592 norm[y][x + 2] = _vv[2]; 593 norm[y][x + 3] = _vv[3]; 594 norm[y][x + 4] = _vv[4]; 595 norm[y][x + 5] = _vv[5]; 596 norm[y][x + 6] = _vv[6]; 597 norm[y][x + 7] = _vv[7]; 598 599 angle[y][x + 0] = _best_o[0]; 600 angle[y][x + 1] = _best_o[1]; 601 angle[y][x + 2] = _best_o[2]; 602 angle[y][x + 3] = _best_o[3]; 603 angle[y][x + 4] = _best_o[4]; 604 angle[y][x + 5] = _best_o[5]; 605 angle[y][x + 6] = _best_o[6]; 606 angle[y][x + 7] = _best_o[7]; 607 } 608 // Now process the right columns that don't fit into simd registers. 609 for (; x < visible_nc; x++) 610 { 611 matrix<float,2,1> grad; 612 float v; 613 get_gradient(y,x,img,grad,v); 614 615 // snap to one of 18 orientations 616 float best_dot = 0; 617 int best_o = 0; 618 for (int o = 0; o < 9; o++) 619 { 620 const float dot = dlib::dot(directions[o], grad); 621 if (dot > best_dot) 622 { 623 best_dot = dot; 624 best_o = o; 625 } 626 else if (-dot > best_dot) 627 { 628 best_dot = -dot; 629 best_o = o+9; 630 } 631 } 632 633 norm[y][x] = v; 634 angle[y][x] = best_o; 635 } 636 } 637 638 const float eps = 0.0001; 639 // compute features 640 for (int y = 0; y < hog_nr; y++) 641 { 642 const int yy = y+padding_rows_offset; 643 for (int x = 0; x < hog_nc; x++) 644 { 645 const simd4f z1(norm[y+1][x+1], 646 norm[y][x+1], 647 norm[y+1][x], 648 norm[y][x]); 649 650 const simd4f z2(norm[y+1][x+2], 651 norm[y][x+2], 652 norm[y+1][x+1], 653 norm[y][x+1]); 654 655 const simd4f z3(norm[y+2][x+1], 656 norm[y+1][x+1], 657 norm[y+2][x], 658 norm[y+1][x]); 659 660 const simd4f z4(norm[y+2][x+2], 661 norm[y+1][x+2], 662 norm[y+2][x+1], 663 norm[y+1][x+1]); 664 665 const simd4f temp0 = std::sqrt(norm[y+1][x+1]); 666 const simd4f nn = 0.2*sqrt(z1+z2+z3+z4+eps); 667 const simd4f n = 0.1/nn; 668 669 simd4f t = 0; 670 671 const int xx = x+padding_cols_offset; 672 673 simd4f h0 = min(temp0,nn)*n; 674 const float vv = sum(h0); 675 set_hog(hog,angle[y+1][x+1],xx,yy, vv); 676 t += h0; 677 678 t *= 2*0.2357; 679 680 // contrast-insensitive features 681 set_hog(hog,angle[y+1][x+1]%9+18,xx,yy, vv); 682 683 684 float temp[4]; 685 t.store(temp); 686 687 // texture features 688 set_hog(hog,27,xx,yy, temp[0]); 689 set_hog(hog,28,xx,yy, temp[1]); 690 set_hog(hog,29,xx,yy, temp[2]); 691 set_hog(hog,30,xx,yy, temp[3]); 692 } 693 } 694 } 695 696 // ------------------------------------------------------------------------------------ 697 698 template < 699 typename image_type, 700 typename out_type 701 > impl_extract_fhog_features(const image_type & img_,out_type & hog,int cell_size,int filter_rows_padding,int filter_cols_padding)702 void impl_extract_fhog_features( 703 const image_type& img_, 704 out_type& hog, 705 int cell_size, 706 int filter_rows_padding, 707 int filter_cols_padding 708 ) 709 { 710 const_image_view<image_type> img(img_); 711 // make sure requires clause is not broken 712 DLIB_ASSERT( cell_size > 0 && 713 filter_rows_padding > 0 && 714 filter_cols_padding > 0 , 715 "\t void extract_fhog_features()" 716 << "\n\t Invalid inputs were given to this function. " 717 << "\n\t cell_size: " << cell_size 718 << "\n\t filter_rows_padding: " << filter_rows_padding 719 << "\n\t filter_cols_padding: " << filter_cols_padding 720 ); 721 722 /* 723 This function implements the HOG feature extraction method described in 724 the paper: 725 P. Felzenszwalb, R. Girshick, D. McAllester, D. Ramanan 726 Object Detection with Discriminatively Trained Part Based Models 727 IEEE Transactions on Pattern Analysis and Machine Intelligence, Vol. 32, No. 9, Sep. 2010 728 729 Moreover, this function is derived from the HOG feature extraction code 730 from the features.cc file in the voc-releaseX code (see 731 http://people.cs.uchicago.edu/~rbg/latent/) which is has the following 732 license (note that the code has been modified to work with grayscale and 733 color as well as planar and interlaced input and output formats): 734 735 Copyright (C) 2011, 2012 Ross Girshick, Pedro Felzenszwalb 736 Copyright (C) 2008, 2009, 2010 Pedro Felzenszwalb, Ross Girshick 737 Copyright (C) 2007 Pedro Felzenszwalb, Deva Ramanan 738 739 Permission is hereby granted, free of charge, to any person obtaining 740 a copy of this software and associated documentation files (the 741 "Software"), to deal in the Software without restriction, including 742 without limitation the rights to use, copy, modify, merge, publish, 743 distribute, sublicense, and/or sell copies of the Software, and to 744 permit persons to whom the Software is furnished to do so, subject to 745 the following conditions: 746 747 The above copyright notice and this permission notice shall be 748 included in all copies or substantial portions of the Software. 749 750 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 751 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 752 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 753 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 754 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 755 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 756 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 757 */ 758 759 if (cell_size == 1) 760 { 761 impl_extract_fhog_features_cell_size_1(img_,hog,filter_rows_padding,filter_cols_padding); 762 return; 763 } 764 765 // unit vectors used to compute gradient orientation 766 matrix<float,2,1> directions[9]; 767 directions[0] = 1.0000, 0.0000; 768 directions[1] = 0.9397, 0.3420; 769 directions[2] = 0.7660, 0.6428; 770 directions[3] = 0.500, 0.8660; 771 directions[4] = 0.1736, 0.9848; 772 directions[5] = -0.1736, 0.9848; 773 directions[6] = -0.5000, 0.8660; 774 directions[7] = -0.7660, 0.6428; 775 directions[8] = -0.9397, 0.3420; 776 777 778 779 // First we allocate memory for caching orientation histograms & their norms. 780 const int cells_nr = (int)((float)img.nr()/(float)cell_size + 0.5); 781 const int cells_nc = (int)((float)img.nc()/(float)cell_size + 0.5); 782 783 if (cells_nr == 0 || cells_nc == 0) 784 { 785 hog.clear(); 786 return; 787 } 788 789 // We give hist extra padding around the edges (1 cell all the way around the 790 // edge) so we can avoid needing to do boundary checks when indexing into it 791 // later on. So some statements assign to the boundary but those values are 792 // never used. 793 array2d<matrix<float,18,1> > hist(cells_nr+2, cells_nc+2); 794 for (long r = 0; r < hist.nr(); ++r) 795 { 796 for (long c = 0; c < hist.nc(); ++c) 797 { 798 hist[r][c] = 0; 799 } 800 } 801 802 array2d<float> norm(cells_nr, cells_nc); 803 assign_all_pixels(norm, 0); 804 805 // memory for HOG features 806 const int hog_nr = std::max(cells_nr-2, 0); 807 const int hog_nc = std::max(cells_nc-2, 0); 808 if (hog_nr == 0 || hog_nc == 0) 809 { 810 hog.clear(); 811 return; 812 } 813 const int padding_rows_offset = (filter_rows_padding-1)/2; 814 const int padding_cols_offset = (filter_cols_padding-1)/2; 815 init_hog(hog, hog_nr, hog_nc, filter_rows_padding, filter_cols_padding); 816 817 const int visible_nr = std::min((long)cells_nr*cell_size,img.nr())-1; 818 const int visible_nc = std::min((long)cells_nc*cell_size,img.nc())-1; 819 820 // First populate the gradient histograms 821 for (int y = 1; y < visible_nr; y++) 822 { 823 const float yp = ((float)y+0.5)/(float)cell_size - 0.5; 824 const int iyp = (int)std::floor(yp); 825 const float vy0 = yp - iyp; 826 const float vy1 = 1.0 - vy0; 827 int x; 828 for (x = 1; x < visible_nc - 7; x += 8) 829 { 830 simd8f xx(x, x + 1, x + 2, x + 3, x + 4, x + 5, x + 6, x + 7); 831 // v will be the length of the gradient vectors. 832 simd8f grad_x, grad_y, v; 833 get_gradient(y, x, img, grad_x, grad_y, v); 834 835 // We will use bilinear interpolation to add into the histogram bins. 836 // So first we precompute the values needed to determine how much each 837 // pixel votes into each bin. 838 simd8f xp = (xx + 0.5) / (float)cell_size + 0.5; 839 simd8i ixp = simd8i(xp); 840 simd8f vx0 = xp - ixp; 841 simd8f vx1 = 1.0f - vx0; 842 843 v = sqrt(v); 844 845 // Now snap the gradient to one of 18 orientations 846 simd8f best_dot = 0; 847 simd8f best_o = 0; 848 for (int o = 0; o < 9; o++) 849 { 850 simd8f dot = grad_x*directions[o](0) + grad_y*directions[o](1); 851 simd8f_bool cmp = dot>best_dot; 852 best_dot = select(cmp, dot, best_dot); 853 dot *= -1; 854 best_o = select(cmp, o, best_o); 855 856 cmp = dot > best_dot; 857 best_dot = select(cmp, dot, best_dot); 858 best_o = select(cmp, o + 9, best_o); 859 } 860 861 862 // Add the gradient magnitude, v, to 4 histograms around pixel using 863 // bilinear interpolation. 864 vx1 *= v; 865 vx0 *= v; 866 // The amounts for each bin 867 simd8f v11 = vy1*vx1; 868 simd8f v01 = vy0*vx1; 869 simd8f v10 = vy1*vx0; 870 simd8f v00 = vy0*vx0; 871 872 int32 _best_o[8]; simd8i(best_o).store(_best_o); 873 int32 _ixp[8]; ixp.store(_ixp); 874 float _v11[8]; v11.store(_v11); 875 float _v01[8]; v01.store(_v01); 876 float _v10[8]; v10.store(_v10); 877 float _v00[8]; v00.store(_v00); 878 879 hist[iyp + 1][_ixp[0]](_best_o[0]) += _v11[0]; 880 hist[iyp + 1 + 1][_ixp[0]](_best_o[0]) += _v01[0]; 881 hist[iyp + 1][_ixp[0] + 1](_best_o[0]) += _v10[0]; 882 hist[iyp + 1 + 1][_ixp[0] + 1](_best_o[0]) += _v00[0]; 883 884 hist[iyp + 1][_ixp[1]](_best_o[1]) += _v11[1]; 885 hist[iyp + 1 + 1][_ixp[1]](_best_o[1]) += _v01[1]; 886 hist[iyp + 1][_ixp[1] + 1](_best_o[1]) += _v10[1]; 887 hist[iyp + 1 + 1][_ixp[1] + 1](_best_o[1]) += _v00[1]; 888 889 hist[iyp + 1][_ixp[2]](_best_o[2]) += _v11[2]; 890 hist[iyp + 1 + 1][_ixp[2]](_best_o[2]) += _v01[2]; 891 hist[iyp + 1][_ixp[2] + 1](_best_o[2]) += _v10[2]; 892 hist[iyp + 1 + 1][_ixp[2] + 1](_best_o[2]) += _v00[2]; 893 894 hist[iyp + 1][_ixp[3]](_best_o[3]) += _v11[3]; 895 hist[iyp + 1 + 1][_ixp[3]](_best_o[3]) += _v01[3]; 896 hist[iyp + 1][_ixp[3] + 1](_best_o[3]) += _v10[3]; 897 hist[iyp + 1 + 1][_ixp[3] + 1](_best_o[3]) += _v00[3]; 898 899 hist[iyp + 1][_ixp[4]](_best_o[4]) += _v11[4]; 900 hist[iyp + 1 + 1][_ixp[4]](_best_o[4]) += _v01[4]; 901 hist[iyp + 1][_ixp[4] + 1](_best_o[4]) += _v10[4]; 902 hist[iyp + 1 + 1][_ixp[4] + 1](_best_o[4]) += _v00[4]; 903 904 hist[iyp + 1][_ixp[5]](_best_o[5]) += _v11[5]; 905 hist[iyp + 1 + 1][_ixp[5]](_best_o[5]) += _v01[5]; 906 hist[iyp + 1][_ixp[5] + 1](_best_o[5]) += _v10[5]; 907 hist[iyp + 1 + 1][_ixp[5] + 1](_best_o[5]) += _v00[5]; 908 909 hist[iyp + 1][_ixp[6]](_best_o[6]) += _v11[6]; 910 hist[iyp + 1 + 1][_ixp[6]](_best_o[6]) += _v01[6]; 911 hist[iyp + 1][_ixp[6] + 1](_best_o[6]) += _v10[6]; 912 hist[iyp + 1 + 1][_ixp[6] + 1](_best_o[6]) += _v00[6]; 913 914 hist[iyp + 1][_ixp[7]](_best_o[7]) += _v11[7]; 915 hist[iyp + 1 + 1][_ixp[7]](_best_o[7]) += _v01[7]; 916 hist[iyp + 1][_ixp[7] + 1](_best_o[7]) += _v10[7]; 917 hist[iyp + 1 + 1][_ixp[7] + 1](_best_o[7]) += _v00[7]; 918 } 919 // Now process the right columns that don't fit into simd registers. 920 for (; x < visible_nc; x++) 921 { 922 matrix<float, 2, 1> grad; 923 float v; 924 get_gradient(y,x,img,grad,v); 925 926 // snap to one of 18 orientations 927 float best_dot = 0; 928 int best_o = 0; 929 for (int o = 0; o < 9; o++) 930 { 931 const float dot = dlib::dot(directions[o], grad); 932 if (dot > best_dot) 933 { 934 best_dot = dot; 935 best_o = o; 936 } 937 else if (-dot > best_dot) 938 { 939 best_dot = -dot; 940 best_o = o+9; 941 } 942 } 943 944 v = std::sqrt(v); 945 // add to 4 histograms around pixel using bilinear interpolation 946 const float xp = ((double)x + 0.5) / (double)cell_size - 0.5; 947 const int ixp = (int)std::floor(xp); 948 const float vx0 = xp - ixp; 949 const float vx1 = 1.0 - vx0; 950 951 hist[iyp+1][ixp+1](best_o) += vy1*vx1*v; 952 hist[iyp+1+1][ixp+1](best_o) += vy0*vx1*v; 953 hist[iyp+1][ixp+1+1](best_o) += vy1*vx0*v; 954 hist[iyp+1+1][ixp+1+1](best_o) += vy0*vx0*v; 955 } 956 } 957 958 // compute energy in each block by summing over orientations 959 for (int r = 0; r < cells_nr; ++r) 960 { 961 for (int c = 0; c < cells_nc; ++c) 962 { 963 for (int o = 0; o < 9; o++) 964 { 965 norm[r][c] += (hist[r+1][c+1](o) + hist[r+1][c+1](o+9)) * (hist[r+1][c+1](o) + hist[r+1][c+1](o+9)); 966 } 967 } 968 } 969 970 const float eps = 0.0001; 971 // compute features 972 for (int y = 0; y < hog_nr; y++) 973 { 974 const int yy = y+padding_rows_offset; 975 for (int x = 0; x < hog_nc; x++) 976 { 977 const simd4f z1(norm[y+1][x+1], 978 norm[y][x+1], 979 norm[y+1][x], 980 norm[y][x]); 981 982 const simd4f z2(norm[y+1][x+2], 983 norm[y][x+2], 984 norm[y+1][x+1], 985 norm[y][x+1]); 986 987 const simd4f z3(norm[y+2][x+1], 988 norm[y+1][x+1], 989 norm[y+2][x], 990 norm[y+1][x]); 991 992 const simd4f z4(norm[y+2][x+2], 993 norm[y+1][x+2], 994 norm[y+2][x+1], 995 norm[y+1][x+1]); 996 997 const simd4f nn = 0.2*sqrt(z1+z2+z3+z4+eps); 998 const simd4f n = 0.1/nn; 999 1000 simd4f t = 0; 1001 1002 const int xx = x+padding_cols_offset; 1003 1004 // contrast-sensitive features 1005 for (int o = 0; o < 18; o+=3) 1006 { 1007 simd4f temp0(hist[y+1+1][x+1+1](o)); 1008 simd4f temp1(hist[y+1+1][x+1+1](o+1)); 1009 simd4f temp2(hist[y+1+1][x+1+1](o+2)); 1010 simd4f h0 = min(temp0,nn)*n; 1011 simd4f h1 = min(temp1,nn)*n; 1012 simd4f h2 = min(temp2,nn)*n; 1013 set_hog(hog,o,xx,yy, sum(h0)); 1014 set_hog(hog,o+1,xx,yy, sum(h1)); 1015 set_hog(hog,o+2,xx,yy, sum(h2)); 1016 t += h0+h1+h2; 1017 } 1018 1019 t *= 2*0.2357; 1020 1021 // contrast-insensitive features 1022 for (int o = 0; o < 9; o+=3) 1023 { 1024 simd4f temp0 = hist[y+1+1][x+1+1](o) + hist[y+1+1][x+1+1](o+9); 1025 simd4f temp1 = hist[y+1+1][x+1+1](o+1) + hist[y+1+1][x+1+1](o+9+1); 1026 simd4f temp2 = hist[y+1+1][x+1+1](o+2) + hist[y+1+1][x+1+1](o+9+2); 1027 simd4f h0 = min(temp0,nn)*n; 1028 simd4f h1 = min(temp1,nn)*n; 1029 simd4f h2 = min(temp2,nn)*n; 1030 set_hog(hog,o+18,xx,yy, sum(h0)); 1031 set_hog(hog,o+18+1,xx,yy, sum(h1)); 1032 set_hog(hog,o+18+2,xx,yy, sum(h2)); 1033 } 1034 1035 1036 float temp[4]; 1037 t.store(temp); 1038 1039 // texture features 1040 set_hog(hog,27,xx,yy, temp[0]); 1041 set_hog(hog,28,xx,yy, temp[1]); 1042 set_hog(hog,29,xx,yy, temp[2]); 1043 set_hog(hog,30,xx,yy, temp[3]); 1044 } 1045 } 1046 } 1047 1048 // ------------------------------------------------------------------------------------ 1049 create_fhog_bar_images(dlib::array<matrix<float>> & mbars,const long w)1050 inline void create_fhog_bar_images ( 1051 dlib::array<matrix<float> >& mbars, 1052 const long w 1053 ) 1054 { 1055 const long bdims = 9; 1056 // Make the oriented lines we use to draw on each HOG cell. 1057 mbars.resize(bdims); 1058 dlib::array<array2d<unsigned char> > bars(bdims); 1059 array2d<unsigned char> temp(w,w); 1060 for (unsigned long i = 0; i < bars.size(); ++i) 1061 { 1062 assign_all_pixels(temp, 0); 1063 draw_line(temp, point(w/2,0), point(w/2,w-1), 255); 1064 rotate_image(temp, bars[i], i*-pi/bars.size()); 1065 1066 mbars[i] = subm(matrix_cast<float>(mat(bars[i])), centered_rect(get_rect(bars[i]),w,w) ); 1067 } 1068 } 1069 1070 } // end namespace impl_fhog 1071 1072 // ---------------------------------------------------------------------------------------- 1073 // ---------------------------------------------------------------------------------------- 1074 // ---------------------------------------------------------------------------------------- 1075 // ---------------------------------------------------------------------------------------- 1076 1077 template < 1078 typename image_type, 1079 typename T, 1080 typename mm1, 1081 typename mm2 1082 > 1083 void extract_fhog_features( 1084 const image_type& img, 1085 dlib::array<array2d<T,mm1>,mm2>& hog, 1086 int cell_size = 8, 1087 int filter_rows_padding = 1, 1088 int filter_cols_padding = 1 1089 ) 1090 { 1091 impl_fhog::impl_extract_fhog_features(img, hog, cell_size, filter_rows_padding, filter_cols_padding); 1092 // If the image is too small then the above function outputs an empty feature map. 1093 // But to make things very uniform in usage we require the output to still have the 1094 // 31 planes (but they are just empty). 1095 if (hog.size() == 0) 1096 hog.resize(31); 1097 } 1098 1099 template < 1100 typename image_type, 1101 typename T, 1102 typename mm 1103 > 1104 void extract_fhog_features( 1105 const image_type& img, 1106 array2d<matrix<T,31,1>,mm>& hog, 1107 int cell_size = 8, 1108 int filter_rows_padding = 1, 1109 int filter_cols_padding = 1 1110 ) 1111 { 1112 impl_fhog::impl_extract_fhog_features(img, hog, cell_size, filter_rows_padding, filter_cols_padding); 1113 } 1114 1115 // ---------------------------------------------------------------------------------------- 1116 1117 template < 1118 typename image_type, 1119 typename T 1120 > 1121 void extract_fhog_features( 1122 const image_type& img, 1123 matrix<T,0,1>& feats, 1124 int cell_size = 8, 1125 int filter_rows_padding = 1, 1126 int filter_cols_padding = 1 1127 ) 1128 { 1129 dlib::array<array2d<T> > hog; 1130 extract_fhog_features(img, hog, cell_size, filter_rows_padding, filter_cols_padding); 1131 feats.set_size(hog.size()*hog[0].size()); 1132 for (unsigned long i = 0; i < hog.size(); ++i) 1133 { 1134 const long size = hog[i].size(); 1135 set_rowm(feats, range(i*size, (i+1)*size-1)) = reshape_to_column_vector(mat(hog[i])); 1136 } 1137 } 1138 1139 // ---------------------------------------------------------------------------------------- 1140 1141 template < 1142 typename image_type 1143 > 1144 matrix<double,0,1> extract_fhog_features( 1145 const image_type& img, 1146 int cell_size = 8, 1147 int filter_rows_padding = 1, 1148 int filter_cols_padding = 1 1149 ) 1150 { 1151 matrix<double, 0, 1> feats; 1152 extract_fhog_features(img, feats, cell_size, filter_rows_padding, filter_cols_padding); 1153 return feats; 1154 } 1155 1156 // ---------------------------------------------------------------------------------------- 1157 // ---------------------------------------------------------------------------------------- 1158 1159 inline point image_to_fhog ( 1160 point p, 1161 int cell_size = 8, 1162 int filter_rows_padding = 1, 1163 int filter_cols_padding = 1 1164 ) 1165 { 1166 // make sure requires clause is not broken 1167 DLIB_ASSERT( cell_size > 0 && 1168 filter_rows_padding > 0 && 1169 filter_cols_padding > 0 , 1170 "\t point image_to_fhog()" 1171 << "\n\t Invalid inputs were given to this function. " 1172 << "\n\t cell_size: " << cell_size 1173 << "\n\t filter_rows_padding: " << filter_rows_padding 1174 << "\n\t filter_cols_padding: " << filter_cols_padding 1175 ); 1176 1177 // There is a one pixel border around the image. 1178 p -= point(1,1); 1179 // There is also a 1 "cell" border around the HOG image formation. 1180 return p/cell_size - point(1,1) + point((filter_cols_padding-1)/2,(filter_rows_padding-1)/2); 1181 } 1182 1183 // ---------------------------------------------------------------------------------------- 1184 1185 inline rectangle image_to_fhog ( 1186 const rectangle& rect, 1187 int cell_size = 8, 1188 int filter_rows_padding = 1, 1189 int filter_cols_padding = 1 1190 ) 1191 { 1192 // make sure requires clause is not broken 1193 DLIB_ASSERT( cell_size > 0 && 1194 filter_rows_padding > 0 && 1195 filter_cols_padding > 0 , 1196 "\t rectangle image_to_fhog()" 1197 << "\n\t Invalid inputs were given to this function. " 1198 << "\n\t cell_size: " << cell_size 1199 << "\n\t filter_rows_padding: " << filter_rows_padding 1200 << "\n\t filter_cols_padding: " << filter_cols_padding 1201 ); 1202 1203 return rectangle(image_to_fhog(rect.tl_corner(),cell_size,filter_rows_padding,filter_cols_padding), 1204 image_to_fhog(rect.br_corner(),cell_size,filter_rows_padding,filter_cols_padding)); 1205 } 1206 1207 // ---------------------------------------------------------------------------------------- 1208 1209 inline point fhog_to_image ( 1210 point p, 1211 int cell_size = 8, 1212 int filter_rows_padding = 1, 1213 int filter_cols_padding = 1 1214 ) 1215 { 1216 // make sure requires clause is not broken 1217 DLIB_ASSERT( cell_size > 0 && 1218 filter_rows_padding > 0 && 1219 filter_cols_padding > 0 , 1220 "\t point fhog_to_image()" 1221 << "\n\t Invalid inputs were given to this function. " 1222 << "\n\t cell_size: " << cell_size 1223 << "\n\t filter_rows_padding: " << filter_rows_padding 1224 << "\n\t filter_cols_padding: " << filter_cols_padding 1225 ); 1226 1227 // Convert to image space and then set to the center of the cell. 1228 point offset; 1229 1230 p = (p+point(1,1)-point((filter_cols_padding-1)/2,(filter_rows_padding-1)/2))*cell_size + point(1,1); 1231 if (p.x() >= 0 && p.y() >= 0) offset = point(cell_size/2,cell_size/2); 1232 if (p.x() < 0 && p.y() >= 0) offset = point(-cell_size/2,cell_size/2); 1233 if (p.x() >= 0 && p.y() < 0) offset = point(cell_size/2,-cell_size/2); 1234 if (p.x() < 0 && p.y() < 0) offset = point(-cell_size/2,-cell_size/2); 1235 return p + offset; 1236 } 1237 1238 // ---------------------------------------------------------------------------------------- 1239 1240 inline rectangle fhog_to_image ( 1241 const rectangle& rect, 1242 int cell_size = 8, 1243 int filter_rows_padding = 1, 1244 int filter_cols_padding = 1 1245 ) 1246 { 1247 // make sure requires clause is not broken 1248 DLIB_ASSERT( cell_size > 0 && 1249 filter_rows_padding > 0 && 1250 filter_cols_padding > 0 , 1251 "\t rectangle fhog_to_image()" 1252 << "\n\t Invalid inputs were given to this function. " 1253 << "\n\t cell_size: " << cell_size 1254 << "\n\t filter_rows_padding: " << filter_rows_padding 1255 << "\n\t filter_cols_padding: " << filter_cols_padding 1256 ); 1257 1258 return rectangle(fhog_to_image(rect.tl_corner(),cell_size,filter_rows_padding,filter_cols_padding), 1259 fhog_to_image(rect.br_corner(),cell_size,filter_rows_padding,filter_cols_padding)); 1260 } 1261 1262 // ---------------------------------------------------------------------------------------- 1263 // ---------------------------------------------------------------------------------------- 1264 1265 template < 1266 typename T, 1267 typename mm1, 1268 typename mm2 1269 > 1270 matrix<unsigned char> draw_fhog( 1271 const dlib::array<array2d<T,mm1>,mm2>& hog, 1272 const long cell_draw_size = 15, 1273 const float min_response_threshold = 0.0 1274 ) 1275 { 1276 // make sure requires clause is not broken 1277 DLIB_ASSERT( cell_draw_size > 0 && hog.size()==31, 1278 "\t matrix<unsigned char> draw_fhog()" 1279 << "\n\t Invalid inputs were given to this function. " 1280 << "\n\t cell_draw_size: " << cell_draw_size 1281 << "\n\t hog.size(): " << hog.size() 1282 ); 1283 1284 dlib::array<matrix<float> > mbars; 1285 impl_fhog::create_fhog_bar_images(mbars,cell_draw_size); 1286 1287 // now draw the bars onto the HOG cells 1288 matrix<float> himg(hog[0].nr()*cell_draw_size, hog[0].nc()*cell_draw_size); 1289 himg = 0; 1290 for (unsigned long d = 0; d < mbars.size(); ++d) 1291 { 1292 for (long r = 0; r < himg.nr(); r+=cell_draw_size) 1293 { 1294 for (long c = 0; c < himg.nc(); c+=cell_draw_size) 1295 { 1296 const float val = hog[d][r/cell_draw_size][c/cell_draw_size] + 1297 hog[d+mbars.size()][r/cell_draw_size][c/cell_draw_size] + 1298 hog[d+mbars.size()*2][r/cell_draw_size][c/cell_draw_size]; 1299 if (val > min_response_threshold) 1300 { 1301 set_subm(himg, r, c, cell_draw_size, cell_draw_size) += val*mbars[d%mbars.size()]; 1302 } 1303 } 1304 } 1305 } 1306 1307 const float thresh = mean(himg) + 4 * stddev(himg); 1308 if (thresh != 0) 1309 return matrix_cast<unsigned char>(upperbound(round(himg*255/thresh),255)); 1310 else 1311 return matrix_cast<unsigned char>(himg); 1312 } 1313 1314 // ---------------------------------------------------------------------------------------- 1315 1316 template < 1317 typename T 1318 > 1319 matrix<unsigned char> draw_fhog ( 1320 const std::vector<matrix<T> >& hog, 1321 const long cell_draw_size = 15, 1322 const float min_response_threshold = 0.0 1323 ) 1324 { 1325 // make sure requires clause is not broken 1326 DLIB_ASSERT( cell_draw_size > 0 && hog.size()==31, 1327 "\t matrix<unsigned char> draw_fhog()" 1328 << "\n\t Invalid inputs were given to this function. " 1329 << "\n\t cell_draw_size: " << cell_draw_size 1330 << "\n\t hog.size(): " << hog.size() 1331 ); 1332 1333 // Just convert the input into the right object and then call the above draw_fhog() 1334 // function on it. 1335 dlib::array<array2d<T> > temp(hog.size()); 1336 for (unsigned long i = 0; i < temp.size(); ++i) 1337 { 1338 temp[i].set_size(hog[i].nr(), hog[i].nc()); 1339 for (long r = 0; r < hog[i].nr(); ++r) 1340 { 1341 for (long c = 0; c < hog[i].nc(); ++c) 1342 { 1343 temp[i][r][c] = hog[i](r,c); 1344 } 1345 } 1346 } 1347 return draw_fhog(temp,cell_draw_size, min_response_threshold); 1348 } 1349 1350 // ---------------------------------------------------------------------------------------- 1351 1352 template < 1353 typename T, 1354 typename mm 1355 > 1356 matrix<unsigned char> draw_fhog( 1357 const array2d<matrix<T,31,1>,mm>& hog, 1358 const long cell_draw_size = 15, 1359 const float min_response_threshold = 0.0 1360 ) 1361 { 1362 // make sure requires clause is not broken 1363 DLIB_ASSERT( cell_draw_size > 0, 1364 "\t matrix<unsigned char> draw_fhog()" 1365 << "\n\t Invalid inputs were given to this function. " 1366 << "\n\t cell_draw_size: " << cell_draw_size 1367 ); 1368 1369 dlib::array<matrix<float> > mbars; 1370 impl_fhog::create_fhog_bar_images(mbars,cell_draw_size); 1371 1372 // now draw the bars onto the HOG cells 1373 matrix<float> himg(hog.nr()*cell_draw_size, hog.nc()*cell_draw_size); 1374 himg = 0; 1375 for (unsigned long d = 0; d < mbars.size(); ++d) 1376 { 1377 for (long r = 0; r < himg.nr(); r+=cell_draw_size) 1378 { 1379 for (long c = 0; c < himg.nc(); c+=cell_draw_size) 1380 { 1381 const float val = hog[r/cell_draw_size][c/cell_draw_size](d) + 1382 hog[r/cell_draw_size][c/cell_draw_size](d+mbars.size()) + 1383 hog[r/cell_draw_size][c/cell_draw_size](d+mbars.size()*2); 1384 if (val > min_response_threshold) 1385 { 1386 set_subm(himg, r, c, cell_draw_size, cell_draw_size) += val*mbars[d%mbars.size()]; 1387 } 1388 } 1389 } 1390 } 1391 1392 const float thresh = mean(himg) + 4 * stddev(himg); 1393 if (thresh != 0) 1394 return matrix_cast<unsigned char>(upperbound(round(himg*255/thresh),255)); 1395 else 1396 return matrix_cast<unsigned char>(himg); 1397 } 1398 1399 // ---------------------------------------------------------------------------------------- 1400 1401 } 1402 1403 #endif // DLIB_fHOG_Hh_ 1404 1405