// Copyright (C) 2012 Davis E. King (davis@dlib.net) // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_INTERPOlATION__ #define DLIB_INTERPOlATION__ #include "interpolation_abstract.h" #include "../pixel.h" #include "../matrix.h" #include "assign_image.h" namespace dlib { // ---------------------------------------------------------------------------------------- class interpolate_nearest_neighbor { public: template <typename image_type, typename pixel_type> bool operator() ( const image_type& img, const dlib::point& p, pixel_type& result ) const { COMPILE_TIME_ASSERT(pixel_traits<typename image_type::type>::has_alpha == false); if (get_rect(img).contains(p)) { assign_pixel(result, img[p.y()][p.x()]); return true; } else { return false; } } }; // ---------------------------------------------------------------------------------------- class interpolate_bilinear { template <typename T> struct is_rgb_image { const static bool value = pixel_traits<typename T::type>::rgb; }; public: template <typename T, typename image_type, typename pixel_type> typename disable_if<is_rgb_image<image_type>,bool>::type operator() ( const image_type& img, const dlib::vector<T,2>& p, pixel_type& result ) const { COMPILE_TIME_ASSERT(pixel_traits<typename image_type::type>::has_alpha == false); const long top = static_cast<long>(std::floor(p.y())); const long bottom = static_cast<long>(std::ceil (p.y())); const long left = static_cast<long>(std::floor(p.x())); const long right = static_cast<long>(std::ceil (p.x())); // if the interpolation goes outside img if (!get_rect(img).contains(rectangle(left,top,right,bottom))) return false; const double lr_frac = p.x() - std::floor(p.x()); const double tb_frac = p.y() - std::floor(p.y()); double tl = 0, tr = 0, bl = 0, br = 0; assign_pixel(tl, img[top][left]); assign_pixel(tr, img[top][right]); assign_pixel(bl, img[bottom][left]); assign_pixel(br, img[bottom][right]); double temp = (1-tb_frac)*((1-lr_frac)*tl + lr_frac*tr) + tb_frac*((1-lr_frac)*bl + lr_frac*br); assign_pixel(result, temp); return true; } template <typename T, typename image_type, typename pixel_type> typename enable_if<is_rgb_image<image_type>,bool>::type operator() ( const image_type& img, const dlib::vector<T,2>& p, pixel_type& result ) const { COMPILE_TIME_ASSERT(pixel_traits<typename image_type::type>::has_alpha == false); const long top = static_cast<long>(std::floor(p.y())); const long bottom = static_cast<long>(std::ceil (p.y())); const long left = static_cast<long>(std::floor(p.x())); const long right = static_cast<long>(std::ceil (p.x())); // if the interpolation goes outside img if (!get_rect(img).contains(rectangle(left,top,right,bottom))) return false; const double lr_frac = p.x() - std::floor(p.x()); const double tb_frac = p.y() - std::floor(p.y()); double tl, tr, bl, br; tl = img[top][left].red; tr = img[top][right].red; bl = img[bottom][left].red; br = img[bottom][right].red; const double red = (1-tb_frac)*((1-lr_frac)*tl + lr_frac*tr) + tb_frac*((1-lr_frac)*bl + lr_frac*br); tl = img[top][left].green; tr = img[top][right].green; bl = img[bottom][left].green; br = img[bottom][right].green; const double green = (1-tb_frac)*((1-lr_frac)*tl + lr_frac*tr) + tb_frac*((1-lr_frac)*bl + lr_frac*br); tl = img[top][left].blue; tr = img[top][right].blue; bl = img[bottom][left].blue; br = img[bottom][right].blue; const double blue = (1-tb_frac)*((1-lr_frac)*tl + lr_frac*tr) + tb_frac*((1-lr_frac)*bl + lr_frac*br); rgb_pixel temp; assign_pixel(temp.red, red); assign_pixel(temp.green, green); assign_pixel(temp.blue, blue); assign_pixel(result, temp); return true; } }; // ---------------------------------------------------------------------------------------- class interpolate_quadratic { template <typename T> struct is_rgb_image { const static bool value = pixel_traits<typename T::type>::rgb; }; public: template <typename T, typename image_type, typename pixel_type> typename disable_if<is_rgb_image<image_type>,bool>::type operator() ( const image_type& img, const dlib::vector<T,2>& p, pixel_type& result ) const { COMPILE_TIME_ASSERT(pixel_traits<typename image_type::type>::has_alpha == false); const point pp(p); // if the interpolation goes outside img if (!get_rect(img).contains(grow_rect(pp,1))) return false; const long r = pp.y(); const long c = pp.x(); const double temp = interpolate(p-pp, img[r-1][c-1], img[r-1][c ], img[r-1][c+1], img[r ][c-1], img[r ][c ], img[r ][c+1], img[r+1][c-1], img[r+1][c ], img[r+1][c+1]); assign_pixel(result, temp); return true; } template <typename T, typename image_type, typename pixel_type> typename enable_if<is_rgb_image<image_type>,bool>::type operator() ( const image_type& img, const dlib::vector<T,2>& p, pixel_type& result ) const { COMPILE_TIME_ASSERT(pixel_traits<typename image_type::type>::has_alpha == false); const point pp(p); // if the interpolation goes outside img if (!get_rect(img).contains(grow_rect(pp,1))) return false; const long r = pp.y(); const long c = pp.x(); const double red = interpolate(p-pp, img[r-1][c-1].red, img[r-1][c ].red, img[r-1][c+1].red, img[r ][c-1].red, img[r ][c ].red, img[r ][c+1].red, img[r+1][c-1].red, img[r+1][c ].red, img[r+1][c+1].red); const double green = interpolate(p-pp, img[r-1][c-1].green, img[r-1][c ].green, img[r-1][c+1].green, img[r ][c-1].green, img[r ][c ].green, img[r ][c+1].green, img[r+1][c-1].green, img[r+1][c ].green, img[r+1][c+1].green); const double blue = interpolate(p-pp, img[r-1][c-1].blue, img[r-1][c ].blue, img[r-1][c+1].blue, img[r ][c-1].blue, img[r ][c ].blue, img[r ][c+1].blue, img[r+1][c-1].blue, img[r+1][c ].blue, img[r+1][c+1].blue); rgb_pixel temp; assign_pixel(temp.red, red); assign_pixel(temp.green, green); assign_pixel(temp.blue, blue); assign_pixel(result, temp); return true; } private: /* tl tm tr ml mm mr bl bm br */ // The above is the pixel layout in our little 3x3 neighborhood. interpolate() will // fit a quadratic to these 9 pixels and then use that quadratic to find the interpolated // value at point p. inline double interpolate( const dlib::vector<double,2>& p, double tl, double tm, double tr, double ml, double mm, double mr, double bl, double bm, double br ) const { matrix<double,6,1> w; // x w(0) = (tr + mr + br - tl - ml - bl)*0.16666666666; // y w(1) = (bl + bm + br - tl - tm - tr)*0.16666666666; // x^2 w(2) = (tl + tr + ml + mr + bl + br)*0.16666666666 - (tm + mm + bm)*0.333333333; // x*y w(3) = (tl - tr - bl + br)*0.25; // y^2 w(4) = (tl + tm + tr + bl + bm + br)*0.16666666666 - (ml + mm + mr)*0.333333333; // 1 (constant term) w(5) = (tm + ml + mr + bm)*0.222222222 - (tl + tr + bl + br)*0.11111111 + (mm)*0.55555556; const double x = p.x(); const double y = p.y(); matrix<double,6,1> z; z = x, y, x*x, x*y, y*y, 1.0; return dot(w,z); } }; // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- // ---------------------------------------------------------------------------------------- class black_background { public: template <typename pixel_type> void operator() ( pixel_type& p) const { assign_pixel(p, 0); } }; class white_background { public: template <typename pixel_type> void operator() ( pixel_type& p) const { assign_pixel(p, 255); } }; class no_background { public: template <typename pixel_type> void operator() ( pixel_type& ) const { } }; // ---------------------------------------------------------------------------------------- template < typename image_type1, typename image_type2, typename interpolation_type, typename point_mapping_type, typename background_type > void transform_image ( const image_type1& in_img, image_type2& out_img, const interpolation_type& interp, const point_mapping_type& map_point, const background_type& set_background, const rectangle& area ) { // make sure requires clause is not broken DLIB_ASSERT( get_rect(out_img).contains(area) == true && is_same_object(in_img, out_img) == false , "\t void transform_image()" << "\n\t Invalid inputs were given to this function." << "\n\t get_rect(out_img).contains(area): " << get_rect(out_img).contains(area) << "\n\t get_rect(out_img): " << get_rect(out_img) << "\n\t area: " << area << "\n\t is_same_object(in_img, out_img): " << is_same_object(in_img, out_img) ); for (long r = area.top(); r <= area.bottom(); ++r) { for (long c = area.left(); c <= area.right(); ++c) { if (!interp(in_img, map_point(dlib::vector<double,2>(c,r)), out_img[r][c])) set_background(out_img[r][c]); } } } // ---------------------------------------------------------------------------------------- template < typename image_type1, typename image_type2, typename interpolation_type, typename point_mapping_type, typename background_type > void transform_image ( const image_type1& in_img, image_type2& out_img, const interpolation_type& interp, const point_mapping_type& map_point, const background_type& set_background ) { // make sure requires clause is not broken DLIB_ASSERT( is_same_object(in_img, out_img) == false , "\t void transform_image()" << "\n\t Invalid inputs were given to this function." << "\n\t is_same_object(in_img, out_img): " << is_same_object(in_img, out_img) ); transform_image(in_img, out_img, interp, map_point, set_background, get_rect(out_img)); } // ---------------------------------------------------------------------------------------- template < typename image_type1, typename image_type2, typename interpolation_type, typename point_mapping_type > void transform_image ( const image_type1& in_img, image_type2& out_img, const interpolation_type& interp, const point_mapping_type& map_point ) { // make sure requires clause is not broken DLIB_ASSERT( is_same_object(in_img, out_img) == false , "\t void transform_image()" << "\n\t Invalid inputs were given to this function." << "\n\t is_same_object(in_img, out_img): " << is_same_object(in_img, out_img) ); transform_image(in_img, out_img, interp, map_point, black_background(), get_rect(out_img)); } // ---------------------------------------------------------------------------------------- template < typename image_type1, typename image_type2, typename interpolation_type > void rotate_image ( const image_type1& in_img, image_type2& out_img, double angle, const interpolation_type& interp ) { // make sure requires clause is not broken DLIB_ASSERT( is_same_object(in_img, out_img) == false , "\t void rotate_image()" << "\n\t Invalid inputs were given to this function." << "\n\t is_same_object(in_img, out_img): " << is_same_object(in_img, out_img) ); const rectangle rimg = get_rect(in_img); // figure out bounding box for rotated rectangle rectangle rect; rect += rotate_point(center(rimg), rimg.tl_corner(), -angle); rect += rotate_point(center(rimg), rimg.tr_corner(), -angle); rect += rotate_point(center(rimg), rimg.bl_corner(), -angle); rect += rotate_point(center(rimg), rimg.br_corner(), -angle); out_img.set_size(rect.height(), rect.width()); const matrix<double,2,2> R = rotation_matrix(angle); transform_image(in_img, out_img, interp, point_transform_affine(R, -R*dcenter(get_rect(out_img)) + dcenter(rimg))); } // ---------------------------------------------------------------------------------------- template < typename image_type1, typename image_type2 > void rotate_image ( const image_type1& in_img, image_type2& out_img, double angle ) { // make sure requires clause is not broken DLIB_ASSERT( is_same_object(in_img, out_img) == false , "\t void rotate_image()" << "\n\t Invalid inputs were given to this function." << "\n\t is_same_object(in_img, out_img): " << is_same_object(in_img, out_img) ); rotate_image(in_img, out_img, angle, interpolate_quadratic()); } // ---------------------------------------------------------------------------------------- namespace impl { class helper_resize_image { public: helper_resize_image( double x_scale_, double y_scale_ ): x_scale(x_scale_), y_scale(y_scale_) {} dlib::vector<double,2> operator() ( const dlib::vector<double,2>& p ) const { return dlib::vector<double,2>(p.x()*x_scale, p.y()*y_scale); } private: const double x_scale; const double y_scale; }; } template < typename image_type1, typename image_type2, typename interpolation_type > void resize_image ( const image_type1& in_img, image_type2& out_img, const interpolation_type& interp ) { // make sure requires clause is not broken DLIB_ASSERT( is_same_object(in_img, out_img) == false , "\t void resize_image()" << "\n\t Invalid inputs were given to this function." << "\n\t is_same_object(in_img, out_img): " << is_same_object(in_img, out_img) ); const double x_scale = (in_img.nc()-1)/(double)std::max<long>((out_img.nc()-1),1); const double y_scale = (in_img.nr()-1)/(double)std::max<long>((out_img.nr()-1),1); transform_image(in_img, out_img, interp, dlib::impl::helper_resize_image(x_scale,y_scale)); } // ---------------------------------------------------------------------------------------- template < typename image_type1, typename image_type2 > void resize_image ( const image_type1& in_img, image_type2& out_img ) { // make sure requires clause is not broken DLIB_ASSERT( is_same_object(in_img, out_img) == false , "\t void resize_image()" << "\n\t Invalid inputs were given to this function." << "\n\t is_same_object(in_img, out_img): " << is_same_object(in_img, out_img) ); resize_image(in_img, out_img, interpolate_quadratic()); } // ---------------------------------------------------------------------------------------- template < typename image_type1, typename image_type2 > void flip_image_left_right ( const image_type1& in_img, image_type2& out_img ) { // make sure requires clause is not broken DLIB_ASSERT( is_same_object(in_img, out_img) == false , "\t void rotate_image()" << "\n\t Invalid inputs were given to this function." << "\n\t is_same_object(in_img, out_img): " << is_same_object(in_img, out_img) ); assign_image(out_img, fliplr(mat(in_img))); } // ---------------------------------------------------------------------------------------- template < typename image_type1, typename image_type2 > void flip_image_up_down ( const image_type1& in_img, image_type2& out_img ) { // make sure requires clause is not broken DLIB_ASSERT( is_same_object(in_img, out_img) == false , "\t void rotate_image()" << "\n\t Invalid inputs were given to this function." << "\n\t is_same_object(in_img, out_img): " << is_same_object(in_img, out_img) ); assign_image(out_img, flipud(mat(in_img))); } // ---------------------------------------------------------------------------------------- namespace impl { class helper_pyramid_up { public: helper_pyramid_up( double x_scale_, double y_scale_, const dlib::vector<double,2> offset_ ): x_scale(x_scale_), y_scale(y_scale_), offset(offset_) {} dlib::vector<double,2> operator() ( const dlib::vector<double,2>& p ) const { return dlib::vector<double,2>((p.x()-offset.x())*x_scale, (p.y()-offset.y())*y_scale); } private: const double x_scale; const double y_scale; const dlib::vector<double,2> offset; }; } template < typename image_type1, typename image_type2, typename pyramid_type, typename interpolation_type > void pyramid_up ( const image_type1& in_img, image_type2& out_img, const pyramid_type& pyr, unsigned int levels, const interpolation_type& interp ) { // make sure requires clause is not broken DLIB_ASSERT( is_same_object(in_img, out_img) == false , "\t void pyramid_up()" << "\n\t Invalid inputs were given to this function." << "\n\t is_same_object(in_img, out_img): " << is_same_object(in_img, out_img) ); if (in_img.size() == 0) { out_img.clear(); return; } if (levels == 0) { assign_image(out_img, in_img); return; } rectangle rect = get_rect(in_img); rectangle uprect = pyr.rect_up(rect,levels); if (uprect.is_empty()) { out_img.clear(); return; } out_img.set_size(uprect.bottom()+1, uprect.right()+1); const double x_scale = (rect.width() -1)/(double)std::max<long>(1,(uprect.width() -1)); const double y_scale = (rect.height()-1)/(double)std::max<long>(1,(uprect.height()-1)); transform_image(in_img, out_img, interp, dlib::impl::helper_pyramid_up(x_scale,y_scale, uprect.tl_corner())); } // ---------------------------------------------------------------------------------------- template < typename image_type1, typename image_type2, typename pyramid_type > void pyramid_up ( const image_type1& in_img, image_type2& out_img, const pyramid_type& pyr, unsigned int levels = 1 ) { // make sure requires clause is not broken DLIB_ASSERT( is_same_object(in_img, out_img) == false , "\t void pyramid_up()" << "\n\t Invalid inputs were given to this function." << "\n\t is_same_object(in_img, out_img): " << is_same_object(in_img, out_img) ); pyramid_up(in_img, out_img, pyr, levels, interpolate_quadratic()); } // ---------------------------------------------------------------------------------------- } #endif // DLIB_INTERPOlATION__