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())
279 imgVector.push_back(componentImg);
280 weightSum += exposureRecord.get(_weightKey);
281 weightVector.push_back(exposureRecord.get(_weightKey));
287 PTR(afw::detection::Psf::Image) image = std::make_shared<afw::detection::Psf::Image>(bbox);
295 return _catalog.size();
300 throw LSST_EXCEPT(pex::exceptions::RangeError,
"index of CoaddPsf component out of range");
302 return _catalog[index].getPsf();
307 throw LSST_EXCEPT(pex::exceptions::RangeError,
"index of CoaddPsf component out of range");
309 return _catalog[index].getWcs();
314 throw LSST_EXCEPT(pex::exceptions::RangeError,
"index of CoaddPsf component out of range");
316 return _catalog[index].getValidPolygon();
321 throw LSST_EXCEPT(pex::exceptions::RangeError,
"index of CoaddPsf component out of range");
323 return _catalog[index].get(_weightKey);
328 throw LSST_EXCEPT(pex::exceptions::RangeError,
"index of CoaddPsf component out of range");
330 return _catalog[index].getId();
335 throw LSST_EXCEPT(pex::exceptions::RangeError,
"index of CoaddPsf component out of range");
337 return _catalog[index].getBBox();
348 namespace tbl = afw::table;
351 class CoaddPsfPersistenceHelper {
359 static CoaddPsfPersistenceHelper
const &
get() {
360 static CoaddPsfPersistenceHelper
const instance;
365 CoaddPsfPersistenceHelper() :
367 coaddWcs(schema.addField<
int>(
"coaddwcs",
"archive ID of the coadd's WCS")),
368 cacheSize(schema.addField<
int>(
"cachesize",
"size of the warping cache")),
370 schema,
"avgpos",
"PSF accessors default position",
"pixel" 372 warpingKernelName(schema.addField<std::string>(
"warpingkernelname",
"warping kernel name", 32))
374 schema.getCitizen().markPersistent();
383 virtual PTR(tbl::io::Persistable)
384 read(InputArchive const & archive, CatalogVector const & catalogs)
const {
385 if (catalogs.size() == 1u) {
389 return readV0(archive, catalogs);
391 LSST_ARCHIVE_ASSERT(catalogs.size() == 2u);
392 CoaddPsfPersistenceHelper
const & keys1 = CoaddPsfPersistenceHelper::get();
393 LSST_ARCHIVE_ASSERT(catalogs.front().getSchema() == keys1.schema);
394 tbl::BaseRecord
const & record1 = catalogs.front().front();
397 tbl::ExposureCatalog::readFromArchive(archive, catalogs.back()),
398 archive.get<afw::image::Wcs>(record1.get(keys1.coaddWcs)),
399 record1.get(keys1.averagePosition),
400 record1.get(keys1.warpingKernelName),
401 record1.get(keys1.cacheSize)
412 std::shared_ptr<tbl::io::Persistable>
413 readV0(InputArchive
const & archive, CatalogVector
const & catalogs)
const {
414 auto internalCat = tbl::ExposureCatalog::readFromArchive(archive, catalogs.front());
416 auto coaddWcs = internalCat.back().getWcs();
417 internalCat.pop_back();
421 tbl::Key<double> weightKey;
423 weightKey = internalCat.getSchema()[
"weight"];
424 }
catch (pex::exceptions::NotFoundError &) {}
425 auto averagePos = computeAveragePosition(internalCat, *coaddWcs, weightKey);
426 return std::shared_ptr<CoaddPsf>(
new CoaddPsf(internalCat, coaddWcs, averagePos));
429 Factory(std::string
const & name) : tbl::io::PersistableFactory(name) {}
435 std::string getCoaddPsfPersistenceName() {
return "CoaddPsf"; }
446 CoaddPsfPersistenceHelper
const & keys1 = CoaddPsfPersistenceHelper::get();
447 tbl::BaseCatalog cat1 = handle.makeCatalog(keys1.schema);
448 PTR(tbl::BaseRecord) record1 = cat1.addNew();
449 record1->set(keys1.coaddWcs, handle.put(_coaddWcs));
450 record1->set(keys1.cacheSize, _warpingControl->getCacheSize());
451 record1->set(keys1.averagePosition, _averagePosition);
452 record1->set(keys1.warpingKernelName, _warpingKernelName);
453 handle.saveCatalog(cat1);
454 _catalog.writeToArchive(handle,
false);
458 afw::table::ExposureCatalog
const & catalog,
459 PTR(afw::image::Wcs
const)
coaddWcs,
464 _catalog(catalog), _coaddWcs(coaddWcs), _weightKey(_catalog.getSchema()[
"weight"]),
465 _averagePosition(averagePosition), _warpingKernelName(warpingKernelName),
466 _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...