summaryrefslogtreecommitdiffstats
path: root/ml/dlib/dlib/image_transforms/fhog.h
diff options
context:
space:
mode:
Diffstat (limited to 'ml/dlib/dlib/image_transforms/fhog.h')
-rw-r--r--ml/dlib/dlib/image_transforms/fhog.h1404
1 files changed, 1404 insertions, 0 deletions
diff --git a/ml/dlib/dlib/image_transforms/fhog.h b/ml/dlib/dlib/image_transforms/fhog.h
new file mode 100644
index 000000000..d99973adf
--- /dev/null
+++ b/ml/dlib/dlib/image_transforms/fhog.h
@@ -0,0 +1,1404 @@
+// Copyright (C) 2013 Davis E. King (davis@dlib.net)
+// License: Boost Software License See LICENSE.txt for the full license.
+#ifndef DLIB_fHOG_Hh_
+#define DLIB_fHOG_Hh_
+
+#include "fhog_abstract.h"
+#include "../matrix.h"
+#include "../array2d.h"
+#include "../array.h"
+#include "../geometry.h"
+#include "assign_image.h"
+#include "draw.h"
+#include "interpolation.h"
+#include "../simd.h"
+
+namespace dlib
+{
+
+// ----------------------------------------------------------------------------------------
+
+ namespace impl_fhog
+ {
+ template <typename image_type, typename T>
+ inline typename dlib::enable_if_c<pixel_traits<typename image_type::pixel_type>::rgb>::type get_gradient (
+ const int r,
+ const int c,
+ const image_type& img,
+ matrix<T,2,1>& grad,
+ T& len
+ )
+ {
+ matrix<T, 2, 1> grad2, grad3;
+ // get the red gradient
+ grad(0) = (int)img[r][c+1].red-(int)img[r][c-1].red;
+ grad(1) = (int)img[r+1][c].red-(int)img[r-1][c].red;
+ len = length_squared(grad);
+
+ // get the green gradient
+ grad2(0) = (int)img[r][c+1].green-(int)img[r][c-1].green;
+ grad2(1) = (int)img[r+1][c].green-(int)img[r-1][c].green;
+ T v2 = length_squared(grad2);
+
+ // get the blue gradient
+ grad3(0) = (int)img[r][c+1].blue-(int)img[r][c-1].blue;
+ grad3(1) = (int)img[r+1][c].blue-(int)img[r-1][c].blue;
+ T v3 = length_squared(grad3);
+
+ // pick color with strongest gradient
+ if (v2 > len)
+ {
+ len = v2;
+ grad = grad2;
+ }
+ if (v3 > len)
+ {
+ len = v3;
+ grad = grad3;
+ }
+ }
+
+ template <typename image_type>
+ inline typename dlib::enable_if_c<pixel_traits<typename image_type::pixel_type>::rgb>::type get_gradient (
+ const int r,
+ const int c,
+ const image_type& img,
+ simd4f& grad_x,
+ simd4f& grad_y,
+ simd4f& len
+ )
+ {
+ simd4i rleft((int)img[r][c-1].red,
+ (int)img[r][c].red,
+ (int)img[r][c+1].red,
+ (int)img[r][c+2].red);
+ simd4i rright((int)img[r][c+1].red,
+ (int)img[r][c+2].red,
+ (int)img[r][c+3].red,
+ (int)img[r][c+4].red);
+ simd4i rtop((int)img[r-1][c].red,
+ (int)img[r-1][c+1].red,
+ (int)img[r-1][c+2].red,
+ (int)img[r-1][c+3].red);
+ simd4i rbottom((int)img[r+1][c].red,
+ (int)img[r+1][c+1].red,
+ (int)img[r+1][c+2].red,
+ (int)img[r+1][c+3].red);
+
+ simd4i gleft((int)img[r][c-1].green,
+ (int)img[r][c].green,
+ (int)img[r][c+1].green,
+ (int)img[r][c+2].green);
+ simd4i gright((int)img[r][c+1].green,
+ (int)img[r][c+2].green,
+ (int)img[r][c+3].green,
+ (int)img[r][c+4].green);
+ simd4i gtop((int)img[r-1][c].green,
+ (int)img[r-1][c+1].green,
+ (int)img[r-1][c+2].green,
+ (int)img[r-1][c+3].green);
+ simd4i gbottom((int)img[r+1][c].green,
+ (int)img[r+1][c+1].green,
+ (int)img[r+1][c+2].green,
+ (int)img[r+1][c+3].green);
+
+ simd4i bleft((int)img[r][c-1].blue,
+ (int)img[r][c].blue,
+ (int)img[r][c+1].blue,
+ (int)img[r][c+2].blue);
+ simd4i bright((int)img[r][c+1].blue,
+ (int)img[r][c+2].blue,
+ (int)img[r][c+3].blue,
+ (int)img[r][c+4].blue);
+ simd4i btop((int)img[r-1][c].blue,
+ (int)img[r-1][c+1].blue,
+ (int)img[r-1][c+2].blue,
+ (int)img[r-1][c+3].blue);
+ simd4i bbottom((int)img[r+1][c].blue,
+ (int)img[r+1][c+1].blue,
+ (int)img[r+1][c+2].blue,
+ (int)img[r+1][c+3].blue);
+
+ simd4i grad_x_red = rright-rleft;
+ simd4i grad_y_red = rbottom-rtop;
+ simd4i grad_x_green = gright-gleft;
+ simd4i grad_y_green = gbottom-gtop;
+ simd4i grad_x_blue = bright-bleft;
+ simd4i grad_y_blue = bbottom-btop;
+
+ simd4i rlen = grad_x_red*grad_x_red + grad_y_red*grad_y_red;
+ simd4i glen = grad_x_green*grad_x_green + grad_y_green*grad_y_green;
+ simd4i blen = grad_x_blue*grad_x_blue + grad_y_blue*grad_y_blue;
+
+ simd4i cmp = rlen>glen;
+ simd4i tgrad_x = select(cmp,grad_x_red,grad_x_green);
+ simd4i tgrad_y = select(cmp,grad_y_red,grad_y_green);
+ simd4i tlen = select(cmp,rlen,glen);
+
+ cmp = tlen>blen;
+ grad_x = select(cmp,tgrad_x,grad_x_blue);
+ grad_y = select(cmp,tgrad_y,grad_y_blue);
+ len = select(cmp,tlen,blen);
+ }
+
+ // ------------------------------------------------------------------------------------
+
+ template <typename image_type>
+ inline typename dlib::enable_if_c<pixel_traits<typename image_type::pixel_type>::rgb>::type get_gradient(
+ const int r,
+ const int c,
+ const image_type& img,
+ simd8f& grad_x,
+ simd8f& grad_y,
+ simd8f& len
+ )
+ {
+ simd8i rleft((int)img[r][c - 1].red,
+ (int)img[r][c].red,
+ (int)img[r][c + 1].red,
+ (int)img[r][c + 2].red,
+ (int)img[r][c + 3].red,
+ (int)img[r][c + 4].red,
+ (int)img[r][c + 5].red,
+ (int)img[r][c + 6].red);
+ simd8i rright((int)img[r][c + 1].red,
+ (int)img[r][c + 2].red,
+ (int)img[r][c + 3].red,
+ (int)img[r][c + 4].red,
+ (int)img[r][c + 5].red,
+ (int)img[r][c + 6].red,
+ (int)img[r][c + 7].red,
+ (int)img[r][c + 8].red);
+ simd8i rtop((int)img[r - 1][c].red,
+ (int)img[r - 1][c + 1].red,
+ (int)img[r - 1][c + 2].red,
+ (int)img[r - 1][c + 3].red,
+ (int)img[r - 1][c + 4].red,
+ (int)img[r - 1][c + 5].red,
+ (int)img[r - 1][c + 6].red,
+ (int)img[r - 1][c + 7].red);
+ simd8i rbottom((int)img[r + 1][c].red,
+ (int)img[r + 1][c + 1].red,
+ (int)img[r + 1][c + 2].red,
+ (int)img[r + 1][c + 3].red,
+ (int)img[r + 1][c + 4].red,
+ (int)img[r + 1][c + 5].red,
+ (int)img[r + 1][c + 6].red,
+ (int)img[r + 1][c + 7].red);
+
+ simd8i gleft((int)img[r][c - 1].green,
+ (int)img[r][c].green,
+ (int)img[r][c + 1].green,
+ (int)img[r][c + 2].green,
+ (int)img[r][c + 3].green,
+ (int)img[r][c + 4].green,
+ (int)img[r][c + 5].green,
+ (int)img[r][c + 6].green);
+ simd8i gright((int)img[r][c + 1].green,
+ (int)img[r][c + 2].green,
+ (int)img[r][c + 3].green,
+ (int)img[r][c + 4].green,
+ (int)img[r][c + 5].green,
+ (int)img[r][c + 6].green,
+ (int)img[r][c + 7].green,
+ (int)img[r][c + 8].green);
+ simd8i gtop((int)img[r - 1][c].green,
+ (int)img[r - 1][c + 1].green,
+ (int)img[r - 1][c + 2].green,
+ (int)img[r - 1][c + 3].green,
+ (int)img[r - 1][c + 4].green,
+ (int)img[r - 1][c + 5].green,
+ (int)img[r - 1][c + 6].green,
+ (int)img[r - 1][c + 7].green);
+ simd8i gbottom((int)img[r + 1][c].green,
+ (int)img[r + 1][c + 1].green,
+ (int)img[r + 1][c + 2].green,
+ (int)img[r + 1][c + 3].green,
+ (int)img[r + 1][c + 4].green,
+ (int)img[r + 1][c + 5].green,
+ (int)img[r + 1][c + 6].green,
+ (int)img[r + 1][c + 7].green);
+
+ simd8i bleft((int)img[r][c - 1].blue,
+ (int)img[r][c].blue,
+ (int)img[r][c + 1].blue,
+ (int)img[r][c + 2].blue,
+ (int)img[r][c + 3].blue,
+ (int)img[r][c + 4].blue,
+ (int)img[r][c + 5].blue,
+ (int)img[r][c + 6].blue);
+ simd8i bright((int)img[r][c + 1].blue,
+ (int)img[r][c + 2].blue,
+ (int)img[r][c + 3].blue,
+ (int)img[r][c + 4].blue,
+ (int)img[r][c + 5].blue,
+ (int)img[r][c + 6].blue,
+ (int)img[r][c + 7].blue,
+ (int)img[r][c + 8].blue);
+ simd8i btop((int)img[r - 1][c].blue,
+ (int)img[r - 1][c + 1].blue,
+ (int)img[r - 1][c + 2].blue,
+ (int)img[r - 1][c + 3].blue,
+ (int)img[r - 1][c + 4].blue,
+ (int)img[r - 1][c + 5].blue,
+ (int)img[r - 1][c + 6].blue,
+ (int)img[r - 1][c + 7].blue);
+ simd8i bbottom((int)img[r + 1][c].blue,
+ (int)img[r + 1][c + 1].blue,
+ (int)img[r + 1][c + 2].blue,
+ (int)img[r + 1][c + 3].blue,
+ (int)img[r + 1][c + 4].blue,
+ (int)img[r + 1][c + 5].blue,
+ (int)img[r + 1][c + 6].blue,
+ (int)img[r + 1][c + 7].blue);
+
+ simd8i grad_x_red = rright - rleft;
+ simd8i grad_y_red = rbottom - rtop;
+ simd8i grad_x_green = gright - gleft;
+ simd8i grad_y_green = gbottom - gtop;
+ simd8i grad_x_blue = bright - bleft;
+ simd8i grad_y_blue = bbottom - btop;
+
+ simd8i rlen = grad_x_red*grad_x_red + grad_y_red*grad_y_red;
+ simd8i glen = grad_x_green*grad_x_green + grad_y_green*grad_y_green;
+ simd8i blen = grad_x_blue*grad_x_blue + grad_y_blue*grad_y_blue;
+
+ simd8i cmp = rlen > glen;
+ simd8i tgrad_x = select(cmp, grad_x_red, grad_x_green);
+ simd8i tgrad_y = select(cmp, grad_y_red, grad_y_green);
+ simd8i tlen = select(cmp, rlen, glen);
+
+ cmp = tlen > blen;
+ grad_x = select(cmp, tgrad_x, grad_x_blue);
+ grad_y = select(cmp, tgrad_y, grad_y_blue);
+ len = select(cmp, tlen, blen);
+ }
+
+ // ------------------------------------------------------------------------------------
+
+ template <typename image_type, typename T>
+ inline typename dlib::disable_if_c<pixel_traits<typename image_type::pixel_type>::rgb>::type get_gradient (
+ const int r,
+ const int c,
+ const image_type& img,
+ matrix<T, 2, 1>& grad,
+ T& len
+ )
+ {
+ grad(0) = (int)get_pixel_intensity(img[r][c+1])-(int)get_pixel_intensity(img[r][c-1]);
+ grad(1) = (int)get_pixel_intensity(img[r+1][c])-(int)get_pixel_intensity(img[r-1][c]);
+ len = length_squared(grad);
+ }
+
+ template <typename image_type>
+ inline typename dlib::disable_if_c<pixel_traits<typename image_type::pixel_type>::rgb>::type get_gradient (
+ int r,
+ int c,
+ const image_type& img,
+ simd4f& grad_x,
+ simd4f& grad_y,
+ simd4f& len
+ )
+ {
+ simd4i left((int)get_pixel_intensity(img[r][c-1]),
+ (int)get_pixel_intensity(img[r][c]),
+ (int)get_pixel_intensity(img[r][c+1]),
+ (int)get_pixel_intensity(img[r][c+2]));
+ simd4i right((int)get_pixel_intensity(img[r][c+1]),
+ (int)get_pixel_intensity(img[r][c+2]),
+ (int)get_pixel_intensity(img[r][c+3]),
+ (int)get_pixel_intensity(img[r][c+4]));
+
+ simd4i top((int)get_pixel_intensity(img[r-1][c]),
+ (int)get_pixel_intensity(img[r-1][c+1]),
+ (int)get_pixel_intensity(img[r-1][c+2]),
+ (int)get_pixel_intensity(img[r-1][c+3]));
+ simd4i bottom((int)get_pixel_intensity(img[r+1][c]),
+ (int)get_pixel_intensity(img[r+1][c+1]),
+ (int)get_pixel_intensity(img[r+1][c+2]),
+ (int)get_pixel_intensity(img[r+1][c+3]));
+
+ grad_x = right-left;
+ grad_y = bottom-top;
+
+ len = (grad_x*grad_x + grad_y*grad_y);
+ }
+
+ // ------------------------------------------------------------------------------------
+
+ template <typename image_type>
+ inline typename dlib::disable_if_c<pixel_traits<typename image_type::pixel_type>::rgb>::type get_gradient(
+ int r,
+ int c,
+ const image_type& img,
+ simd8f& grad_x,
+ simd8f& grad_y,
+ simd8f& len
+ )
+ {
+ simd8i left((int)get_pixel_intensity(img[r][c - 1]),
+ (int)get_pixel_intensity(img[r][c]),
+ (int)get_pixel_intensity(img[r][c + 1]),
+ (int)get_pixel_intensity(img[r][c + 2]),
+ (int)get_pixel_intensity(img[r][c + 3]),
+ (int)get_pixel_intensity(img[r][c + 4]),
+ (int)get_pixel_intensity(img[r][c + 5]),
+ (int)get_pixel_intensity(img[r][c + 6]));
+ simd8i right((int)get_pixel_intensity(img[r][c + 1]),
+ (int)get_pixel_intensity(img[r][c + 2]),
+ (int)get_pixel_intensity(img[r][c + 3]),
+ (int)get_pixel_intensity(img[r][c + 4]),
+ (int)get_pixel_intensity(img[r][c + 5]),
+ (int)get_pixel_intensity(img[r][c + 6]),
+ (int)get_pixel_intensity(img[r][c + 7]),
+ (int)get_pixel_intensity(img[r][c + 8]));
+
+ simd8i top((int)get_pixel_intensity(img[r - 1][c]),
+ (int)get_pixel_intensity(img[r - 1][c + 1]),
+ (int)get_pixel_intensity(img[r - 1][c + 2]),
+ (int)get_pixel_intensity(img[r - 1][c + 3]),
+ (int)get_pixel_intensity(img[r - 1][c + 4]),
+ (int)get_pixel_intensity(img[r - 1][c + 5]),
+ (int)get_pixel_intensity(img[r - 1][c + 6]),
+ (int)get_pixel_intensity(img[r - 1][c + 7]));
+ simd8i bottom((int)get_pixel_intensity(img[r + 1][c]),
+ (int)get_pixel_intensity(img[r + 1][c + 1]),
+ (int)get_pixel_intensity(img[r + 1][c + 2]),
+ (int)get_pixel_intensity(img[r + 1][c + 3]),
+ (int)get_pixel_intensity(img[r + 1][c + 4]),
+ (int)get_pixel_intensity(img[r + 1][c + 5]),
+ (int)get_pixel_intensity(img[r + 1][c + 6]),
+ (int)get_pixel_intensity(img[r + 1][c + 7]));
+
+ grad_x = right - left;
+ grad_y = bottom - top;
+
+ len = (grad_x*grad_x + grad_y*grad_y);
+ }
+
+ // ------------------------------------------------------------------------------------
+
+ template <typename T, typename mm1, typename mm2>
+ inline void set_hog (
+ dlib::array<array2d<T,mm1>,mm2>& hog,
+ int o,
+ int x,
+ int y,
+ const float& value
+ )
+ {
+ hog[o][y][x] = value;
+ }
+
+ template <typename T, typename mm1, typename mm2>
+ void init_hog (
+ dlib::array<array2d<T,mm1>,mm2>& hog,
+ int hog_nr,
+ int hog_nc,
+ int filter_rows_padding,
+ int filter_cols_padding
+ )
+ {
+ const int num_hog_bands = 27+4;
+ hog.resize(num_hog_bands);
+ for (int i = 0; i < num_hog_bands; ++i)
+ {
+ hog[i].set_size(hog_nr+filter_rows_padding-1, hog_nc+filter_cols_padding-1);
+ rectangle rect = get_rect(hog[i]);
+ rect.top() += (filter_rows_padding-1)/2;
+ rect.left() += (filter_cols_padding-1)/2;
+ rect.right() -= filter_cols_padding/2;
+ rect.bottom() -= filter_rows_padding/2;
+ zero_border_pixels(hog[i],rect);
+ }
+ }
+
+ template <typename T, typename mm1, typename mm2>
+ void 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
+ )
+ {
+ const int num_hog_bands = 27+4;
+ hog.resize(num_hog_bands);
+ for (int i = 0; i < num_hog_bands; ++i)
+ {
+ hog[i].set_size(hog_nr+filter_rows_padding-1, hog_nc+filter_cols_padding-1);
+ assign_all_pixels(hog[i], 0);
+ }
+ }
+
+ // ------------------------------------------------------------------------------------
+
+ template <typename T, typename mm>
+ inline void set_hog (
+ array2d<matrix<T,31,1>,mm>& hog,
+ int o,
+ int x,
+ int y,
+ const float& value
+ )
+ {
+ hog[y][x](o) = value;
+ }
+
+ template <typename T, typename mm>
+ void init_hog (
+ array2d<matrix<T,31,1>,mm>& hog,
+ int hog_nr,
+ int hog_nc,
+ int filter_rows_padding,
+ int filter_cols_padding
+ )
+ {
+ hog.set_size(hog_nr+filter_rows_padding-1, hog_nc+filter_cols_padding-1);
+
+ // now zero out the border region
+ rectangle rect = get_rect(hog);
+ rect.top() += (filter_rows_padding-1)/2;
+ rect.left() += (filter_cols_padding-1)/2;
+ rect.right() -= filter_cols_padding/2;
+ rect.bottom() -= filter_rows_padding/2;
+ border_enumerator be(get_rect(hog),rect);
+ while (be.move_next())
+ {
+ const point p = be.element();
+ set_all_elements(hog[p.y()][p.x()], 0);
+ }
+ }
+
+ template <typename T, typename mm>
+ void 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
+ )
+ {
+ hog.set_size(hog_nr+filter_rows_padding-1, hog_nc+filter_cols_padding-1);
+
+ for (long r = 0; r < hog.nr(); ++r)
+ {
+ for (long c = 0; c < hog.nc(); ++c)
+ {
+ set_all_elements(hog[r][c], 0);
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------------------
+
+ template <
+ typename image_type,
+ typename out_type
+ >
+ void impl_extract_fhog_features_cell_size_1(
+ const image_type& img_,
+ out_type& hog,
+ int filter_rows_padding,
+ int filter_cols_padding
+ )
+ {
+ const_image_view<image_type> img(img_);
+ // make sure requires clause is not broken
+ DLIB_ASSERT( filter_rows_padding > 0 &&
+ filter_cols_padding > 0 ,
+ "\t void extract_fhog_features()"
+ << "\n\t Invalid inputs were given to this function. "
+ << "\n\t filter_rows_padding: " << filter_rows_padding
+ << "\n\t filter_cols_padding: " << filter_cols_padding
+ );
+
+ /*
+ This function is an optimized version of impl_extract_fhog_features() for
+ the case where cell_size == 1.
+ */
+
+
+ // unit vectors used to compute gradient orientation
+ matrix<float,2,1> directions[9];
+ directions[0] = 1.0000, 0.0000;
+ directions[1] = 0.9397, 0.3420;
+ directions[2] = 0.7660, 0.6428;
+ directions[3] = 0.500, 0.8660;
+ directions[4] = 0.1736, 0.9848;
+ directions[5] = -0.1736, 0.9848;
+ directions[6] = -0.5000, 0.8660;
+ directions[7] = -0.7660, 0.6428;
+ directions[8] = -0.9397, 0.3420;
+
+
+
+ if (img.nr() <= 2 || img.nc() <= 2)
+ {
+ hog.clear();
+ return;
+ }
+
+ array2d<unsigned char> angle(img.nr(), img.nc());
+
+ array2d<float> norm(img.nr(), img.nc());
+ zero_border_pixels(norm,1,1);
+
+ // memory for HOG features
+ const long hog_nr = img.nr()-2;
+ const long hog_nc = img.nc()-2;
+
+ const int padding_rows_offset = (filter_rows_padding-1)/2;
+ const int padding_cols_offset = (filter_cols_padding-1)/2;
+ init_hog_zero_everything(hog, hog_nr, hog_nc, filter_rows_padding, filter_cols_padding);
+
+
+ const int visible_nr = img.nr()-1;
+ const int visible_nc = img.nc()-1;
+
+ // First populate the gradient histograms
+ for (int y = 1; y < visible_nr; y++)
+ {
+ int x;
+ for (x = 1; x < visible_nc - 7; x += 8)
+ {
+ // v will be the length of the gradient vectors.
+ simd8f grad_x, grad_y, v;
+ get_gradient(y, x, img, grad_x, grad_y, v);
+
+ float _vv[8];
+ v.store(_vv);
+
+ // Now snap the gradient to one of 18 orientations
+ simd8f best_dot = 0;
+ simd8f best_o = 0;
+ for (int o = 0; o < 9; o++)
+ {
+ simd8f dot = grad_x*directions[o](0) + grad_y*directions[o](1);
+ simd8f_bool cmp = dot>best_dot;
+ best_dot = select(cmp, dot, best_dot);
+ dot *= -1;
+ best_o = select(cmp, o, best_o);
+
+ cmp = dot > best_dot;
+ best_dot = select(cmp, dot, best_dot);
+ best_o = select(cmp, o + 9, best_o);
+ }
+
+ int32 _best_o[8]; simd8i(best_o).store(_best_o);
+
+ norm[y][x + 0] = _vv[0];
+ norm[y][x + 1] = _vv[1];
+ norm[y][x + 2] = _vv[2];
+ norm[y][x + 3] = _vv[3];
+ norm[y][x + 4] = _vv[4];
+ norm[y][x + 5] = _vv[5];
+ norm[y][x + 6] = _vv[6];
+ norm[y][x + 7] = _vv[7];
+
+ angle[y][x + 0] = _best_o[0];
+ angle[y][x + 1] = _best_o[1];
+ angle[y][x + 2] = _best_o[2];
+ angle[y][x + 3] = _best_o[3];
+ angle[y][x + 4] = _best_o[4];
+ angle[y][x + 5] = _best_o[5];
+ angle[y][x + 6] = _best_o[6];
+ angle[y][x + 7] = _best_o[7];
+ }
+ // Now process the right columns that don't fit into simd registers.
+ for (; x < visible_nc; x++)
+ {
+ matrix<float,2,1> grad;
+ float v;
+ get_gradient(y,x,img,grad,v);
+
+ // snap to one of 18 orientations
+ float best_dot = 0;
+ int best_o = 0;
+ for (int o = 0; o < 9; o++)
+ {
+ const float dot = dlib::dot(directions[o], grad);
+ if (dot > best_dot)
+ {
+ best_dot = dot;
+ best_o = o;
+ }
+ else if (-dot > best_dot)
+ {
+ best_dot = -dot;
+ best_o = o+9;
+ }
+ }
+
+ norm[y][x] = v;
+ angle[y][x] = best_o;
+ }
+ }
+
+ const float eps = 0.0001;
+ // compute features
+ for (int y = 0; y < hog_nr; y++)
+ {
+ const int yy = y+padding_rows_offset;
+ for (int x = 0; x < hog_nc; x++)
+ {
+ const simd4f z1(norm[y+1][x+1],
+ norm[y][x+1],
+ norm[y+1][x],
+ norm[y][x]);
+
+ const simd4f z2(norm[y+1][x+2],
+ norm[y][x+2],
+ norm[y+1][x+1],
+ norm[y][x+1]);
+
+ const simd4f z3(norm[y+2][x+1],
+ norm[y+1][x+1],
+ norm[y+2][x],
+ norm[y+1][x]);
+
+ const simd4f z4(norm[y+2][x+2],
+ norm[y+1][x+2],
+ norm[y+2][x+1],
+ norm[y+1][x+1]);
+
+ const simd4f temp0 = std::sqrt(norm[y+1][x+1]);
+ const simd4f nn = 0.2*sqrt(z1+z2+z3+z4+eps);
+ const simd4f n = 0.1/nn;
+
+ simd4f t = 0;
+
+ const int xx = x+padding_cols_offset;
+
+ simd4f h0 = min(temp0,nn)*n;
+ const float vv = sum(h0);
+ set_hog(hog,angle[y+1][x+1],xx,yy, vv);
+ t += h0;
+
+ t *= 2*0.2357;
+
+ // contrast-insensitive features
+ set_hog(hog,angle[y+1][x+1]%9+18,xx,yy, vv);
+
+
+ float temp[4];
+ t.store(temp);
+
+ // texture features
+ set_hog(hog,27,xx,yy, temp[0]);
+ set_hog(hog,28,xx,yy, temp[1]);
+ set_hog(hog,29,xx,yy, temp[2]);
+ set_hog(hog,30,xx,yy, temp[3]);
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------------------
+
+ template <
+ typename image_type,
+ typename out_type
+ >
+ void impl_extract_fhog_features(
+ const image_type& img_,
+ out_type& hog,
+ int cell_size,
+ int filter_rows_padding,
+ int filter_cols_padding
+ )
+ {
+ const_image_view<image_type> img(img_);
+ // make sure requires clause is not broken
+ DLIB_ASSERT( cell_size > 0 &&
+ filter_rows_padding > 0 &&
+ filter_cols_padding > 0 ,
+ "\t void extract_fhog_features()"
+ << "\n\t Invalid inputs were given to this function. "
+ << "\n\t cell_size: " << cell_size
+ << "\n\t filter_rows_padding: " << filter_rows_padding
+ << "\n\t filter_cols_padding: " << filter_cols_padding
+ );
+
+ /*
+ This function implements the HOG feature extraction method described in
+ the paper:
+ P. Felzenszwalb, R. Girshick, D. McAllester, D. Ramanan
+ Object Detection with Discriminatively Trained Part Based Models
+ IEEE Transactions on Pattern Analysis and Machine Intelligence, Vol. 32, No. 9, Sep. 2010
+
+ Moreover, this function is derived from the HOG feature extraction code
+ from the features.cc file in the voc-releaseX code (see
+ http://people.cs.uchicago.edu/~rbg/latent/) which is has the following
+ license (note that the code has been modified to work with grayscale and
+ color as well as planar and interlaced input and output formats):
+
+ Copyright (C) 2011, 2012 Ross Girshick, Pedro Felzenszwalb
+ Copyright (C) 2008, 2009, 2010 Pedro Felzenszwalb, Ross Girshick
+ Copyright (C) 2007 Pedro Felzenszwalb, Deva Ramanan
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+ if (cell_size == 1)
+ {
+ impl_extract_fhog_features_cell_size_1(img_,hog,filter_rows_padding,filter_cols_padding);
+ return;
+ }
+
+ // unit vectors used to compute gradient orientation
+ matrix<float,2,1> directions[9];
+ directions[0] = 1.0000, 0.0000;
+ directions[1] = 0.9397, 0.3420;
+ directions[2] = 0.7660, 0.6428;
+ directions[3] = 0.500, 0.8660;
+ directions[4] = 0.1736, 0.9848;
+ directions[5] = -0.1736, 0.9848;
+ directions[6] = -0.5000, 0.8660;
+ directions[7] = -0.7660, 0.6428;
+ directions[8] = -0.9397, 0.3420;
+
+
+
+ // First we allocate memory for caching orientation histograms & their norms.
+ const int cells_nr = (int)((float)img.nr()/(float)cell_size + 0.5);
+ const int cells_nc = (int)((float)img.nc()/(float)cell_size + 0.5);
+
+ if (cells_nr == 0 || cells_nc == 0)
+ {
+ hog.clear();
+ return;
+ }
+
+ // We give hist extra padding around the edges (1 cell all the way around the
+ // edge) so we can avoid needing to do boundary checks when indexing into it
+ // later on. So some statements assign to the boundary but those values are
+ // never used.
+ array2d<matrix<float,18,1> > hist(cells_nr+2, cells_nc+2);
+ for (long r = 0; r < hist.nr(); ++r)
+ {
+ for (long c = 0; c < hist.nc(); ++c)
+ {
+ hist[r][c] = 0;
+ }
+ }
+
+ array2d<float> norm(cells_nr, cells_nc);
+ assign_all_pixels(norm, 0);
+
+ // memory for HOG features
+ const int hog_nr = std::max(cells_nr-2, 0);
+ const int hog_nc = std::max(cells_nc-2, 0);
+ if (hog_nr == 0 || hog_nc == 0)
+ {
+ hog.clear();
+ return;
+ }
+ const int padding_rows_offset = (filter_rows_padding-1)/2;
+ const int padding_cols_offset = (filter_cols_padding-1)/2;
+ init_hog(hog, hog_nr, hog_nc, filter_rows_padding, filter_cols_padding);
+
+ const int visible_nr = std::min((long)cells_nr*cell_size,img.nr())-1;
+ const int visible_nc = std::min((long)cells_nc*cell_size,img.nc())-1;
+
+ // First populate the gradient histograms
+ for (int y = 1; y < visible_nr; y++)
+ {
+ const float yp = ((float)y+0.5)/(float)cell_size - 0.5;
+ const int iyp = (int)std::floor(yp);
+ const float vy0 = yp - iyp;
+ const float vy1 = 1.0 - vy0;
+ int x;
+ for (x = 1; x < visible_nc - 7; x += 8)
+ {
+ simd8f xx(x, x + 1, x + 2, x + 3, x + 4, x + 5, x + 6, x + 7);
+ // v will be the length of the gradient vectors.
+ simd8f grad_x, grad_y, v;
+ get_gradient(y, x, img, grad_x, grad_y, v);
+
+ // We will use bilinear interpolation to add into the histogram bins.
+ // So first we precompute the values needed to determine how much each
+ // pixel votes into each bin.
+ simd8f xp = (xx + 0.5) / (float)cell_size + 0.5;
+ simd8i ixp = simd8i(xp);
+ simd8f vx0 = xp - ixp;
+ simd8f vx1 = 1.0f - vx0;
+
+ v = sqrt(v);
+
+ // Now snap the gradient to one of 18 orientations
+ simd8f best_dot = 0;
+ simd8f best_o = 0;
+ for (int o = 0; o < 9; o++)
+ {
+ simd8f dot = grad_x*directions[o](0) + grad_y*directions[o](1);
+ simd8f_bool cmp = dot>best_dot;
+ best_dot = select(cmp, dot, best_dot);
+ dot *= -1;
+ best_o = select(cmp, o, best_o);
+
+ cmp = dot > best_dot;
+ best_dot = select(cmp, dot, best_dot);
+ best_o = select(cmp, o + 9, best_o);
+ }
+
+
+ // Add the gradient magnitude, v, to 4 histograms around pixel using
+ // bilinear interpolation.
+ vx1 *= v;
+ vx0 *= v;
+ // The amounts for each bin
+ simd8f v11 = vy1*vx1;
+ simd8f v01 = vy0*vx1;
+ simd8f v10 = vy1*vx0;
+ simd8f v00 = vy0*vx0;
+
+ int32 _best_o[8]; simd8i(best_o).store(_best_o);
+ int32 _ixp[8]; ixp.store(_ixp);
+ float _v11[8]; v11.store(_v11);
+ float _v01[8]; v01.store(_v01);
+ float _v10[8]; v10.store(_v10);
+ float _v00[8]; v00.store(_v00);
+
+ hist[iyp + 1][_ixp[0]](_best_o[0]) += _v11[0];
+ hist[iyp + 1 + 1][_ixp[0]](_best_o[0]) += _v01[0];
+ hist[iyp + 1][_ixp[0] + 1](_best_o[0]) += _v10[0];
+ hist[iyp + 1 + 1][_ixp[0] + 1](_best_o[0]) += _v00[0];
+
+ hist[iyp + 1][_ixp[1]](_best_o[1]) += _v11[1];
+ hist[iyp + 1 + 1][_ixp[1]](_best_o[1]) += _v01[1];
+ hist[iyp + 1][_ixp[1] + 1](_best_o[1]) += _v10[1];
+ hist[iyp + 1 + 1][_ixp[1] + 1](_best_o[1]) += _v00[1];
+
+ hist[iyp + 1][_ixp[2]](_best_o[2]) += _v11[2];
+ hist[iyp + 1 + 1][_ixp[2]](_best_o[2]) += _v01[2];
+ hist[iyp + 1][_ixp[2] + 1](_best_o[2]) += _v10[2];
+ hist[iyp + 1 + 1][_ixp[2] + 1](_best_o[2]) += _v00[2];
+
+ hist[iyp + 1][_ixp[3]](_best_o[3]) += _v11[3];
+ hist[iyp + 1 + 1][_ixp[3]](_best_o[3]) += _v01[3];
+ hist[iyp + 1][_ixp[3] + 1](_best_o[3]) += _v10[3];
+ hist[iyp + 1 + 1][_ixp[3] + 1](_best_o[3]) += _v00[3];
+
+ hist[iyp + 1][_ixp[4]](_best_o[4]) += _v11[4];
+ hist[iyp + 1 + 1][_ixp[4]](_best_o[4]) += _v01[4];
+ hist[iyp + 1][_ixp[4] + 1](_best_o[4]) += _v10[4];
+ hist[iyp + 1 + 1][_ixp[4] + 1](_best_o[4]) += _v00[4];
+
+ hist[iyp + 1][_ixp[5]](_best_o[5]) += _v11[5];
+ hist[iyp + 1 + 1][_ixp[5]](_best_o[5]) += _v01[5];
+ hist[iyp + 1][_ixp[5] + 1](_best_o[5]) += _v10[5];
+ hist[iyp + 1 + 1][_ixp[5] + 1](_best_o[5]) += _v00[5];
+
+ hist[iyp + 1][_ixp[6]](_best_o[6]) += _v11[6];
+ hist[iyp + 1 + 1][_ixp[6]](_best_o[6]) += _v01[6];
+ hist[iyp + 1][_ixp[6] + 1](_best_o[6]) += _v10[6];
+ hist[iyp + 1 + 1][_ixp[6] + 1](_best_o[6]) += _v00[6];
+
+ hist[iyp + 1][_ixp[7]](_best_o[7]) += _v11[7];
+ hist[iyp + 1 + 1][_ixp[7]](_best_o[7]) += _v01[7];
+ hist[iyp + 1][_ixp[7] + 1](_best_o[7]) += _v10[7];
+ hist[iyp + 1 + 1][_ixp[7] + 1](_best_o[7]) += _v00[7];
+ }
+ // Now process the right columns that don't fit into simd registers.
+ for (; x < visible_nc; x++)
+ {
+ matrix<float, 2, 1> grad;
+ float v;
+ get_gradient(y,x,img,grad,v);
+
+ // snap to one of 18 orientations
+ float best_dot = 0;
+ int best_o = 0;
+ for (int o = 0; o < 9; o++)
+ {
+ const float dot = dlib::dot(directions[o], grad);
+ if (dot > best_dot)
+ {
+ best_dot = dot;
+ best_o = o;
+ }
+ else if (-dot > best_dot)
+ {
+ best_dot = -dot;
+ best_o = o+9;
+ }
+ }
+
+ v = std::sqrt(v);
+ // add to 4 histograms around pixel using bilinear interpolation
+ const float xp = ((double)x + 0.5) / (double)cell_size - 0.5;
+ const int ixp = (int)std::floor(xp);
+ const float vx0 = xp - ixp;
+ const float vx1 = 1.0 - vx0;
+
+ hist[iyp+1][ixp+1](best_o) += vy1*vx1*v;
+ hist[iyp+1+1][ixp+1](best_o) += vy0*vx1*v;
+ hist[iyp+1][ixp+1+1](best_o) += vy1*vx0*v;
+ hist[iyp+1+1][ixp+1+1](best_o) += vy0*vx0*v;
+ }
+ }
+
+ // compute energy in each block by summing over orientations
+ for (int r = 0; r < cells_nr; ++r)
+ {
+ for (int c = 0; c < cells_nc; ++c)
+ {
+ for (int o = 0; o < 9; o++)
+ {
+ 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));
+ }
+ }
+ }
+
+ const float eps = 0.0001;
+ // compute features
+ for (int y = 0; y < hog_nr; y++)
+ {
+ const int yy = y+padding_rows_offset;
+ for (int x = 0; x < hog_nc; x++)
+ {
+ const simd4f z1(norm[y+1][x+1],
+ norm[y][x+1],
+ norm[y+1][x],
+ norm[y][x]);
+
+ const simd4f z2(norm[y+1][x+2],
+ norm[y][x+2],
+ norm[y+1][x+1],
+ norm[y][x+1]);
+
+ const simd4f z3(norm[y+2][x+1],
+ norm[y+1][x+1],
+ norm[y+2][x],
+ norm[y+1][x]);
+
+ const simd4f z4(norm[y+2][x+2],
+ norm[y+1][x+2],
+ norm[y+2][x+1],
+ norm[y+1][x+1]);
+
+ const simd4f nn = 0.2*sqrt(z1+z2+z3+z4+eps);
+ const simd4f n = 0.1/nn;
+
+ simd4f t = 0;
+
+ const int xx = x+padding_cols_offset;
+
+ // contrast-sensitive features
+ for (int o = 0; o < 18; o+=3)
+ {
+ simd4f temp0(hist[y+1+1][x+1+1](o));
+ simd4f temp1(hist[y+1+1][x+1+1](o+1));
+ simd4f temp2(hist[y+1+1][x+1+1](o+2));
+ simd4f h0 = min(temp0,nn)*n;
+ simd4f h1 = min(temp1,nn)*n;
+ simd4f h2 = min(temp2,nn)*n;
+ set_hog(hog,o,xx,yy, sum(h0));
+ set_hog(hog,o+1,xx,yy, sum(h1));
+ set_hog(hog,o+2,xx,yy, sum(h2));
+ t += h0+h1+h2;
+ }
+
+ t *= 2*0.2357;
+
+ // contrast-insensitive features
+ for (int o = 0; o < 9; o+=3)
+ {
+ simd4f temp0 = hist[y+1+1][x+1+1](o) + hist[y+1+1][x+1+1](o+9);
+ simd4f temp1 = hist[y+1+1][x+1+1](o+1) + hist[y+1+1][x+1+1](o+9+1);
+ simd4f temp2 = hist[y+1+1][x+1+1](o+2) + hist[y+1+1][x+1+1](o+9+2);
+ simd4f h0 = min(temp0,nn)*n;
+ simd4f h1 = min(temp1,nn)*n;
+ simd4f h2 = min(temp2,nn)*n;
+ set_hog(hog,o+18,xx,yy, sum(h0));
+ set_hog(hog,o+18+1,xx,yy, sum(h1));
+ set_hog(hog,o+18+2,xx,yy, sum(h2));
+ }
+
+
+ float temp[4];
+ t.store(temp);
+
+ // texture features
+ set_hog(hog,27,xx,yy, temp[0]);
+ set_hog(hog,28,xx,yy, temp[1]);
+ set_hog(hog,29,xx,yy, temp[2]);
+ set_hog(hog,30,xx,yy, temp[3]);
+ }
+ }
+ }
+
+ // ------------------------------------------------------------------------------------
+
+ inline void create_fhog_bar_images (
+ dlib::array<matrix<float> >& mbars,
+ const long w
+ )
+ {
+ const long bdims = 9;
+ // Make the oriented lines we use to draw on each HOG cell.
+ mbars.resize(bdims);
+ dlib::array<array2d<unsigned char> > bars(bdims);
+ array2d<unsigned char> temp(w,w);
+ for (unsigned long i = 0; i < bars.size(); ++i)
+ {
+ assign_all_pixels(temp, 0);
+ draw_line(temp, point(w/2,0), point(w/2,w-1), 255);
+ rotate_image(temp, bars[i], i*-pi/bars.size());
+
+ mbars[i] = subm(matrix_cast<float>(mat(bars[i])), centered_rect(get_rect(bars[i]),w,w) );
+ }
+ }
+
+ } // end namespace impl_fhog
+
+// ----------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename image_type,
+ typename T,
+ typename mm1,
+ typename mm2
+ >
+ void extract_fhog_features(
+ const image_type& img,
+ dlib::array<array2d<T,mm1>,mm2>& hog,
+ int cell_size = 8,
+ int filter_rows_padding = 1,
+ int filter_cols_padding = 1
+ )
+ {
+ impl_fhog::impl_extract_fhog_features(img, hog, cell_size, filter_rows_padding, filter_cols_padding);
+ // If the image is too small then the above function outputs an empty feature map.
+ // But to make things very uniform in usage we require the output to still have the
+ // 31 planes (but they are just empty).
+ if (hog.size() == 0)
+ hog.resize(31);
+ }
+
+ template <
+ typename image_type,
+ typename T,
+ typename mm
+ >
+ void extract_fhog_features(
+ const image_type& img,
+ array2d<matrix<T,31,1>,mm>& hog,
+ int cell_size = 8,
+ int filter_rows_padding = 1,
+ int filter_cols_padding = 1
+ )
+ {
+ impl_fhog::impl_extract_fhog_features(img, hog, cell_size, filter_rows_padding, filter_cols_padding);
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename image_type,
+ typename T
+ >
+ void extract_fhog_features(
+ const image_type& img,
+ matrix<T,0,1>& feats,
+ int cell_size = 8,
+ int filter_rows_padding = 1,
+ int filter_cols_padding = 1
+ )
+ {
+ dlib::array<array2d<T> > hog;
+ extract_fhog_features(img, hog, cell_size, filter_rows_padding, filter_cols_padding);
+ feats.set_size(hog.size()*hog[0].size());
+ for (unsigned long i = 0; i < hog.size(); ++i)
+ {
+ const long size = hog[i].size();
+ set_rowm(feats, range(i*size, (i+1)*size-1)) = reshape_to_column_vector(mat(hog[i]));
+ }
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename image_type
+ >
+ matrix<double,0,1> extract_fhog_features(
+ const image_type& img,
+ int cell_size = 8,
+ int filter_rows_padding = 1,
+ int filter_cols_padding = 1
+ )
+ {
+ matrix<double, 0, 1> feats;
+ extract_fhog_features(img, feats, cell_size, filter_rows_padding, filter_cols_padding);
+ return feats;
+ }
+
+// ----------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------------------
+
+ inline point image_to_fhog (
+ point p,
+ int cell_size = 8,
+ int filter_rows_padding = 1,
+ int filter_cols_padding = 1
+ )
+ {
+ // make sure requires clause is not broken
+ DLIB_ASSERT( cell_size > 0 &&
+ filter_rows_padding > 0 &&
+ filter_cols_padding > 0 ,
+ "\t point image_to_fhog()"
+ << "\n\t Invalid inputs were given to this function. "
+ << "\n\t cell_size: " << cell_size
+ << "\n\t filter_rows_padding: " << filter_rows_padding
+ << "\n\t filter_cols_padding: " << filter_cols_padding
+ );
+
+ // There is a one pixel border around the image.
+ p -= point(1,1);
+ // There is also a 1 "cell" border around the HOG image formation.
+ return p/cell_size - point(1,1) + point((filter_cols_padding-1)/2,(filter_rows_padding-1)/2);
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ inline rectangle image_to_fhog (
+ const rectangle& rect,
+ int cell_size = 8,
+ int filter_rows_padding = 1,
+ int filter_cols_padding = 1
+ )
+ {
+ // make sure requires clause is not broken
+ DLIB_ASSERT( cell_size > 0 &&
+ filter_rows_padding > 0 &&
+ filter_cols_padding > 0 ,
+ "\t rectangle image_to_fhog()"
+ << "\n\t Invalid inputs were given to this function. "
+ << "\n\t cell_size: " << cell_size
+ << "\n\t filter_rows_padding: " << filter_rows_padding
+ << "\n\t filter_cols_padding: " << filter_cols_padding
+ );
+
+ return rectangle(image_to_fhog(rect.tl_corner(),cell_size,filter_rows_padding,filter_cols_padding),
+ image_to_fhog(rect.br_corner(),cell_size,filter_rows_padding,filter_cols_padding));
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ inline point fhog_to_image (
+ point p,
+ int cell_size = 8,
+ int filter_rows_padding = 1,
+ int filter_cols_padding = 1
+ )
+ {
+ // make sure requires clause is not broken
+ DLIB_ASSERT( cell_size > 0 &&
+ filter_rows_padding > 0 &&
+ filter_cols_padding > 0 ,
+ "\t point fhog_to_image()"
+ << "\n\t Invalid inputs were given to this function. "
+ << "\n\t cell_size: " << cell_size
+ << "\n\t filter_rows_padding: " << filter_rows_padding
+ << "\n\t filter_cols_padding: " << filter_cols_padding
+ );
+
+ // Convert to image space and then set to the center of the cell.
+ point offset;
+
+ p = (p+point(1,1)-point((filter_cols_padding-1)/2,(filter_rows_padding-1)/2))*cell_size + point(1,1);
+ if (p.x() >= 0 && p.y() >= 0) offset = point(cell_size/2,cell_size/2);
+ if (p.x() < 0 && p.y() >= 0) offset = point(-cell_size/2,cell_size/2);
+ if (p.x() >= 0 && p.y() < 0) offset = point(cell_size/2,-cell_size/2);
+ if (p.x() < 0 && p.y() < 0) offset = point(-cell_size/2,-cell_size/2);
+ return p + offset;
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ inline rectangle fhog_to_image (
+ const rectangle& rect,
+ int cell_size = 8,
+ int filter_rows_padding = 1,
+ int filter_cols_padding = 1
+ )
+ {
+ // make sure requires clause is not broken
+ DLIB_ASSERT( cell_size > 0 &&
+ filter_rows_padding > 0 &&
+ filter_cols_padding > 0 ,
+ "\t rectangle fhog_to_image()"
+ << "\n\t Invalid inputs were given to this function. "
+ << "\n\t cell_size: " << cell_size
+ << "\n\t filter_rows_padding: " << filter_rows_padding
+ << "\n\t filter_cols_padding: " << filter_cols_padding
+ );
+
+ return rectangle(fhog_to_image(rect.tl_corner(),cell_size,filter_rows_padding,filter_cols_padding),
+ fhog_to_image(rect.br_corner(),cell_size,filter_rows_padding,filter_cols_padding));
+ }
+
+// ----------------------------------------------------------------------------------------
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename T,
+ typename mm1,
+ typename mm2
+ >
+ matrix<unsigned char> draw_fhog(
+ const dlib::array<array2d<T,mm1>,mm2>& hog,
+ const long cell_draw_size = 15,
+ const float min_response_threshold = 0.0
+ )
+ {
+ // make sure requires clause is not broken
+ DLIB_ASSERT( cell_draw_size > 0 && hog.size()==31,
+ "\t matrix<unsigned char> draw_fhog()"
+ << "\n\t Invalid inputs were given to this function. "
+ << "\n\t cell_draw_size: " << cell_draw_size
+ << "\n\t hog.size(): " << hog.size()
+ );
+
+ dlib::array<matrix<float> > mbars;
+ impl_fhog::create_fhog_bar_images(mbars,cell_draw_size);
+
+ // now draw the bars onto the HOG cells
+ matrix<float> himg(hog[0].nr()*cell_draw_size, hog[0].nc()*cell_draw_size);
+ himg = 0;
+ for (unsigned long d = 0; d < mbars.size(); ++d)
+ {
+ for (long r = 0; r < himg.nr(); r+=cell_draw_size)
+ {
+ for (long c = 0; c < himg.nc(); c+=cell_draw_size)
+ {
+ const float val = hog[d][r/cell_draw_size][c/cell_draw_size] +
+ hog[d+mbars.size()][r/cell_draw_size][c/cell_draw_size] +
+ hog[d+mbars.size()*2][r/cell_draw_size][c/cell_draw_size];
+ if (val > min_response_threshold)
+ {
+ set_subm(himg, r, c, cell_draw_size, cell_draw_size) += val*mbars[d%mbars.size()];
+ }
+ }
+ }
+ }
+
+ const float thresh = mean(himg) + 4 * stddev(himg);
+ if (thresh != 0)
+ return matrix_cast<unsigned char>(upperbound(round(himg*255/thresh),255));
+ else
+ return matrix_cast<unsigned char>(himg);
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename T
+ >
+ matrix<unsigned char> draw_fhog (
+ const std::vector<matrix<T> >& hog,
+ const long cell_draw_size = 15,
+ const float min_response_threshold = 0.0
+ )
+ {
+ // make sure requires clause is not broken
+ DLIB_ASSERT( cell_draw_size > 0 && hog.size()==31,
+ "\t matrix<unsigned char> draw_fhog()"
+ << "\n\t Invalid inputs were given to this function. "
+ << "\n\t cell_draw_size: " << cell_draw_size
+ << "\n\t hog.size(): " << hog.size()
+ );
+
+ // Just convert the input into the right object and then call the above draw_fhog()
+ // function on it.
+ dlib::array<array2d<T> > temp(hog.size());
+ for (unsigned long i = 0; i < temp.size(); ++i)
+ {
+ temp[i].set_size(hog[i].nr(), hog[i].nc());
+ for (long r = 0; r < hog[i].nr(); ++r)
+ {
+ for (long c = 0; c < hog[i].nc(); ++c)
+ {
+ temp[i][r][c] = hog[i](r,c);
+ }
+ }
+ }
+ return draw_fhog(temp,cell_draw_size, min_response_threshold);
+ }
+
+// ----------------------------------------------------------------------------------------
+
+ template <
+ typename T,
+ typename mm
+ >
+ matrix<unsigned char> draw_fhog(
+ const array2d<matrix<T,31,1>,mm>& hog,
+ const long cell_draw_size = 15,
+ const float min_response_threshold = 0.0
+ )
+ {
+ // make sure requires clause is not broken
+ DLIB_ASSERT( cell_draw_size > 0,
+ "\t matrix<unsigned char> draw_fhog()"
+ << "\n\t Invalid inputs were given to this function. "
+ << "\n\t cell_draw_size: " << cell_draw_size
+ );
+
+ dlib::array<matrix<float> > mbars;
+ impl_fhog::create_fhog_bar_images(mbars,cell_draw_size);
+
+ // now draw the bars onto the HOG cells
+ matrix<float> himg(hog.nr()*cell_draw_size, hog.nc()*cell_draw_size);
+ himg = 0;
+ for (unsigned long d = 0; d < mbars.size(); ++d)
+ {
+ for (long r = 0; r < himg.nr(); r+=cell_draw_size)
+ {
+ for (long c = 0; c < himg.nc(); c+=cell_draw_size)
+ {
+ const float val = hog[r/cell_draw_size][c/cell_draw_size](d) +
+ hog[r/cell_draw_size][c/cell_draw_size](d+mbars.size()) +
+ hog[r/cell_draw_size][c/cell_draw_size](d+mbars.size()*2);
+ if (val > min_response_threshold)
+ {
+ set_subm(himg, r, c, cell_draw_size, cell_draw_size) += val*mbars[d%mbars.size()];
+ }
+ }
+ }
+ }
+
+ const float thresh = mean(himg) + 4 * stddev(himg);
+ if (thresh != 0)
+ return matrix_cast<unsigned char>(upperbound(round(himg*255/thresh),255));
+ else
+ return matrix_cast<unsigned char>(himg);
+ }
+
+// ----------------------------------------------------------------------------------------
+
+}
+
+#endif // DLIB_fHOG_Hh_
+