22 :
23 */
24
35
38namespace table {
39namespace io {
40
43
44}
45}
46}
47
48namespace meas {
49namespace algorithms {
50
51namespace {
52
53
54
56 int yPad) {
57 int nx = im.getWidth();
58 int ny = im.getHeight();
59
60 auto out = std::make_shared<afw::detection::Psf::Image>(nx + 2 * xPad, ny + 2 * yPad);
61 out->setXY0(im.getX0() - xPad, im.getY0() - yPad);
62
65
66 return out;
67}
68
70
71
72
73
74 static const double maxTransformCoeff = 200.0;
75
77 throw LSST_EXCEPT(pex::exceptions::RangeError,
"Unexpectedly large transform passed to WarpedPsf");
78 }
79
80
82
83
85 auto const in_corners = in_box_fp.
getCorners();
86 for (auto const & in_corner : in_corners) {
87 auto out_corner = t(in_corner);
89 }
90
91
92
93
99}
100
116 afw::math::WarpingControl const &wc) {
119
120 afw::math::SeparableKernel const &kernel = *wc.getWarpingKernel();
122 int const xPad =
std::max(center.getX(), kernel.getWidth() - center.getX());
123 int const yPad =
std::max(center.getY(), kernel.getHeight() - center.getY());
124
125
127
128 auto ret = std::make_shared<afw::detection::Psf::Image>(
bbox);
129
130
132
133
135 return ret;
136}
137
138
139
140
141
142
143
144
145struct PreparedTransforms {
146
147 static PreparedTransforms compute(
148 afw::detection::Psf const & src_psf,
151 ) {
152
153
155 *distortion.inverted(),
156 dest_position
157 );
158
159
160
161
162
163
164
165
169 );
171
172
173
178 }
179
182};
183
184}
185
189 : ImagePsf(false),
190 _undistortedPsf(undistortedPsf),
191 _distortion(distortion),
192 _warpingControl(control) {
193 _init();
194}
195
199 : ImagePsf(false),
200 _undistortedPsf(undistortedPsf),
201 _distortion(distortion),
202 _warpingControl(new
afw::math::WarpingControl(kernelName,
"", cache)) {
203 _init();
204}
205
206void WarpedPsf::_init() {
207 if (!_undistortedPsf) {
209 "Undistorted Psf passed to WarpedPsf must not be None/NULL");
210 }
211 if (!_distortion) {
212 throw LSST_EXCEPT(pex::exceptions::LogicError,
"Transform passed to WarpedPsf must not be None/NULL");
213 }
214 if (!_warpingControl) {
216 "WarpingControl passed to WarpedPsf must not be None/NULL");
217 }
218}
219
221 return _distortion->applyForward(_undistortedPsf->getAveragePosition());
222}
223
225 return std::make_shared<WarpedPsf>(_undistortedPsf->clone(), _distortion, _warpingControl);
226}
227
229
230
231
232 throw LSST_EXCEPT(pex::exceptions::LogicError,
"Not Implemented");
233}
234
236 geom::Point2D const &position, afw::image::Color
const &color)
const {
237 auto prepared_transforms = PreparedTransforms::compute(*_undistortedPsf, *_distortion, position);
240 *src_im,
241 prepared_transforms.src_to_dest_unrounded,
242 *_warpingControl
243 );
244
245 double normFactor = 1.0;
246
247
248
249
250 normFactor = 0.0;
251 for (
int y = 0;
y != ret->getHeight(); ++
y) {
252 afw::detection::Psf::Image::x_iterator imEnd = ret->row_end(
y);
253 for (afw::detection::Psf::Image::x_iterator imPtr = ret->row_begin(
y); imPtr != imEnd; imPtr++) {
254 normFactor += *imPtr;
255 }
256 }
257 if (normFactor == 0.0) {
258 throw LSST_EXCEPT(pex::exceptions::InvalidParameterError,
"psf image has sum 0");
259 }
260 *ret /= normFactor;
261 return ret;
262}
263
265 auto prepared_transforms = PreparedTransforms::compute(*_undistortedPsf, *_distortion, position);
266 auto src_bbox = _undistortedPsf->computeImageBBox(prepared_transforms.src_position, color);
267 return computeBBoxFromTransform(src_bbox, prepared_transforms.src_to_dest_unrounded);
268}
269
270namespace {
271
272struct PersistenceHelper {
273 afw::table::Schema
schema;
277
278 static PersistenceHelper const &get() {
279 static PersistenceHelper const instance;
280 return instance;
281 }
282
283private:
284 PersistenceHelper()
285 : schema(),
286 psfIndex(schema.addField<
int>(
"psfIndex",
"archive ID of nested Psf object")),
287 transformIndex(schema.addField<
int>(
"transformIndex",
"archive ID of nested Transform object")),
289 schema.addField<
int>(
"controlIndex",
"archive ID of nested WarpingControl object")) {}
290};
291
292std::string _getPersistenceName() {
return "WarpedPsf"; }
293
294class : public afw::table::io::PersistableFactory {
295public:
297 afw::table::io::InputArchive const &archive,
298 afw::table::io::CatalogVector const &catalogs) const {
299 static PersistenceHelper
const &
keys = PersistenceHelper::get();
302 afw::table::BaseRecord const &record = catalogs.front().front();
304 return std::make_shared<WarpedPsf>(
305 archive.get<afw::detection::Psf>(record.get(
keys.psfIndex)),
306 archive.get<afw::geom::TransformPoint2ToPoint2>(record.get(
keys.transformIndex)),
307 archive.get<afw::math::WarpingControl>(record.get(
keys.controlIndex)));
308 }
309
310 using afw::table::io::PersistableFactory::PersistableFactory;
311} warpedPsfFactory(_getPersistenceName());
312
313}
314
315std::string WarpedPsf::getPersistenceName()
const {
return _getPersistenceName(); }
316std::string WarpedPsf::getPythonModule()
const {
return "lsst.meas.algorithms"; }
317
318void WarpedPsf::write(OutputArchiveHandle &handle) const {
319 static PersistenceHelper
const &
keys = PersistenceHelper::get();
320 afw::table::BaseCatalog catalog = handle.makeCatalog(
keys.schema);
322
323 record->set(
keys.psfIndex, handle.put(_undistortedPsf));
324 record->set(
keys.transformIndex, handle.put(_distortion));
325 record->set(
keys.controlIndex, handle.put(_warpingControl));
326
327 handle.saveCatalog(catalog);
328}
329
330}
331}
332}
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
image::Image< Pixel > Image
Image type returned by computeImage.
static std::shared_ptr< T > dynamicCast(std::shared_ptr< Persistable > const &ptr)
Dynamically cast a shared_ptr.
A floating-point coordinate rectangle geometry.
Extent2D const getDimensions() const noexcept
1-d interval accessors
void include(Point2D const &point) noexcept
Expand this to ensure that this->contains(point).
std::vector< Point2D > getCorners() const
Get the corner points.
An integer coordinate rectangle.
void include(Point2I const &point)
Expand this to ensure that this->contains(point).
Box2I dilatedBy(Extent const &buffer) const
Increase the size of the box by the given amount(s) on all sides (returning a new object).
std::shared_ptr< TransformPoint2ToPoint2 > makeTransform(lsst::geom::AffineTransform const &affine)
Wrap an lsst::geom::AffineTransform as a Transform.
Transform< Point2Endpoint, Point2Endpoint > TransformPoint2ToPoint2
lsst::geom::AffineTransform linearizeTransform(TransformPoint2ToPoint2 const &original, lsst::geom::Point2D const &inPoint)
Approximate a Transform by its local linearization.
int warpImage(DestImageT &destImage, geom::SkyWcs const &destWcs, SrcImageT const &srcImage, geom::SkyWcs const &srcWcs, WarpingControl const &control, typename DestImageT::SinglePixel padValue=lsst::afw::math::edgePixel< DestImageT >(typename lsst::afw::image::detail::image_traits< DestImageT >::image_category()))
Warp an Image or MaskedImage to a new Wcs.
Extent< int, N > floor(Extent< double, N > const &input) noexcept
Return the component-wise floor (round towards more negative).
afw::table::Key< int > controlIndex
afw::table::Key< int > transformIndex
afw::table::Key< int > psfIndex
afw::table::Schema schema
virtual std::shared_ptr< afw::table::io::Persistable > read(afw::table::io::InputArchive const &archive, afw::table::io::CatalogVector const &catalogs) const
geom::Point2D src_position
geom::AffineTransform src_to_dest_unrounded