You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

274 lines
9.2 KiB

// 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 <perrot.romuald_AT_gmail.com>
// @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 <typename TYPE>
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 <typename TYPE>
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 <typename TYPE>
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 <typename TYPE>
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 <typename TYPE>
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 <typename IMAGE, typename SAMPLER, typename POINT, typename TYPE>
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