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::math::LinearCombinationKernel
const)
95 PsfexPsf::getKernel(afw::geom::Point2D position)
const
97 double pos[MAXCONTEXT];
98 int const ndim = _context.size();
100 throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
101 str(boost::format(
"Only spatial variation (ndim == 2) is supported; saw %d")
106 if (!std::isfinite(position[0])) {
107 position = _averagePosition;
110 for (
int i = 0; i <
ndim; ++i) {
111 pos[i] = (position[i] - _context[i].first)/_context[i].second;
114 poly_func(_poly, pos);
117 std::vector<float> fullresIm(w*h);
121 const int nbasis =
_size.size() > 2 ?
_size[2] : 1;
122 afw::math::KernelList kernels; kernels.reserve(nbasis);
123 std::vector<double> weights; weights.reserve(nbasis);
126 float const dx = 0.0, dy = 0.0;
128 afw::geom::Box2I bbox = _doComputeBBox(position, afw::geom::Point2D(0, 0));
129 afw::detection::Psf::Image kim(bbox);
131 int sampleW = bbox.getWidth();
132 int sampleH = bbox.getHeight();
134 std::vector<float> sampledBasis(sampleW*sampleH);
136 for (
int i = 0; i != nbasis; ++i) {
140 vignet_resample(const_cast<float *>(&
_comp[i*w*h]), w, h,
141 &sampledBasis[0], sampleW, sampleH,
142 -dx*vigstep, -dy*vigstep, vigstep, 1.0);
147 float *pl = &sampledBasis[0];
148 for (
int y = 0; y != sampleH; ++y) {
149 for (
int x = 0; x != sampleW; ++x) {
155 kernels.push_back(std::make_shared<afw::math::FixedKernel>(kim));
156 weights.push_back(_poly->basis[i]);
159 _kernel = std::make_shared<afw::math::LinearCombinationKernel>(kernels, weights);
164 PTR(afw::detection::Psf::Image)
165 PsfexPsf::doComputeImage(afw::geom::Point2D const & position,
166 afw::image::Color const & color)
const {
167 return _doComputeImage(position, color, position);
170 PTR(afw::detection::Psf::Image)
171 PsfexPsf::doComputeKernelImage(afw::geom::Point2D const& position,
172 afw::image::Color const& color)
const
174 return _doComputeImage(position, color, afw::geom::Point2D(0, 0));
177 afw::geom::Box2I PsfexPsf::doComputeBBox(afw::geom::Point2D
const & position,
178 afw::image::Color
const & color)
const {
179 return _doComputeBBox(position, afw::geom::Point2D(0, 0));
182 afw::geom::Box2I PsfexPsf::_doComputeBBox(afw::geom::Point2D
const & position,
183 afw::geom::Point2D
const & center)
const {
185 int sampleW =
static_cast<int>(w*
_pixstep);
186 int sampleH =
static_cast<int>(h*
_pixstep);
189 if (sampleW % 2 == 0) sampleW += 1;
190 if (sampleH % 2 == 0) sampleH += 1;
192 float dx = center[0] -
static_cast<int>(center[0]);
193 float dy = center[1] -
static_cast<int>(center[1]);
195 if (dx > 0.5) dx -= 1.0;
196 if (dy > 0.5) dy -= 1.0;
199 afw::geom::Box2I bbox(afw::geom::Point2I(static_cast<int>(center[0] - dx + 0.5) - sampleW/2,
200 static_cast<int>(center[1] - dy + 0.5) - sampleH/2),
201 afw::geom::Extent2I(sampleW, sampleH));
205 PTR(afw::detection::Psf::Image)
206 PsfexPsf::_doComputeImage(afw::geom::Point2D const& position,
207 afw::image::Color const& color,
208 afw::geom::Point2D const& center
211 double pos[MAXCONTEXT];
212 int const ndim = _context.size();
214 throw LSST_EXCEPT(lsst::pex::exceptions::InvalidParameterError,
215 str(boost::format(
"Only spatial variation (ndim == 2) is supported; saw %d")
220 for (
int i = 0; i <
ndim; ++i) {
221 pos[i] = (position[i] - _context[i].first)/_context[i].second;
224 poly_func(_poly, pos);
227 std::vector<float> fullresIm(w*h);
228 const int nbasis =
_size.size() > 2 ?
_size[2] : 1;
231 int const npix = w*h;
232 for (
int i = 0; i != nbasis; ++i) {
233 float *pl = &fullresIm[0];
234 float const fac = _poly->basis[i];
235 float const *ppc = &
_comp[i*w*h];
237 for (
int j = 0; j != npix; ++j) {
246 float dx = center[0] -
static_cast<int>(center[0]);
247 float dy = center[1] -
static_cast<int>(center[1]);
248 if (dx > 0.5) dx -= 1.0;
249 if (dy > 0.5) dy -= 1.0;
253 afw::geom::Box2I bbox = _doComputeBBox(position, center);
254 PTR(afw::detection::Psf::Image) im = std::make_shared<afw::detection::Psf::Image>(bbox);
256 int sampleW = bbox.getWidth();
257 int sampleH = bbox.getHeight();
259 std::vector<
float> sampledIm(sampleW*sampleH);
261 vignet_resample(&fullresIm[0], w, h,
262 &sampledIm[0], sampleW, sampleH,
263 -dx*vigstep, -dy*vigstep, vigstep, 1.0);
265 float *pl = &sampledIm[0];
266 float const sum = std::accumulate(pl, pl + sampleW*sampleH, static_cast<float>(0));
267 for (
int y = 0; y != sampleH; ++y) {
268 for (
int x = 0; x != sampleW; ++x) {
269 (*im)(x, y) = *pl++/sum;
281 namespace table = afw::table;
285 class PsfexPsfSchema1 {
289 ndim(
schema.addField<int>(
"ndim",
"Number of elements in group")),
290 ngroup(
schema.addField<int>(
"ngroup",
"Number of elements in degree")),
291 ncoeff(
schema.addField<int>(
"ncoeff",
"Number of coefficients")),
297 averagePosition(afw::table::PointKey<double>::addFields(
schema,
"averagePosition",
"average position of stars used to make the PSF",
"pixel")),
298 _pixstep(
schema.addField<float>(
"_pixstep",
"oversampling",
"pixel"))
318 class PsfexPsfSchema2 {
320 PsfexPsfSchema2(
int const ndim,
int const ngroup,
int const ncoeff,
321 int size_size,
int comp_size,
int context_size) :
324 group(
schema.addField<table::Array<int> >(
"group",
"Groups (of coefficients?)", ndim)),
325 degree(
schema.addField<table::Array<int> >(
"degree",
"Degree in each group", ngroup)),
326 basis(
schema.addField<table::Array<double> >(
"basis",
"Values of the basis functions", ncoeff)),
327 coeff(
schema.addField<table::Array<double> >(
"coeff",
"Polynomial coefficients", ncoeff)),
328 _size(
schema.addField<table::Array<int> >(
"_size",
"PSF dimensions", size_size)),
329 _comp(
schema.addField<table::Array<float> >(
"_comp",
"Complete pixel data", comp_size)),
331 "Offset to apply to context data", context_size)),
333 "Scale to apply to context data", context_size))
340 table::Key<table::Array<int> >
group;
342 table::Key<table::Array<double> >
basis;
343 table::Key<table::Array<double> >
coeff;
345 table::Key<table::Array<int> >
_size;
346 table::Key<table::Array<float> >
_comp;
351 std::string getPsfexPsfPersistenceName() {
return "PsfexPsf"; }
361 virtual PTR(table::io::Persistable)
362 read(InputArchive const & archive, CatalogVector const & catalogs)
const {
363 LSST_ARCHIVE_ASSERT(catalogs.size() == 2u);
368 int size_size, comp_size, context_size;
370 PsfexPsfSchema1
const & keys = PsfexPsfSchema1();
371 LSST_ARCHIVE_ASSERT(catalogs[0].size() == 1u);
372 LSST_ARCHIVE_ASSERT(catalogs[0].getSchema() == keys.schema);
373 table::BaseRecord
const & record = catalogs[0].front();
376 ndim = record.get(keys.ndim);
377 ngroup = record.get(keys.ngroup);
378 ncoeff = record.get(keys.ncoeff);
380 result->_averagePosition = record.get(keys.averagePosition);
381 result->_pixstep = record.get(keys._pixstep);
383 size_size = record.get(keys._size_size);
384 comp_size = record.get(keys._comp_size);
385 context_size = record.get(keys._context_size);
389 PsfexPsfSchema2
const keys(ndim, ngroup, ncoeff,
390 size_size, comp_size, context_size);
392 LSST_ARCHIVE_ASSERT(catalogs[1].size() == 1u);
393 LSST_ARCHIVE_ASSERT(catalogs[1].getSchema() == keys.schema);
394 table::BaseRecord
const & record = catalogs[1].front();
397 std::vector<int>
group;
399 int const *begin = record.getElement(keys.group);
400 group.assign(begin, begin + ndim);
402 for (
int i = 0; i !=
ndim; ++i) {
408 int const *begin = record.getElement(keys.degree);
409 degree.assign(begin, begin + ngroup);
411 result->_poly = poly_init(&group[0], group.size(), °ree[0], degree.size());
412 LSST_ARCHIVE_ASSERT(result->_poly->ncoeff == ncoeff);
415 double const *begin = record.getElement(keys.basis);
416 std::copy(begin, begin + ncoeff, result->_poly->basis);
419 double const *begin = record.getElement(keys.coeff);
420 std::copy(begin, begin + ncoeff, result->_poly->coeff);
424 int const *begin = record.getElement(keys._size);
425 result->_size.assign(begin, begin + size_size);
428 float const *begin = record.getElement(keys._comp);
429 result->_comp.assign(begin, begin + comp_size);
432 double const *begin1 = record.getElement(keys._context_first);
433 double const *begin2 = record.getElement(keys._context_second);
434 result->_context.resize(context_size);
435 for (
int i = 0; i != context_size; ++i) {
436 result->_context[i].first = begin1[i];
437 result->_context[i].second = begin2[i];
445 explicit PsfexPsfFactory(std::string
const & name) : table::io::PersistableFactory(name) {}
451 detail::PsfexPsfFactory registration(getPsfexPsfPersistenceName());
456 std::string PsfexPsf::getPythonModule()
const {
return "lsst.meas.extensions.psfex"; }
458 std::string PsfexPsf::getPersistenceName()
const {
return getPsfexPsfPersistenceName(); }
460 void PsfexPsf::write(afw::table::io::OutputArchiveHandle & handle)
const {
464 PsfexPsfSchema1
const keys;
465 afw::table::BaseCatalog cat = handle.makeCatalog(keys.schema);
466 PTR(afw::table::BaseRecord) record = cat.addNew();
469 record->set(keys.ndim, _poly->ndim);
470 record->set(keys.ngroup, _poly->ngroup);
471 record->set(keys.ncoeff, _poly->ncoeff);
473 record->set(keys.averagePosition, _averagePosition);
474 record->set(keys._pixstep,
_pixstep);
475 record->set(keys._size_size,
_size.size());
476 record->set(keys._comp_size,
_comp.size());
477 record->set(keys._context_size, _context.size());
479 handle.saveCatalog(cat);
483 PsfexPsfSchema2
const keys(_poly->ndim, _poly->ngroup, _poly->ncoeff,
485 afw::table::BaseCatalog cat = handle.makeCatalog(keys.schema);
487 PTR(afw::table::BaseRecord) record = cat.addNew();
489 int *begin = record->getElement(keys.group);
490 std::copy(_poly->group, _poly->group + _poly->ndim, begin);
493 int *begin = record->getElement(keys.degree);
494 std::copy(_poly->degree, _poly->degree + _poly->ngroup, begin);
497 double *begin = record->getElement(keys.basis);
498 std::copy(_poly->basis, _poly->basis + _poly->ncoeff, begin);
501 double *begin = record->getElement(keys.coeff);
502 std::copy(_poly->coeff, _poly->coeff + _poly->ncoeff, begin);
506 int *begin = record->getElement(keys._size);
510 float *begin = record->getElement(keys._comp);
514 double *begin1 = record->getElement(keys._context_first);
515 double *begin2 = record->getElement(keys._context_second);
516 for (
unsigned int i = 0; i != _context.size(); ++i) {
517 begin1[i] = _context[i].first;
518 begin2[i] = _context[i].second;
522 handle.saveCatalog(cat);
table::Key< table::Array< int > > degree
table::Key< int > _size_size
PsfexPsfFactory(std::string const &name)
Represent a PSF as a linear combination of PSFEX (== Karhunen-Loeve) basis functions.
table::Key< table::Array< double > > _context_second
table::Key< table::Array< int > > _size
table::Key< table::Array< double > > coeff
table::Key< int > _comp_size
table::Key< table::Array< double > > basis
table::Key< table::Array< float > > _comp
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