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
61 _taperwidth1(taperwidth),
62 _taperwidth2(taperwidth),
63 _k1(1.0/(2.0*taperwidth)),
64 _k2(1.0/(2.0*taperwidth)),
65 _taperLo1(radius1 - 0.5*taperwidth),
66 _taperHi1(radius1 + 0.5*taperwidth),
67 _taperLo2(radius2 - 0.5*taperwidth),
68 _taperHi2(radius2 + 0.5*taperwidth) {
74 if (_radius1 > _radius2) {
75 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
76 (boost::format(
"rad2 less than rad1: (rad1=%.2f, rad2=%.2f) ") %
77 _radius1 % _radius2).str());
79 if (_radius1 < 0.0 || _radius2 < 0.0) {
80 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
81 (boost::format(
"radii must be >= 0 (rad1=%.2f, rad2=%.2f) ") %
82 _radius1 % _radius2).str());
91 if ( _radius1 < 0.5*_taperwidth1) {
92 _taperwidth1 = 2.0*_radius1;
93 _k1 = 1.0/(2.0*_taperwidth1);
97 if ((_radius2 - _radius1) < 0.5*(_taperwidth1+_taperwidth2)) {
101 if ((_radius2 - _radius2) < 0.5*_taperwidth1) {
102 _taperwidth1 = _taperwidth2 = 0.5*(_radius2 - _radius1);
103 _k1 = _k2 = 1.0/(2.0*_taperwidth1);
107 _taperwidth2 = _radius2 - _radius1 - _taperwidth1;
108 _k2 = 1.0/(2.0*_taperwidth2);
111 _taperLo1 = _radius1 - 0.5*_taperwidth1;
112 _taperHi1 = _radius1 + 0.5*_taperwidth1;
113 _taperLo2 = _radius2 - 0.5*_taperwidth2;
114 _taperHi2 = _radius2 + 0.5*_taperwidth2;
120 CoordT operator() (CoordT
const x, CoordT
const y)
const {
121 CoordT
const xyrad =
std::sqrt(x*x + y*y);
122 if ( xyrad < _taperLo1 ) {
124 }
else if (xyrad >= _taperLo1 && xyrad <= _taperHi1 ) {
126 }
else if (xyrad > _taperHi1 && xyrad <= _taperLo2 ) {
128 }
else if (xyrad > _taperLo2 && xyrad <= _taperHi2 ) {
135 CoordT getRadius1() {
return _radius1; }
136 CoordT getRadius2() {
return _radius2; }
139 CoordT _radius1, _radius2;
140 CoordT _taperwidth1, _taperwidth2;
142 CoordT _taperLo1, _taperHi1;
143 CoordT _taperLo2, _taperHi2;
147 template<
typename 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>
166 CircularAperture<IntegrandT>
const &ap,
170 : _ap(ap), _ix(ix), _iy(iy) {}
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;
188 GaussPowerFunctor(
double sigma) : _sigma(sigma) {}
190 double operator()(
double kx,
double ky)
const {
191 double const k = ::sqrt(kx*kx + ky*ky);
192 double const gauss = ::exp(-0.5*k*k*_sigma*_sigma);
201 GaussPowerFunctor gaussPower(sigma);
210 double trueLeak = (powerInf - truePower)/powerInf;
221 template<
typename PixelT>
223 calcImageRealSpace(
double const rad1,
double const rad2,
double const taperwidth) {
225 PixelT initweight = 0.0;
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;
240 std::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) {
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) {
313 coeffImage->writeFits(
"cimage.fits");
321 FftShifter(
int xwid) : _xwid(xwid) {}
326 return x + _xwid/2 + 1;
334 double c = ::cos(angle);
335 double s = ::sin(angle);
345 template<
typename PixelT>
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);
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;
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;
399 fftw_destroy_plan(plan);
405 for (
int iY = 0; iY != coeffImage->getHeight(); ++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>
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);
483 for (
int iY = 0; iY != coeffImage->getHeight(); ++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>
508 SincCoeffs<PixelT>& SincCoeffs<PixelT>::getInstance()
510 static SincCoeffs<PixelT> instance;
514 template<
typename PixelT>
517 if (r1 < 0.0 || r2 < r1) {
519 (boost::format(
"Invalid r1,r2 = %f,%f") % r1 % r2).str());
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>
542 if (innerFactor < 0.0 || innerFactor > 1.0) {
544 (boost::format(
"innerFactor = %f is not between 0 and 1") % innerFactor).str());
550 if (!FuzzyCompare<float>().isEqual(axes.
getA(), axes.
getB())) {
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>
565 if (innerFactor < 0.0 || innerFactor > 1.0) {
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();
576 if (FuzzyCompare<float>().isEqual(axes.getA(), axes.getB())) {
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);
double const getB() const
double const getA() const
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)
def scale(algorithm, min, max=None, frame=None)
_view_t::x_iterator x_iterator
A singleton to calculate and cache the coefficients for sinc photometry.
static void cache(float rInner, float rOuter)
Cache the coefficients for a particular aperture.
#define LSST_EXCEPT(type,...)
UnaryFunctionT::result_type integrate(UnaryFunctionT func, typename UnaryFunctionT::argument_type const a, typename UnaryFunctionT::argument_type const b, double eps=1.0e-6)