/*****************************************************************************/
|
// Copyright 2008 Adobe Systems Incorporated
|
// All Rights Reserved.
|
//
|
// NOTICE: Adobe permits you to use, modify, and distribute this file in
|
// accordance with the terms of the Adobe license agreement accompanying it.
|
/*****************************************************************************/
|
|
/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_lens_correction.cpp#1 $ */
|
/* $DateTime: 2012/05/30 13:28:51 $ */
|
/* $Change: 832332 $ */
|
/* $Author: tknoll $ */
|
|
/*****************************************************************************/
|
|
#include <cfloat>
|
#include <limits.h>
|
|
#include "dng_1d_table.h"
|
#include "dng_assertions.h"
|
#include "dng_bottlenecks.h"
|
#include "dng_exceptions.h"
|
#include "dng_filter_task.h"
|
#include "dng_globals.h"
|
#include "dng_host.h"
|
#include "dng_image.h"
|
#include "dng_lens_correction.h"
|
#include "dng_negative.h"
|
#include "dng_safe_arithmetic.h"
|
#include "dng_sdk_limits.h"
|
#include "dng_tag_values.h"
|
#include "dng_utils.h"
|
|
/*****************************************************************************/
|
|
dng_warp_params::dng_warp_params ()
|
|
: fPlanes (1)
|
, fCenter (0.5, 0.5)
|
|
{
|
|
}
|
|
/*****************************************************************************/
|
|
dng_warp_params::dng_warp_params (uint32 planes,
|
const dng_point_real64 ¢er)
|
|
: fPlanes (planes)
|
, fCenter (center)
|
|
{
|
|
DNG_ASSERT (planes >= 1, "Too few planes." );
|
DNG_ASSERT (planes <= kMaxColorPlanes, "Too many planes.");
|
|
DNG_ASSERT (fCenter.h >= 0.0 && fCenter.h <= 1.0,
|
"Center (horizontal) out of range.");
|
|
DNG_ASSERT (fCenter.v >= 0.0 && fCenter.v <= 1.0,
|
"Center (vertical) out of range.");
|
|
}
|
|
/*****************************************************************************/
|
|
dng_warp_params::~dng_warp_params ()
|
{
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_warp_params::IsNOPAll () const
|
{
|
|
return IsRadNOPAll () &&
|
IsTanNOPAll ();
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_warp_params::IsNOP (uint32 plane) const
|
{
|
|
return IsRadNOP (plane) &&
|
IsTanNOP (plane);
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_warp_params::IsRadNOPAll () const
|
{
|
|
for (uint32 plane = 0; plane < fPlanes; plane++)
|
{
|
|
if (!IsRadNOP (plane))
|
{
|
return false;
|
}
|
|
}
|
|
return true;
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_warp_params::IsRadNOP (uint32 /* plane */) const
|
{
|
|
return false;
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_warp_params::IsTanNOPAll () const
|
{
|
|
for (uint32 plane = 0; plane < fPlanes; plane++)
|
{
|
|
if (!IsTanNOP (plane))
|
{
|
return false;
|
}
|
|
}
|
|
return true;
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_warp_params::IsTanNOP (uint32 /* plane */) const
|
{
|
|
return false;
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_warp_params::IsValid () const
|
{
|
|
if (fPlanes < 1 || fPlanes > kMaxColorPlanes)
|
{
|
|
return false;
|
|
}
|
|
if (fCenter.h < 0.0 ||
|
fCenter.h > 1.0 ||
|
fCenter.v < 0.0 ||
|
fCenter.v > 1.0)
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_warp_params::IsValidForNegative (const dng_negative &negative) const
|
{
|
|
if (!IsValid ())
|
{
|
return false;
|
}
|
|
if ((fPlanes != 1) &&
|
(fPlanes != negative.ColorChannels ()))
|
{
|
return false;
|
}
|
|
return true;
|
|
}
|
|
/*****************************************************************************/
|
|
real64 dng_warp_params::EvaluateInverse (uint32 plane,
|
real64 y) const
|
{
|
|
const uint32 kMaxIterations = 30;
|
const real64 kNearZero = 1.0e-10;
|
|
real64 x0 = 0.0;
|
real64 y0 = Evaluate (plane,
|
x0);
|
|
real64 x1 = 1.0;
|
real64 y1 = Evaluate (plane,
|
x1);
|
|
for (uint32 iteration = 0; iteration < kMaxIterations; iteration++)
|
{
|
|
if (Abs_real64 (y1 - y0) < kNearZero)
|
{
|
break;
|
}
|
|
const real64 x2 = Pin_real64 (0.0,
|
x1 + (y - y1) * (x1 - x0) / (y1 - y0),
|
1.0);
|
|
const real64 y2 = Evaluate (plane,
|
x2);
|
|
x0 = x1;
|
y0 = y1;
|
|
x1 = x2;
|
y1 = y2;
|
|
}
|
|
return x1;
|
|
}
|
|
/*****************************************************************************/
|
|
dng_point_real64 dng_warp_params::EvaluateTangential2 (uint32 plane,
|
const dng_point_real64 &diff) const
|
{
|
|
const real64 dvdv = diff.v * diff.v;
|
const real64 dhdh = diff.h * diff.h;
|
|
const real64 rr = dvdv + dhdh;
|
|
dng_point_real64 diffSqr (dvdv,
|
dhdh);
|
|
return EvaluateTangential (plane,
|
rr,
|
diff,
|
diffSqr);
|
|
}
|
|
/*****************************************************************************/
|
|
dng_point_real64 dng_warp_params::EvaluateTangential3 (uint32 plane,
|
real64 r2,
|
const dng_point_real64 &diff) const
|
{
|
|
dng_point_real64 diffSqr (diff.v * diff.v,
|
diff.h * diff.h);
|
|
return EvaluateTangential (plane,
|
r2,
|
diff,
|
diffSqr);
|
|
}
|
|
/*****************************************************************************/
|
|
void dng_warp_params::Dump () const
|
{
|
|
#if qDNGValidate
|
|
printf ("Planes: %u\n", (unsigned) fPlanes);
|
|
printf (" Optical center:\n"
|
" h = %.6lf\n"
|
" v = %.6lf\n",
|
fCenter.h,
|
fCenter.v);
|
|
#endif
|
|
}
|
|
/*****************************************************************************/
|
|
dng_warp_params_rectilinear::dng_warp_params_rectilinear ()
|
|
: dng_warp_params ()
|
|
{
|
|
for (uint32 plane = 0; plane < kMaxColorPlanes; plane++)
|
{
|
|
fRadParams [plane] = dng_vector (4);
|
fTanParams [plane] = dng_vector (2);
|
|
fRadParams [plane][0] = 1.0;
|
|
}
|
|
}
|
|
/*****************************************************************************/
|
|
dng_warp_params_rectilinear::dng_warp_params_rectilinear (uint32 planes,
|
const dng_vector radParams [],
|
const dng_vector tanParams [],
|
const dng_point_real64 ¢er)
|
|
: dng_warp_params (planes,
|
center)
|
|
{
|
|
for (uint32 i = 0; i < fPlanes; i++)
|
{
|
fRadParams [i] = radParams [i];
|
fTanParams [i] = tanParams [i];
|
}
|
|
}
|
|
/*****************************************************************************/
|
|
dng_warp_params_rectilinear::~dng_warp_params_rectilinear ()
|
{
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_warp_params_rectilinear::IsRadNOP (uint32 plane) const
|
{
|
|
DNG_ASSERT (plane < fPlanes, "plane out of range.");
|
|
const dng_vector &r = fRadParams [plane];
|
|
return (r [0] == 1.0 &&
|
r [1] == 0.0 &&
|
r [2] == 0.0 &&
|
r [3] == 0.0);
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_warp_params_rectilinear::IsTanNOP (uint32 plane) const
|
{
|
|
DNG_ASSERT (plane < fPlanes, "plane out of range.");
|
|
const dng_vector &t = fTanParams [plane];
|
|
return (t [0] == 0.0 &&
|
t [1] == 0.0);
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_warp_params_rectilinear::IsValid () const
|
{
|
|
for (uint32 plane = 0; plane < fPlanes; plane++)
|
{
|
|
if (fRadParams [plane].Count () != 4)
|
{
|
return false;
|
}
|
|
if (fTanParams [plane].Count () < 2)
|
{
|
return false;
|
}
|
|
}
|
|
return dng_warp_params::IsValid ();
|
|
}
|
|
/*****************************************************************************/
|
|
void dng_warp_params_rectilinear::PropagateToAllPlanes (uint32 totalPlanes)
|
{
|
|
for (uint32 plane = fPlanes; plane < totalPlanes; plane++)
|
{
|
|
fRadParams [plane] = fRadParams [0];
|
fTanParams [plane] = fTanParams [0];
|
|
}
|
|
}
|
|
/*****************************************************************************/
|
|
real64 dng_warp_params_rectilinear::Evaluate (uint32 plane,
|
real64 x) const
|
{
|
|
const dng_vector &K = fRadParams [plane]; // Coefficients.
|
|
const real64 x2 = x * x;
|
|
return x * (K [0] + x2 * (K [1] + x2 * (K [2] + x2 * K [3])));
|
|
}
|
|
/*****************************************************************************/
|
|
real64 dng_warp_params_rectilinear::EvaluateRatio (uint32 plane,
|
real64 r2) const
|
{
|
|
const dng_vector &K = fRadParams [plane]; // Coefficients.
|
|
return K [0] + r2 * (K [1] + r2 * (K [2] + r2 * K [3]));
|
|
}
|
|
/*****************************************************************************/
|
|
dng_point_real64 dng_warp_params_rectilinear::EvaluateTangential (uint32 plane,
|
real64 r2,
|
const dng_point_real64 &diff,
|
const dng_point_real64 &diff2) const
|
{
|
|
const real64 kt0 = fTanParams [plane][0];
|
const real64 kt1 = fTanParams [plane][1];
|
|
const real64 dh = diff.h;
|
const real64 dv = diff.v;
|
|
const real64 dhdh = diff2.h;
|
const real64 dvdv = diff2.v;
|
|
return dng_point_real64 (kt0 * (r2 + 2.0 * dvdv) + (2.0 * kt1 * dh * dv), // v
|
kt1 * (r2 + 2.0 * dhdh) + (2.0 * kt0 * dh * dv)); // h
|
|
}
|
|
/*****************************************************************************/
|
|
real64 dng_warp_params_rectilinear::MaxSrcRadiusGap (real64 maxDstGap) const
|
{
|
|
real64 maxSrcGap = 0.0;
|
|
for (uint32 plane = 0; plane < fPlanes; plane++)
|
{
|
|
const dng_vector &coefs = fRadParams [plane];
|
|
const real64 k3 = coefs [1];
|
const real64 k5 = coefs [2];
|
const real64 k7 = coefs [3];
|
|
//
|
// Let f (r) be the radius warp function. Consider the function
|
//
|
// gap (r) = f (r + maxDstGap) - f (r)
|
//
|
// We wish to maximize gap (r) over the domain [0, 1 - maxDstGap]. This will
|
// give us a reasonable upper bound on the src tile size, independent of
|
// dstBounds.
|
//
|
// As usual, we maximize gap (r) by examining its critical points, i.e., by
|
// considering the roots of its derivative which lie in the domain [0, 1 -
|
// maxDstGap]. gap' (r) is a 5th-order polynomial. One of its roots is
|
// -maxDstGap / 2, which is negative and hence lies outside the domain of
|
// interest. This leaves 4 other possible roots. We solve for these
|
// analytically below.
|
//
|
|
real64 roots [4];
|
uint32 numRoots = 0;
|
|
if (k7 == 0.0)
|
{
|
|
if (k5 == 0.0)
|
{
|
|
// No roots in [0,1].
|
|
}
|
|
else
|
{
|
|
// k7 is zero, but k5 is non-zero. At most two real roots.
|
|
const real64 discrim = 25.0 * (-6.0 * k3 * k5 - 5.0 * k5 * maxDstGap * maxDstGap);
|
|
if (discrim >= 0.0)
|
{
|
|
// Two real roots.
|
|
const real64 scale = 0.1 * k5;
|
const real64 offset = -5.0 * maxDstGap * k5;
|
const real64 sDiscrim = sqrt (discrim);
|
|
roots [numRoots++] = scale * (offset + sDiscrim);
|
roots [numRoots++] = scale * (offset - sDiscrim);
|
|
}
|
|
}
|
|
}
|
|
else
|
{
|
|
// k7 is non-zero. Up to 4 real roots.
|
|
const real64 d = maxDstGap;
|
const real64 d2 = d * d;
|
const real64 d4 = d2 * d2;
|
|
const real64 discrim = 25.0 * k5 * k5
|
- 63.0 * k3 * k7
|
+ 35.0 * d2 * k5 * k7
|
+ 49.0 * d4 * k7 * k7;
|
|
if (discrim >= 0.0)
|
{
|
|
const real64 sDiscrim = 4.0 * k7 * sqrt (discrim);
|
|
const real64 offset = -20.0 * k5 * k7 - 35.0 * d2 * k7 * k7;
|
|
const real64 discrim1 = offset - sDiscrim;
|
const real64 discrim2 = offset + sDiscrim;
|
|
const real64 scale = sqrt (21.0) / (42.0 * k7);
|
|
if (discrim1 >= 0.0)
|
{
|
|
const real64 offset1 = -d * 0.5;
|
const real64 sDiscrim1 = scale * sqrt (discrim1);
|
|
roots [numRoots++] = offset1 + sDiscrim1;
|
roots [numRoots++] = offset1 - sDiscrim1;
|
|
}
|
|
if (discrim2 >= 0.0)
|
{
|
|
const real64 offset2 = -d * 0.5;
|
const real64 sDiscrim2 = scale * sqrt (discrim2);
|
|
roots [numRoots++] = offset2 + sDiscrim2;
|
roots [numRoots++] = offset2 - sDiscrim2;
|
|
}
|
|
}
|
|
}
|
|
real64 planeMaxSrcGap = 0.0;
|
|
// Examine the endpoints.
|
|
{
|
|
// Check left endpoint: f (maxDstGap) - f (0). Remember that f (0) == 0.
|
|
const real64 gap1 = Evaluate (plane, maxDstGap);
|
|
planeMaxSrcGap = Max_real64 (planeMaxSrcGap, gap1);
|
|
// Check right endpoint: f (1) - f (1 - maxDstGap).
|
|
const real64 gap2 = Evaluate (plane, 1.0)
|
- Evaluate (plane, 1.0 - maxDstGap);
|
|
planeMaxSrcGap = Max_real64 (planeMaxSrcGap, gap2);
|
|
}
|
|
// Examine the roots we found, if any.
|
|
for (uint32 i = 0; i < numRoots; i++)
|
{
|
|
const real64 r = roots [i];
|
|
if (r > 0.0 && r < 1.0 - maxDstGap)
|
{
|
|
const real64 gap = Evaluate (plane, r + maxDstGap)
|
- Evaluate (plane, r);
|
|
planeMaxSrcGap = Max_real64 (planeMaxSrcGap, gap);
|
|
}
|
|
}
|
|
maxSrcGap = Max_real64 (maxSrcGap,
|
planeMaxSrcGap);
|
|
}
|
|
return maxSrcGap;
|
|
}
|
|
/*****************************************************************************/
|
|
dng_point_real64 dng_warp_params_rectilinear::MaxSrcTanGap (dng_point_real64 minDst,
|
dng_point_real64 maxDst) const
|
{
|
|
const real64 v [] = { minDst.v, maxDst.v, 0.0 };
|
const real64 h [] = { minDst.h, maxDst.h, 0.0 };
|
|
dng_point_real64 maxGap;
|
|
for (uint32 plane = 0; plane < fPlanes; plane++)
|
{
|
|
real64 hMin = +FLT_MAX;
|
real64 hMax = -FLT_MAX;
|
|
real64 vMin = +FLT_MAX;
|
real64 vMax = -FLT_MAX;
|
|
for (uint32 i = 0; i < 3; i++)
|
{
|
|
for (uint32 j = 0; j < 3; j++)
|
{
|
|
dng_point_real64 dstDiff (v [i],
|
h [j]);
|
|
dng_point_real64 srcDiff = EvaluateTangential2 (plane,
|
dstDiff);
|
|
hMin = Min_real64 (hMin, srcDiff.h);
|
hMax = Max_real64 (hMax, srcDiff.h);
|
|
vMin = Min_real64 (vMin, srcDiff.v);
|
vMax = Max_real64 (vMax, srcDiff.v);
|
|
}
|
|
}
|
|
const real64 hGap = hMax - hMin;
|
const real64 vGap = vMax - vMin;
|
|
maxGap.h = Max_real64 (maxGap.h, hGap);
|
maxGap.v = Max_real64 (maxGap.v, vGap);
|
|
}
|
|
return maxGap;
|
|
}
|
|
/*****************************************************************************/
|
|
void dng_warp_params_rectilinear::Dump () const
|
{
|
|
#if qDNGValidate
|
|
dng_warp_params::Dump ();
|
|
for (uint32 plane = 0; plane < fPlanes; plane++)
|
{
|
|
printf (" Plane %u:\n", (unsigned) plane);
|
|
printf (" Radial params: %.6lf, %.6lf, %.6lf, %.6lf\n",
|
fRadParams [plane][0],
|
fRadParams [plane][1],
|
fRadParams [plane][2],
|
fRadParams [plane][3]);
|
|
printf (" Tangential params: %.6lf, %.6lf\n",
|
fTanParams [plane][0],
|
fTanParams [plane][1]);
|
|
}
|
|
#endif
|
|
}
|
|
/*****************************************************************************/
|
|
dng_warp_params_fisheye::dng_warp_params_fisheye ()
|
|
: dng_warp_params ()
|
|
{
|
|
for (uint32 plane = 0; plane < kMaxColorPlanes; plane++)
|
{
|
|
fRadParams [plane] = dng_vector (4);
|
|
}
|
|
}
|
|
/*****************************************************************************/
|
|
dng_warp_params_fisheye::dng_warp_params_fisheye (uint32 planes,
|
const dng_vector radParams [],
|
const dng_point_real64 ¢er)
|
|
: dng_warp_params (planes, center)
|
|
{
|
|
for (uint32 i = 0; i < fPlanes; i++)
|
{
|
|
fRadParams [i] = radParams [i];
|
|
}
|
|
}
|
|
/*****************************************************************************/
|
|
dng_warp_params_fisheye::~dng_warp_params_fisheye ()
|
{
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_warp_params_fisheye::IsRadNOP (uint32 /* plane */) const
|
{
|
|
return false;
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_warp_params_fisheye::IsTanNOP (uint32 /* plane */) const
|
{
|
|
return true;
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_warp_params_fisheye::IsValid () const
|
{
|
|
for (uint32 plane = 0; plane < fPlanes; plane++)
|
{
|
|
if (fRadParams [plane].Count () != 4)
|
{
|
return false;
|
}
|
|
}
|
|
return dng_warp_params::IsValid ();
|
|
}
|
|
/*****************************************************************************/
|
|
void dng_warp_params_fisheye::PropagateToAllPlanes (uint32 totalPlanes)
|
{
|
|
for (uint32 plane = fPlanes; plane < totalPlanes; plane++)
|
{
|
|
fRadParams [plane] = fRadParams [0];
|
|
}
|
|
}
|
|
/*****************************************************************************/
|
|
real64 dng_warp_params_fisheye::Evaluate (uint32 plane,
|
real64 r) const
|
{
|
|
const real64 t = atan (r);
|
|
const dng_vector &K = fRadParams [plane];
|
|
const real64 t2 = t * t;
|
|
return t * (K [0] + t2 * (K [1] + t2 * (K [2] + t2 * K [3])));
|
|
}
|
|
/*****************************************************************************/
|
|
real64 dng_warp_params_fisheye::EvaluateRatio (uint32 plane,
|
real64 rSqr) const
|
{
|
|
const real64 eps = 1.0e-12;
|
|
if (rSqr < eps)
|
{
|
|
// r is very close to zero.
|
|
return 1.0;
|
|
}
|
|
const real64 r = sqrt (rSqr);
|
|
return Evaluate (plane, r) / r;
|
|
}
|
|
/*****************************************************************************/
|
|
dng_point_real64 dng_warp_params_fisheye::EvaluateTangential (uint32 /* plane */,
|
real64 /* r2 */,
|
const dng_point_real64 & /* diff */,
|
const dng_point_real64 & /* diff2 */) const
|
{
|
|
// This fisheye model does not support tangential warping.
|
|
ThrowProgramError ();
|
|
return dng_point_real64 (0.0, 0.0);
|
|
}
|
|
/*****************************************************************************/
|
|
real64 dng_warp_params_fisheye::MaxSrcRadiusGap (real64 maxDstGap) const
|
{
|
|
//
|
// Let f (r) be the radius warp function. Consider the function
|
//
|
// gap (r) = f (r + maxDstGap) - f (r)
|
//
|
// We wish to maximize gap (r) over the domain [0, 1 - maxDstGap]. This will
|
// give us a reasonable upper bound on the src tile size, independent of
|
// dstBounds.
|
//
|
// Ideally, we'd like to maximize gap (r) by examining its critical points,
|
// i.e., by considering the roots of its derivative which lie in the domain [0,
|
// 1 - maxDstGap]. However, gap' (r) is too complex to find its roots
|
// analytically.
|
//
|
|
real64 maxSrcGap = 0.0;
|
|
DNG_REQUIRE (maxDstGap > 0.0, "maxDstGap must be positive.");
|
|
const real64 kMaxValue = 1.0 - maxDstGap;
|
|
const uint32 kSteps = 128;
|
|
const real64 kNorm = kMaxValue / real64 (kSteps - 1);
|
|
for (uint32 plane = 0; plane < fPlanes; plane++)
|
{
|
|
for (uint32 i = 0; i < kSteps; i++)
|
{
|
|
const real64 tt = i * kNorm;
|
|
const real64 gap = Evaluate (plane, tt + maxDstGap)
|
- Evaluate (plane, tt);
|
|
maxSrcGap = Max_real64 (maxSrcGap,
|
gap);
|
|
}
|
|
}
|
|
return maxSrcGap;
|
|
}
|
|
/*****************************************************************************/
|
|
dng_point_real64 dng_warp_params_fisheye::MaxSrcTanGap (dng_point_real64 /* minDst */,
|
dng_point_real64 /* maxDst */) const
|
{
|
|
// This fisheye model does not support tangential distortion.
|
|
return dng_point_real64 (0.0, 0.0);
|
|
}
|
|
/*****************************************************************************/
|
|
void dng_warp_params_fisheye::Dump () const
|
{
|
|
#if qDNGValidate
|
|
dng_warp_params::Dump ();
|
|
for (uint32 plane = 0; plane < fPlanes; plane++)
|
{
|
|
printf (" Plane %u:\n", (unsigned) plane);
|
|
printf (" Radial params: %.6lf, %.6lf, %.6lf, %.6lf\n",
|
fRadParams [plane][0],
|
fRadParams [plane][1],
|
fRadParams [plane][2],
|
fRadParams [plane][3]);
|
|
}
|
|
#endif
|
|
}
|
|
/*****************************************************************************/
|
|
class dng_filter_warp: public dng_filter_task
|
{
|
|
protected:
|
|
AutoPtr<dng_warp_params> fParams;
|
|
dng_point_real64 fCenter;
|
|
dng_resample_weights_2d fWeights;
|
|
real64 fNormRadius;
|
real64 fInvNormRadius;
|
|
bool fIsRadNOP;
|
bool fIsTanNOP;
|
|
const real64 fPixelScaleV;
|
const real64 fPixelScaleVInv;
|
|
public:
|
|
dng_filter_warp (const dng_image &srcImage,
|
dng_image &dstImage,
|
const dng_negative &negative,
|
AutoPtr<dng_warp_params> ¶ms);
|
|
virtual void Initialize (dng_host &host);
|
|
virtual dng_rect SrcArea (const dng_rect &dstArea);
|
|
virtual dng_point SrcTileSize (const dng_point &dstTileSize);
|
|
virtual void ProcessArea (uint32 threadIndex,
|
dng_pixel_buffer &srcBuffer,
|
dng_pixel_buffer &dstBuffer);
|
|
virtual dng_point_real64 GetSrcPixelPosition (const dng_point_real64 &dst,
|
uint32 plane);
|
|
};
|
|
/*****************************************************************************/
|
|
dng_filter_warp::dng_filter_warp (const dng_image &srcImage,
|
dng_image &dstImage,
|
const dng_negative &negative,
|
AutoPtr<dng_warp_params> ¶ms)
|
|
: dng_filter_task (srcImage,
|
dstImage)
|
|
, fParams (params.Release ())
|
|
, fCenter ()
|
|
, fWeights ()
|
|
, fNormRadius (1.0)
|
, fInvNormRadius (1.0)
|
|
, fIsRadNOP (false)
|
, fIsTanNOP (false)
|
|
, fPixelScaleV (1.0 / negative.PixelAspectRatio ())
|
, fPixelScaleVInv (1.0 / fPixelScaleV)
|
|
{
|
|
// Force float processing.
|
|
fSrcPixelType = ttFloat;
|
fDstPixelType = ttFloat;
|
|
fIsRadNOP = fParams->IsRadNOPAll ();
|
fIsTanNOP = fParams->IsTanNOPAll ();
|
|
const uint32 negPlanes = negative.ColorChannels ();
|
|
DNG_ASSERT (negPlanes >= 1, "Too few planes." );
|
DNG_ASSERT (negPlanes <= kMaxColorPlanes, "Too many planes.");
|
|
(void) negPlanes;
|
|
// At least one set of params must do something interesting.
|
|
if (fIsRadNOP && fIsTanNOP)
|
{
|
ThrowProgramError ();
|
}
|
|
// Make sure the warp params are valid for this negative.
|
|
if (!fParams->IsValidForNegative (negative))
|
{
|
ThrowBadFormat ();
|
}
|
|
// Compute center.
|
|
const dng_rect bounds = srcImage.Bounds ();
|
|
fCenter.h = Lerp_real64 ((real64) bounds.l,
|
(real64) bounds.r,
|
fParams->fCenter.h);
|
|
fCenter.v = Lerp_real64 ((real64) bounds.t,
|
(real64) bounds.b,
|
fParams->fCenter.v);
|
|
// Compute max pixel radius and derive various normalized radius values. Note
|
// that when computing the max pixel radius, we must take into account the pixel
|
// aspect ratio.
|
|
{
|
|
dng_rect squareBounds (bounds);
|
|
squareBounds.b = squareBounds.t +
|
Round_int32 (fPixelScaleV * (real64) squareBounds.H ());
|
|
const dng_point_real64 squareCenter (Lerp_real64 ((real64) squareBounds.t,
|
(real64) squareBounds.b,
|
fParams->fCenter.v),
|
|
Lerp_real64 ((real64) squareBounds.l,
|
(real64) squareBounds.r,
|
fParams->fCenter.h));
|
|
fNormRadius = MaxDistancePointToRect (squareCenter,
|
squareBounds);
|
|
fInvNormRadius = 1.0 / fNormRadius;
|
|
}
|
|
// Propagate warp params to other planes.
|
|
fParams->PropagateToAllPlanes (fDstPlanes);
|
|
}
|
|
/*****************************************************************************/
|
|
void dng_filter_warp::Initialize (dng_host &host)
|
{
|
|
// Make resample weights.
|
|
const dng_resample_function &kernel = dng_resample_bicubic::Get ();
|
|
fWeights.Initialize (kernel,
|
host.Allocator ());
|
|
}
|
|
/*****************************************************************************/
|
|
dng_rect dng_filter_warp::SrcArea (const dng_rect &dstArea)
|
{
|
|
// Walk each pixel of the boundary of dstArea, map it to the uncorrected src
|
// pixel position, and return the rectangle that contains all such src pixels.
|
|
int32 xMin = INT_MAX;
|
int32 xMax = INT_MIN;
|
int32 yMin = INT_MAX;
|
int32 yMax = INT_MIN;
|
|
for (uint32 plane = 0; plane < fDstPlanes; plane++)
|
{
|
|
// Top and bottom edges.
|
|
for (int32 c = dstArea.l; c < dstArea.r; c++)
|
{
|
|
// Top edge.
|
|
{
|
|
const dng_point_real64 dst (dstArea.t, c);
|
|
const dng_point_real64 src = GetSrcPixelPosition (dst, plane);
|
|
const int32 y = ConvertDoubleToInt32 (floor (src.v));
|
|
yMin = Min_int32 (yMin, y);
|
|
}
|
|
// Bottom edge.
|
|
{
|
|
const dng_point_real64 dst (dstArea.b - 1, c);
|
|
const dng_point_real64 src = GetSrcPixelPosition (dst, plane);
|
|
const int32 y = ConvertDoubleToInt32 (ceil (src.v));
|
|
yMax = Max_int32 (yMax, y);
|
|
}
|
|
}
|
|
// Left and right edges.
|
|
for (int32 r = dstArea.t; r < dstArea.b; r++)
|
{
|
|
// Left edge.
|
|
{
|
|
const dng_point_real64 dst (r, dstArea.l);
|
|
const dng_point_real64 src = GetSrcPixelPosition (dst, plane);
|
|
const int32 x = ConvertDoubleToInt32 (floor (src.h));
|
|
xMin = Min_int32 (xMin, x);
|
|
}
|
|
// Right edge.
|
|
{
|
|
const dng_point_real64 dst (r, dstArea.r - 1);
|
|
const dng_point_real64 src = GetSrcPixelPosition (dst, plane);
|
|
const int32 x = ConvertDoubleToInt32 (ceil (src.h));
|
|
xMax = Max_int32 (xMax, x);
|
|
}
|
|
}
|
|
}
|
|
// Pad each side by filter radius.
|
|
const int32 pad = ConvertUint32ToInt32(fWeights.Radius());
|
|
xMin = SafeInt32Sub(xMin, pad);
|
yMin = SafeInt32Sub(yMin, pad);
|
xMax = SafeInt32Add(xMax, pad);
|
yMax = SafeInt32Add(yMax, pad);
|
|
xMax = SafeInt32Add(xMax, 1);
|
yMax = SafeInt32Add(yMax, 1);
|
|
const dng_rect srcArea (yMin,
|
xMin,
|
yMax,
|
xMax);
|
|
return srcArea;
|
|
}
|
|
/*****************************************************************************/
|
|
dng_point dng_filter_warp::SrcTileSize (const dng_point &dstTileSize)
|
{
|
|
// Obtain an upper bound on the source tile size. We'll do this by considering
|
// upper bounds on the radial and tangential warp components separately, then add
|
// them together. This is not a tight bound but is good enough because the
|
// tangential terms are usually quite small.
|
|
// Get upper bound on src tile size from radial warp.
|
|
DNG_REQUIRE (dstTileSize.v > 0, "Invalid tile height.");
|
DNG_REQUIRE (dstTileSize.h > 0, "Invalid tile width.");
|
|
const real64 maxDstGap = fInvNormRadius * hypot ((real64) dstTileSize.h,
|
(real64) dstTileSize.v);
|
|
dng_point srcTileSize;
|
|
if (maxDstGap >= 1.0)
|
{
|
|
// The proposed tile size is unusually large, i.e., its hypotenuse is larger
|
// than the maximum radius. Bite the bullet and just return a tile size big
|
// enough to process the whole image.
|
|
srcTileSize = SrcArea (fDstImage.Bounds ()).Size ();
|
|
}
|
|
else
|
{
|
|
// maxDstGap < 1.0.
|
|
const real64 maxSrcGap = fParams->MaxSrcRadiusGap (maxDstGap);
|
|
const int32 dim = ConvertDoubleToInt32 (ceil (maxSrcGap * fNormRadius));
|
|
srcTileSize = dng_point (dim, dim);
|
|
}
|
|
srcTileSize.h += ConvertUint32ToInt32(fWeights.Width());
|
srcTileSize.v += ConvertUint32ToInt32(fWeights.Width());
|
|
// Get upper bound on src tile size from tangential warp.
|
|
const dng_rect_real64 bounds (fSrcImage.Bounds ());
|
|
const dng_point_real64 minDst ((bounds.t - fCenter.v) * fInvNormRadius,
|
(bounds.l - fCenter.h) * fInvNormRadius);
|
|
const dng_point_real64 maxDst ((bounds.b - 1.0 - fCenter.v) * fInvNormRadius,
|
(bounds.r - 1.0 - fCenter.h) * fInvNormRadius);
|
|
const dng_point_real64 srcTanGap = fParams->MaxSrcTanGap (minDst,
|
maxDst);
|
|
// Add the two bounds together.
|
|
srcTileSize.v += ConvertDoubleToInt32 (ceil (srcTanGap.v * fNormRadius));
|
srcTileSize.h += ConvertDoubleToInt32 (ceil (srcTanGap.h * fNormRadius));
|
|
return srcTileSize;
|
|
}
|
|
/*****************************************************************************/
|
|
void dng_filter_warp::ProcessArea (uint32 /* threadIndex */,
|
dng_pixel_buffer &srcBuffer,
|
dng_pixel_buffer &dstBuffer)
|
{
|
|
// Prepare resample constants.
|
|
const int32 wCount = fWeights.Width ();
|
|
const dng_point srcOffset (fWeights.Offset (),
|
fWeights.Offset ());
|
|
const real64 numSubsamples = (real64) kResampleSubsampleCount2D;
|
|
// Prepare area and step constants.
|
|
const dng_rect srcArea = srcBuffer.fArea;
|
const dng_rect dstArea = dstBuffer.fArea;
|
|
const int32 srcRowStep = (int32) srcBuffer.RowStep ();
|
|
const int32 hMin = srcArea.l;
|
const int32 hMax = SafeInt32Sub (SafeInt32Sub (srcArea.r, wCount), 1);
|
|
const int32 vMin = srcArea.t;
|
const int32 vMax = SafeInt32Sub (SafeInt32Sub (srcArea.b, wCount), 1);
|
|
if (hMax < hMin || vMax < vMin)
|
{
|
|
ThrowBadFormat ("Empty source area in dng_filter_warp.");
|
|
}
|
|
// Warp each plane.
|
|
for (uint32 plane = 0; plane < dstBuffer.fPlanes; plane++)
|
{
|
|
real32 *dPtr = dstBuffer.DirtyPixel_real32 (dstArea.t,
|
dstArea.l,
|
plane);
|
|
for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++)
|
{
|
|
uint32 dstIndex = 0;
|
|
for (int32 dstCol = dstArea.l; dstCol < dstArea.r; dstCol++, dstIndex++)
|
{
|
|
// Get destination (corrected) pixel position.
|
|
const dng_point_real64 dPos ((real64) dstRow,
|
(real64) dstCol);
|
|
// Warp to source (uncorrected) pixel position.
|
|
const dng_point_real64 sPos = GetSrcPixelPosition (dPos,
|
plane);
|
|
// Decompose into integer and fractional parts.
|
|
dng_point sInt (ConvertDoubleToInt32 (floor (sPos.v)),
|
ConvertDoubleToInt32 (floor (sPos.h)));
|
|
dng_point sFct (ConvertDoubleToInt32 ((sPos.v - (real64) sInt.v) * numSubsamples),
|
ConvertDoubleToInt32 ((sPos.h - (real64) sInt.h) * numSubsamples));
|
|
// Add resample offset.
|
|
sInt = sInt + srcOffset;
|
|
// Clip.
|
|
if (sInt.h < hMin)
|
{
|
sInt.h = hMin;
|
sFct.h = 0;
|
}
|
|
else if (sInt.h > hMax)
|
{
|
sInt.h = hMax;
|
sFct.h = 0;
|
}
|
|
if (sInt.v < vMin)
|
{
|
sInt.v = vMin;
|
sFct.v = 0;
|
}
|
|
else if (sInt.v > vMax)
|
{
|
sInt.v = vMax;
|
sFct.v = 0;
|
}
|
|
// Perform 2D resample.
|
|
const real32 *w = fWeights.Weights32 (sFct);
|
|
const real32 *s = srcBuffer.ConstPixel_real32 (sInt.v,
|
sInt.h,
|
plane);
|
|
real32 total = 0.0f;
|
|
for (int32 i = 0; i < wCount; i++)
|
{
|
|
for (int32 j = 0; j < wCount; j++)
|
{
|
|
total += w [j] * s [j];
|
|
}
|
|
w += wCount;
|
s += srcRowStep;
|
|
}
|
|
// Store final pixel value.
|
|
dPtr [dstIndex] = Pin_real32 (total);
|
|
}
|
|
// Advance to next row.
|
|
dPtr += dstBuffer.RowStep ();
|
|
}
|
|
}
|
|
}
|
|
/*****************************************************************************/
|
|
dng_point_real64 dng_filter_warp::GetSrcPixelPosition (const dng_point_real64 &dst,
|
uint32 plane)
|
{
|
|
const dng_point_real64 diff = dst - fCenter;
|
|
const dng_point_real64 diffNorm (diff.v * fInvNormRadius,
|
diff.h * fInvNormRadius);
|
|
const dng_point_real64 diffNormScaled (diffNorm.v * fPixelScaleV,
|
diffNorm.h);
|
|
const dng_point_real64 diffNormSqr (diffNormScaled.v * diffNormScaled.v,
|
diffNormScaled.h * diffNormScaled.h);
|
|
const real64 rr = Min_real64 (diffNormSqr.v + diffNormSqr.h,
|
1.0);
|
|
dng_point_real64 dSrc;
|
|
if (fIsTanNOP)
|
{
|
|
// Radial only.
|
|
const real64 ratio = fParams->EvaluateRatio (plane,
|
rr);
|
|
dSrc.h = diff.h * ratio;
|
dSrc.v = diff.v * ratio;
|
|
}
|
|
else if (fIsRadNOP)
|
{
|
|
// Tangential only.
|
|
const dng_point_real64 tan = fParams->EvaluateTangential (plane,
|
rr,
|
diffNormScaled,
|
diffNormSqr);
|
|
dSrc.h = diff.h + (fNormRadius * tan.h);
|
dSrc.v = diff.v + (fNormRadius * tan.v * fPixelScaleVInv);
|
|
}
|
|
else
|
{
|
|
// Radial and tangential.
|
|
const real64 ratio = fParams->EvaluateRatio (plane,
|
rr);
|
|
const dng_point_real64 tan = fParams->EvaluateTangential (plane,
|
rr,
|
diffNormScaled,
|
diffNormSqr);
|
|
dSrc.h = fNormRadius * (diffNorm.h * ratio + tan.h);
|
dSrc.v = fNormRadius * (diffNorm.v * ratio + tan.v * fPixelScaleVInv);
|
|
}
|
|
return fCenter + dSrc;
|
|
}
|
|
/*****************************************************************************/
|
|
dng_opcode_WarpRectilinear::dng_opcode_WarpRectilinear (const dng_warp_params_rectilinear ¶ms,
|
uint32 flags)
|
|
: dng_opcode (dngOpcode_WarpRectilinear,
|
dngVersion_1_3_0_0,
|
flags)
|
|
, fWarpParams (params)
|
|
{
|
|
if (!params.IsValid ())
|
{
|
ThrowBadFormat ();
|
}
|
|
}
|
|
/*****************************************************************************/
|
|
dng_opcode_WarpRectilinear::dng_opcode_WarpRectilinear (dng_stream &stream)
|
|
: dng_opcode (dngOpcode_WarpRectilinear,
|
stream,
|
"WarpRectilinear")
|
|
, fWarpParams ()
|
|
{
|
|
// Grab the size in bytes.
|
|
const uint32 bytes = stream.Get_uint32 ();
|
|
// Grab the number of planes to warp.
|
|
fWarpParams.fPlanes = stream.Get_uint32 ();
|
|
// Verify number of planes.
|
|
if (fWarpParams.fPlanes == 0 ||
|
fWarpParams.fPlanes > kMaxColorPlanes)
|
{
|
ThrowBadFormat ();
|
}
|
|
// Verify the size.
|
|
if (bytes != ParamBytes (fWarpParams.fPlanes))
|
{
|
ThrowBadFormat ();
|
}
|
|
// Read warp parameters for each plane.
|
|
for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++)
|
{
|
|
fWarpParams.fRadParams [plane][0] = stream.Get_real64 ();
|
fWarpParams.fRadParams [plane][1] = stream.Get_real64 ();
|
fWarpParams.fRadParams [plane][2] = stream.Get_real64 ();
|
fWarpParams.fRadParams [plane][3] = stream.Get_real64 ();
|
|
fWarpParams.fTanParams [plane][0] = stream.Get_real64 ();
|
fWarpParams.fTanParams [plane][1] = stream.Get_real64 ();
|
|
}
|
|
// Read the image center.
|
|
fWarpParams.fCenter.h = stream.Get_real64 ();
|
fWarpParams.fCenter.v = stream.Get_real64 ();
|
|
#if qDNGValidate
|
|
if (gVerbose)
|
{
|
|
fWarpParams.Dump ();
|
|
}
|
|
#endif
|
|
if (!fWarpParams.IsValid ())
|
{
|
ThrowBadFormat ();
|
}
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_opcode_WarpRectilinear::IsNOP () const
|
{
|
|
return fWarpParams.IsNOPAll ();
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_opcode_WarpRectilinear::IsValidForNegative (const dng_negative &negative) const
|
{
|
|
return fWarpParams.IsValidForNegative (negative);
|
|
}
|
|
/*****************************************************************************/
|
|
void dng_opcode_WarpRectilinear::PutData (dng_stream &stream) const
|
{
|
|
const uint32 bytes = ParamBytes (fWarpParams.fPlanes);
|
|
stream.Put_uint32 (bytes);
|
|
stream.Put_uint32 (fWarpParams.fPlanes);
|
|
for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++)
|
{
|
|
stream.Put_real64 (fWarpParams.fRadParams [plane][0]);
|
stream.Put_real64 (fWarpParams.fRadParams [plane][1]);
|
stream.Put_real64 (fWarpParams.fRadParams [plane][2]);
|
stream.Put_real64 (fWarpParams.fRadParams [plane][3]);
|
|
stream.Put_real64 (fWarpParams.fTanParams [plane][0]);
|
stream.Put_real64 (fWarpParams.fTanParams [plane][1]);
|
|
}
|
|
stream.Put_real64 (fWarpParams.fCenter.h);
|
stream.Put_real64 (fWarpParams.fCenter.v);
|
|
}
|
|
/*****************************************************************************/
|
|
void dng_opcode_WarpRectilinear::Apply (dng_host &host,
|
dng_negative &negative,
|
AutoPtr<dng_image> &image)
|
{
|
|
#if qDNGValidate
|
|
dng_timer timer ("WarpRectilinear time");
|
|
#endif
|
|
// Allocate destination image.
|
|
AutoPtr<dng_image> dstImage (host.Make_dng_image (image->Bounds (),
|
image->Planes (),
|
image->PixelType ()));
|
|
// Warp the image.
|
|
AutoPtr<dng_warp_params> params (new dng_warp_params_rectilinear (fWarpParams));
|
|
dng_filter_warp filter (*image,
|
*dstImage,
|
negative,
|
params);
|
|
filter.Initialize (host);
|
|
host.PerformAreaTask (filter,
|
image->Bounds ());
|
|
// Return the new image.
|
|
image.Reset (dstImage.Release ());
|
|
}
|
|
/*****************************************************************************/
|
|
uint32 dng_opcode_WarpRectilinear::ParamBytes (uint32 planes)
|
{
|
|
return (1 * (uint32) sizeof (uint32) ) + // Number of planes.
|
(6 * (uint32) sizeof (real64) * planes) + // Warp coefficients.
|
(2 * (uint32) sizeof (real64) ); // Optical center.
|
|
}
|
|
/*****************************************************************************/
|
|
dng_opcode_WarpFisheye::dng_opcode_WarpFisheye (const dng_warp_params_fisheye ¶ms,
|
uint32 flags)
|
|
: dng_opcode (dngOpcode_WarpFisheye,
|
dngVersion_1_3_0_0,
|
flags)
|
|
, fWarpParams (params)
|
|
{
|
|
if (!params.IsValid ())
|
{
|
ThrowBadFormat ();
|
}
|
|
}
|
|
/*****************************************************************************/
|
|
dng_opcode_WarpFisheye::dng_opcode_WarpFisheye (dng_stream &stream)
|
|
: dng_opcode (dngOpcode_WarpFisheye,
|
stream,
|
"WarpFisheye")
|
|
, fWarpParams ()
|
|
{
|
|
// Grab the size in bytes.
|
|
const uint32 bytes = stream.Get_uint32 ();
|
|
// Grab the number of planes to warp.
|
|
fWarpParams.fPlanes = stream.Get_uint32 ();
|
|
// Verify number of planes.
|
|
if (fWarpParams.fPlanes == 0 ||
|
fWarpParams.fPlanes > kMaxColorPlanes)
|
{
|
ThrowBadFormat ();
|
}
|
|
// Verify the size.
|
|
if (bytes != ParamBytes (fWarpParams.fPlanes))
|
{
|
ThrowBadFormat ();
|
}
|
|
// Read warp parameters for each plane.
|
|
for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++)
|
{
|
|
fWarpParams.fRadParams [plane][0] = stream.Get_real64 ();
|
fWarpParams.fRadParams [plane][1] = stream.Get_real64 ();
|
fWarpParams.fRadParams [plane][2] = stream.Get_real64 ();
|
fWarpParams.fRadParams [plane][3] = stream.Get_real64 ();
|
|
}
|
|
// Read the image center.
|
|
fWarpParams.fCenter.h = stream.Get_real64 ();
|
fWarpParams.fCenter.v = stream.Get_real64 ();
|
|
#if qDNGValidate
|
|
if (gVerbose)
|
{
|
|
fWarpParams.Dump ();
|
|
}
|
|
#endif
|
|
if (!fWarpParams.IsValid ())
|
{
|
ThrowBadFormat ();
|
}
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_opcode_WarpFisheye::IsNOP () const
|
{
|
|
return fWarpParams.IsNOPAll ();
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_opcode_WarpFisheye::IsValidForNegative (const dng_negative &negative) const
|
{
|
|
return fWarpParams.IsValidForNegative (negative);
|
|
}
|
|
/*****************************************************************************/
|
|
void dng_opcode_WarpFisheye::PutData (dng_stream &stream) const
|
{
|
|
const uint32 bytes = ParamBytes (fWarpParams.fPlanes);
|
|
stream.Put_uint32 (bytes);
|
|
// Write the number of planes.
|
|
stream.Put_uint32 (fWarpParams.fPlanes);
|
|
// Write the warp coefficients.
|
|
for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++)
|
{
|
|
stream.Put_real64 (fWarpParams.fRadParams [plane][0]);
|
stream.Put_real64 (fWarpParams.fRadParams [plane][1]);
|
stream.Put_real64 (fWarpParams.fRadParams [plane][2]);
|
stream.Put_real64 (fWarpParams.fRadParams [plane][3]);
|
|
}
|
|
// Write the optical center.
|
|
stream.Put_real64 (fWarpParams.fCenter.h);
|
stream.Put_real64 (fWarpParams.fCenter.v);
|
|
}
|
|
/*****************************************************************************/
|
|
void dng_opcode_WarpFisheye::Apply (dng_host &host,
|
dng_negative &negative,
|
AutoPtr<dng_image> &image)
|
{
|
|
#if qDNGValidate
|
|
dng_timer timer ("WarpFisheye time");
|
|
#endif
|
|
// Allocate destination image.
|
|
AutoPtr<dng_image> dstImage (host.Make_dng_image (image->Bounds (),
|
image->Planes (),
|
image->PixelType ()));
|
|
// Warp the image.
|
|
AutoPtr<dng_warp_params> params (new dng_warp_params_fisheye (fWarpParams));
|
|
dng_filter_warp filter (*image,
|
*dstImage,
|
negative,
|
params);
|
|
filter.Initialize (host);
|
|
host.PerformAreaTask (filter,
|
image->Bounds ());
|
|
// Return the new image.
|
|
image.Reset (dstImage.Release ());
|
|
}
|
|
/*****************************************************************************/
|
|
uint32 dng_opcode_WarpFisheye::ParamBytes (uint32 planes)
|
{
|
|
return (1 * (uint32) sizeof (uint32) ) + // Number of planes.
|
(4 * (uint32) sizeof (real64) * planes) + // Warp coefficients.
|
(2 * (uint32) sizeof (real64) ); // Optical center.
|
|
}
|
|
/*****************************************************************************/
|
|
dng_vignette_radial_params::dng_vignette_radial_params ()
|
|
: fParams (kNumTerms)
|
, fCenter (0.5, 0.5)
|
|
{
|
|
}
|
|
/*****************************************************************************/
|
|
dng_vignette_radial_params::dng_vignette_radial_params (const dng_std_vector<real64> ¶ms,
|
const dng_point_real64 ¢er)
|
|
: fParams (params)
|
, fCenter (center)
|
|
{
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_vignette_radial_params::IsNOP () const
|
{
|
|
for (uint32 i = 0; i < fParams.size (); i++)
|
{
|
|
if (fParams [i] != 0.0)
|
{
|
return false;
|
}
|
|
}
|
|
return true;
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_vignette_radial_params::IsValid () const
|
{
|
|
if (fParams.size () != kNumTerms)
|
{
|
return false;
|
}
|
|
if (fCenter.h < 0.0 ||
|
fCenter.h > 1.0 ||
|
fCenter.v < 0.0 ||
|
fCenter.v > 1.0)
|
{
|
return false;
|
}
|
|
return true;
|
|
}
|
|
/*****************************************************************************/
|
|
void dng_vignette_radial_params::Dump () const
|
{
|
|
#if qDNGValidate
|
|
printf (" Radial vignette params: ");
|
|
for (uint32 i = 0; i < fParams.size (); i++)
|
{
|
|
printf ("%s%.6lf",
|
(i == 0) ? "" : ", ",
|
fParams [i]);
|
|
}
|
|
printf ("\n");
|
|
printf (" Optical center:\n"
|
" h = %.6lf\n"
|
" v = %.6lf\n",
|
fCenter.h,
|
fCenter.v);
|
|
#endif
|
|
}
|
|
/*****************************************************************************/
|
|
class dng_vignette_radial_function: public dng_1d_function
|
{
|
|
protected:
|
|
const dng_vignette_radial_params fParams;
|
|
public:
|
|
explicit dng_vignette_radial_function (const dng_vignette_radial_params ¶ms)
|
|
: fParams (params)
|
|
{
|
|
}
|
|
// x represents r^2, where r is the normalized Euclidean distance from the
|
// optical center to a pixel. r lies in [0,1], so r^2 (and hence x) also lies
|
// in [0,1].
|
|
virtual real64 Evaluate (real64 x) const
|
{
|
|
DNG_REQUIRE (fParams.fParams.size () == dng_vignette_radial_params::kNumTerms,
|
"Bad number of vignette opcode coefficients.");
|
|
real64 sum = 0.0;
|
|
const dng_std_vector<real64> &v = fParams.fParams;
|
|
for (dng_std_vector<real64>::const_reverse_iterator i = v.rbegin (); i != v.rend (); i++)
|
{
|
sum = x * ((*i) + sum);
|
}
|
|
sum += 1.0;
|
|
return sum;
|
|
}
|
|
};
|
|
/*****************************************************************************/
|
|
dng_opcode_FixVignetteRadial::dng_opcode_FixVignetteRadial (const dng_vignette_radial_params ¶ms,
|
uint32 flags)
|
|
: dng_inplace_opcode (dngOpcode_FixVignetteRadial,
|
dngVersion_1_3_0_0,
|
flags)
|
|
, fParams (params)
|
|
, fImagePlanes (1)
|
|
, fSrcOriginH (0)
|
, fSrcOriginV (0)
|
|
, fSrcStepH (0)
|
, fSrcStepV (0)
|
|
, fTableInputBits (0)
|
, fTableOutputBits (0)
|
|
, fGainTable ()
|
|
{
|
|
if (!params.IsValid ())
|
{
|
ThrowBadFormat ();
|
}
|
|
}
|
|
/*****************************************************************************/
|
|
dng_opcode_FixVignetteRadial::dng_opcode_FixVignetteRadial (dng_stream &stream)
|
|
: dng_inplace_opcode (dngOpcode_FixVignetteRadial,
|
stream,
|
"FixVignetteRadial")
|
|
, fParams ()
|
|
, fImagePlanes (1)
|
|
, fSrcOriginH (0)
|
, fSrcOriginV (0)
|
|
, fSrcStepH (0)
|
, fSrcStepV (0)
|
|
, fTableInputBits (0)
|
, fTableOutputBits (0)
|
|
, fGainTable ()
|
|
{
|
|
// Grab the size in bytes.
|
|
const uint32 bytes = stream.Get_uint32 ();
|
|
// Verify the size.
|
|
if (bytes != ParamBytes ())
|
{
|
ThrowBadFormat ();
|
}
|
|
// Read vignette coefficients.
|
|
fParams.fParams = dng_std_vector<real64> (dng_vignette_radial_params::kNumTerms);
|
|
for (uint32 i = 0; i < dng_vignette_radial_params::kNumTerms; i++)
|
{
|
fParams.fParams [i] = stream.Get_real64 ();
|
}
|
|
// Read the image center.
|
|
fParams.fCenter.h = stream.Get_real64 ();
|
fParams.fCenter.v = stream.Get_real64 ();
|
|
// Debug.
|
|
#if qDNGValidate
|
|
if (gVerbose)
|
{
|
|
fParams.Dump ();
|
|
}
|
|
#endif
|
|
if (!fParams.IsValid ())
|
{
|
ThrowBadFormat ();
|
}
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_opcode_FixVignetteRadial::IsNOP () const
|
{
|
|
return fParams.IsNOP ();
|
|
}
|
|
/*****************************************************************************/
|
|
bool dng_opcode_FixVignetteRadial::IsValidForNegative (const dng_negative & /* negative */) const
|
{
|
|
return fParams.IsValid ();
|
|
}
|
|
/*****************************************************************************/
|
|
void dng_opcode_FixVignetteRadial::PutData (dng_stream &stream) const
|
{
|
|
const uint32 bytes = ParamBytes ();
|
|
stream.Put_uint32 (bytes);
|
|
DNG_REQUIRE (fParams.fParams.size () == dng_vignette_radial_params::kNumTerms,
|
"Bad number of vignette opcode coefficients.");
|
|
for (uint32 i = 0; i < dng_vignette_radial_params::kNumTerms; i++)
|
{
|
stream.Put_real64 (fParams.fParams [i]);
|
}
|
|
stream.Put_real64 (fParams.fCenter.h);
|
stream.Put_real64 (fParams.fCenter.v);
|
|
}
|
|
/*****************************************************************************/
|
|
void dng_opcode_FixVignetteRadial::Prepare (dng_negative &negative,
|
uint32 threadCount,
|
const dng_point &tileSize,
|
const dng_rect &imageBounds,
|
uint32 imagePlanes,
|
uint32 bufferPixelType,
|
dng_memory_allocator &allocator)
|
{
|
|
// This opcode is restricted to 32-bit images.
|
|
if (bufferPixelType != ttFloat)
|
{
|
ThrowBadFormat ();
|
}
|
|
// Sanity check number of planes.
|
|
DNG_ASSERT (imagePlanes >= 1 && imagePlanes <= kMaxColorPlanes,
|
"Bad number of planes.");
|
|
if (imagePlanes < 1 || imagePlanes > kMaxColorPlanes)
|
{
|
ThrowProgramError ();
|
}
|
|
fImagePlanes = imagePlanes;
|
|
// Set the vignette correction curve.
|
|
const dng_vignette_radial_function curve (fParams);
|
|
// Grab the destination image area.
|
|
const dng_rect_real64 bounds (imageBounds);
|
|
// Determine the optical center and maximum radius in pixel coordinates.
|
|
const dng_point_real64 centerPixel (Lerp_real64 (bounds.t,
|
bounds.b,
|
fParams.fCenter.v),
|
|
Lerp_real64 (bounds.l,
|
bounds.r,
|
fParams.fCenter.h));
|
|
const real64 pixelScaleV = 1.0 / negative.PixelAspectRatio ();
|
|
const real64 maxRadius = hypot (Max_real64 (Abs_real64 (centerPixel.v - bounds.t),
|
Abs_real64 (centerPixel.v - bounds.b)) * pixelScaleV,
|
|
Max_real64 (Abs_real64 (centerPixel.h - bounds.l),
|
Abs_real64 (centerPixel.h - bounds.r)));
|
|
const dng_point_real64 radius (maxRadius,
|
maxRadius);
|
|
// Find origin and scale.
|
|
const real64 pixelScaleH = 1.0;
|
|
fSrcOriginH = Real64ToFixed64 (-centerPixel.h * pixelScaleH / radius.h);
|
fSrcOriginV = Real64ToFixed64 (-centerPixel.v * pixelScaleV / radius.v);
|
|
fSrcStepH = Real64ToFixed64 (pixelScaleH / radius.h);
|
fSrcStepV = Real64ToFixed64 (pixelScaleV / radius.v);
|
|
// Adjust for pixel centers.
|
|
fSrcOriginH += fSrcStepH >> 1;
|
fSrcOriginV += fSrcStepV >> 1;
|
|
// Evaluate 32-bit vignette correction table.
|
|
dng_1d_table table32;
|
|
table32.Initialize (allocator,
|
curve,
|
false);
|
|
// Find maximum scale factor.
|
|
const real64 maxScale = Max_real32 (table32.Interpolate (0.0f),
|
table32.Interpolate (1.0f));
|
|
// Find table input bits.
|
|
fTableInputBits = 16;
|
|
// Find table output bits.
|
|
fTableOutputBits = 15;
|
|
while ((1 << fTableOutputBits) * maxScale > 65535.0)
|
{
|
fTableOutputBits--;
|
}
|
|
// Allocate 16-bit scale table.
|
|
const uint32 tableEntries = (1 << fTableInputBits) + 1;
|
|
fGainTable.Reset (allocator.Allocate (tableEntries * (uint32) sizeof (uint16)));
|
|
uint16 *table16 = fGainTable->Buffer_uint16 ();
|
|
// Interpolate 32-bit table into 16-bit table.
|
|
const real32 scale0 = 1.0f / (1 << fTableInputBits );
|
const real32 scale1 = 1.0f * (1 << fTableOutputBits);
|
|
for (uint32 index = 0; index < tableEntries; index++)
|
{
|
|
real32 x = index * scale0;
|
|
real32 y = table32.Interpolate (x) * scale1;
|
|
table16 [index] = (uint16) Round_uint32 (y);
|
|
}
|
|
// Prepare vignette mask buffers.
|
|
{
|
|
const uint32 pixelType = ttShort;
|
const uint32 bufferSize = ComputeBufferSize(pixelType, tileSize,
|
imagePlanes, pad16Bytes);
|
|
for (uint32 threadIndex = 0; threadIndex < threadCount; threadIndex++)
|
{
|
|
fMaskBuffers [threadIndex] . Reset (allocator.Allocate (bufferSize));
|
|
}
|
|
}
|
|
}
|
|
/*****************************************************************************/
|
|
void dng_opcode_FixVignetteRadial::ProcessArea (dng_negative & /* negative */,
|
uint32 threadIndex,
|
dng_pixel_buffer &buffer,
|
const dng_rect &dstArea,
|
const dng_rect & /* imageBounds */)
|
{
|
|
// Setup mask pixel buffer.
|
|
dng_pixel_buffer maskPixelBuffer(dstArea, 0, fImagePlanes, ttShort,
|
pcRowInterleavedAlign16,
|
fMaskBuffers [threadIndex]->Buffer ());
|
|
// Compute mask.
|
|
DoVignetteMask16 (maskPixelBuffer.DirtyPixel_uint16 (dstArea.t, dstArea.l),
|
dstArea.H (),
|
dstArea.W (),
|
maskPixelBuffer.RowStep (),
|
fSrcOriginH + fSrcStepH * dstArea.l,
|
fSrcOriginV + fSrcStepV * dstArea.t,
|
fSrcStepH,
|
fSrcStepV,
|
fTableInputBits,
|
fGainTable->Buffer_uint16 ());
|
|
// Apply mask.
|
|
DoVignette32 (buffer.DirtyPixel_real32 (dstArea.t, dstArea.l),
|
maskPixelBuffer.ConstPixel_uint16 (dstArea.t, dstArea.l),
|
dstArea.H (),
|
dstArea.W (),
|
fImagePlanes,
|
buffer.RowStep (),
|
buffer.PlaneStep (),
|
maskPixelBuffer.RowStep (),
|
fTableOutputBits);
|
|
}
|
|
/*****************************************************************************/
|
|
uint32 dng_opcode_FixVignetteRadial::ParamBytes ()
|
{
|
|
const uint32 N = dng_vignette_radial_params::kNumTerms;
|
|
return ((N * sizeof (real64)) + // Vignette coefficients.
|
(2 * sizeof (real64))); // Optical center.
|
|
}
|
|
/*****************************************************************************/
|