26 #include "boost/math/special_functions/bessel.hpp"
27 #include "boost/shared_array.hpp"
34 namespace lsst {
namespace meas {
namespace base {
namespace {
37 inline double J1(
double const x) {
38 return boost::math::cyl_bessel_j(1, x);
43 inline T sinc(T
const x) {
44 return (x != 0.0) ? (std::sin(x) /
x) : 1.0;
50 template<
typename CoordT>
51 class CircularAperture {
57 CoordT
const taperwidth
63 _k1(1.0/(2.0*taperwidth)),
64 _k2(1.0/(2.0*taperwidth)),
75 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
76 (
boost::format(
"rad2 less than rad1: (rad1=%.2f, rad2=%.2f) ") %
80 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
81 (
boost::format(
"radii must be >= 0 (rad1=%.2f, rad2=%.2f) ") %
120 CoordT operator() (CoordT
const x, CoordT
const y)
const {
121 CoordT
const xyrad = std::sqrt(x*x + y*y);
135 CoordT getRadius1() {
return _radius1; }
136 CoordT getRadius2() {
return _radius2; }
147 template<
typename CoordT>
148 class CircApPolar :
public std::unary_function<CoordT, CoordT> {
150 CircApPolar(
double radius,
double taperwidth) :
_ap(CircularAperture<CoordT>(0.0, radius, taperwidth)) {}
151 CoordT operator() (
double r)
const {
152 return r*
_ap(r, 0.0);
155 CircularAperture<CoordT>
_ap;
161 template<
typename IntegrandT>
162 class SincAperture :
public std::binary_function<IntegrandT, IntegrandT, IntegrandT> {
166 CircularAperture<IntegrandT>
const &ap,
172 IntegrandT operator() (IntegrandT
const x, IntegrandT
const y)
const {
174 double const dx = fourierConvention*(x -
_ix);
175 double const dy = fourierConvention*(y -
_iy);
176 double const fx = sinc<double>(dx);
177 double const fy = sinc<double>(dy);
178 return (1.0 +
_ap(x, y)*fx*fy);
182 CircularAperture<IntegrandT>
const &
_ap;
186 class GaussPowerFunctor :
public std::binary_function<double, double, double> {
190 double operator()(
double kx,
double ky)
const {
191 double const k = ::sqrt(kx*kx + ky*ky);
199 std::pair<double, double> computeGaussLeakage(
double const sigma) {
201 GaussPowerFunctor gaussPower(sigma);
210 double trueLeak = (powerInf - truePower)/powerInf;
217 return std::pair<double, double>(trueLeak, estLeak);
221 template<
typename PixelT>
222 typename afw::image::Image<PixelT>::Ptr
223 calcImageRealSpace(
double const rad1,
double const rad2,
double const taperwidth) {
227 int log2 =
static_cast<int>(
::ceil(::log10(2.0*rad2)/log10(2.0)));
228 if (log2 < 3) { log2 = 3; }
229 int hwid = pow(2, log2);
230 int width = 2*hwid - 1;
232 int const xwidth =
width;
233 int const ywidth =
width;
235 int const x0 = -xwidth/2;
236 int const y0 = -ywidth/2;
239 typename afw::image::Image<PixelT>::Ptr coeffImage =
240 boost::make_shared<afw::image::Image<PixelT> >(
afw::geom::ExtentI(xwidth, ywidth), initweight);
241 coeffImage->setXY0(x0, y0);
245 double tolerance = 1.0e-12;
247 double err = 2.0*tolerance;
252 while (err > tolerance && i < maxIt) {
253 CircApPolar<double> apPolar1(radIn, taperwidth);
254 CircApPolar<double> apPolar2(radIn+dr, taperwidth);
257 double dadr = (a2 - a1)/dr;
258 double radNew = radIn - (a1 - apEff)/dadr;
259 err = (a1 - apEff)/apEff;
263 CircularAperture<double> ap(rad1, rad2, taperwidth);
269 double const limit = rad2 + taperwidth;
270 double const x1 = -limit;
271 double const x2 = limit;
272 double const y1 = -limit;
273 double const y2 = limit;
275 for (
int iY = y0; iY != y0 + coeffImage->getHeight(); ++iY) {
277 typename afw::image::Image<PixelT>::x_iterator end = coeffImage->row_end(iY-y0);
279 typename afw::image::Image<PixelT>::x_iterator ptr = coeffImage->row_begin(iY-y0);
285 SincAperture<double> sincAp(ap, iX, iY);
292 double const dx = iX;
293 double const dy = iY;
294 *ptr = (std::sqrt(dx*dx + dy*dy) < xwidth/2) ?
295 integral - (x2 - x1)*(y2 - y1) : 0.0;
302 for (
int iY = y0; iY != y0 + coeffImage->getHeight(); ++iY) {
303 typename afw::image::Image<PixelT>::x_iterator end = coeffImage->row_end(iY-y0);
305 typename afw::image::Image<PixelT>::x_iterator ptr = coeffImage->row_begin(iY-y0);
313 coeffImage->writeFits(
"cimage.fits");
321 FftShifter(
int xwid) :
_xwid(xwid) {}
326 return x +
_xwid/2 + 1;
333 std::pair<double, double> rotate(
double x,
double y,
double angle) {
334 double c = ::cos(angle);
335 double s = ::sin(angle);
336 return std::pair<double, double>(x*c + y*s, -x*s + y*c);
345 template<
typename PixelT>
346 typename afw::image::Image<PixelT>::Ptr calcImageKSpaceCplx(
double const rad1,
double const rad2,
347 double const posAng,
double const ellipticity
352 int log2 =
static_cast<int>(
::ceil(::log10(2.0*rad2)/log10(2.0)));
353 if (log2 < 3) { log2 = 3; }
354 int hwid = pow(2, log2);
355 int wid = 2*hwid - 1;
356 int xcen = wid/2, ycen = wid/2;
357 FftShifter fftshift(wid);
359 boost::shared_array<std::complex<double> > cimg(
new std::complex<double>[wid*wid]);
360 std::complex<double> *c = cimg.get();
363 fftw_plan plan = fftw_plan_dft_2d(wid, wid,
364 reinterpret_cast<fftw_complex*>(c),
365 reinterpret_cast<fftw_complex*>(c),
366 FFTW_BACKWARD, FFTW_ESTIMATE);
371 double const scale = (1.0 - ellipticity);
372 for (
int iY = 0; iY < wid; ++iY) {
373 int const fY = fftshift.shift(iY);
374 double const ky = (
static_cast<double>(iY) - ycen)/wid;
376 for (
int iX = 0; iX < wid; ++iX) {
378 int const fX = fftshift.shift(iX);
379 double const kx =
static_cast<double>(iX - xcen)/wid;
382 std::pair<double, double> coo = rotate(kx, ky, posAng);
383 double kxr = coo.first;
384 double kyr = coo.second;
386 double const k = ::sqrt(kxr*kxr + scale*scale*kyr*kyr);
388 double const airy1 = (rad1 > 0 ? rad1*J1(twoPiRad1*k) : 0.0)/k;
389 double const airy2 = rad2*J1(twoPiRad2*k)/k;
390 double const airy = airy2 - airy1;
392 c[fY*wid + fX] = std::complex<double>(scale*airy, 0.0);
399 fftw_destroy_plan(plan);
402 typename afw::image::Image<PixelT>::Ptr coeffImage =
405 for (
int iY = 0; iY != coeffImage->getHeight(); ++iY) {
407 typename afw::image::Image<PixelT>::x_iterator end = coeffImage->row_end(iY);
409 typename afw::image::Image<PixelT>::x_iterator ptr = coeffImage->row_begin(iY);\
413 int fX = fftshift.shift(iX);
414 int fY = fftshift.shift(iY);
415 *ptr =
static_cast<PixelT>(c[fY*wid + fX].real()/(wid*wid));
421 coeffImage->setXY0(-wid/2, -wid/2);
431 template<
typename PixelT>
432 typename afw::image::Image<PixelT>::Ptr calcImageKSpaceReal(
double const rad1,
double const rad2) {
436 int log2 =
static_cast<int>(
::ceil(::log10(2.0*rad2)/log10(2.0)));
437 if (log2 < 3) { log2 = 3; }
438 int hwid = pow(2, log2);
439 int wid = 2*hwid - 1;
440 int xcen = wid/2, ycen = wid/2;
441 FftShifter fftshift(wid);
443 boost::shared_array<double> cimg(
new double[wid*wid]);
444 double *c = cimg.get();
447 fftw_plan plan = fftw_plan_r2r_2d(wid, wid, c, c, FFTW_R2HC, FFTW_R2HC, FFTW_ESTIMATE);
452 for (
int iY = 0; iY < wid; ++iY) {
454 int const fY = fftshift.shift(iY);
455 double const ky = (
static_cast<double>(iY) - ycen)/wid;
457 for (
int iX = 0; iX < wid; ++iX) {
458 int const fX = fftshift.shift(iX);
461 double const iXcen =
static_cast<double>(iX - xcen);
462 double const kx = iXcen/wid;
464 double const k = ::sqrt(kx*kx + ky*ky);
465 double const airy1 = (rad1 > 0 ? rad1*J1(twoPiRad1*k) : 0.0)/k;
466 double const airy2 = rad2*J1(twoPiRad2*k)/k;
467 double const airy = airy2 - airy1;
468 c[fY*wid + fX] = airy;
472 int fxy = fftshift.shift(wid/2);
477 fftw_destroy_plan(plan);
480 typename afw::image::Image<PixelT>::Ptr coeffImage =
483 for (
int iY = 0; iY != coeffImage->getHeight(); ++iY) {
485 typename afw::image::Image<PixelT>::x_iterator end = coeffImage->row_end(iY);
487 typename afw::image::Image<PixelT>::x_iterator ptr = coeffImage->row_begin(iY);
493 int fX = iX < hwid ? hwid - iX - 1 : iX - hwid + 1;
494 int fY = iY < hwid ? hwid - iY - 1 : iY - hwid + 1;
495 *ptr =
static_cast<PixelT>(c[fY*wid + fX]/(wid*wid));
501 coeffImage->setXY0(-wid/2, -wid/2);
507 template<
typename PixelT>
514 template<
typename PixelT>
517 if (r1 < 0.0 || r2 < r1) {
518 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
521 double const innerFactor = r1/r2;
523 if (!getInstance()._lookup(axes, innerFactor)) {
525 coeff->markPersistent();
526 getInstance()._cache[r2][innerFactor] = coeff;
530 template<
typename PixelT>
535 return coeff ? coeff : calculate(axes, innerFactor);
538 template<
typename PixelT>
540 SincCoeffs<
PixelT>::_lookup(afw::geom::ellipses::Axes const& axes,
double const innerFactor)
const
542 if (innerFactor < 0.0 || innerFactor > 1.0) {
543 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
544 (
boost::format(
"innerFactor = %f is not between 0 and 1") % innerFactor).str());
553 typename CoeffMapMap::const_iterator iter1 = _cache.find(axes.getA());
554 if (iter1 == _cache.end()) {
557 typename CoeffMap::const_iterator iter2 = iter1->second.find(innerFactor);
558 return (iter2 == iter1->second.end()) ? null : iter2->second;
561 template<
typename PixelT>
563 SincCoeffs<
PixelT>::calculate(afw::geom::ellipses::Axes const& axes,
double const innerFactor)
565 if (innerFactor < 0.0 || innerFactor > 1.0) {
566 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
567 (
boost::format(
"innerFactor = %f is not between 0 and 1") % innerFactor).str());
573 double const rad1 = axes.getA() * innerFactor;
574 double const rad2 = axes.getA();
578 return calcImageKSpaceReal<PixelT>(rad1, rad2);
581 double const ellipticity = 1.0 - axes.getB()/axes.getA();
582 return calcImageKSpaceCplx<PixelT>(rad1, rad2, axes.getTheta(), ellipticity);
BinaryFunctionT::result_type integrate2d(BinaryFunctionT func, typename BinaryFunctionT::first_argument_type const x1, typename BinaryFunctionT::first_argument_type const x2, typename BinaryFunctionT::second_argument_type const y1, typename BinaryFunctionT::second_argument_type const y2, double eps=1.0e-6)
The 2D integrator.
void shift(Vector< T, N > const &offset, Array< std::complex< T >, N, C > const &array, int const real_last_dim)
Perform a Fourier-space translation transform.
Extent< int, N > ceil(Extent< double, N > const &input)
boost::enable_if< typename ExpressionTraits< Scalar >::IsScalar, Scalar >::type sum(Scalar const &scalar)
bool isEqual(T x, T y) const
afw::table::Key< double > sigma
static SincCoeffs & getInstance()
void ImageT ImageT int float saturatedPixelValue int const width
double const PI
The ratio of a circle's circumference to diameter.
CircularAperture< CoordT > _ap
static void cache(float rInner, float rOuter)
#define LSST_EXCEPT(type,...)
An ellipse core for the semimajor/semiminor axis and position angle parametrization (a...
Compute 1d and 2d integral.
A class to represent a 2-dimensional array of pixels.
UnaryFunctionT::result_type integrate(UnaryFunctionT func, typename UnaryFunctionT::argument_type const a, typename UnaryFunctionT::argument_type const b, double eps=1.0e-6)
The 1D integrator.