Loading [MathJax]/extensions/tex2jax.js
LSST Applications g04dff08e69+fafbcb10e2,g0d33ba9806+e09a96fa4e,g0fba68d861+cc01b48236,g1e78f5e6d3+fb95f9dda6,g1ec0fe41b4+f536777771,g1fd858c14a+ae46bc2a71,g35bb328faa+fcb1d3bbc8,g4af146b050+dd94f3aad7,g4d2262a081+7ee6f976aa,g53246c7159+fcb1d3bbc8,g5a012ec0e7+b20b785ecb,g60b5630c4e+e09a96fa4e,g6273192d42+bf8cfc5e62,g67b6fd64d1+4086c0989b,g78460c75b0+2f9a1b4bcd,g786e29fd12+cf7ec2a62a,g7b71ed6315+fcb1d3bbc8,g87b7deb4dc+831c06c8fc,g8852436030+54b48a5987,g89139ef638+4086c0989b,g9125e01d80+fcb1d3bbc8,g94187f82dc+e09a96fa4e,g989de1cb63+4086c0989b,g9f33ca652e+64be6d9d51,g9f7030ddb1+d11454dffd,ga2b97cdc51+e09a96fa4e,gabe3b4be73+1e0a283bba,gabf8522325+fa80ff7197,gb1101e3267+23605820ec,gb58c049af0+f03b321e39,gb89ab40317+4086c0989b,gcf25f946ba+54b48a5987,gd6cbbdb0b4+af3c3595f5,gd9a9a58781+fcb1d3bbc8,gde0f65d7ad+15f2daff9d,ge278dab8ac+d65b3c2b70,ge410e46f29+4086c0989b,gf67bdafdda+4086c0989b,v29.0.0.rc5
LSST Data Management Base Package
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
SincCoeffs.cc
Go to the documentation of this file.
1// -*- lsst-c++ -*-
2/*
3 * LSST Data Management System
4 * Copyright 2008-2013 LSST Corporation.
5 *
6 * This product includes software developed by the
7 * LSST Project (http://www.lsst.org/).
8 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the LSST License Statement and
20 * the GNU General Public License along with this program. If not,
21 * see <http://www.lsstcorp.org/LegalNotices/>.
22 */
23
24#include <complex>
25
26#include "boost/math/special_functions/bessel.hpp"
27#include "boost/shared_array.hpp"
28#include "fftw3.h"
29
31#include "lsst/geom/Angle.h"
32#include "lsst/geom/Extent.h"
35
36namespace lsst {
37namespace meas {
38namespace base {
39namespace {
40
41// Convenient wrapper for a Bessel function
42inline double J1(double const x) {
43 // Some versions of boost assert that x >= 0
44 // which fails for NaN. Retain previous behavior
45 // and return NaN for NaN.
46 if (std::isnan(x)) {
47 return x;
48 }
49 return boost::math::cyl_bessel_j(1, x);
50}
51
52// sinc function
53template <typename T>
54inline T sinc(T const x) {
55 return (x != 0.0) ? (std::sin(x) / x) : 1.0;
56}
57
58/*
59 * Define a circular aperture function object g_i, cos-tapered?
60 */
61template <typename CoordT>
62class CircularAperture {
63public:
64 CircularAperture(
65 CoordT const radius1,
66 CoordT const radius2,
67 CoordT const taperwidth
68 )
69 : _radius1(radius1),
70 _radius2(radius2),
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) {
79 // if we're asked for a radius smaller than our taperwidth,
80 // adjust the taper width smaller so it fits exactly
81 // with smooth derivative=0 at r=0
82
83 if (_radius1 > _radius2) {
84 throw LSST_EXCEPT(
85 pex::exceptions::InvalidParameterError,
86 (boost::format("rad2 less than rad1: (rad1=%.2f, rad2=%.2f) ") % _radius1 % _radius2)
87 .str());
88 }
89 if (_radius1 < 0.0 || _radius2 < 0.0) {
90 throw LSST_EXCEPT(
91 pex::exceptions::InvalidParameterError,
92 (boost::format("radii must be >= 0 (rad1=%.2f, rad2=%.2f) ") % _radius1 % _radius2)
93 .str());
94 }
95
96 if (_radius1 == 0) {
97 _taperwidth1 = 0.0;
98 _k1 = 0.0;
99 }
100
101 // if we don't have room to taper at r=0
102 if (_radius1 < 0.5 * _taperwidth1) {
103 _taperwidth1 = 2.0 * _radius1;
104 _k1 = 1.0 / (2.0 * _taperwidth1);
105 }
106
107 // if we don't have room to taper between r1 and r2
108 if ((_radius2 - _radius1) < 0.5 * (_taperwidth1 + _taperwidth2)) {
109 // if we *really* don't have room ... taper1 by itself is too big
110 // - set taper1,2 to be equal and split the r2-r1 range
111 if ((_radius2 - _radius2) < 0.5 * _taperwidth1) {
112 _taperwidth1 = _taperwidth2 = 0.5 * (_radius2 - _radius1);
113 _k1 = _k2 = 1.0 / (2.0 * _taperwidth1);
114
115 // if there's room for taper1, but not taper1 and 2
116 } else {
117 _taperwidth2 = _radius2 - _radius1 - _taperwidth1;
118 _k2 = 1.0 / (2.0 * _taperwidth2);
119 }
120
121 _taperLo1 = _radius1 - 0.5 * _taperwidth1;
122 _taperHi1 = _radius1 + 0.5 * _taperwidth1;
123 _taperLo2 = _radius2 - 0.5 * _taperwidth2;
124 _taperHi2 = _radius2 + 0.5 * _taperwidth2;
125 }
126 }
127
128 // When called, return the throughput at the requested x,y
129 // todo: replace the sinusoid taper with a band-limited
130 CoordT operator()(CoordT const x, CoordT const y) const {
131 CoordT const xyrad = std::sqrt(x * x + y * y);
132 if (xyrad < _taperLo1) {
133 return 0.0;
134 } else if (xyrad >= _taperLo1 && xyrad <= _taperHi1) {
135 return 0.5 * (1.0 + std::cos((geom::TWOPI * _k1) * (xyrad - _taperHi1)));
136 } else if (xyrad > _taperHi1 && xyrad <= _taperLo2) {
137 return 1.0;
138 } else if (xyrad > _taperLo2 && xyrad <= _taperHi2) {
139 return 0.5 * (1.0 + std::cos((geom::TWOPI * _k2) * (xyrad - _taperLo2)));
140 } else {
141 return 0.0;
142 }
143 }
144
145 CoordT getRadius1() { return _radius1; }
146 CoordT getRadius2() { return _radius2; }
147
148private:
149 CoordT _radius1, _radius2;
150 CoordT _taperwidth1, _taperwidth2;
151 CoordT _k1, _k2; // the angular wavenumber corresponding to a cosine with wavelength 2*taperwidth
152 CoordT _taperLo1, _taperHi1;
153 CoordT _taperLo2, _taperHi2;
154};
155
156template <typename CoordT>
157class CircApPolar {
158public:
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); }
161
162private:
163 CircularAperture<CoordT> _ap;
164};
165
166/*
167 * Define a Sinc functor to be integrated over for Sinc interpolation
168 */
169template <typename IntegrandT>
170class SincAperture {
171public:
172 SincAperture(CircularAperture<IntegrandT> const& ap,
173 int const ix, // sinc center x
174 int const iy // sinc center y
175 )
176 : _ap(ap), _ix(ix), _iy(iy) {}
177
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);
185 }
186
187private:
188 CircularAperture<IntegrandT> const& _ap;
189 double _ix, _iy;
190};
191
192class GaussPowerFunctor {
193public:
194 GaussPowerFunctor(double sigma) : _sigma(sigma) {}
195
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;
200 }
201
202private:
203 double _sigma;
204};
205
206std::pair<double, double> computeGaussLeakage(double const sigma) {
207 GaussPowerFunctor gaussPower(sigma);
208
209 double lim = geom::PI;
210
211 // total power: integrate GaussPowerFunctor -inf<x<inf, -inf<y<inf (can be done analytically)
212 double powerInf = geom::PI / (sigma * sigma);
213
214 // true power: integrate GaussPowerFunctor -lim<x<lim, -lim<y<lim (must be done numerically)
215 double truePower = afw::math::integrate2d(gaussPower, -lim, lim, -lim, lim, 1.0e-8);
216 double trueLeak = (powerInf - truePower) / powerInf;
217
218 // estimated power: function is circular, but coords are cartesian
219 // - true power does the actual integral numerically, but we can estimate it by integrating
220 // in polar coords over lim <= radius < infinity. The integral is analytic.
221 double estLeak = ::exp(-sigma * sigma * geom::PI * geom::PI) / powerInf;
222
223 return std::pair<double, double>(trueLeak, estLeak);
224}
225
226template <typename PixelT>
227std::shared_ptr<afw::image::Image<PixelT>> calcImageRealSpace(double const rad1, double const rad2,
228 double const taperwidth) {
229 PixelT initweight = 0.0; // initialize the coeff values
230
231 int log2 = static_cast<int>(::ceil(::log10(2.0 * rad2) / log10(2.0)));
232 if (log2 < 3) {
233 log2 = 3;
234 }
235 int hwid = pow(2, log2);
236 int width = 2 * hwid - 1;
237
238 int const xwidth = width;
239 int const ywidth = width;
240
241 int const x0 = -xwidth / 2;
242 int const y0 = -ywidth / 2;
243
244 // create an image to hold the coefficient image
245 auto coeffImage = std::make_shared<afw::image::Image<PixelT>>(geom::ExtentI(xwidth, ywidth), initweight);
246 coeffImage->setXY0(x0, y0);
247
248 // create the aperture function object
249 // determine the radius to use that makes 'radius' the effective radius of the aperture
250 double tolerance = 1.0e-12;
251 double dr = 1.0e-6;
252 double err = 2.0 * tolerance;
253 double apEff = geom::PI * rad2 * rad2;
254 double radIn = rad2;
255 int maxIt = 20;
256 int i = 0;
257 while (err > tolerance && i < maxIt) {
258 CircApPolar<double> apPolar1(radIn, taperwidth);
259 CircApPolar<double> apPolar2(radIn + dr, taperwidth);
260 double a1 = geom::TWOPI * afw::math::integrate(apPolar1, 0.0, radIn + taperwidth, tolerance);
261 double a2 = geom::TWOPI * afw::math::integrate(apPolar2, 0.0, radIn + dr + taperwidth, tolerance);
262 double dadr = (a2 - a1) / dr;
263 double radNew = radIn - (a1 - apEff) / dadr;
264 err = (a1 - apEff) / apEff;
265 radIn = radNew;
266 i++;
267 }
268 CircularAperture<double> ap(rad1, rad2, taperwidth);
269
270 /* ******************************* */
271 // integrate over the aperture
272
273 // the limits of the integration over the sinc aperture
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;
279
280 for (int iY = y0; iY != y0 + coeffImage->getHeight(); ++iY) {
281 int iX = x0;
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;
284 ++ptr) {
285 // create a sinc function in the CircularAperture at our location
286 SincAperture<double> sincAp(ap, iX, iY);
287
288 // integrate the sinc
289 PixelT integral = afw::math::integrate2d(sincAp, x1, x2, y1, y2, 1.0e-8);
290
291 // we actually integrated function+1.0 and now must subtract the excess volume
292 // - just force it to zero in the corners
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;
296 ++iX;
297 }
298 }
299
300 double sum = 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;
304 ++ptr) {
305 sum += *ptr;
306 }
307 }
308
309#if 0 // debugging
310 coeffImage->writeFits("cimage.fits");
311#endif
312
313 return coeffImage;
314}
315
316class FftShifter {
317public:
318 FftShifter(int xwid) : _xwid(xwid) {}
319 int shift(int x) {
320 if (x >= _xwid / 2) {
321 return x - _xwid / 2;
322 } else {
323 return x + _xwid / 2 + 1;
324 }
325 }
326
327private:
328 int _xwid;
329};
330
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);
335}
336
337/* todo
338 * - try sub pixel shift if it doesn't break even symmetry
339 * - put values directly in an Image
340 * - precompute the plan
341 */
342
343template <typename PixelT>
344std::shared_ptr<afw::image::Image<PixelT>> calcImageKSpaceCplx(double const rad1, double const rad2,
345 double const posAng,
346 double const ellipticity) {
347 // we only need a half-width due to symmetry
348 // make the hwid 2*rad2 so we have some buffer space and round up to the next power of 2
349 int log2 = static_cast<int>(::ceil(::log10(2.0 * rad2) / log10(2.0)));
350 if (log2 < 3) {
351 log2 = 3;
352 }
353 int hwid = pow(2, log2);
354 int wid = 2 * hwid - 1;
355 int xcen = wid / 2, ycen = wid / 2;
356 FftShifter fftshift(wid);
357
358 boost::shared_array<std::complex<double>> cimg(new std::complex<double>[wid * wid]);
359 std::complex<double>* c = cimg.get();
360 // fftplan args: nx, ny, *in, *out, direction, flags
361 // - done in-situ if *in == *out
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);
364
365 // compute the k-space values and put them in the cimg array
366 double const twoPiRad1 = geom::TWOPI * rad1;
367 double const twoPiRad2 = geom::TWOPI * rad2;
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;
372
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;
376
377 // rotate
378 std::pair<double, double> coo = rotate(kx, ky, posAng);
379 double kxr = coo.first;
380 double kyr = coo.second;
381 // rescale
382 double const k = ::sqrt(kxr * kxr + scale * scale * kyr * kyr);
383
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;
387
388 c[fY * wid + fX] = std::complex<double>(scale * airy, 0.0);
389 }
390 }
391 c[0] = scale * geom::PI * (rad2 * rad2 - rad1 * rad1);
392
393 // perform the fft and clean up after ourselves
394 fftw_execute(plan);
395 fftw_destroy_plan(plan);
396
397 // put the coefficients into an image
398 auto coeffImage = std::make_shared<afw::image::Image<PixelT>>(geom::ExtentI(wid, wid), 0.0);
399
400 for (int iY = 0; iY != coeffImage->getHeight(); ++iY) {
401 int iX = 0;
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;
404 ++ptr) {
405 int fX = fftshift.shift(iX);
406 int fY = fftshift.shift(iY);
407 *ptr = static_cast<PixelT>(c[fY * wid + fX].real() / (wid * wid));
408 iX++;
409 }
410 }
411
412 // reset the origin to be the middle of the image
413 coeffImage->setXY0(-wid / 2, -wid / 2);
414 return coeffImage;
415}
416
417// I'm not sure why this doesn't work with DCT-I (REDFT00), the DCT should take advantage of symmetry
418// and be much faster than the DFT. It runs but the numbers are slightly off ...
419// but I have to do it as real-to-halfcplx (R2HC) to get the correct numbers.
420
421template <typename PixelT>
422std::shared_ptr<afw::image::Image<PixelT>> calcImageKSpaceReal(double const rad1, double const rad2) {
423 // we only need a half-width due to symmertry
424 // make the hwid 2*rad2 so we have some buffer space and round up to the next power of 2
425 int log2 = static_cast<int>(::ceil(::log10(2.0 * rad2) / log10(2.0)));
426 if (log2 < 3) {
427 log2 = 3;
428 }
429 int hwid = pow(2, log2);
430 int wid = 2 * hwid - 1;
431 int xcen = wid / 2, ycen = wid / 2;
432 FftShifter fftshift(wid);
433
434 boost::shared_array<std::complex<double>> cimg(new std::complex<double>[wid * wid]);
435 std::complex<double>* c = cimg.get();
436 // fftplan args: nx, ny, *in, *out, kindx, kindy, flags
437 // - done in-situ if *in == *out
438
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);
441 // compute the k-space values and put them in the cimg array
442 double const twoPiRad1 = geom::TWOPI * rad1;
443 double const twoPiRad2 = geom::TWOPI * rad2;
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;
447
448 for (int iX = 0; iX < wid; ++iX) {
449 int const fX = fftshift.shift(iX);
450
451 // emacs indent breaks if this isn't separte
452 double const iXcen = static_cast<double>(iX - xcen);
453 double const kx = iXcen / wid;
454
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;
460 }
461 }
462 int fxy = fftshift.shift(wid / 2);
463 c[fxy * wid + fxy] = {geom::PI * (rad2 * rad2 - rad1 * rad1), 0.};
464
465 // perform the fft and clean up after ourselves
466 fftw_execute(plan);
467 fftw_destroy_plan(plan);
468
469 // put the coefficients into an image
470 auto coeffImage = std::make_shared<afw::image::Image<PixelT>>(geom::ExtentI(wid, wid), 0.0);
471
472 for (int iY = 0; iY != coeffImage->getHeight(); ++iY) {
473 int iX = 0;
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;
476 ++ptr) {
477 // now need to reflect the quadrant we solved to the other three
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));
481 iX++;
482 }
483 }
484
485 // reset the origin to be the middle of the image
486 coeffImage->setXY0(-wid / 2, -wid / 2);
487 return coeffImage;
488}
489
490} // namespace
491
492template <typename PixelT>
493SincCoeffs<PixelT>& SincCoeffs<PixelT>::getInstance() {
494 static SincCoeffs<PixelT> instance;
495 return instance;
496}
497
498template <typename PixelT>
499void SincCoeffs<PixelT>::cache(float r1, float r2) {
500 if (r1 < 0.0 || r2 < r1) {
502 (boost::format("Invalid r1,r2 = %f,%f") % r1 % r2).str());
503 }
504 double const innerFactor = r1 / r2;
505 afw::geom::ellipses::Axes axes(r2, r2, 0.0);
506 if (!getInstance()._lookup(axes, innerFactor)) {
508 getInstance()._cache[r2][innerFactor] = coeff;
509 }
510}
511
512template <typename PixelT>
514SincCoeffs<PixelT>::get(afw::geom::ellipses::Axes const& axes, float const innerFactor) {
515 std::shared_ptr<CoeffT const> coeff = getInstance()._lookup(axes, innerFactor);
516 return coeff ? coeff : calculate(axes, innerFactor);
517}
518
519template <typename PixelT>
521SincCoeffs<PixelT>::_lookup(afw::geom::ellipses::Axes const& axes, double const innerFactor) const {
522 if (innerFactor < 0.0 || innerFactor > 1.0) {
524 (boost::format("innerFactor = %f is not between 0 and 1") % innerFactor).str());
525 }
526
528
529 // We only cache circular apertures
530 if (!FuzzyCompare<float>().isEqual(axes.getA(), axes.getB())) {
531 return null;
532 }
533 typename CoeffMapMap::const_iterator iter1 = _cache.find(axes.getA());
534 if (iter1 == _cache.end()) {
535 return null;
536 }
537 typename CoeffMap::const_iterator iter2 = iter1->second.find(innerFactor);
538 return (iter2 == iter1->second.end()) ? null : iter2->second;
539}
540
541template <typename PixelT>
542std::shared_ptr<typename SincCoeffs<PixelT>::CoeffT>
543SincCoeffs<PixelT>::calculate(afw::geom::ellipses::Axes const& axes, double const innerFactor) {
544 if (innerFactor < 0.0 || innerFactor > 1.0) {
546 (boost::format("innerFactor = %f is not between 0 and 1") % innerFactor).str());
547 }
548
549 // Kspace-real is fastest, but only slightly faster than kspace cplx
550 // but real won't work for elliptical apertures due to symmetries assumed for real transform
551
552 double const rad1 = axes.getA() * innerFactor;
553 double const rad2 = axes.getA();
554 // if there's no angle and no ellipticity
555 if (FuzzyCompare<float>().isEqual(axes.getA(), axes.getB())) {
556 // here we call the real transform
557 return calcImageKSpaceReal<PixelT>(rad1, rad2);
558 } else {
559 // here we call the complex transform
560 double const ellipticity = 1.0 - axes.getB() / axes.getA();
561 return calcImageKSpaceCplx<PixelT>(rad1, rad2, axes.getTheta(), ellipticity);
562 }
563}
564
565template class SincCoeffs<float>;
566template class SincCoeffs<double>;
567
568} // namespace base
569} // namespace meas
570} // namespace lsst
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition Exception.h:48
An ellipse core for the semimajor/semiminor axis and position angle parametrization (a,...
Definition Axes.h:47
double const getTheta() const
Definition Axes.h:57
double const getA() const
Definition Axes.h:51
double const getB() const
Definition Axes.h:54
A singleton to calculate and cache the coefficients for sinc photometry.
Definition SincCoeffs.h:45
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.
Definition Runtime.h:66
T cos(T... args)
T end(T... args)
T isnan(T... args)
T log10(T... args)
T make_shared(T... args)
scale(algorithm, min, max=None, frame=None)
Definition ds9.py:108
auto integrate(UnaryFunctionT func, Arg const a, Arg const b, double eps=1.0e-6)
The 1D integrator.
Definition Integrate.h:765
auto integrate2d(BinaryFunctionT func, X x1, X x2, Y y1, Y y2, double eps=1.0e-6)
The 2D integrator.
Definition Integrate.h:779
double constexpr TWOPI
Definition Angle.h:41
Extent< int, 2 > ExtentI
Definition Extent.h:396
double constexpr PI
The ratio of a circle's circumference to diameter.
Definition Angle.h:40
std::uint8_t log2(std::uint64_t x)
Definition curve.h:105
T pow(T... args)
T real(T... args)
T rotate(T... args)
T sin(T... args)
T sqrt(T... args)