33 #include "boost/iterator/iterator_adaptor.hpp" 34 #include "boost/iterator/transform_iterator.hpp" 35 #include "ndarray/eigen.h" 36 #include "lsst/base.h" 37 #include "lsst/pex/exceptions.h" 38 #include "lsst/afw/image/ImageUtils.h" 39 #include "lsst/afw/math/Statistics.h" 41 #include "lsst/afw/table/io/OutputArchive.h" 42 #include "lsst/afw/table/io/InputArchive.h" 43 #include "lsst/afw/table/io/CatalogVector.h" 48 namespace algorithms {
59 explicit AvgPosItem(
double wx_=0.0,
double wy_=0.0,
double w_=0.0) : wx(wx_), wy(wy_), w(w_) {}
62 afw::geom::Point2D getPoint()
const {
return afw::geom::Point2D(wx/w, wy/w); }
65 bool operator<(AvgPosItem
const & other)
const {
69 AvgPosItem & operator+=(AvgPosItem
const & other) {
76 AvgPosItem & operator-=(AvgPosItem
const & other) {
83 friend AvgPosItem operator+(AvgPosItem a, AvgPosItem
const &
b) {
return a +=
b; }
85 friend AvgPosItem operator-(AvgPosItem a, AvgPosItem
const & b) {
return a -=
b; }
88 afw::geom::Point2D computeAveragePosition(
89 afw::table::ExposureCatalog
const & catalog,
91 afw::table::Key<double> weightKey
93 afw::table::Key<int> goodPixKey;
95 goodPixKey = catalog.getSchema()[
"goodpix"];
96 }
catch (pex::exceptions::NotFoundError &) {}
97 std::vector<AvgPosItem> items;
98 items.reserve(catalog.size());
99 for (afw::table::ExposureCatalog::const_iterator i = catalog.begin(); i != catalog.end(); ++i) {
100 afw::geom::Point2D p = coaddWcs.skyToPixel(
101 *i->getWcs()->pixelToSky(
102 i->getPsf()->getAveragePosition()
105 AvgPosItem item(p.getX(), p.getY(), i->get(weightKey));
106 if (goodPixKey.isValid()) {
107 item.w *= i->get(goodPixKey);
111 items.push_back(item);
117 std::sort(items.begin(), items.end());
118 AvgPosItem result = std::accumulate(items.begin(), items.end(), AvgPosItem());
122 std::vector<AvgPosItem>::iterator iter = items.begin();
123 catalog.subsetContaining(result.getPoint(),
coaddWcs,
true).empty();
126 if (iter == items.end()) {
131 pex::exceptions::RuntimeError,
132 "Could not find a valid average position for CoaddPsf" 137 return result.getPoint();
143 afw::table::ExposureCatalog
const & catalog,
145 std::string
const & weightFieldName,
149 _coaddWcs(coaddWcs.clone()),
150 _warpingKernelName(warpingKernelName),
151 _warpingControl(
std::make_shared<afw::math::WarpingControl>(warpingKernelName,
"", cacheSize))
153 afw::table::SchemaMapper mapper(catalog.getSchema());
154 mapper.addMinimalSchema(afw::table::ExposureTable::makeMinimalSchema(),
true);
158 afw::table::Key<int> goodPixKey = catalog.getSchema()[
"goodpix"];
159 mapper.addMapping(goodPixKey,
true);
160 }
catch (pex::exceptions::NotFoundError &) {}
163 afw::table::Field<double> weightField = afw::table::Field<double>(
"weight",
"Coadd weight");
164 afw::table::Key<double> weightKey = catalog.getSchema()[weightFieldName];
165 _weightKey = mapper.addMapping(weightKey, weightField);
167 _catalog = afw::table::ExposureCatalog(mapper.getOutputSchema());
168 for (afw::table::ExposureCatalog::const_iterator i = catalog.begin(); i != catalog.end(); ++i) {
169 PTR(afw::table::ExposureRecord) record = _catalog.getTable()->makeRecord();
170 record->assign(*i, mapper);
171 _catalog.push_back(record);
173 _averagePosition = computeAveragePosition(_catalog, *_coaddWcs, _weightKey);
177 return std::make_shared<CoaddPsf>(*this);
182 throw LSST_EXCEPT(pex::exceptions::LogicError,
"Not Implemented");
189 afw::geom::Box2I bbox;
191 for (
unsigned int i = 0; i < imgVector.size(); i ++) {
193 afw::geom::Box2I cBBox = componentImg->getBBox();
205 std::vector<double>
const & weightVector
207 assert(imgVector.size() == weightVector.size());
208 for (
unsigned int i = 0; i < imgVector.size(); i ++) {
210 double weight = weightVector[i];
211 double sum = componentImg->getArray().asEigen().sum();
216 afw::geom::Box2I cBBox = componentImg->getBBox();
217 afw::geom::Box2I overlap(cBBox);
218 overlap.clip(image->getBBox());
223 targetSubImage.scaledPlus(weight/sum, cSubImage);
229 afw::geom::Point2D
const & ccdXY,
230 afw::image::Color
const & color
232 afw::table::ExposureCatalog subcat = _catalog.subsetContaining(ccdXY, *_coaddWcs,
true);
233 if (subcat.empty()) {
235 pex::exceptions::InvalidParameterError,
236 (boost::format(
"Cannot compute BBox at point %s; no input images at that point.")
240 afw::geom::Box2I ret;
241 for (
auto const & exposureRecord : subcat) {
242 PTR(afw::geom::XYTransform) xytransform(
243 new afw::image::XYTransformFromWcsPair(_coaddWcs, exposureRecord.getWcs()));
245 afw::geom::Box2I componentBBox = warpedPsf.computeBBox(ccdXY, color);
246 ret.include(componentBBox);
253 afw::geom::Point2D
const & ccdXY,
254 afw::image::Color
const & color
257 afw::table::ExposureCatalog subcat = _catalog.subsetContaining(ccdXY, *_coaddWcs,
true);
258 if (subcat.empty()) {
260 pex::exceptions::InvalidParameterError,
261 (boost::format(
"Cannot compute CoaddPsf at point %s; no input images at that point.")
265 double weightSum = 0.0;
270 std::vector<PTR(afw::image::Image<double>)> imgVector;
271 std::vector<double> weightVector;
273 for (
auto const & exposureRecord : subcat) {
274 PTR(afw::geom::XYTransform) xytransform(
275 new afw::image::XYTransformFromWcsPair(_coaddWcs, exposureRecord.getWcs())
280 componentImg = warpedPsf.computeKernelImage(ccdXY, color);
281 }
catch (pex::exceptions::RangeError & exc) {
282 LSST_EXCEPT_ADD(exc, (boost::format(
"Computing WarpedPsf kernel image for id=%d") %
283 exposureRecord.getId()).str());
286 imgVector.push_back(componentImg);
287 weightSum += exposureRecord.get(_weightKey);
288 weightVector.push_back(exposureRecord.get(_weightKey));
294 PTR(afw::detection::Psf::Image) image = std::make_shared<afw::detection::Psf::Image>(bbox);
302 return _catalog.size();
307 throw LSST_EXCEPT(pex::exceptions::RangeError,
"index of CoaddPsf component out of range");
309 return _catalog[index].getPsf();
314 throw LSST_EXCEPT(pex::exceptions::RangeError,
"index of CoaddPsf component out of range");
316 return _catalog[index].getWcs();
321 throw LSST_EXCEPT(pex::exceptions::RangeError,
"index of CoaddPsf component out of range");
323 return _catalog[index].getValidPolygon();
328 throw LSST_EXCEPT(pex::exceptions::RangeError,
"index of CoaddPsf component out of range");
330 return _catalog[index].get(_weightKey);
335 throw LSST_EXCEPT(pex::exceptions::RangeError,
"index of CoaddPsf component out of range");
337 return _catalog[index].getId();
342 throw LSST_EXCEPT(pex::exceptions::RangeError,
"index of CoaddPsf component out of range");
344 return _catalog[index].getBBox();
355 namespace tbl = afw::table;
358 class CoaddPsfPersistenceHelper {
366 static CoaddPsfPersistenceHelper
const &
get() {
367 static CoaddPsfPersistenceHelper
const instance;
372 CoaddPsfPersistenceHelper() :
374 coaddWcs(schema.addField<
int>(
"coaddwcs",
"archive ID of the coadd's WCS")),
375 cacheSize(schema.addField<
int>(
"cachesize",
"size of the warping cache")),
377 schema,
"avgpos",
"PSF accessors default position",
"pixel" 379 warpingKernelName(schema.addField<std::string>(
"warpingkernelname",
"warping kernel name", 32))
381 schema.getCitizen().markPersistent();
390 virtual PTR(tbl::io::Persistable)
391 read(InputArchive const & archive, CatalogVector const & catalogs)
const {
392 if (catalogs.size() == 1u) {
396 return readV0(archive, catalogs);
398 LSST_ARCHIVE_ASSERT(catalogs.size() == 2u);
399 CoaddPsfPersistenceHelper
const & keys1 = CoaddPsfPersistenceHelper::get();
400 LSST_ARCHIVE_ASSERT(catalogs.front().getSchema() == keys1.schema);
401 tbl::BaseRecord
const & record1 = catalogs.front().front();
404 tbl::ExposureCatalog::readFromArchive(archive, catalogs.back()),
405 archive.get<afw::image::Wcs>(record1.get(keys1.coaddWcs)),
406 record1.get(keys1.averagePosition),
407 record1.get(keys1.warpingKernelName),
408 record1.get(keys1.cacheSize)
419 std::shared_ptr<tbl::io::Persistable>
420 readV0(InputArchive
const & archive, CatalogVector
const & catalogs)
const {
421 auto internalCat = tbl::ExposureCatalog::readFromArchive(archive, catalogs.front());
423 auto coaddWcs = internalCat.back().getWcs();
424 internalCat.pop_back();
428 tbl::Key<double> weightKey;
430 weightKey = internalCat.getSchema()[
"weight"];
431 }
catch (pex::exceptions::NotFoundError &) {}
432 auto averagePos = computeAveragePosition(internalCat, *coaddWcs, weightKey);
433 return std::shared_ptr<CoaddPsf>(
new CoaddPsf(internalCat, coaddWcs, averagePos));
436 Factory(std::string
const & name) : tbl::io::PersistableFactory(name) {}
442 std::string getCoaddPsfPersistenceName() {
return "CoaddPsf"; }
453 CoaddPsfPersistenceHelper
const & keys1 = CoaddPsfPersistenceHelper::get();
454 tbl::BaseCatalog cat1 = handle.makeCatalog(keys1.schema);
455 PTR(tbl::BaseRecord) record1 = cat1.addNew();
456 record1->set(keys1.coaddWcs, handle.put(_coaddWcs));
457 record1->set(keys1.cacheSize, _warpingControl->getCacheSize());
458 record1->set(keys1.averagePosition, _averagePosition);
459 record1->set(keys1.warpingKernelName, _warpingKernelName);
460 handle.saveCatalog(cat1);
461 _catalog.writeToArchive(handle,
false);
465 afw::table::ExposureCatalog
const & catalog,
466 PTR(afw::image::Wcs
const)
coaddWcs,
471 _catalog(catalog), _coaddWcs(coaddWcs), _weightKey(_catalog.getSchema()[
"weight"]),
472 _averagePosition(averagePosition), _warpingKernelName(warpingKernelName),
473 _warpingControl(new afw::math::WarpingControl(warpingKernelName,
"", cacheSize))
afw::geom::Box2I getOverallBBox(std::vector< boost::shared_ptr< afw::image::Image< double > >> const &imgVector)
tbl::Key< double > weight
afw::geom::Box2I getBBox(int index)
Get the bounding box (in component image Pixel coordinates) of the component image at index...
double getWeight(int index)
Get the weight of the component image at index.
virtual std::string getPythonModule() const
boost::shared_ptr< afw::image::Wcs const > getWcs(int index)
Get the Wcs of the component image at index.
CoaddPsf(afw::table::ExposureCatalog const &catalog, afw::image::Wcs const &coaddWcs, std::string const &weightFieldName="weight", std::string const &warpingKernelName="lanczos3", int cacheSize=10000)
Main constructors for CoaddPsf.
virtual boost::shared_ptr< afw::detection::Psf > resized(int width, int height) const
Return a clone with specified kernel dimensions.
virtual std::string getPersistenceName() const
virtual void write(OutputArchiveHandle &handle) const
tbl::Key< int > cacheSize
int getComponentCount() const
Return the number of component Psfs in this CoaddPsf.
afw::table::RecordId getId(int index)
Get the exposure ID of the component image at index.
Factory(std::string const &name)
virtual afw::geom::Box2I doComputeBBox(afw::geom::Point2D const &position, afw::image::Color const &color) const
CoaddPsf is the Psf derived to be used for non-PSF-matched Coadd images.
boost::shared_ptr< afw::detection::Psf::Image > doComputeKernelImage(afw::geom::Point2D const &ccdXY, afw::image::Color const &color) const
tbl::Key< std::string > warpingKernelName
virtual boost::shared_ptr< afw::detection::Psf > clone() const
Polymorphic deep copy. Usually unnecessary, as Psfs are immutable.
void addToImage(boost::shared_ptr< afw::image::Image< double > > image, std::vector< boost::shared_ptr< afw::image::Image< double > >> const &imgVector, std::vector< double > const &weightVector)
afw::table::Key< double > b
tbl::PointKey< double > averagePosition
std::shared_ptr< tbl::io::Persistable > readV0(InputArchive const &archive, CatalogVector const &catalogs) const
A Psf class that maps an arbitrary Psf through a coordinate transformation.
boost::shared_ptr< afw::detection::Psf const > getPsf(int index)
Get the Psf of the component image at index.
boost::shared_ptr< afw::geom::polygon::Polygon const > getValidPolygon(int index)
Get the validPolygon (in component image Pixel coordinates) of the component image at index...