diff options
Diffstat (limited to 'ml/dlib/dlib/image_transforms/fhog.h')
-rw-r--r-- | ml/dlib/dlib/image_transforms/fhog.h | 1404 |
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_ + |