26#include "boost/math/special_functions/bessel.hpp"
27#include "boost/shared_array.hpp"
42inline double J1(
double const x) {
49 return boost::math::cyl_bessel_j(1, x);
54inline T sinc(T
const x) {
55 return (x != 0.0) ? (
std::sin(x) /
x) : 1.0;
61template <
typename CoordT>
62class CircularAperture {
67 CoordT
const taperwidth
71 _taperwidth1(taperwidth),
72 _taperwidth2(taperwidth),
73 _k1(1.0 / (2.0 * taperwidth)),
74 _k2(1.0 / (2.0 * taperwidth)),
75 _taperLo1(radius1 - 0.5 * taperwidth),
76 _taperHi1(radius1 + 0.5 * taperwidth),
77 _taperLo2(radius2 - 0.5 * taperwidth),
78 _taperHi2(radius2 + 0.5 * taperwidth) {
83 if (_radius1 > _radius2) {
85 pex::exceptions::InvalidParameterError,
86 (boost::format(
"rad2 less than rad1: (rad1=%.2f, rad2=%.2f) ") % _radius1 % _radius2)
89 if (_radius1 < 0.0 || _radius2 < 0.0) {
91 pex::exceptions::InvalidParameterError,
92 (boost::format(
"radii must be >= 0 (rad1=%.2f, rad2=%.2f) ") % _radius1 % _radius2)
102 if (_radius1 < 0.5 * _taperwidth1) {
103 _taperwidth1 = 2.0 * _radius1;
104 _k1 = 1.0 / (2.0 * _taperwidth1);
108 if ((_radius2 - _radius1) < 0.5 * (_taperwidth1 + _taperwidth2)) {
111 if ((_radius2 - _radius2) < 0.5 * _taperwidth1) {
112 _taperwidth1 = _taperwidth2 = 0.5 * (_radius2 - _radius1);
113 _k1 = _k2 = 1.0 / (2.0 * _taperwidth1);
117 _taperwidth2 = _radius2 - _radius1 - _taperwidth1;
118 _k2 = 1.0 / (2.0 * _taperwidth2);
121 _taperLo1 = _radius1 - 0.5 * _taperwidth1;
122 _taperHi1 = _radius1 + 0.5 * _taperwidth1;
123 _taperLo2 = _radius2 - 0.5 * _taperwidth2;
124 _taperHi2 = _radius2 + 0.5 * _taperwidth2;
130 CoordT operator()(CoordT
const x, CoordT
const y)
const {
131 CoordT
const xyrad =
std::sqrt(x * x + y * y);
132 if (xyrad < _taperLo1) {
134 }
else if (xyrad >= _taperLo1 && xyrad <= _taperHi1) {
136 }
else if (xyrad > _taperHi1 && xyrad <= _taperLo2) {
138 }
else if (xyrad > _taperLo2 && xyrad <= _taperHi2) {
145 CoordT getRadius1() {
return _radius1; }
146 CoordT getRadius2() {
return _radius2; }
149 CoordT _radius1, _radius2;
150 CoordT _taperwidth1, _taperwidth2;
152 CoordT _taperLo1, _taperHi1;
153 CoordT _taperLo2, _taperHi2;
156template <
typename CoordT>
159 CircApPolar(
double radius,
double taperwidth) : _ap(CircularAperture<CoordT>(0.0,
radius, taperwidth)) {}
160 CoordT operator()(
double r)
const {
return r * _ap(r, 0.0); }
163 CircularAperture<CoordT> _ap;
169template <
typename IntegrandT>
172 SincAperture(CircularAperture<IntegrandT>
const& ap,
176 : _ap(ap), _ix(ix), _iy(iy) {}
178 IntegrandT operator()(IntegrandT
const x, IntegrandT
const y)
const {
179 double const fourierConvention =
geom::PI;
180 double const dx = fourierConvention * (
x - _ix);
181 double const dy = fourierConvention * (
y - _iy);
182 double const fx = sinc<double>(dx);
183 double const fy = sinc<double>(dy);
184 return (1.0 + _ap(x, y) * fx * fy);
188 CircularAperture<IntegrandT>
const& _ap;
192class GaussPowerFunctor {
194 GaussPowerFunctor(
double sigma) : _sigma(sigma) {}
196 double operator()(
double kx,
double ky)
const {
197 double const k = ::sqrt(kx * kx + ky * ky);
198 double const gauss = ::exp(-0.5 * k * k * _sigma * _sigma);
199 return gauss * gauss;
206std::pair<double, double> computeGaussLeakage(
double const sigma) {
207 GaussPowerFunctor gaussPower(sigma);
212 double powerInf =
geom::PI / (sigma * sigma);
216 double trueLeak = (powerInf - truePower) / powerInf;
223 return std::pair<double, double>(trueLeak, estLeak);
226template <
typename PixelT>
227std::shared_ptr<afw::image::Image<PixelT>> calcImageRealSpace(
double const rad1,
double const rad2,
228 double const taperwidth) {
231 int log2 =
static_cast<int>(::ceil(::log10(2.0 * rad2) /
log10(2.0)));
235 int hwid =
pow(2, log2);
236 int width = 2 * hwid - 1;
238 int const xwidth = width;
239 int const ywidth = width;
241 int const x0 = -xwidth / 2;
242 int const y0 = -ywidth / 2;
246 coeffImage->setXY0(x0, y0);
250 double tolerance = 1.0e-12;
252 double err = 2.0 * tolerance;
253 double apEff =
geom::PI * rad2 * rad2;
257 while (err > tolerance && i < maxIt) {
258 CircApPolar<double> apPolar1(radIn, taperwidth);
259 CircApPolar<double> apPolar2(radIn + dr, taperwidth);
262 double dadr = (a2 - a1) / dr;
263 double radNew = radIn - (a1 - apEff) / dadr;
264 err = (a1 - apEff) / apEff;
268 CircularAperture<double> ap(rad1, rad2, taperwidth);
274 double const limit = rad2 + taperwidth;
275 double const x1 = -limit;
276 double const x2 = limit;
277 double const y1 = -limit;
278 double const y2 = limit;
280 for (
int iY = y0; iY != y0 + coeffImage->getHeight(); ++iY) {
282 typename afw::image::Image<PixelT>::x_iterator
end = coeffImage->row_end(iY - y0);
283 for (
typename afw::image::Image<PixelT>::x_iterator ptr = coeffImage->row_begin(iY - y0); ptr != end;
286 SincAperture<double> sincAp(ap, iX, iY);
293 double const dx = iX;
294 double const dy = iY;
295 *ptr = (
std::sqrt(dx * dx + dy * dy) < xwidth / 2) ? integral - (x2 - x1) * (y2 - y1) : 0.0;
301 for (
int iY = y0; iY != y0 + coeffImage->getHeight(); ++iY) {
302 typename afw::image::Image<PixelT>::x_iterator
end = coeffImage->row_end(iY - y0);
303 for (
typename afw::image::Image<PixelT>::x_iterator ptr = coeffImage->row_begin(iY - y0); ptr != end;
310 coeffImage->writeFits(
"cimage.fits");
318 FftShifter(
int xwid) : _xwid(xwid) {}
320 if (x >= _xwid / 2) {
321 return x - _xwid / 2;
323 return x + _xwid / 2 + 1;
331std::pair<double, double>
rotate(
double x,
double y,
double angle) {
332 double c = ::cos(angle);
333 double s = ::sin(angle);
334 return std::pair<double, double>(x * c + y * s, -x * s + y * c);
343template <
typename PixelT>
344std::shared_ptr<afw::image::Image<PixelT>> calcImageKSpaceCplx(
double const rad1,
double const rad2,
346 double const ellipticity) {
349 int log2 =
static_cast<int>(::ceil(::log10(2.0 * rad2) /
log10(2.0)));
353 int hwid =
pow(2, log2);
354 int wid = 2 * hwid - 1;
355 int xcen = wid / 2, ycen = wid / 2;
356 FftShifter fftshift(wid);
358 boost::shared_array<std::complex<double>> cimg(
new std::complex<double>[wid * wid]);
359 std::complex<double>* c = cimg.get();
362 fftw_plan plan = fftw_plan_dft_2d(wid, wid,
reinterpret_cast<fftw_complex*
>(c),
363 reinterpret_cast<fftw_complex*
>(c), FFTW_BACKWARD, FFTW_ESTIMATE);
368 double const scale = (1.0 - ellipticity);
369 for (
int iY = 0; iY < wid; ++iY) {
370 int const fY = fftshift.shift(iY);
371 double const ky = (
static_cast<double>(iY) - ycen) / wid;
373 for (
int iX = 0; iX < wid; ++iX) {
374 int const fX = fftshift.shift(iX);
375 double const kx =
static_cast<double>(iX - xcen) / wid;
378 std::pair<double, double> coo =
rotate(kx, ky, posAng);
379 double kxr = coo.first;
380 double kyr = coo.second;
382 double const k = ::sqrt(kxr * kxr + scale * scale * kyr * kyr);
384 double const airy1 = (rad1 > 0 ? rad1 * J1(twoPiRad1 * k) : 0.0) / k;
385 double const airy2 = rad2 * J1(twoPiRad2 * k) / k;
386 double const airy = airy2 - airy1;
388 c[fY * wid + fX] = std::complex<double>(scale * airy, 0.0);
395 fftw_destroy_plan(plan);
400 for (
int iY = 0; iY != coeffImage->getHeight(); ++iY) {
402 typename afw::image::Image<PixelT>::x_iterator
end = coeffImage->row_end(iY);
403 for (
typename afw::image::Image<PixelT>::x_iterator ptr = coeffImage->row_begin(iY); ptr != end;
405 int fX = fftshift.shift(iX);
406 int fY = fftshift.shift(iY);
407 *ptr =
static_cast<PixelT>(c[fY * wid + fX].
real() / (wid * wid));
413 coeffImage->setXY0(-wid / 2, -wid / 2);
421template <
typename PixelT>
422std::shared_ptr<afw::image::Image<PixelT>> calcImageKSpaceReal(
double const rad1,
double const rad2) {
425 int log2 =
static_cast<int>(::ceil(::log10(2.0 * rad2) /
log10(2.0)));
429 int hwid =
pow(2, log2);
430 int wid = 2 * hwid - 1;
431 int xcen = wid / 2, ycen = wid / 2;
432 FftShifter fftshift(wid);
434 boost::shared_array<std::complex<double>> cimg(
new std::complex<double>[wid * wid]);
435 std::complex<double>* c = cimg.get();
439 fftw_plan plan = fftw_plan_dft_2d(wid, wid,
reinterpret_cast<fftw_complex*
>(c),
440 reinterpret_cast<fftw_complex*
>(c), FFTW_BACKWARD, FFTW_ESTIMATE);
444 for (
int iY = 0; iY < wid; ++iY) {
445 int const fY = fftshift.shift(iY);
446 double const ky = (
static_cast<double>(iY) - ycen) / wid;
448 for (
int iX = 0; iX < wid; ++iX) {
449 int const fX = fftshift.shift(iX);
452 double const iXcen =
static_cast<double>(iX - xcen);
453 double const kx = iXcen / wid;
455 double const k = ::sqrt(kx * kx + ky * ky);
456 double const airy1 = (rad1 > 0 ? rad1 * J1(twoPiRad1 * k) : 0.0) / k;
457 double const airy2 = rad2 * J1(twoPiRad2 * k) / k;
458 double const airy = airy2 - airy1;
459 c[fY * wid + fX] = airy;
462 int fxy = fftshift.shift(wid / 2);
463 c[fxy * wid + fxy] = {
geom::PI * (rad2 * rad2 - rad1 * rad1), 0.};
467 fftw_destroy_plan(plan);
472 for (
int iY = 0; iY != coeffImage->getHeight(); ++iY) {
474 typename afw::image::Image<PixelT>::x_iterator
end = coeffImage->row_end(iY);
475 for (
typename afw::image::Image<PixelT>::x_iterator ptr = coeffImage->row_begin(iY); ptr != end;
478 int fX = iX < hwid ? hwid - iX - 1 : iX - hwid + 1;
479 int fY = iY < hwid ? hwid - iY - 1 : iY - hwid + 1;
480 *ptr =
static_cast<PixelT>(c[fY * wid + fX].
real() / (wid * wid));
486 coeffImage->setXY0(-wid / 2, -wid / 2);
492template <
typename PixelT>
498template <
typename PixelT>
500 if (r1 < 0.0 || r2 < r1) {
502 (boost::format(
"Invalid r1,r2 = %f,%f") % r1 % r2).str());
504 double const innerFactor = r1 / r2;
506 if (!getInstance()._lookup(axes, innerFactor)) {
508 getInstance()._cache[r2][innerFactor] = coeff;
512template <
typename PixelT>
516 return coeff ? coeff :
calculate(axes, innerFactor);
519template <
typename PixelT>
522 if (innerFactor < 0.0 || innerFactor > 1.0) {
524 (boost::format(
"innerFactor = %f is not between 0 and 1") % innerFactor).str());
530 if (!FuzzyCompare<float>().isEqual(axes.
getA(), axes.
getB())) {
533 typename CoeffMapMap::const_iterator iter1 = _cache.find(axes.
getA());
534 if (iter1 == _cache.end()) {
537 typename CoeffMap::const_iterator iter2 = iter1->second.find(innerFactor);
538 return (iter2 == iter1->second.end()) ? null : iter2->second;
541template <
typename PixelT>
542std::shared_ptr<typename SincCoeffs<PixelT>::CoeffT>
544 if (innerFactor < 0.0 || innerFactor > 1.0) {
546 (boost::format(
"innerFactor = %f is not between 0 and 1") % innerFactor).str());
552 double const rad1 = axes.
getA() * innerFactor;
553 double const rad2 = axes.
getA();
555 if (FuzzyCompare<float>().isEqual(axes.
getA(), axes.
getB())) {
557 return calcImageKSpaceReal<PixelT>(rad1, rad2);
560 double const ellipticity = 1.0 - axes.
getB() / axes.
getA();
561 return calcImageKSpaceCplx<PixelT>(rad1, rad2, axes.
getTheta(), ellipticity);
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
An ellipse core for the semimajor/semiminor axis and position angle parametrization (a,...
double const getTheta() const
double const getA() const
double const getB() const
A singleton to calculate and cache the coefficients for sinc photometry.
static std::shared_ptr< CoeffT > calculate(afw::geom::ellipses::Axes const &outerEllipse, double const innerFactor=0.0)
Calculate the coefficients for an aperture.
static void cache(float rInner, float rOuter)
Cache the coefficients for a particular aperture.
static std::shared_ptr< CoeffT const > get(afw::geom::ellipses::Axes const &outerEllipse, float const innerRadiusFactor=0.0)
Get the coefficients for an aperture.
Reports invalid arguments.
scale(algorithm, min, max=None, frame=None)
auto integrate(UnaryFunctionT func, Arg const a, Arg const b, double eps=1.0e-6)
The 1D integrator.
auto integrate2d(BinaryFunctionT func, X x1, X x2, Y y1, Y y2, double eps=1.0e-6)
The 2D integrator.
double constexpr PI
The ratio of a circle's circumference to diameter.
std::uint8_t log2(std::uint64_t x)