35 #include "lsst/base.h" 36 #include "lsst/pex/exceptions.h" 37 #include "lsst/afw/image/ImageUtils.h" 38 #include "lsst/afw/math/Statistics.h" 43 #include "lsst/meas/algorithms/KernelPsfFactory.h" 44 #include "lsst/afw/table/aggregates.h" 46 namespace lsst {
namespace meas {
namespace extensions {
namespace psfex {
48 namespace afw = lsst::afw;
51 lsst::meas::extensions::psfex::Psf
const&
psf,
53 ) : ImagePsf(), _averagePosition(averagePosition),
55 _comp(psf.impl->npix),
56 _context(psf.impl->poly->
ndim)
59 _poly = poly_copy(psf.impl->poly);
63 std::copy(psf.impl->size, psf.impl->size + psf.impl->dim,
_size.begin());
65 std::copy(psf.impl->comp, psf.impl->comp + psf.impl->npix,
_comp.begin());
67 for (
int i = 0; i != psf.impl->poly->ndim; ++i) {
68 _context[i].first = psf.impl->contextoffset[i];
69 _context[i].second = psf.impl->contextscale[i];
73 PsfexPsf::PsfexPsf() : ImagePsf(),
74 _averagePosition(afw::geom::Point2I(0, 0)),
89 PTR(afw::detection::Psf)
91 return std::make_shared<PsfexPsf>(*this);
94 PTR(afw::detection::Psf)
96 throw LSST_EXCEPT(pex::exceptions::LogicError,
"Not Implemented");
99 PTR(afw::math::LinearCombinationKernel
const)
102 double pos[MAXCONTEXT];
103 int const ndim = _context.size();
105 throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
106 str(boost::format(
"Only spatial variation (ndim == 2) is supported; saw %d")
111 if (!std::isfinite(position[0])) {
112 position = _averagePosition;
115 for (
int i = 0; i <
ndim; ++i) {
116 pos[i] = (position[i] - _context[i].first)/_context[i].second;
119 poly_func(_poly, pos);
122 std::vector<float> fullresIm(w*h);
126 const int nbasis =
_size.size() > 2 ?
_size[2] : 1;
127 afw::math::KernelList kernels; kernels.reserve(nbasis);
128 std::vector<double> weights; weights.reserve(nbasis);
131 float const dx = 0.0, dy = 0.0;
133 afw::geom::Box2I bbox = _doComputeBBox(position, afw::geom::Point2D(0, 0));
134 afw::detection::Psf::Image kim(bbox);
136 int sampleW = bbox.getWidth();
137 int sampleH = bbox.getHeight();
139 std::vector<float> sampledBasis(sampleW*sampleH);
141 for (
int i = 0; i != nbasis; ++i) {
145 vignet_resample(const_cast<float *>(&
_comp[i*w*h]), w, h,
146 &sampledBasis[0], sampleW, sampleH,
147 -dx*vigstep, -dy*vigstep, vigstep, 1.0);
152 float *pl = &sampledBasis[0];
153 for (
int y = 0; y != sampleH; ++y) {
154 for (
int x = 0; x != sampleW; ++x) {
160 kernels.push_back(std::make_shared<afw::math::FixedKernel>(kim));
161 weights.push_back(_poly->basis[i]);
164 _kernel = std::make_shared<afw::math::LinearCombinationKernel>(kernels, weights);
169 PTR(afw::detection::Psf::Image)
170 PsfexPsf::doComputeImage(afw::geom::Point2D
const & position,
171 afw::image::Color
const & color)
const {
172 return _doComputeImage(position, color, position);
175 PTR(afw::detection::Psf::Image)
176 PsfexPsf::doComputeKernelImage(afw::geom::Point2D
const& position,
177 afw::image::Color
const& color)
const 179 return _doComputeImage(position, color, afw::geom::Point2D(0, 0));
182 afw::geom::Box2I PsfexPsf::doComputeBBox(afw::geom::Point2D
const & position,
183 afw::image::Color
const & color)
const {
184 return _doComputeBBox(position, afw::geom::Point2D(0, 0));
187 afw::geom::Box2I PsfexPsf::_doComputeBBox(afw::geom::Point2D
const & position,
188 afw::geom::Point2D
const & center)
const {
190 int sampleW =
static_cast<int>(w*
_pixstep);
191 int sampleH =
static_cast<int>(h*
_pixstep);
194 if (sampleW % 2 == 0) sampleW += 1;
195 if (sampleH % 2 == 0) sampleH += 1;
197 float dx = center[0] -
static_cast<int>(center[0]);
198 float dy = center[1] -
static_cast<int>(center[1]);
200 if (dx > 0.5) dx -= 1.0;
201 if (dy > 0.5) dy -= 1.0;
204 afw::geom::Box2I bbox(afw::geom::Point2I(static_cast<int>(center[0] - dx + 0.5) - sampleW/2,
205 static_cast<int>(center[1] - dy + 0.5) - sampleH/2),
206 afw::geom::Extent2I(sampleW, sampleH));
210 PTR(afw::detection::Psf::Image)
211 PsfexPsf::_doComputeImage(afw::geom::Point2D
const& position,
212 afw::image::Color
const& color,
213 afw::geom::Point2D
const& center
216 double pos[MAXCONTEXT];
217 int const ndim = _context.size();
219 throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
220 str(boost::format(
"Only spatial variation (ndim == 2) is supported; saw %d")
225 for (
int i = 0; i <
ndim; ++i) {
226 pos[i] = (position[i] - _context[i].first)/_context[i].second;
229 poly_func(_poly, pos);
232 std::vector<float> fullresIm(w*h);
233 const int nbasis =
_size.size() > 2 ?
_size[2] : 1;
236 int const npix = w*h;
237 for (
int i = 0; i != nbasis; ++i) {
238 float *pl = &fullresIm[0];
239 float const fac = _poly->basis[i];
240 float const *ppc = &
_comp[i*w*h];
242 for (
int j = 0; j != npix; ++j) {
251 float dx = center[0] -
static_cast<int>(center[0]);
252 float dy = center[1] -
static_cast<int>(center[1]);
253 if (dx > 0.5) dx -= 1.0;
254 if (dy > 0.5) dy -= 1.0;
258 afw::geom::Box2I bbox = _doComputeBBox(position, center);
259 PTR(afw::detection::Psf::Image) im = std::make_shared<afw::detection::Psf::Image>(bbox);
261 int sampleW = bbox.getWidth();
262 int sampleH = bbox.getHeight();
264 std::vector<float> sampledIm(sampleW*sampleH);
266 vignet_resample(&fullresIm[0], w, h,
267 &sampledIm[0], sampleW, sampleH,
268 -dx*vigstep, -dy*vigstep, vigstep, 1.0);
270 float *pl = &sampledIm[0];
271 float const sum = std::accumulate(pl, pl + sampleW*sampleH, static_cast<float>(0));
272 for (
int y = 0; y != sampleH; ++y) {
273 for (
int x = 0; x != sampleW; ++x) {
274 (*im)(x, y) = *pl++/sum;
286 namespace table = afw::table;
290 class PsfexPsfSchema1 {
294 ndim(
schema.addField<
int>(
"ndim",
"Number of elements in group")),
295 ngroup(
schema.addField<
int>(
"ngroup",
"Number of elements in degree")),
296 ncoeff(
schema.addField<
int>(
"ncoeff",
"Number of coefficients")),
302 averagePosition(afw::table::PointKey<double>::addFields(
schema,
"averagePosition",
"average position of stars used to make the PSF",
"pixel")),
303 _pixstep(
schema.addField<
float>(
"_pixstep",
"oversampling",
"pixel"))
323 class PsfexPsfSchema2 {
326 int size_size,
int comp_size,
int context_size) :
329 group(
schema.addField<table::Array<int> >(
"group",
"Groups (of coefficients?)", ndim)),
330 degree(
schema.addField<table::Array<int> >(
"degree",
"Degree in each group", ngroup)),
331 basis(
schema.addField<table::Array<double> >(
"basis",
"Values of the basis functions", ncoeff)),
332 coeff(
schema.addField<table::Array<double> >(
"coeff",
"Polynomial coefficients", ncoeff)),
333 _size(
schema.addField<table::Array<int> >(
"_size",
"PSF dimensions", size_size)),
334 _comp(
schema.addField<table::Array<float> >(
"_comp",
"Complete pixel data", comp_size)),
336 "Offset to apply to context data", context_size)),
338 "Scale to apply to context data", context_size))
345 table::Key<table::Array<int> >
group;
347 table::Key<table::Array<double> >
basis;
348 table::Key<table::Array<double> >
coeff;
350 table::Key<table::Array<int> >
_size;
351 table::Key<table::Array<float> >
_comp;
356 std::string getPsfexPsfPersistenceName() {
return "PsfexPsf"; }
366 virtual PTR(table::io::Persistable)
367 read(InputArchive const & archive, CatalogVector const & catalogs)
const {
368 LSST_ARCHIVE_ASSERT(catalogs.size() == 2u);
373 int size_size, comp_size, context_size;
375 PsfexPsfSchema1
const & keys = PsfexPsfSchema1();
376 LSST_ARCHIVE_ASSERT(catalogs[0].size() == 1u);
377 LSST_ARCHIVE_ASSERT(catalogs[0].getSchema() == keys.schema);
378 table::BaseRecord
const & record = catalogs[0].front();
381 ndim = record.get(keys.ndim);
382 ngroup = record.get(keys.ngroup);
383 ncoeff = record.get(keys.ncoeff);
385 result->_averagePosition = record.get(keys.averagePosition);
386 result->_pixstep = record.get(keys._pixstep);
388 size_size = record.get(keys._size_size);
389 comp_size = record.get(keys._comp_size);
390 context_size = record.get(keys._context_size);
394 PsfexPsfSchema2
const keys(ndim, ngroup, ncoeff,
395 size_size, comp_size, context_size);
397 LSST_ARCHIVE_ASSERT(catalogs[1].size() == 1u);
398 LSST_ARCHIVE_ASSERT(catalogs[1].getSchema() == keys.schema);
399 table::BaseRecord
const & record = catalogs[1].front();
402 std::vector<int>
group;
404 int const *begin = record.getElement(keys.group);
405 group.assign(begin, begin + ndim);
407 for (
int i = 0; i !=
ndim; ++i) {
413 int const *begin = record.getElement(keys.degree);
414 degree.assign(begin, begin + ngroup);
416 result->_poly = poly_init(&group[0], group.size(), °ree[0], degree.size());
417 LSST_ARCHIVE_ASSERT(result->_poly->ncoeff == ncoeff);
420 double const *begin = record.getElement(keys.basis);
421 std::copy(begin, begin + ncoeff, result->_poly->basis);
424 double const *begin = record.getElement(keys.coeff);
425 std::copy(begin, begin + ncoeff, result->_poly->coeff);
429 int const *begin = record.getElement(keys._size);
430 result->_size.assign(begin, begin + size_size);
433 float const *begin = record.getElement(keys._comp);
434 result->_comp.assign(begin, begin + comp_size);
437 double const *begin1 = record.getElement(keys._context_first);
438 double const *begin2 = record.getElement(keys._context_second);
439 result->_context.resize(context_size);
440 for (
int i = 0; i != context_size; ++i) {
441 result->_context[i].first = begin1[i];
442 result->_context[i].second = begin2[i];
450 explicit PsfexPsfFactory(std::string
const & name) : table::io::PersistableFactory(name) {}
461 std::string PsfexPsf::getPythonModule()
const {
return "lsst.meas.extensions.psfex"; }
463 std::string PsfexPsf::getPersistenceName()
const {
return getPsfexPsfPersistenceName(); }
469 PsfexPsfSchema1
const keys;
470 afw::table::BaseCatalog cat = handle.makeCatalog(keys.schema);
471 PTR(afw::table::BaseRecord) record = cat.addNew();
474 record->set(keys.ndim, _poly->ndim);
475 record->set(keys.ngroup, _poly->ngroup);
476 record->set(keys.ncoeff, _poly->ncoeff);
478 record->set(keys.averagePosition, _averagePosition);
479 record->set(keys._pixstep,
_pixstep);
480 record->set(keys._size_size,
_size.size());
481 record->set(keys._comp_size,
_comp.size());
482 record->set(keys._context_size, _context.size());
484 handle.saveCatalog(cat);
488 PsfexPsfSchema2
const keys(_poly->ndim, _poly->ngroup, _poly->ncoeff,
490 afw::table::BaseCatalog cat = handle.makeCatalog(keys.schema);
492 PTR(afw::table::BaseRecord) record = cat.addNew();
494 int *begin = record->getElement(keys.group);
495 std::copy(_poly->group, _poly->group + _poly->ndim, begin);
498 int *begin = record->getElement(keys.degree);
499 std::copy(_poly->degree, _poly->degree + _poly->ngroup, begin);
502 double *begin = record->getElement(keys.basis);
503 std::copy(_poly->basis, _poly->basis + _poly->ncoeff, begin);
506 double *begin = record->getElement(keys.coeff);
507 std::copy(_poly->coeff, _poly->coeff + _poly->ncoeff, begin);
511 int *begin = record->getElement(keys._size);
515 float *begin = record->getElement(keys._comp);
519 double *begin1 = record->getElement(keys._context_first);
520 double *begin2 = record->getElement(keys._context_second);
521 for (
unsigned int i = 0; i != _context.size(); ++i) {
522 begin1[i] = _context[i].first;
523 begin2[i] = _context[i].second;
527 handle.saveCatalog(cat);
table::Key< table::Array< int > > degree
table::Key< int > _size_size
virtual boost::shared_ptr< lsst::afw::detection::Psf > clone() const
Polymorphic deep copy; should usually be unnecessary as Psfs are immutable.x.
PsfexPsfFactory(std::string const &name)
Represent a PSF as a linear combination of PSFEX (== Karhunen-Loeve) basis functions.
virtual boost::shared_ptr< afw::detection::Psf > resized(int width, int height) const
Return a clone with specified kernel dimensions.
table::Key< table::Array< double > > _context_second
table::Key< table::Array< int > > _size
table::Key< table::Array< double > > coeff
table::Key< int > _comp_size
boost::shared_ptr< lsst::afw::math::LinearCombinationKernel const > getKernel(lsst::afw::geom::Point2D=lsst::afw::geom::Point2D(std::numeric_limits< double >::quiet_NaN())) const
Return the PSF's basis functions as a spatially-invariant LinearCombinationKernel with unit weights...
table::Key< table::Array< double > > basis
table::Key< table::Array< float > > _comp
void write(lsst::afw::table::io::OutputArchiveHandle &handle) const
table::Key< table::Array< double > > _context_first
table::Key< int > _context_size
table::Key< table::Array< int > > group
table::Key< float > _pixstep
table::PointKey< double > averagePosition