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,
144 afw::image::Wcs
const & coaddWcs,
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);
185 afw::geom::Box2I bbox;
187 for (
unsigned int i = 0; i < imgVector.size(); i ++) {
189 afw::geom::Box2I cBBox = componentImg->getBBox();
201 std::vector<double>
const & weightVector
203 assert(imgVector.size() == weightVector.size());
204 for (
unsigned int i = 0; i < imgVector.size(); i ++) {
206 double weight = weightVector[i];
207 double sum = componentImg->getArray().asEigen().sum();
212 afw::geom::Box2I cBBox = componentImg->getBBox();
213 afw::geom::Box2I overlap(cBBox);
214 overlap.clip(image->getBBox());
219 targetSubImage.scaledPlus(weight/sum, cSubImage);
225 afw::geom::Point2D
const & ccdXY,
226 afw::image::Color
const & color
228 afw::table::ExposureCatalog subcat = _catalog.subsetContaining(ccdXY, *_coaddWcs,
true);
229 if (subcat.empty()) {
231 pex::exceptions::InvalidParameterError,
232 (boost::format(
"Cannot compute BBox at point %s; no input images at that point.")
236 afw::geom::Box2I ret;
237 for (
auto const & exposureRecord : subcat) {
238 PTR(afw::geom::XYTransform) xytransform(
239 new afw::image::XYTransformFromWcsPair(_coaddWcs, exposureRecord.getWcs()));
240 WarpedPsf warpedPsf =
WarpedPsf(exposureRecord.getPsf(), xytransform, _warpingControl);
241 afw::geom::Box2I componentBBox = warpedPsf.computeBBox(ccdXY, color);
242 ret.include(componentBBox);
248 PTR(afw::detection::Psf::Image)
CoaddPsf::doComputeKernelImage(
249 afw::geom::Point2D const & ccdXY,
250 afw::image::Color const & color
253 afw::table::ExposureCatalog subcat = _catalog.subsetContaining(ccdXY, *_coaddWcs,
true);
254 if (subcat.empty()) {
256 pex::exceptions::InvalidParameterError,
257 (boost::format(
"Cannot compute CoaddPsf at point %s; no input images at that point.")
261 double weightSum = 0.0;
266 std::vector<PTR(afw::image::Image<double>)> imgVector;
267 std::vector<double> weightVector;
269 for (
auto const & exposureRecord : subcat) {
270 PTR(afw::geom::XYTransform) xytransform(
271 new afw::image::XYTransformFromWcsPair(_coaddWcs, exposureRecord.getWcs())
273 WarpedPsf warpedPsf =
WarpedPsf(exposureRecord.getPsf(), xytransform, _warpingControl);
275 imgVector.push_back(componentImg);
276 weightSum += exposureRecord.get(_weightKey);
277 weightVector.push_back(exposureRecord.get(_weightKey));
283 PTR(afw::detection::Psf::Image) image = std::make_shared<afw::detection::Psf::Image>(bbox);
291 return _catalog.size();
294 CONST_PTR(afw::detection::Psf)
CoaddPsf::getPsf(
int index) {
295 if (index < 0 || index > getComponentCount()) {
296 throw LSST_EXCEPT(pex::exceptions::RangeError,
"index of CoaddPsf component out of range");
298 return _catalog[index].getPsf();
301 CONST_PTR(afw::image::Wcs)
CoaddPsf::getWcs(
int index) {
302 if (index < 0 || index > getComponentCount()) {
303 throw LSST_EXCEPT(pex::exceptions::RangeError,
"index of CoaddPsf component out of range");
305 return _catalog[index].getWcs();
308 CONST_PTR(afw::geom::polygon::Polygon)
CoaddPsf::getValidPolygon(
int index) {
309 if (index < 0 || index > getComponentCount()) {
310 throw LSST_EXCEPT(pex::exceptions::RangeError,
"index of CoaddPsf component out of range");
312 return _catalog[index].getValidPolygon();
317 throw LSST_EXCEPT(pex::exceptions::RangeError,
"index of CoaddPsf component out of range");
319 return _catalog[index].get(_weightKey);
324 throw LSST_EXCEPT(pex::exceptions::RangeError,
"index of CoaddPsf component out of range");
326 return _catalog[index].getId();
331 throw LSST_EXCEPT(pex::exceptions::RangeError,
"index of CoaddPsf component out of range");
333 return _catalog[index].getBBox();
344 namespace tbl = afw::table;
347 class CoaddPsfPersistenceHelper {
355 static CoaddPsfPersistenceHelper
const &
get() {
356 static CoaddPsfPersistenceHelper
const instance;
361 CoaddPsfPersistenceHelper() :
363 coaddWcs(
schema.addField<int>(
"coaddwcs",
"archive ID of the coadd's WCS")),
364 cacheSize(
schema.addField<int>(
"cachesize",
"size of the warping cache")),
366 schema,
"avgpos",
"PSF accessors default position",
"pixel"
370 schema.getCitizen().markPersistent();
379 virtual PTR(tbl::io::Persistable)
380 read(InputArchive const & archive, CatalogVector const & catalogs)
const {
381 if (catalogs.size() == 1u) {
385 return readV0(archive, catalogs);
387 LSST_ARCHIVE_ASSERT(catalogs.size() == 2u);
388 CoaddPsfPersistenceHelper
const & keys1 = CoaddPsfPersistenceHelper::get();
389 LSST_ARCHIVE_ASSERT(catalogs.front().getSchema() == keys1.schema);
390 tbl::BaseRecord
const & record1 = catalogs.front().front();
393 tbl::ExposureCatalog::readFromArchive(archive, catalogs.back()),
394 archive.get<afw::image::Wcs>(record1.get(keys1.coaddWcs)),
395 record1.get(keys1.averagePosition),
396 record1.get(keys1.warpingKernelName),
397 record1.get(keys1.cacheSize)
408 std::shared_ptr<tbl::io::Persistable>
409 readV0(InputArchive
const & archive, CatalogVector
const & catalogs)
const {
410 auto internalCat = tbl::ExposureCatalog::readFromArchive(archive, catalogs.front());
412 auto coaddWcs = internalCat.back().getWcs();
413 internalCat.pop_back();
417 tbl::Key<double> weightKey;
419 weightKey = internalCat.getSchema()[
"weight"];
420 }
catch (pex::exceptions::NotFoundError &) {}
421 auto averagePos = computeAveragePosition(internalCat, *coaddWcs, weightKey);
422 return std::shared_ptr<CoaddPsf>(
new CoaddPsf(internalCat, coaddWcs, averagePos));
425 Factory(std::string
const & name) : tbl::io::PersistableFactory(name) {}
431 std::string getCoaddPsfPersistenceName() {
return "CoaddPsf"; }
433 CoaddPsf::Factory registration(getCoaddPsfPersistenceName());
442 CoaddPsfPersistenceHelper
const & keys1 = CoaddPsfPersistenceHelper::get();
443 tbl::BaseCatalog cat1 = handle.makeCatalog(keys1.schema);
444 PTR(tbl::BaseRecord) record1 = cat1.addNew();
445 record1->set(keys1.coaddWcs, handle.put(_coaddWcs));
446 record1->set(keys1.cacheSize, _warpingControl->getCacheSize());
447 record1->set(keys1.averagePosition, _averagePosition);
448 record1->set(keys1.warpingKernelName, _warpingKernelName);
449 handle.saveCatalog(cat1);
450 _catalog.writeToArchive(handle,
false);
454 afw::table::ExposureCatalog
const & catalog,
455 PTR(afw::image::Wcs
const) coaddWcs,
460 _catalog(catalog), _coaddWcs(coaddWcs), _weightKey(_catalog.getSchema()[
"weight"]),
461 _averagePosition(averagePosition), _warpingKernelName(warpingKernelName),
462 _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.
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.
tbl::Key< int > cacheSize
afw::table::RecordId getId(int index)
Get the exposure ID of the component image at index.
Factory(std::string const &name)
virtual std::string getPersistenceName() const
CoaddPsf is the Psf derived to be used for non-PSF-matched Coadd images.
int getComponentCount() const
Return the number of component Psfs in this CoaddPsf.
std::shared_ptr< tbl::io::Persistable > readV0(InputArchive const &archive, CatalogVector const &catalogs) const
virtual boost::shared_ptr< tbl::io::Persistable > read(InputArchive const &archive, CatalogVector const &catalogs) const
tbl::Key< std::string > warpingKernelName
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
virtual afw::geom::Box2I doComputeBBox(afw::geom::Point2D const &position, afw::image::Color const &color) const
virtual void write(OutputArchiveHandle &handle) const
tbl::PointKey< double > averagePosition
virtual std::string getPythonModule() const
A Psf class that maps an arbitrary Psf through a coordinate transformation.