LSST Applications 30.0.7,g0e76e35be5+e8e946ae08,g19811a7679+138f7293ba,g199a45376c+5e234f8357,g1fd858c14a+2f48dbc4c4,g262e1987ae+fb36cac54d,g29ae962dfc+d9108a0941,g2c21b0017a+4f59a27f16,g31e44d4a5c+b0138be388,g33ac35c1f1+28b9f72785,g35bb328faa+b0138be388,g40c9b15c53+823ad735c1,g47891489e3+bcc48a0b46,g53246c7159+b0138be388,g64539dfbff+e8e946ae08,g67b6fd64d1+bcc48a0b46,g74acd417e5+422380537a,g76965917b2+a5ca99c4d9,g786e29fd12+796b79145d,g7aefaa3e3d+dc0c200193,g86b635cae8+734fe384f0,g87389fa792+d8b5378923,g89139ef638+bcc48a0b46,g8bbb235e95+3f4f7f9447,g8ea07a8fe4+78a4c88802,g9290983e33+ffdc83c6f7,g92c671f44c+e8e946ae08,gaa753fd333+03f406da14,gbf99507273+b0138be388,gc49b57b85e+8df26ee1f0,gca7fc764a6+bcc48a0b46,gd7ef33dd92+bcc48a0b46,gdab6d2f7ff+422380537a,ge1c02a5578+b0138be388,ge410e46f29+bcc48a0b46,ge80df9fc40+e6db5413d1,geaed405ab2+1de65a85c6,gf5dcc679e7+35a0ce2edd,gf5f1c85443+e8e946ae08
LSST Data Management Base Package
Loading...
Searching...
No Matches
warpExposure.cc
Go to the documentation of this file.
1// -*- LSST-C++ -*- // fixed format comment for emacs
2
3/*
4 * LSST Data Management System
5 * Copyright 2008, 2009, 2010 LSST Corporation.
6 *
7 * This product includes software developed by the
8 * LSST Project (http://www.lsst.org/).
9 *
10 * This program is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the LSST License Statement and
21 * the GNU General Public License along with this program. If not,
22 * see <http://www.lsstcorp.org/LegalNotices/>.
23 */
24/*
25 * Support for warping an %image to a new Wcs.
26 */
27
28#include <cassert>
29#include <cmath>
30#include <limits>
31#include <memory>
32#include <sstream>
33#include <string>
34#include <vector>
35#include <utility>
36#include <regex>
37
38#include "astshim.h"
39
40#include "lsst/log/Log.h"
41#include "lsst/pex/exceptions.h"
42#include "lsst/geom.h"
44#include "lsst/afw/geom.h"
51#include "lsst/afw/table/io/Persistable.cc" // Needed for PersistableFacade::dynamicCast
52
53namespace pexExcept = lsst::pex::exceptions;
54
55using std::swap;
56
57namespace lsst {
58namespace afw {
59namespace math {
60
61//
62// A helper function for the warping kernels which provides error-checking:
63// the warping kernels are designed to work in two cases
64// 0 < x < 1 and ctrX=(size-1)/2
65// -1 < x < 0 and ctrX=(size+1)/2
66// (and analogously for y). Note that to get the second case, Kernel::setCtr(1, y) must be
67// called before calling Kernel::setKernelParameter(). [see afw::math::offsetImage() for
68// an example]
69//
70// FIXME eventually the 3 warping kernels will inherit from a common base class WarpingKernel
71// and this routine can be eliminated by putting the code in WarpingKernel::setKernelParameter()
72//
73static inline void checkWarpingKernelParameter(const SeparableKernel *p, unsigned int ind, double value) {
74 if (ind > 1) {
75 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
76 "bad ind argument in WarpingKernel::setKernelParameter()");
77 }
78 int ctr = p->getCtr()[ind];
79 int size = p->getDimensions()[ind];
80
81 if (ctr == (size - 1) / 2) {
82 if (value < -1e-6 || value > 1 + 1e-6) {
83 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
84 "bad coordinate in WarpingKernel::setKernelParameter()");
85 }
86 } else if (ctr == (size + 1) / 2) {
87 if (value < -1 - 1e-6 || value > 1e-6) {
88 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
89 "bad coordinate in WarpingKernel::setKernelParameter()");
90 }
91 } else {
92 throw LSST_EXCEPT(pexExcept::InvalidParameterError,
93 "bad ctr value in WarpingKernel::setKernelParameter()");
94 }
95}
96
100
101int LanczosWarpingKernel::getOrder() const { return this->getWidth() / 2; }
102
103void LanczosWarpingKernel::setKernelParameter(unsigned int ind, double value) const {
104 checkWarpingKernelParameter(this, ind, value);
106}
107
111
113 //
114 // this->_params[0] = value of x where we want to interpolate the function
115 // x = integer value of x where we evaluate the function in the interpolation
116 //
117 // The following weird-looking expression has no if/else statements, is roundoff-tolerant,
118 // and works in the following two cases:
119 // 0 < this->_params[0] < 1, x \in {0,1}
120 // -1 < this->_params[0] < 0, x \in {-1,0}
121 //
122 // The checks in BilinearWarpingKernel::setKernelParameter() ensure that one of these
123 // conditions is satisfied
124 //
125 return 0.5 + (1.0 - (2.0 * fabs(this->_params[0]))) * (0.5 - fabs(x));
126}
127
128void BilinearWarpingKernel::setKernelParameter(unsigned int ind, double value) const {
129 checkWarpingKernelParameter(this, ind, value);
131}
132
135 os << "_BilinearFunction1: ";
136 os << Function1<Kernel::Pixel>::toString(prefix);
137 return os.str();
138}
139
143
145 // this expression is faster than using conditionals, but offers no sanity checking
146 return static_cast<double>((fabs(this->_params[0]) < 0.5) == (fabs(x) < 0.5));
147}
148
149void NearestWarpingKernel::setKernelParameter(unsigned int ind, double value) const {
150 checkWarpingKernelParameter(this, ind, value);
152}
153
156 os << "_NearestFunction1: ";
157 os << Function1<Kernel::Pixel>::toString(prefix);
158 return os.str();
159}
160
161namespace {
162
163struct LanczosKernelPersistenceHelper {
164 table::Schema schema;
165 table::Key<int> order;
166
167 static LanczosKernelPersistenceHelper const &get() {
168 static LanczosKernelPersistenceHelper const instance;
169 return instance;
170 }
171
172 LanczosKernelPersistenceHelper(LanczosKernelPersistenceHelper const &) = delete;
173 LanczosKernelPersistenceHelper(LanczosKernelPersistenceHelper &&) = delete;
174 LanczosKernelPersistenceHelper &operator=(LanczosKernelPersistenceHelper const &) = delete;
175 LanczosKernelPersistenceHelper &operator=(LanczosKernelPersistenceHelper &&) = delete;
176
177private:
178 LanczosKernelPersistenceHelper()
179 : schema(), order(schema.addField<int>("order", "order of Lanczos function")) {}
180};
181
182class : public table::io::PersistableFactory {
183 std::shared_ptr<table::io::Persistable> read(table::io::InputArchive const &archive,
184 table::io::CatalogVector const &catalogs) const override {
185 auto const &keys = LanczosKernelPersistenceHelper::get();
186 LSST_ARCHIVE_ASSERT(catalogs.size() == 1u);
187 LSST_ARCHIVE_ASSERT(catalogs.front().size() == 1u);
188 afw::table::BaseRecord const &record = catalogs.front().front();
189 LSST_ARCHIVE_ASSERT(record.getSchema() == keys.schema);
190 return std::make_shared<LanczosWarpingKernel>(record.get(keys.order));
191 }
192
194} lanczosFactory("LanczosWarpingKernel");
195
196template <class T>
197class DefaultPersistableFactory : public table::io::PersistableFactory {
198 std::shared_ptr<table::io::Persistable> read(table::io::InputArchive const &archive,
199 table::io::CatalogVector const &catalogs) const override {
201 return std::make_shared<T>();
202 }
203
204 using table::io::PersistableFactory::PersistableFactory;
205};
206
207DefaultPersistableFactory<BilinearWarpingKernel> bilinearFactory("BilinearWarpingKernel");
208DefaultPersistableFactory<NearestWarpingKernel> nearestFactory("NearestWarpingKernel");
209
210} // namespace
211
213 auto const &keys = LanczosKernelPersistenceHelper::get();
214 table::BaseCatalog catalog = handle.makeCatalog(keys.schema);
215 std::shared_ptr<table::BaseRecord> record = catalog.addNew();
216 record->set(keys.order, getOrder());
217 handle.saveCatalog(catalog);
218}
219
221
223
225 using KernelPtr = std::shared_ptr<SeparableKernel>;
226 std::smatch matches;
227 static const std::regex LanczosRE("lanczos(\\d+)");
228 if (name == "bilinear") {
229 return KernelPtr(new BilinearWarpingKernel());
230 } else if (std::regex_match(name, matches, LanczosRE)) {
231 std::string orderStr(matches[1].first, matches[1].second);
232 int order = std::stoi(orderStr);
233 return KernelPtr(new LanczosWarpingKernel(order));
234 } else if (name == "nearest") {
235 return KernelPtr(new NearestWarpingKernel());
236 } else {
237 throw LSST_EXCEPT(pexExcept::InvalidParameterError, "unknown warping kernel name: \"" + name + "\"");
238 }
239}
240
242 if (_warpingKernelPtr->getCacheSize() != _cacheSize) {
243 _warpingKernelPtr->computeCache(_cacheSize);
244 }
245 return _warpingKernelPtr;
246};
247
248void WarpingControl::setWarpingKernelName(std::string const &warpingKernelName) {
249 std::shared_ptr<SeparableKernel> warpingKernelPtr(makeWarpingKernel(warpingKernelName));
250 setWarpingKernel(*warpingKernelPtr);
251}
252
254 if (_maskWarpingKernelPtr) {
255 _testWarpingKernels(warpingKernel, *_maskWarpingKernelPtr);
256 }
257 std::shared_ptr<SeparableKernel> warpingKernelPtr(
259 _warpingKernelPtr = warpingKernelPtr;
260}
261
263 if (_maskWarpingKernelPtr) { // lazily update kernel cache
264 if (_maskWarpingKernelPtr->getCacheSize() != _cacheSize) {
265 _maskWarpingKernelPtr->computeCache(_cacheSize);
266 }
267 }
268 return _maskWarpingKernelPtr;
269}
270
271void WarpingControl::setMaskWarpingKernelName(std::string const &maskWarpingKernelName) {
272 if (!maskWarpingKernelName.empty()) {
273 std::shared_ptr<SeparableKernel> maskWarpingKernelPtr(makeWarpingKernel(maskWarpingKernelName));
274 setMaskWarpingKernel(*maskWarpingKernelPtr);
275 } else {
276 _maskWarpingKernelPtr.reset();
277 }
278}
279
281 _testWarpingKernels(*_warpingKernelPtr, maskWarpingKernel);
282 _maskWarpingKernelPtr = std::static_pointer_cast<SeparableKernel>(maskWarpingKernel.clone());
283}
284
285void WarpingControl::_testWarpingKernels(SeparableKernel const &warpingKernel,
286 SeparableKernel const &maskWarpingKernel) const {
287 lsst::geom::Box2I kernelBBox =
289 warpingKernel.getDimensions());
290 lsst::geom::Box2I maskKernelBBox =
292 maskWarpingKernel.getDimensions());
293 if (!kernelBBox.contains(maskKernelBBox)) {
295 "warping kernel is smaller than mask warping kernel");
296 }
297}
298
299namespace {
300
301struct WarpingControlPersistenceHelper {
302 table::Schema schema;
303 table::Key<int> warpingKernelIndex;
304 table::Key<table::Flag> hasMaskKernel;
305 table::Key<int> maskKernelIndex; // undefined if !hasMaskKernel
306 table::Key<int> cacheSize;
307 table::Key<int> interpLength;
308 table::Key<image::MaskPixel> growFullMask;
309
310 static WarpingControlPersistenceHelper const &get() {
311 static WarpingControlPersistenceHelper const instance;
312 return instance;
313 }
314
315 WarpingControlPersistenceHelper(WarpingControlPersistenceHelper const &) = delete;
316 WarpingControlPersistenceHelper(WarpingControlPersistenceHelper &&) = delete;
317 WarpingControlPersistenceHelper &operator=(WarpingControlPersistenceHelper const &) = delete;
318 WarpingControlPersistenceHelper &operator=(WarpingControlPersistenceHelper &&) = delete;
319
320private:
321 WarpingControlPersistenceHelper()
322 : schema(),
323 warpingKernelIndex(
324 schema.addField<int>("warpingKernelIndex", "archive ID of nested warping kernel")),
325 hasMaskKernel(schema.addField<table::Flag>("hasMaskKernel", "whether a mask kernel is stored")),
326 maskKernelIndex(schema.addField<int>("maskKernelIndex",
327 "archive ID of nested mask kernel. "
328 "Valid only if hasMaskKernel")),
329 cacheSize(schema.addField<int>("cacheSize", "Cache size for warping kernel(s)")),
330 interpLength(schema.addField<int>("interpLength",
331 "Distance over which WCS can be linearly interpolated")),
332 growFullMask(schema.addField<image::MaskPixel>(
333 "growFullMask", "bits to grow to full width of image/variance kernel")) {}
334};
335
336std::string _getWarpingControlPersistenceName() { return "WarpingControl"; }
337
338class : public table::io::PersistableFactory {
339 std::shared_ptr<table::io::Persistable> read(table::io::InputArchive const &archive,
340 table::io::CatalogVector const &catalogs) const override {
341 auto const &keys = WarpingControlPersistenceHelper::get();
342 LSST_ARCHIVE_ASSERT(catalogs.size() == 1u);
343 LSST_ARCHIVE_ASSERT(catalogs.front().size() == 1u);
344 afw::table::BaseRecord const &record = catalogs.front().front();
345 LSST_ARCHIVE_ASSERT(record.getSchema() == keys.schema);
346
347 // Warping kernels are dummy values, set true kernels later
348 auto control = std::make_shared<WarpingControl>("bilinear", "", record.get(keys.cacheSize),
349 record.get(keys.interpLength),
350 record.get(keys.growFullMask));
351 // archive.get returns a shared_ptr, so this code depends on the
352 // undocumented fact that setWarpingKernel and setMaskWarpingKernel
353 // make defensive copies.
354 control->setWarpingKernel(*archive.get<SeparableKernel>(record.get(keys.warpingKernelIndex)));
355 if (record.get(keys.hasMaskKernel)) {
356 control->setMaskWarpingKernel(*archive.get<SeparableKernel>(record.get(keys.maskKernelIndex)));
357 }
358 return control;
359 }
360
362} controlFactory(_getWarpingControlPersistenceName());
363
364} // namespace
365
366std::string WarpingControl::getPersistenceName() const { return _getWarpingControlPersistenceName(); }
367
368std::string WarpingControl::getPythonModule() const { return "lsst.afw.math"; }
369
370bool WarpingControl::isPersistable() const noexcept {
371 return _warpingKernelPtr->isPersistable() &&
372 (!hasMaskWarpingKernel() || _maskWarpingKernelPtr->isPersistable());
373}
374
376 auto const &keys = WarpingControlPersistenceHelper::get();
377 table::BaseCatalog catalog = handle.makeCatalog(keys.schema);
378 std::shared_ptr<table::BaseRecord> record = catalog.addNew();
379
380 record->set(keys.warpingKernelIndex, handle.put(_warpingKernelPtr));
381 record->set(keys.hasMaskKernel, hasMaskWarpingKernel());
382 if (hasMaskWarpingKernel()) {
383 record->set(keys.maskKernelIndex, handle.put(_maskWarpingKernelPtr));
384 }
385 record->set(keys.cacheSize, _cacheSize);
386 record->set(keys.interpLength, _interpLength);
387 record->set(keys.growFullMask, _growFullMask);
388
389 handle.saveCatalog(catalog);
390}
391
392template <typename DestExposureT, typename SrcExposureT>
393int warpExposure(DestExposureT &destExposure, SrcExposureT const &srcExposure, WarpingControl const &control,
394 typename DestExposureT::MaskedImageT::SinglePixel padValue) {
395 if (!destExposure.hasWcs()) {
396 throw LSST_EXCEPT(pexExcept::InvalidParameterError, "destExposure has no Wcs");
397 }
398 if (!srcExposure.hasWcs()) {
399 throw LSST_EXCEPT(pexExcept::InvalidParameterError, "srcExposure has no Wcs");
400 }
401 typename DestExposureT::MaskedImageT mi = destExposure.getMaskedImage();
402 if (srcExposure.getInfo()->hasId()) {
403 destExposure.getInfo()->setId(srcExposure.getInfo()->getId());
404 }
405 destExposure.setPhotoCalib(srcExposure.getPhotoCalib());
406 destExposure.setFilter(srcExposure.getFilter());
407 destExposure.getInfo()->setVisitInfo(srcExposure.getInfo()->getVisitInfo());
408 return warpImage(mi, *destExposure.getWcs(), srcExposure.getMaskedImage(), *srcExposure.getWcs(), control,
409 padValue);
410}
411
412namespace {
413
414inline lsst::geom::Point2D computeSrcPos(
415 int destCol,
416 int destRow,
417 lsst::geom::Point2D const &destXY0,
418 geom::SkyWcs const &destWcs,
419 geom::SkyWcs const &srcWcs)
420{
421 lsst::geom::Point2D const destPix(image::indexToPosition(destCol + destXY0[0]),
422 image::indexToPosition(destRow + destXY0[1]));
423 return srcWcs.skyToPixel(destWcs.pixelToSky(destPix));
424}
425
426inline double computeRelativeArea(
427 lsst::geom::Point2D const &srcPos,
429 &leftSrcPos,
430 lsst::geom::Point2D const &upSrcPos)
431{
432 lsst::geom::Extent2D dSrcA = srcPos - leftSrcPos;
433 lsst::geom::Extent2D dSrcB = srcPos - upSrcPos;
434
435 return std::abs(dSrcA.getX() * dSrcB.getY() - dSrcA.getY() * dSrcB.getX());
436}
437
438} // namespace
439
440template <typename DestImageT, typename SrcImageT>
441int warpImage(DestImageT &destImage, geom::SkyWcs const &destWcs, SrcImageT const &srcImage,
442 geom::SkyWcs const &srcWcs, WarpingControl const &control,
443 typename DestImageT::SinglePixel padValue) {
444 auto srcToDest = geom::makeWcsPairTransform(srcWcs, destWcs);
445 return warpImage(destImage, srcImage, *srcToDest, control, padValue);
446}
447
448template <typename DestImageT, typename SrcImageT>
449int warpImage(DestImageT &destImage, SrcImageT const &srcImage,
450 geom::TransformPoint2ToPoint2 const &srcToDest, WarpingControl const &control,
451 typename DestImageT::SinglePixel padValue) {
452 if (imagesOverlap(destImage, srcImage)) {
453 throw LSST_EXCEPT(pexExcept::InvalidParameterError, "destImage overlaps srcImage; cannot warp");
454 }
455 if (destImage.getBBox(image::LOCAL).isEmpty()) {
456 return 0;
457 }
458 // if src image is too small then don't try to warp
459 std::shared_ptr<SeparableKernel> warpingKernelPtr = control.getWarpingKernel();
460 try {
461 warpingKernelPtr->shrinkBBox(srcImage.getBBox(image::LOCAL));
463 for (int y = 0, height = destImage.getHeight(); y < height; ++y) {
464 for (typename DestImageT::x_iterator destPtr = destImage.row_begin(y), end = destImage.row_end(y);
465 destPtr != end; ++destPtr) {
466 *destPtr = padValue;
467 }
468 }
469 return 0;
470 }
471 int interpLength = control.getInterpLength();
472
473 std::shared_ptr<LanczosWarpingKernel const> const lanczosKernelPtr =
475
476 int numGoodPixels = 0;
477
478 // compute a transform from local destination pixels to parent source pixels
479 auto const parentDestToParentSrc = srcToDest.inverted();
480 std::vector<double> const localDestToParentDestVec = {static_cast<double>(destImage.getX0()),
481 static_cast<double>(destImage.getY0())};
482 auto const localDestToParentDest = geom::TransformPoint2ToPoint2(ast::ShiftMap(localDestToParentDestVec));
483 auto const localDestToParentSrc = localDestToParentDest.then(*parentDestToParentSrc);
484
485 // Get the source MaskedImage and a pixel accessor to it.
486 int const srcWidth = srcImage.getWidth();
487 int const srcHeight = srcImage.getHeight();
488 LOGL_DEBUG("TRACE2.lsst.afw.math.warp", "source image width=%d; height=%d", srcWidth, srcHeight);
489
490 int const destWidth = destImage.getWidth();
491 int const destHeight = destImage.getHeight();
492 LOGL_DEBUG("TRACE2.lsst.afw.math.warp", "remap image width=%d; height=%d", destWidth, destHeight);
493
494 // Set each pixel of destExposure's MaskedImage
495 LOGL_DEBUG("TRACE3.lsst.afw.math.warp", "Remapping masked image");
496
497 int const maxCol = destWidth - 1;
498 int const maxRow = destHeight - 1;
499
500 detail::WarpAtOnePoint<DestImageT, SrcImageT> warpAtOnePoint(srcImage, control, padValue);
501
502 if (interpLength > 0) {
503 // Use interpolation. Note that 1 produces the same result as no interpolation
504 // but uses this code branch, thus providing an easy way to compare the two branches.
505
506 // Estimate for number of horizontal interpolation band edges, to reserve memory in vectors
507 int const numColEdges = 2 + ((destWidth - 1) / interpLength);
508
509 // A list of edge column indices for interpolation bands;
510 // starts at -1, increments by interpLen (except the final interval), and ends at destWidth-1
511 std::vector<int> edgeColList;
512 edgeColList.reserve(numColEdges);
513
514 // A list of 1/column width for horizontal interpolation bands; the first value is garbage.
515 // The inverse is used for speed because the values are always multiplied.
516 std::vector<double> invWidthList;
517 invWidthList.reserve(numColEdges);
518
519 // Compute edgeColList and invWidthList
520 edgeColList.push_back(-1);
521 invWidthList.push_back(0.0);
522 for (int prevEndCol = -1; prevEndCol < maxCol; prevEndCol += interpLength) {
523 int endCol = prevEndCol + interpLength;
524 if (endCol > maxCol) {
525 endCol = maxCol;
526 }
527 edgeColList.push_back(endCol);
528 assert(endCol - prevEndCol > 0);
529 invWidthList.push_back(1.0 / static_cast<double>(endCol - prevEndCol));
530 }
531 assert(edgeColList.back() == maxCol);
532
533 // A list of delta source positions along the edge columns of the horizontal interpolation bands
534 std::vector<lsst::geom::Extent2D> yDeltaSrcPosList(edgeColList.size());
535
536 // A cache of pixel positions on the source corresponding to the previous or current row
537 // of the destination image.
538 // The first value is for column -1 because the previous source position is used to compute relative
539 // area To simplify the indexing, use an iterator that starts at begin+1, thus: srcPosView =
540 // srcPosList.begin() + 1 srcPosView[col-1] and lower indices are for this row srcPosView[col] and
541 // higher indices are for the previous row
542 std::vector<lsst::geom::Point2D> srcPosList(1 + destWidth);
543 std::vector<lsst::geom::Point2D>::iterator const srcPosView = srcPosList.begin() + 1;
544
546 endColPosList.reserve(numColEdges);
547
548 // Initialize srcPosList for row -1
549 for (int endCol : edgeColList) {
550 endColPosList.emplace_back(lsst::geom::Point2D(endCol, -1));
551 }
552 auto rightSrcPosList = localDestToParentSrc->applyForward(endColPosList);
553 srcPosView[-1] = rightSrcPosList[0];
554 for (int colBand = 1, endBand = edgeColList.size(); colBand < endBand; ++colBand) {
555 int const prevEndCol = edgeColList[colBand - 1];
556 int const endCol = edgeColList[colBand];
557 lsst::geom::Point2D leftSrcPos = srcPosView[prevEndCol];
558
559 lsst::geom::Extent2D xDeltaSrcPos =
560 (rightSrcPosList[colBand] - leftSrcPos) * invWidthList[colBand];
561
562 for (int col = prevEndCol + 1; col <= endCol; ++col) {
563 srcPosView[col] = srcPosView[col - 1] + xDeltaSrcPos;
564 }
565 }
566
567 int endRow = -1;
568 while (endRow < maxRow) {
569 // Next horizontal interpolation band
570
571 int prevEndRow = endRow;
572 endRow = prevEndRow + interpLength;
573 if (endRow > maxRow) {
574 endRow = maxRow;
575 }
576 assert(endRow - prevEndRow > 0);
577 double interpInvHeight = 1.0 / static_cast<double>(endRow - prevEndRow);
578
579 // Set yDeltaSrcPosList for this horizontal interpolation band
581 destRowPosList.reserve(edgeColList.size());
582 for (int endCol : edgeColList) {
583 destRowPosList.emplace_back(lsst::geom::Point2D(endCol, endRow));
584 }
585 auto bottomSrcPosList = localDestToParentSrc->applyForward(destRowPosList);
586 for (int colBand = 0, endBand = edgeColList.size(); colBand < endBand; ++colBand) {
587 int endCol = edgeColList[colBand];
588 yDeltaSrcPosList[colBand] =
589 (bottomSrcPosList[colBand] - srcPosView[endCol]) * interpInvHeight;
590 }
591
592 for (int row = prevEndRow + 1; row <= endRow; ++row) {
593 typename DestImageT::x_iterator destXIter = destImage.row_begin(row);
594 srcPosView[-1] += yDeltaSrcPosList[0];
595 for (int colBand = 1, endBand = edgeColList.size(); colBand < endBand; ++colBand) {
596 // Next vertical interpolation band
597
598 int const prevEndCol = edgeColList[colBand - 1];
599 int const endCol = edgeColList[colBand];
600
601 // Compute xDeltaSrcPos; remember that srcPosView contains
602 // positions for this row in prevEndCol and smaller indices,
603 // and positions for the previous row for larger indices (including endCol)
604 lsst::geom::Point2D leftSrcPos = srcPosView[prevEndCol];
605 lsst::geom::Point2D rightSrcPos = srcPosView[endCol] + yDeltaSrcPosList[colBand];
606 lsst::geom::Extent2D xDeltaSrcPos = (rightSrcPos - leftSrcPos) * invWidthList[colBand];
607
608 for (int col = prevEndCol + 1; col <= endCol; ++col, ++destXIter) {
609 lsst::geom::Point2D leftSrcPos = srcPosView[col - 1];
610 lsst::geom::Point2D srcPos;
611 if (col == endCol) {
612 if (row == endRow) {
613 // Set srcPosView to bottowSrcPosList to reset values to new band of rows in
614 // order to override possible nans:
615 srcPos = bottomSrcPosList[colBand];
616 }
617 else {
618 // Set srcPos to rightSrcPos to reset values to new band of columns in order
619 // to override possible nans:
620 srcPos = rightSrcPos;
621 }
622 }
623 else {
624 srcPos = leftSrcPos + xDeltaSrcPos;
625 }
626 double relativeArea = computeRelativeArea(srcPos, leftSrcPos, srcPosView[col]);
627
628 srcPosView[col] = srcPos;
629
630 if (warpAtOnePoint(
631 destXIter, srcPos, relativeArea,
633 ++numGoodPixels;
634 }
635 } // for col
636 } // for col band
637 } // for row
638 } // while next row band
639
640 } else {
641 // No interpolation
642
643 // prevSrcPosList = source positions from the previous row; these are used to compute pixel area;
644 // to begin, compute sources positions corresponding to destination row = -1
646 destPosList.reserve(1 + destWidth);
647 for (int col = -1; col < destWidth; ++col) {
648 destPosList.emplace_back(lsst::geom::Point2D(col, -1));
649 }
650 auto prevSrcPosList = localDestToParentSrc->applyForward(destPosList);
651
652 for (int row = 0; row < destHeight; ++row) {
653 destPosList.clear();
654 for (int col = -1; col < destWidth; ++col) {
655 destPosList.emplace_back(lsst::geom::Point2D(col, row));
656 }
657 auto srcPosList = localDestToParentSrc->applyForward(destPosList);
658
659 typename DestImageT::x_iterator destXIter = destImage.row_begin(row);
660 for (int col = 0; col < destWidth; ++col, ++destXIter) {
661 // column index = column + 1 because the first entry in srcPosList is for column -1
662 auto srcPos = srcPosList[col + 1];
663 double relativeArea =
664 computeRelativeArea(srcPos, prevSrcPosList[col], prevSrcPosList[col + 1]);
665
666 if (warpAtOnePoint(destXIter, srcPos, relativeArea,
668 ++numGoodPixels;
669 }
670 } // for col
671 // move points from srcPosList to prevSrcPosList (we don't care about what ends up in srcPosList
672 // because it will be reallocated anyway)
673 swap(srcPosList, prevSrcPosList);
674 } // for row
675 } // if interp
676
677 return numGoodPixels;
678}
679
680template <typename DestImageT, typename SrcImageT>
681int warpCenteredImage(DestImageT &destImage, SrcImageT const &srcImage,
682 lsst::geom::LinearTransform const &linearTransform,
683 lsst::geom::Point2D const &centerPosition, WarpingControl const &control,
684 typename DestImageT::SinglePixel padValue) {
685 // force src and dest to be the same size and xy0
686 if ((destImage.getWidth() != srcImage.getWidth()) || (destImage.getHeight() != srcImage.getHeight()) ||
687 (destImage.getXY0() != srcImage.getXY0())) {
688 std::ostringstream errStream;
689 errStream << "src and dest images must have same size and xy0.";
690 throw LSST_EXCEPT(pexExcept::InvalidParameterError, errStream.str());
691 }
692
693 // set the xy0 coords to 0,0 to make life easier
694 SrcImageT srcImageCopy(srcImage, true);
695 srcImageCopy.setXY0(0, 0);
696 destImage.setXY0(0, 0);
697 lsst::geom::Extent2D cLocal =
698 lsst::geom::Extent2D(centerPosition) - lsst::geom::Extent2D(srcImage.getXY0());
699
700 // for the affine transform, the centerPosition will not only get sheared, but also
701 // moved slightly. So we'll include a translation to move it back by an amount
702 // centerPosition - translatedCenterPosition
703 lsst::geom::AffineTransform affTran(linearTransform, cLocal - linearTransform(cLocal));
704 std::shared_ptr<geom::TransformPoint2ToPoint2> affineTransform22 = geom::makeTransform(affTran);
705
706// now warp
707#if 0
708 static float t = 0.0;
709 float t_before = 1.0*clock()/CLOCKS_PER_SEC;
710 int n = warpImage(destImage, srcImageCopy, affTran, control, padValue);
711 float t_after = 1.0*clock()/CLOCKS_PER_SEC;
712 float dt = t_after - t_before;
713 t += dt;
714 std::cout <<srcImage.getWidth()<<"x"<<srcImage.getHeight()<<": "<< dt <<" "<< t <<std::endl;
715#else
716 int n = warpImage(destImage, srcImageCopy, *affineTransform22, control, padValue);
717#endif
718
719 // fix the origin and we're done.
720 destImage.setXY0(srcImage.getXY0());
721
722 return n;
723}
724
725//
726// Explicit instantiations
727//
729// may need to omit default params for EXPOSURE -- original code did that and it worked
730#define EXPOSURE(PIXTYPE) image::Exposure<PIXTYPE, image::MaskPixel, image::VariancePixel>
731#define MASKEDIMAGE(PIXTYPE) image::MaskedImage<PIXTYPE, image::MaskPixel, image::VariancePixel>
732#define IMAGE(PIXTYPE) image::Image<PIXTYPE>
733#define NL /* */
734
735#define INSTANTIATE(DESTIMAGEPIXELT, SRCIMAGEPIXELT) \
736 template int warpCenteredImage( \
737 IMAGE(DESTIMAGEPIXELT) & destImage, IMAGE(SRCIMAGEPIXELT) const &srcImage, \
738 lsst::geom::LinearTransform const &linearTransform, lsst::geom::Point2D const &centerPosition, \
739 WarpingControl const &control, IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
740 NL template int warpCenteredImage( \
741 MASKEDIMAGE(DESTIMAGEPIXELT) & destImage, MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, \
742 lsst::geom::LinearTransform const &linearTransform, lsst::geom::Point2D const &centerPosition, \
743 WarpingControl const &control, MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
744 NL template int warpImage(IMAGE(DESTIMAGEPIXELT) & destImage, IMAGE(SRCIMAGEPIXELT) const &srcImage, \
745 geom::TransformPoint2ToPoint2 const &srcToDest, WarpingControl const &control, \
746 IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
747 NL template int warpImage(MASKEDIMAGE(DESTIMAGEPIXELT) & destImage, \
748 MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, \
749 geom::TransformPoint2ToPoint2 const &srcToDest, WarpingControl const &control, \
750 MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
751 NL template int warpImage(IMAGE(DESTIMAGEPIXELT) & destImage, geom::SkyWcs const &destWcs, \
752 IMAGE(SRCIMAGEPIXELT) const &srcImage, geom::SkyWcs const &srcWcs, \
753 WarpingControl const &control, IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
754 NL template int warpImage(MASKEDIMAGE(DESTIMAGEPIXELT) & destImage, geom::SkyWcs const &destWcs, \
755 MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, geom::SkyWcs const &srcWcs, \
756 WarpingControl const &control, \
757 MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
758 NL template int warpExposure(EXPOSURE(DESTIMAGEPIXELT) & destExposure, \
759 EXPOSURE(SRCIMAGEPIXELT) const &srcExposure, WarpingControl const &control, \
760 EXPOSURE(DESTIMAGEPIXELT)::MaskedImageT::SinglePixel padValue);
761
762INSTANTIATE(double, double)
763INSTANTIATE(double, float)
764INSTANTIATE(double, int)
766INSTANTIATE(float, float)
767INSTANTIATE(float, int)
769INSTANTIATE(int, int)
772} // namespace math
773
774template std::shared_ptr<math::LanczosWarpingKernel> table::io::PersistableFacade<
775 math::LanczosWarpingKernel>::dynamicCast(std::shared_ptr<table::io::Persistable> const &);
776template std::shared_ptr<math::BilinearWarpingKernel> table::io::PersistableFacade<
777 math::BilinearWarpingKernel>::dynamicCast(std::shared_ptr<table::io::Persistable> const &);
778template std::shared_ptr<math::NearestWarpingKernel> table::io::PersistableFacade<
779 math::NearestWarpingKernel>::dynamicCast(std::shared_ptr<table::io::Persistable> const &);
780template std::shared_ptr<math::WarpingControl> table::io::PersistableFacade<
781 math::WarpingControl>::dynamicCast(std::shared_ptr<table::io::Persistable> const &);
782
783} // namespace afw
784} // namespace lsst
#define INSTANTIATE(FROMSYS, TOSYS)
Definition Detector.cc:509
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
Definition Exception.h:48
LSST DM logging module built on log4cxx.
#define LOGL_DEBUG(logger, message...)
Log a debug-level message using a varargs/printf style interface.
Definition Log.h:515
Implementation of the Photometric Calibration class.
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
Definition Persistable.h:48
T back(T... args)
T begin(T... args)
ShiftMap is a linear Mapping which shifts each axis by a specified constant value.
Definition ShiftMap.h:40
std::string toString(std::string const &="") const override
Return string representation.
Kernel::Pixel operator()(double x) const override
Solve bilinear equation.
void write(OutputArchiveHandle &handle) const override
Write the object to one or more catalogs.
std::shared_ptr< Kernel > clone() const override
Return a pointer to a deep copy of this kernel.
void setKernelParameter(unsigned int ind, double value) const override
Set one kernel parameter.
lsst::geom::Extent2I const getDimensions() const
Return the Kernel's dimensions (width, height)
Definition Kernel.h:212
lsst::geom::Point2I getCtr() const
Return index of kernel's center.
Definition Kernel.h:234
int getWidth() const
Return the Kernel's width.
Definition Kernel.h:224
Lanczos warping: accurate but slow and can introduce ringing artifacts.
int getOrder() const
get the order of the kernel
void setKernelParameter(unsigned int ind, double value) const override
Set one kernel parameter.
std::shared_ptr< Kernel > clone() const override
Return a pointer to a deep copy of this kernel.
void write(OutputArchiveHandle &handle) const override
Write the object to one or more catalogs.
Kernel::Pixel operator()(double x) const override
Solve nearest neighbor equation.
std::string toString(std::string const &="") const override
Return string representation.
Nearest neighbor warping: fast; good for undersampled data.
std::shared_ptr< Kernel > clone() const override
Return a pointer to a deep copy of this kernel.
void write(OutputArchiveHandle &handle) const override
Write the object to one or more catalogs.
void setKernelParameter(unsigned int ind, double value) const override
Set one kernel parameter.
A kernel described by a pair of functions: func(x, y) = colFunc(x) * rowFunc(y)
Definition Kernel.h:860
std::shared_ptr< Kernel > clone() const override
Return a pointer to a deep copy of this kernel.
void setKernelParameter(unsigned int ind, double value) const override
Set one kernel parameter.
SeparableKernel()
Construct an empty spatially invariant SeparableKernel of size 0x0.
Parameters to control convolution.
bool hasMaskWarpingKernel() const
return true if there is a mask kernel
void setWarpingKernel(SeparableKernel const &warpingKernel)
set the warping kernel
int getInterpLength() const
get the interpolation length (pixels)
std::string getPythonModule() const override
Return the fully-qualified Python module that should be imported to guarantee that its factory is reg...
void setWarpingKernelName(std::string const &warpingKernelName)
set the warping kernel by name
void setMaskWarpingKernelName(std::string const &maskWarpingKernelName)
set or clear the mask warping kernel by name
std::string getPersistenceName() const override
Return the unique name used to persist this object and look up its factory.
void setMaskWarpingKernel(SeparableKernel const &maskWarpingKernel)
set the mask warping kernel
std::shared_ptr< SeparableKernel > getWarpingKernel() const
get the warping kernel
void write(OutputArchiveHandle &handle) const override
Write the object to one or more catalogs.
std::shared_ptr< SeparableKernel > getMaskWarpingKernel() const
get the mask warping kernel
bool isPersistable() const noexcept override
Return true if this particular object can be persisted using afw::table::io.
A functor that computes one warped pixel.
Base class for all records.
Definition BaseRecord.h:31
Field< T >::Value get(Key< T > const &key) const
Return the value of a field for the given key.
Definition BaseRecord.h:151
Schema getSchema() const
Return the Schema that holds this record's fields and keys.
Definition BaseRecord.h:80
A class used as a handle to a particular field in a table.
Definition Key.h:53
Defines the fields and offsets for a table.
Definition Schema.h:51
void saveCatalog(BaseCatalog const &catalog)
Save a catalog in the archive.
void saveEmpty()
Indicate that the object being persisted has no state, and hence will never call makeCatalog() or sav...
BaseCatalog makeCatalog(Schema const &schema)
Return a new, empty catalog with the given schema.
int put(Persistable const *obj, bool permissive=false)
Save an object to the archive and return a unique ID that can be used to retrieve it from an InputArc...
PersistableFactory(std::string const &name)
Constructor for the factory.
io::OutputArchiveHandle OutputArchiveHandle
An affine coordinate transformation consisting of a linear transformation and an offset.
An integer coordinate rectangle.
Definition Box.h:55
bool contains(Point2I const &point) const noexcept
Return true if the box contains the point.
Definition Box.cc:114
A 2D linear coordinate transformation.
Reports invalid arguments.
Definition Runtime.h:66
T clear(T... args)
T emplace_back(T... args)
T empty(T... args)
T endl(T... args)
T make_shared(T... args)
double indexToPosition(double ind)
Convert image index to image position.
Definition ImageUtils.h:55
std::shared_ptr< SeparableKernel > makeWarpingKernel(std::string name)
Return a warping kernel given its name.
int warpCenteredImage(DestImageT &destImage, SrcImageT const &srcImage, lsst::geom::LinearTransform const &linearTransform, lsst::geom::Point2D const &centerPosition, 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 with a LinearTranform about a specified point.
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.
int warpExposure(DestExposureT &destExposure, SrcExposureT const &srcExposure, WarpingControl const &control, typename DestExposureT::MaskedImageT::SinglePixel padValue=lsst::afw::math::edgePixel< typename DestExposureT::MaskedImageT >(typename lsst::afw::image::detail::image_traits< typename DestExposureT::MaskedImageT >::image_category()))
Warp (remap) one exposure to another.
CatalogT< BaseRecord > BaseCatalog
Definition fwd.h:72
Extent< double, 2 > Extent2D
Definition Extent.h:400
Point< double, 2 > Point2D
Definition Point.h:324
Extent< int, 2 > Extent2I
Definition Extent.h:397
Point< int, 2 > Point2I
Definition Point.h:321
T static_pointer_cast(T... args)
T push_back(T... args)
T regex_match(T... args)
T reserve(T... args)
T size(T... args)
T stoi(T... args)
T str(T... args)
typename ImageT::image_category image_category
Definition ImageBase.h:67
T swap(T... args)
T swap(T... args)
std::shared_ptr< table::io::Persistable > read(table::io::InputArchive const &archive, table::io::CatalogVector const &catalogs) const override