// Copyright (c) 2012-2015 OpenMVG. // Copyright (c) 2012-2015 Pierre MOULON. // Copyright (c) 2015 Romuald Perrot. // Copyright (c) 2015 cDc@seacave. // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. namespace Sampler { // Sampling functors // These functors computes weight associated to each pixels // // For a (relative) sampling position x (in [0,1]) between two (consecutive) points : // // A .... x ......... B // // w[0] is the weight associated to A // w[1] is the weight associated to B // // Note: The following functors generalize the sampling to more than two neighbors // They all contains the width variable that specify the number of neighbors used for sampling // // All contain the operator() with the following definition: // // @brief Computes weight associated to neighboring pixels // @author Romuald Perrot // @param x Sampling position // @param[out] weight Sampling factors associated to the neighboring // @note weight must be at least width length // Linear sampling (ie: linear interpolation between two pixels) template struct Linear { typedef TYPE Type; enum { halfWidth = 1 }; enum { width = halfWidth*2 }; inline Linear() {} inline void operator() (const TYPE x, TYPE* const weight) const { weight[0] = TYPE(1) - x; weight[1] = x; } }; // Cubic interpolation between 4 pixels // // Interpolation weight is for A,B,C and D pixels given a x position as illustrated as follow : // // A B x C D // // Cubic Convolution Interpolation for Digital Image Processing , R. Keys, eq(4) // // The cubic filter family with the two free parameters usually named B and C: // if |x|<1 // ((12-9B-6C)|x|^3 + (-18+12B+6C)|x|^2 + (6-2B))/6 // if 1<=|x|<2 // ((-B-6C)|x|^3 + (6B+30C)|x|^2 + (-12B-48C)|x| + (8B+24C))/6 template struct Cubic { typedef TYPE Type; enum { halfWidth = 2 }; enum { width = halfWidth*2 }; // Sharpness coefficient used to control sharpness of the cubic curve (between 0.5 to 0.75) // cubic(0,1/2): 0.5 gives better mathematically result (ie: approximation at 3 order precision - Catmull-Rom) const TYPE sharpness; inline Cubic(const TYPE& _sharpness=TYPE(0.5)) : sharpness(_sharpness) {} inline void operator() (const TYPE x, TYPE* const weight) const { // remember : // A B x C D // weight[0] -> weight for A // weight[1] -> weight for B // weight[2] -> weight for C // weight[3] -> weight for D weight[0] = CubicInter12(x + TYPE(1)); weight[1] = CubicInter01(x); weight[2] = CubicInter01(TYPE(1) - x); weight[3] = CubicInter12(TYPE(2) - x); } // Cubic interpolation for x (in [0,1]) inline TYPE CubicInter01(const TYPE x) const { // A = -sharpness // f(x) = (A + 2) * x^3 - (A + 3) * x^2 + 1 return ((TYPE(2)-sharpness)*x - (TYPE(3)-sharpness))*x*x + TYPE(1); } // Cubic interpolation for x (in [1,2]) inline TYPE CubicInter12(const TYPE x) const { // A = -sharpness // f(x) = A * x^3 - 5 * A * x^2 + 8 * A * x - 4 * A return (((TYPE(5)-x)*x - TYPE(8))*x + TYPE(4))*sharpness; } }; // Sampler spline16 -> Interpolation on 4 points used for 2D ressampling (16 = 4x4 sampling) // Cubic interpolation with 0-derivative at edges (ie at A and D points) // See Helmut Dersch for more details // // Some refs : // - http://forum.doom9.org/archive/index.php/t-147117.html // - http://avisynth.nl/index.php/Resampling // - http://www.ipol.im/pub/art/2011/g_lmii/ // // The idea is to consider 3 cubic splines (f1,f2,f3) in the sampling interval : // // A f1 B f2 C f3 D // // with curves defined as follow : // f1(x) = a1 x^3 + b1 x^2 + c1 x + d1 // f2(x) = a2 x^3 + b2 x^2 + c2 x + d2 // f3(x) = a3 x^3 + b2 x^2 + c3 x + d3 // // We want to compute spline coefs for A,B,C,D assuming that: // y0 = coef[A] = f1(-1) // y1 = coef[B] = f1(0) = f2(0) // y2 = coef[C] = f2(1) = f3(1) // y3 = coef[D] = f3(2) // // coef are computed using the following constraints : // Curve is continuous, ie: // f1(0) = f2(0) // f2(1) = f3(1) // First derivative are equals, ie: // f1'(0) = f2'(0) // f2'(1) = f3'(1) // Second derivative are equals, ie: // f1''(0) = f2''(0) // f2''(1) = f3''(0) // Curve is, at boundary, with second derivative set to zero (it's a constraint introduced by Dersch), ie: // f1''(-1) = 0 // f3''(2) = 0 // // Then, you can solve for (a1,a2,a3,b1,b2,b3,c1,c2,c3,d1,d2,d3) // // for ex, for curve f2 you find : // // d2 = y1 // easy since y1 = f2(0) // c2 = - 7/15 y0 - 1/5 y1 + 4/5 y2 - 2/15 y3 // b2 = 4/5 y0 - 9/5 y1 + 6/5 y2 - 1/5 y3 // a2 = - 1/3 y0 + y1 - y2 + 1/3 y3 // // // When you have coefs, you just have to express your curve as a linear combination of the control points, fort ex // with f2 : // // // f2(x) = w0(x) * y0 + w1(x) + y1 + w2(x) * y2 + w3(x) * y3 // // with : // // w0(x) = - 1/3 * x^3 + 4/5 * x^2 - 7/15 * x // w1(x) = x^3 - 9/5 * x^2 - 1/5 * x + 1 // w2(x) = -x^3 + 6/5 * x^2 + 4/5 * x // w3(x) = 1/3 * x^3 - 1/5 * x^2 - 2/15 * x // // substituting boundary conditions gives the correct coefficients for y0,y1,y2,y3 giving the final sampling scheme template struct Spline16 { typedef TYPE Type; enum { halfWidth = 2 }; enum { width = halfWidth*2 }; inline Spline16() {} inline void operator() (const TYPE x, TYPE* const weight) const { weight[0] = ((TYPE(-1) / TYPE(3) * x + TYPE(4) / TYPE(5)) * x - TYPE(7) / TYPE(15)) * x; weight[1] = ((x - TYPE(9) / TYPE(5)) * x - TYPE(1) / TYPE(5)) * x + TYPE(1); weight[2] = ((TYPE(6) / TYPE(5) - x) * x + TYPE(4) / TYPE(5)) * x; weight[3] = ((TYPE(1) / TYPE(3) * x - TYPE(1) / TYPE(5)) * x - TYPE(2) / TYPE(15)) * x; } }; // Sampler spline 36 // Same as spline 16 but on 6 neighbors (used for 6x6 frame) template struct Spline36 { typedef TYPE Type; enum { halfWidth = 3 }; enum { width = halfWidth*2 }; inline Spline36() {} inline void operator() (const TYPE x, TYPE* const weight) const { weight[0] = ((TYPE(1) / TYPE(11) * x - TYPE(45) / TYPE(209)) * x + TYPE(26) / TYPE(209)) * x; weight[1] = ((TYPE(-6) / TYPE(11) * x + TYPE(270) / TYPE(209)) * x - TYPE(156) / TYPE(209)) * x; weight[2] = ((TYPE(13) / TYPE(11) * x - TYPE(453) / TYPE(209)) * x - TYPE(3) / TYPE(209)) * x + TYPE(1); weight[3] = ((TYPE(-13) / TYPE(11) * x + TYPE(288) / TYPE(209)) * x + TYPE(168) / TYPE(209)) * x; weight[4] = ((TYPE(6) / TYPE(11) * x - TYPE(72) / TYPE(209)) * x - TYPE(42) / TYPE(209)) * x; weight[5] = ((TYPE(-1) / TYPE(11) * x + TYPE(12) / TYPE(209)) * x + TYPE(7) / TYPE(209)) * x; } }; // Sampler spline 64 // Same as spline 16 but on 8 neighbors (used for 8x8 frame) template struct Spline64 { typedef TYPE Type; enum { halfWidth = 4 }; enum { width = halfWidth*2 }; inline Spline64() {} inline void operator() (const TYPE x, TYPE* const weight) const { weight[0] = ((TYPE(-1) / TYPE(41) * x + TYPE(168) / TYPE(2911)) * x - TYPE(97) / TYPE(2911)) * x; weight[1] = ((TYPE(6) / TYPE(41) * x - TYPE(1008) / TYPE(2911)) * x + TYPE(582) / TYPE(2911)) * x; weight[2] = ((TYPE(-24) / TYPE(41) * x + TYPE(4032) / TYPE(2911)) * x - TYPE(2328) / TYPE(2911)) * x; weight[3] = ((TYPE(49) / TYPE(41) * x - TYPE(6387) / TYPE(2911)) * x - TYPE(3) / TYPE(2911)) * x + TYPE(1); weight[4] = ((TYPE(-49) / TYPE(41) * x + TYPE(4050) / TYPE(2911)) * x + TYPE(2340) / TYPE(2911)) * x; weight[5] = ((TYPE(24) / TYPE(41) * x - TYPE(1080) / TYPE(2911)) * x - TYPE(624) / TYPE(2911)) * x; weight[6] = ((TYPE(-6) / TYPE(41) * x + TYPE(270) / TYPE(2911)) * x + TYPE(156) / TYPE(2911)) * x; weight[7] = ((TYPE(1) / TYPE(41) * x - TYPE(45) / TYPE(2911)) * x - TYPE(26) / TYPE(2911)) * x; } }; // Sample image at a specified position // @param image to be sampled // @param sampler used to make the sampling // @param pt X and Y-coordinate of sampling // @return sampled value template inline TYPE Sample(const IMAGE& image, const SAMPLER& sampler, const POINT& pt) { typedef typename SAMPLER::Type T; // integer position of sample (x,y) const int grid_x(FLOOR2INT(pt.x)); const int grid_y(FLOOR2INT(pt.y)); // compute difference between exact pixel location and sample const T dx(pt.x-(T)grid_x); const T dy(pt.y-(T)grid_y); // get sampler weights T coefs_x[SAMPLER::width]; sampler(dx, coefs_x); T coefs_y[SAMPLER::width]; sampler(dy, coefs_y); // Sample a grid around specified grid point TYPE res(0); for (int i = 0; i < SAMPLER::width; ++i) { // get current i value // +1 for correct scheme (draw it to be convinced) const int cur_i(grid_y + 1 + i - SAMPLER::halfWidth); // handle out of range if (cur_i < 0 || cur_i >= image.rows) continue; for (int j = 0; j < SAMPLER::width; ++j) { // get current j value // +1 for the same reason const int cur_j(grid_x + 1 + j - SAMPLER::halfWidth); // handle out of range if (cur_j < 0 || cur_j >= image.cols) continue; // sample input image and weight according to sampler const T w = coefs_x[j] * coefs_y[i]; const TYPE pixel = image(cur_i, cur_j); res += pixel * w; } } return res; } } // namespace Sampler