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
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
|
|
|