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