73static inline void checkWarpingKernelParameter(
const SeparableKernel *p,
unsigned int ind,
double value) {
76 "bad ind argument in WarpingKernel::setKernelParameter()");
78 int ctr = p->getCtr()[ind];
79 int size = p->getDimensions()[ind];
81 if (ctr == (size - 1) / 2) {
82 if (value < -1e-6 || value > 1 + 1e-6) {
84 "bad coordinate in WarpingKernel::setKernelParameter()");
86 }
else if (ctr == (size + 1) / 2) {
87 if (value < -1 - 1e-6 || value > 1e-6) {
89 "bad coordinate in WarpingKernel::setKernelParameter()");
93 "bad ctr value in WarpingKernel::setKernelParameter()");
104 checkWarpingKernelParameter(
this, ind, value);
125 return 0.5 + (1.0 - (2.0 * fabs(this->
_params[0]))) * (0.5 - fabs(
x));
129 checkWarpingKernelParameter(
this, ind, value);
135 os <<
"_BilinearFunction1: ";
136 os << Function1<Kernel::Pixel>::toString(
prefix);
141 return std::make_shared<NearestWarpingKernel>();
146 return static_cast<double>((fabs(this->_params[0]) < 0.5) == (fabs(
x) < 0.5));
150 checkWarpingKernelParameter(
this, ind, value);
156 os <<
"_NearestFunction1: ";
157 os << Function1<Kernel::Pixel>::toString(
prefix);
163struct LanczosKernelPersistenceHelper {
167 static LanczosKernelPersistenceHelper
const &get() {
168 static LanczosKernelPersistenceHelper
const instance;
172 LanczosKernelPersistenceHelper(LanczosKernelPersistenceHelper
const &) =
delete;
173 LanczosKernelPersistenceHelper(LanczosKernelPersistenceHelper &&) =
delete;
174 LanczosKernelPersistenceHelper &operator=(LanczosKernelPersistenceHelper
const &) =
delete;
175 LanczosKernelPersistenceHelper &operator=(LanczosKernelPersistenceHelper &&) =
delete;
178 LanczosKernelPersistenceHelper()
182class :
public table::io::PersistableFactory {
184 table::io::CatalogVector
const &catalogs)
const override {
185 auto const &
keys = LanczosKernelPersistenceHelper::get();
188 afw::table::BaseRecord
const &record = catalogs.front().front();
190 return std::make_shared<LanczosWarpingKernel>(record.get(
keys.order));
194} lanczosFactory(
"LanczosWarpingKernel");
197class DefaultPersistableFactory :
public table::io::PersistableFactory {
199 table::io::CatalogVector
const &catalogs)
const override {
201 return std::make_shared<T>();
204 using table::io::PersistableFactory::PersistableFactory;
207DefaultPersistableFactory<BilinearWarpingKernel> bilinearFactory(
"BilinearWarpingKernel");
208DefaultPersistableFactory<NearestWarpingKernel> nearestFactory(
"NearestWarpingKernel");
213 auto const &keys = LanczosKernelPersistenceHelper::get();
216 record->set(keys.order, getOrder());
227 static const std::regex LanczosRE(
"lanczos(\\d+)");
228 if (
name ==
"bilinear") {
231 std::string orderStr(matches[1].first, matches[1].second);
234 }
else if (
name ==
"nearest") {
242 if (_warpingKernelPtr->getCacheSize() != _cacheSize) {
243 _warpingKernelPtr->computeCache(_cacheSize);
245 return _warpingKernelPtr;
250 setWarpingKernel(*warpingKernelPtr);
254 if (_maskWarpingKernelPtr) {
255 _testWarpingKernels(warpingKernel, *_maskWarpingKernelPtr);
258 std::static_pointer_cast<SeparableKernel>(warpingKernel.
clone()));
259 _warpingKernelPtr = warpingKernelPtr;
263 if (_maskWarpingKernelPtr) {
264 if (_maskWarpingKernelPtr->getCacheSize() != _cacheSize) {
265 _maskWarpingKernelPtr->computeCache(_cacheSize);
268 return _maskWarpingKernelPtr;
272 if (!maskWarpingKernelName.
empty()) {
274 setMaskWarpingKernel(*maskWarpingKernelPtr);
276 _maskWarpingKernelPtr.reset();
281 _testWarpingKernels(*_warpingKernelPtr, maskWarpingKernel);
282 _maskWarpingKernelPtr = std::static_pointer_cast<SeparableKernel>(maskWarpingKernel.
clone());
285void WarpingControl::_testWarpingKernels(
SeparableKernel const &warpingKernel,
293 if (!kernelBBox.
contains(maskKernelBBox)) {
295 "warping kernel is smaller than mask warping kernel");
301struct WarpingControlPersistenceHelper {
310 static WarpingControlPersistenceHelper
const &get() {
311 static WarpingControlPersistenceHelper
const instance;
315 WarpingControlPersistenceHelper(WarpingControlPersistenceHelper
const &) =
delete;
316 WarpingControlPersistenceHelper(WarpingControlPersistenceHelper &&) =
delete;
317 WarpingControlPersistenceHelper &operator=(WarpingControlPersistenceHelper
const &) =
delete;
318 WarpingControlPersistenceHelper &operator=(WarpingControlPersistenceHelper &&) =
delete;
321 WarpingControlPersistenceHelper()
324 schema.addField<int>(
"warpingKernelIndex",
"archive ID of nested warping kernel")),
325 hasMaskKernel(
schema.addField<table::Flag>(
"hasMaskKernel",
"whether a mask kernel is stored")),
327 "archive ID of nested mask kernel. "
328 "Valid only if hasMaskKernel")),
329 cacheSize(
schema.addField<int>(
"cacheSize",
"Cache size for warping kernel(s)")),
331 "Distance over which WCS can be linearly interpolated")),
333 "growFullMask",
"bits to grow to full width of image/variance kernel")) {}
336std::string _getWarpingControlPersistenceName() {
return "WarpingControl"; }
338class :
public table::io::PersistableFactory {
340 table::io::CatalogVector
const &catalogs)
const override {
341 auto const &
keys = WarpingControlPersistenceHelper::get();
344 afw::table::BaseRecord
const &record = catalogs.front().front();
348 auto control = std::make_shared<WarpingControl>(
"bilinear",
"", record.get(
keys.cacheSize),
349 record.get(
keys.interpLength),
350 record.get(
keys.growFullMask));
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)));
362} controlFactory(_getWarpingControlPersistenceName());
371 return _warpingKernelPtr->isPersistable() &&
372 (!hasMaskWarpingKernel() || _maskWarpingKernelPtr->isPersistable());
376 auto const &keys = WarpingControlPersistenceHelper::get();
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));
385 record->set(keys.cacheSize, _cacheSize);
386 record->set(keys.interpLength, _interpLength);
387 record->set(keys.growFullMask, _growFullMask);
392template <
typename DestExposureT,
typename SrcExposureT>
394 typename DestExposureT::MaskedImageT::SinglePixel padValue) {
395 if (!destExposure.hasWcs()) {
398 if (!srcExposure.hasWcs()) {
401 typename DestExposureT::MaskedImageT mi = destExposure.getMaskedImage();
402 if (srcExposure.getInfo()->hasId()) {
403 destExposure.getInfo()->setId(srcExposure.getInfo()->getId());
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,
418 geom::SkyWcs
const &destWcs,
419 geom::SkyWcs
const &srcWcs)
423 return srcWcs.skyToPixel(destWcs.pixelToSky(destPix));
426inline double computeRelativeArea(
435 return std::abs(dSrcA.getX() * dSrcB.getY() - dSrcA.getY() * dSrcB.getX());
440template <
typename DestImageT,
typename SrcImageT>
441int warpImage(DestImageT &destImage, geom::SkyWcs
const &destWcs, SrcImageT
const &srcImage,
443 typename DestImageT::SinglePixel padValue) {
444 auto srcToDest = geom::makeWcsPairTransform(srcWcs, destWcs);
445 return warpImage(destImage, srcImage, *srcToDest, control, padValue);
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)) {
455 if (destImage.getBBox(image::LOCAL).isEmpty()) {
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) {
474 std::dynamic_pointer_cast<LanczosWarpingKernel>(warpingKernelPtr);
476 int numGoodPixels = 0;
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);
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);
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);
495 LOGL_DEBUG(
"TRACE3.lsst.afw.math.warp",
"Remapping masked image");
497 int const maxCol = destWidth - 1;
498 int const maxRow = destHeight - 1;
507 int const numColEdges = 2 + ((destWidth - 1) /
interpLength);
512 edgeColList.
reserve(numColEdges);
517 invWidthList.
reserve(numColEdges);
522 for (
int prevEndCol = -1; prevEndCol < maxCol; prevEndCol +=
interpLength) {
524 if (endCol > maxCol) {
528 assert(endCol - prevEndCol > 0);
529 invWidthList.
push_back(1.0 /
static_cast<double>(endCol - prevEndCol));
531 assert(edgeColList.
back() == maxCol);
546 endColPosList.
reserve(numColEdges);
549 for (
int endCol : edgeColList) {
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];
560 (rightSrcPosList[colBand] - leftSrcPos) * invWidthList[colBand];
562 for (
int col = prevEndCol + 1;
col <= endCol; ++
col) {
563 srcPosView[
col] = srcPosView[
col - 1] + xDeltaSrcPos;
568 while (endRow < maxRow) {
571 int prevEndRow = endRow;
573 if (endRow > maxRow) {
576 assert(endRow - prevEndRow > 0);
577 double interpInvHeight = 1.0 /
static_cast<double>(endRow - prevEndRow);
582 for (
int endCol : edgeColList) {
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;
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) {
598 int const prevEndCol = edgeColList[colBand - 1];
599 int const endCol = edgeColList[colBand];
608 for (
int col = prevEndCol + 1;
col <= endCol; ++
col, ++destXIter) {
611 double relativeArea = computeRelativeArea(srcPos, leftSrcPos, srcPosView[
col]);
613 srcPosView[
col] = srcPos;
616 destXIter, srcPos, relativeArea,
631 destPosList.
reserve(1 + destWidth);
632 for (
int col = -1;
col < destWidth; ++
col) {
635 auto prevSrcPosList = localDestToParentSrc->applyForward(destPosList);
637 for (
int row = 0;
row < destHeight; ++
row) {
639 for (
int col = -1;
col < destWidth; ++
col) {
642 auto srcPosList = localDestToParentSrc->applyForward(destPosList);
644 typename DestImageT::x_iterator destXIter = destImage.row_begin(
row);
645 for (
int col = 0;
col < destWidth; ++
col, ++destXIter) {
647 auto srcPos = srcPosList[
col + 1];
648 double relativeArea =
649 computeRelativeArea(srcPos, prevSrcPosList[
col], prevSrcPosList[
col + 1]);
651 if (warpAtOnePoint(destXIter, srcPos, relativeArea,
658 swap(srcPosList, prevSrcPosList);
662 return numGoodPixels;
665template <
typename DestImageT,
typename SrcImageT>
669 typename DestImageT::SinglePixel padValue) {
671 if ((destImage.getWidth() != srcImage.getWidth()) || (destImage.getHeight() != srcImage.getHeight()) ||
672 (destImage.getXY0() != srcImage.getXY0())) {
674 errStream <<
"src and dest images must have same size and xy0.";
679 SrcImageT srcImageCopy(srcImage,
true);
680 srcImageCopy.setXY0(0, 0);
681 destImage.setXY0(0, 0);
693 static float t = 0.0;
694 float t_before = 1.0*clock()/CLOCKS_PER_SEC;
695 int n =
warpImage(destImage, srcImageCopy, affTran, control, padValue);
696 float t_after = 1.0*clock()/CLOCKS_PER_SEC;
697 float dt = t_after - t_before;
699 std::cout <<srcImage.getWidth()<<
"x"<<srcImage.getHeight()<<
": "<< dt <<
" "<< t <<
std::endl;
701 int n =
warpImage(destImage, srcImageCopy, *affineTransform22, control, padValue);
705 destImage.setXY0(srcImage.getXY0());
715#define EXPOSURE(PIXTYPE) image::Exposure<PIXTYPE, image::MaskPixel, image::VariancePixel>
716#define MASKEDIMAGE(PIXTYPE) image::MaskedImage<PIXTYPE, image::MaskPixel, image::VariancePixel>
717#define IMAGE(PIXTYPE) image::Image<PIXTYPE>
720#define INSTANTIATE(DESTIMAGEPIXELT, SRCIMAGEPIXELT) \
721 template int warpCenteredImage( \
722 IMAGE(DESTIMAGEPIXELT) & destImage, IMAGE(SRCIMAGEPIXELT) const &srcImage, \
723 lsst::geom::LinearTransform const &linearTransform, lsst::geom::Point2D const ¢erPosition, \
724 WarpingControl const &control, IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
725 NL template int warpCenteredImage( \
726 MASKEDIMAGE(DESTIMAGEPIXELT) & destImage, MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, \
727 lsst::geom::LinearTransform const &linearTransform, lsst::geom::Point2D const ¢erPosition, \
728 WarpingControl const &control, MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
729 NL template int warpImage(IMAGE(DESTIMAGEPIXELT) & destImage, IMAGE(SRCIMAGEPIXELT) const &srcImage, \
730 geom::TransformPoint2ToPoint2 const &srcToDest, WarpingControl const &control, \
731 IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
732 NL template int warpImage(MASKEDIMAGE(DESTIMAGEPIXELT) & destImage, \
733 MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, \
734 geom::TransformPoint2ToPoint2 const &srcToDest, WarpingControl const &control, \
735 MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
736 NL template int warpImage(IMAGE(DESTIMAGEPIXELT) & destImage, geom::SkyWcs const &destWcs, \
737 IMAGE(SRCIMAGEPIXELT) const &srcImage, geom::SkyWcs const &srcWcs, \
738 WarpingControl const &control, IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
739 NL template int warpImage(MASKEDIMAGE(DESTIMAGEPIXELT) & destImage, geom::SkyWcs const &destWcs, \
740 MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, geom::SkyWcs const &srcWcs, \
741 WarpingControl const &control, \
742 MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
743 NL template int warpExposure(EXPOSURE(DESTIMAGEPIXELT) & destExposure, \
744 EXPOSURE(SRCIMAGEPIXELT) const &srcExposure, WarpingControl const &control, \
745 EXPOSURE(DESTIMAGEPIXELT)::MaskedImageT::SinglePixel padValue);
table::Key< std::string > name
#define INSTANTIATE(FROMSYS, TOSYS)
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
LSST DM logging module built on log4cxx.
#define LOGL_DEBUG(logger, message...)
Log a debug-level message using a varargs/printf style interface.
Implementation of the Photometric Calibration class.
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
ShiftMap is a linear Mapping which shifts each axis by a specified constant value.
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.
std::vector< double > _params
lsst::geom::Extent2I const getDimensions() const
Return the Kernel's dimensions (width, height)
lsst::geom::Point2I getCtr() const
Return index of kernel's center.
int getWidth() const
Return the Kernel's width.
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)
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.
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.
Defines the fields and offsets for a table.
An object passed to Persistable::write to allow it to persist itself.
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.
An integer coordinate rectangle.
bool contains(Point2I const &point) const noexcept
Return true if the box contains the point.
Reports invalid arguments.
T emplace_back(T... args)
std::int32_t MaskPixel
default type for Masks and MaskedImage Masks
double indexToPosition(double ind)
Convert image index to image position.
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 ¢erPosition, 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.
Extent< double, 2 > Extent2D
afw::table::Key< std::string > warpingKernelName
typename ImageT::image_category image_category
table::Key< int > warpingKernelIndex
std::shared_ptr< table::io::Persistable > read(table::io::InputArchive const &archive, table::io::CatalogVector const &catalogs) const override
table::Key< int > interpLength
table::Key< image::MaskPixel > growFullMask
table::Key< int > cacheSize
table::Key< int > maskKernelIndex
table::Key< table::Flag > hasMaskKernel