41 #include "boost/pointer_cast.hpp"
77 static inline void checkWarpingKernelParameter(
const SeparableKernel *p,
unsigned int ind,
double value) {
80 "bad ind argument in WarpingKernel::setKernelParameter()");
82 int ctr = p->getCtr()[ind];
83 int size = p->getDimensions()[ind];
85 if (ctr == (size - 1) / 2) {
86 if (value < -1e-6 || value > 1 + 1e-6) {
88 "bad coordinate in WarpingKernel::setKernelParameter()");
90 }
else if (ctr == (size + 1) / 2) {
91 if (value < -1 - 1e-6 || value > 1e-6) {
93 "bad coordinate in WarpingKernel::setKernelParameter()");
97 "bad ctr value in WarpingKernel::setKernelParameter()");
108 checkWarpingKernelParameter(
this, ind, value);
129 return 0.5 + (1.0 - (2.0 * fabs(this->
_params[0]))) * (0.5 - fabs(
x));
133 checkWarpingKernelParameter(
this, ind, value);
139 os <<
"_BilinearFunction1: ";
140 os << Function1<Kernel::Pixel>::toString(
prefix);
145 return std::make_shared<NearestWarpingKernel>();
150 return static_cast<double>((fabs(this->_params[0]) < 0.5) == (fabs(
x) < 0.5));
154 checkWarpingKernelParameter(
this, ind, value);
160 os <<
"_NearestFunction1: ";
161 os << Function1<Kernel::Pixel>::toString(
prefix);
167 struct LanczosKernelPersistenceHelper {
171 static LanczosKernelPersistenceHelper
const &get() {
172 static LanczosKernelPersistenceHelper
const instance;
176 LanczosKernelPersistenceHelper(LanczosKernelPersistenceHelper
const &) =
delete;
177 LanczosKernelPersistenceHelper(LanczosKernelPersistenceHelper &&) =
delete;
178 LanczosKernelPersistenceHelper &
operator=(LanczosKernelPersistenceHelper
const &) =
delete;
179 LanczosKernelPersistenceHelper &
operator=(LanczosKernelPersistenceHelper &&) =
delete;
182 LanczosKernelPersistenceHelper()
186 class :
public table::io::PersistableFactory {
188 table::io::CatalogVector
const &catalogs)
const override {
189 auto const &
keys = LanczosKernelPersistenceHelper::get();
192 afw::table::BaseRecord
const &record = catalogs.front().front();
194 return std::make_shared<LanczosWarpingKernel>(record.get(
keys.order));
198 } lanczosFactory(
"LanczosWarpingKernel");
201 class DefaultPersistableFactory :
public table::io::PersistableFactory {
203 table::io::CatalogVector
const &catalogs)
const override {
205 return std::make_shared<T>();
211 DefaultPersistableFactory<BilinearWarpingKernel> bilinearFactory(
"BilinearWarpingKernel");
212 DefaultPersistableFactory<NearestWarpingKernel> nearestFactory(
"NearestWarpingKernel");
217 auto const &
keys = LanczosKernelPersistenceHelper::get();
220 record->set(
keys.order, getOrder());
231 static const std::regex LanczosRE(
"lanczos(\\d+)");
232 if (
name ==
"bilinear") {
238 }
else if (
name ==
"nearest") {
246 if (_warpingKernelPtr->getCacheSize() != _cacheSize) {
247 _warpingKernelPtr->computeCache(_cacheSize);
249 return _warpingKernelPtr;
254 setWarpingKernel(*warpingKernelPtr);
258 if (_maskWarpingKernelPtr) {
259 _testWarpingKernels(warpingKernel, *_maskWarpingKernelPtr);
262 std::static_pointer_cast<SeparableKernel>(warpingKernel.
clone()));
263 _warpingKernelPtr = warpingKernelPtr;
267 if (_maskWarpingKernelPtr) {
268 if (_maskWarpingKernelPtr->getCacheSize() != _cacheSize) {
269 _maskWarpingKernelPtr->computeCache(_cacheSize);
272 return _maskWarpingKernelPtr;
276 if (!maskWarpingKernelName.
empty()) {
278 setMaskWarpingKernel(*maskWarpingKernelPtr);
280 _maskWarpingKernelPtr.reset();
285 _testWarpingKernels(*_warpingKernelPtr, maskWarpingKernel);
286 _maskWarpingKernelPtr = std::static_pointer_cast<SeparableKernel>(maskWarpingKernel.
clone());
289 void WarpingControl::_testWarpingKernels(
SeparableKernel const &warpingKernel,
297 if (!kernelBBox.
contains(maskKernelBBox)) {
299 "warping kernel is smaller than mask warping kernel");
305 struct WarpingControlPersistenceHelper {
314 static WarpingControlPersistenceHelper
const &get() {
315 static WarpingControlPersistenceHelper
const instance;
319 WarpingControlPersistenceHelper(WarpingControlPersistenceHelper
const &) =
delete;
320 WarpingControlPersistenceHelper(WarpingControlPersistenceHelper &&) =
delete;
321 WarpingControlPersistenceHelper &
operator=(WarpingControlPersistenceHelper
const &) =
delete;
322 WarpingControlPersistenceHelper &
operator=(WarpingControlPersistenceHelper &&) =
delete;
325 WarpingControlPersistenceHelper()
328 schema.addField<int>(
"warpingKernelIndex",
"archive ID of nested warping kernel")),
329 hasMaskKernel(
schema.addField<table::Flag>(
"hasMaskKernel",
"whether a mask kernel is stored")),
331 "archive ID of nested mask kernel. "
332 "Valid only if hasMaskKernel")),
333 cacheSize(
schema.addField<int>(
"cacheSize",
"Cache size for warping kernel(s)")),
335 "Distance over which WCS can be linearly interpolated")),
337 "growFullMask",
"bits to grow to full width of image/variance kernel")) {}
340 std::string _getWarpingControlPersistenceName() {
return "WarpingControl"; }
342 class :
public table::io::PersistableFactory {
344 table::io::CatalogVector
const &catalogs)
const override {
345 auto const &
keys = WarpingControlPersistenceHelper::get();
348 afw::table::BaseRecord
const &record = catalogs.front().front();
352 auto control = std::make_shared<WarpingControl>(
"bilinear",
"", record.get(
keys.cacheSize),
353 record.get(
keys.interpLength),
354 record.get(
keys.growFullMask));
358 control->setWarpingKernel(*archive.get<
SeparableKernel>(record.get(
keys.warpingKernelIndex)));
359 if (record.get(
keys.hasMaskKernel)) {
360 control->setMaskWarpingKernel(*archive.get<
SeparableKernel>(record.get(
keys.maskKernelIndex)));
366 } controlFactory(_getWarpingControlPersistenceName());
375 return _warpingKernelPtr->isPersistable() &&
376 (!hasMaskWarpingKernel() || _maskWarpingKernelPtr->isPersistable());
380 auto const &
keys = WarpingControlPersistenceHelper::get();
384 record->set(
keys.warpingKernelIndex, handle.
put(_warpingKernelPtr));
385 record->set(
keys.hasMaskKernel, hasMaskWarpingKernel());
386 if (hasMaskWarpingKernel()) {
387 record->set(
keys.maskKernelIndex, handle.
put(_maskWarpingKernelPtr));
389 record->set(
keys.cacheSize, _cacheSize);
390 record->set(
keys.interpLength, _interpLength);
391 record->set(
keys.growFullMask, _growFullMask);
396 template <
typename DestExposureT,
typename SrcExposureT>
398 typename DestExposureT::MaskedImageT::SinglePixel padValue) {
399 if (!destExposure.hasWcs()) {
402 if (!srcExposure.hasWcs()) {
405 typename DestExposureT::MaskedImageT mi = destExposure.getMaskedImage();
406 destExposure.setPhotoCalib(srcExposure.getPhotoCalib());
407 destExposure.setFilterLabel(srcExposure.getFilterLabel());
408 destExposure.getInfo()->setVisitInfo(srcExposure.getInfo()->getVisitInfo());
409 return warpImage(mi, *destExposure.getWcs(), srcExposure.getMaskedImage(), *srcExposure.getWcs(), control,
427 inline double computeRelativeArea(
436 return std::abs(dSrcA.getX() * dSrcB.getY() - dSrcA.getY() * dSrcB.getX());
441 template <
typename DestImageT,
typename SrcImageT>
444 typename DestImageT::SinglePixel padValue) {
446 return warpImage(destImage, srcImage, *srcToDest, control, padValue);
449 template <
typename DestImageT,
typename SrcImageT>
450 int warpImage(DestImageT &destImage, SrcImageT
const &srcImage,
452 typename DestImageT::SinglePixel padValue) {
462 warpingKernelPtr->shrinkBBox(srcImage.getBBox(
image::LOCAL));
464 for (
int y = 0, height = destImage.getHeight();
y < height; ++
y) {
465 for (
typename DestImageT::x_iterator destPtr = destImage.row_begin(
y),
end = destImage.row_end(
y);
466 destPtr !=
end; ++destPtr) {
475 std::dynamic_pointer_cast<LanczosWarpingKernel>(warpingKernelPtr);
477 int numGoodPixels = 0;
480 auto const parentDestToParentSrc = srcToDest.
inverted();
481 std::vector<double> const localDestToParentDestVec = {
static_cast<double>(destImage.getX0()),
482 static_cast<double>(destImage.getY0())};
484 auto const localDestToParentSrc = localDestToParentDest.then(*parentDestToParentSrc);
487 int const srcWidth = srcImage.getWidth();
488 int const srcHeight = srcImage.getHeight();
489 LOGL_DEBUG(
"TRACE2.afw.math.warp",
"source image width=%d; height=%d", srcWidth, srcHeight);
491 int const destWidth = destImage.getWidth();
492 int const destHeight = destImage.getHeight();
493 LOGL_DEBUG(
"TRACE2.afw.math.warp",
"remap image width=%d; height=%d", destWidth, destHeight);
496 LOGL_DEBUG(
"TRACE3.afw.math.warp",
"Remapping masked image");
498 int const maxCol = destWidth - 1;
499 int const maxRow = destHeight - 1;
508 int const numColEdges = 2 + ((destWidth - 1) /
interpLength);
513 edgeColList.
reserve(numColEdges);
518 invWidthList.
reserve(numColEdges);
523 for (
int prevEndCol = -1; prevEndCol < maxCol; prevEndCol +=
interpLength) {
525 if (endCol > maxCol) {
529 assert(endCol - prevEndCol > 0);
530 invWidthList.
push_back(1.0 /
static_cast<double>(endCol - prevEndCol));
532 assert(edgeColList.
back() == maxCol);
547 endColPosList.
reserve(numColEdges);
550 for (
int endCol : edgeColList) {
553 auto rightSrcPosList = localDestToParentSrc->applyForward(endColPosList);
554 srcPosView[-1] = rightSrcPosList[0];
555 for (
int colBand = 1, endBand = edgeColList.
size(); colBand < endBand; ++colBand) {
556 int const prevEndCol = edgeColList[colBand - 1];
557 int const endCol = edgeColList[colBand];
561 (rightSrcPosList[colBand] - leftSrcPos) * invWidthList[colBand];
563 for (
int col = prevEndCol + 1; col <= endCol; ++col) {
564 srcPosView[col] = srcPosView[col - 1] + xDeltaSrcPos;
569 while (endRow < maxRow) {
572 int prevEndRow = endRow;
574 if (endRow > maxRow) {
577 assert(endRow - prevEndRow > 0);
578 double interpInvHeight = 1.0 /
static_cast<double>(endRow - prevEndRow);
583 for (
int endCol : edgeColList) {
586 auto bottomSrcPosList = localDestToParentSrc->applyForward(destRowPosList);
587 for (
int colBand = 0, endBand = edgeColList.size(); colBand < endBand; ++colBand) {
588 int endCol = edgeColList[colBand];
589 yDeltaSrcPosList[colBand] =
590 (bottomSrcPosList[colBand] - srcPosView[endCol]) * interpInvHeight;
593 for (
int row = prevEndRow + 1; row <= endRow; ++row) {
594 typename DestImageT::x_iterator destXIter = destImage.row_begin(row);
595 srcPosView[-1] += yDeltaSrcPosList[0];
596 for (
int colBand = 1, endBand = edgeColList.size(); colBand < endBand; ++colBand) {
599 int const prevEndCol = edgeColList[colBand - 1];
600 int const endCol = edgeColList[colBand];
609 for (
int col = prevEndCol + 1; col <= endCol; ++col, ++destXIter) {
612 double relativeArea = computeRelativeArea(srcPos, leftSrcPos, srcPosView[col]);
614 srcPosView[col] = srcPos;
617 destXIter, srcPos, relativeArea,
632 destPosList.
reserve(1 + destWidth);
633 for (
int col = -1; col < destWidth; ++col) {
636 auto prevSrcPosList = localDestToParentSrc->applyForward(destPosList);
638 for (
int row = 0; row < destHeight; ++row) {
640 for (
int col = -1; col < destWidth; ++col) {
643 auto srcPosList = localDestToParentSrc->applyForward(destPosList);
645 typename DestImageT::x_iterator destXIter = destImage.row_begin(row);
646 for (
int col = 0; col < destWidth; ++col, ++destXIter) {
648 auto srcPos = srcPosList[col + 1];
649 double relativeArea =
650 computeRelativeArea(srcPos, prevSrcPosList[col], prevSrcPosList[col + 1]);
652 if (warpAtOnePoint(destXIter, srcPos, relativeArea,
659 swap(srcPosList, prevSrcPosList);
663 return numGoodPixels;
666 template <
typename DestImageT,
typename SrcImageT>
670 typename DestImageT::SinglePixel padValue) {
672 if ((destImage.getWidth() != srcImage.getWidth()) || (destImage.getHeight() != srcImage.getHeight()) ||
673 (destImage.getXY0() != srcImage.getXY0())) {
675 errStream <<
"src and dest images must have same size and xy0.";
680 SrcImageT srcImageCopy(srcImage,
true);
681 srcImageCopy.setXY0(0, 0);
682 destImage.setXY0(0, 0);
694 static float t = 0.0;
695 float t_before = 1.0*clock()/CLOCKS_PER_SEC;
696 int n =
warpImage(destImage, srcImageCopy, affTran, control, padValue);
697 float t_after = 1.0*clock()/CLOCKS_PER_SEC;
698 float dt = t_after - t_before;
700 std::cout <<srcImage.getWidth()<<
"x"<<srcImage.getHeight()<<
": "<< dt <<
" "<< t <<
std::endl;
702 int n =
warpImage(destImage, srcImageCopy, *affineTransform22, control, padValue);
706 destImage.setXY0(srcImage.getXY0());
716 #define EXPOSURE(PIXTYPE) image::Exposure<PIXTYPE, image::MaskPixel, image::VariancePixel>
717 #define MASKEDIMAGE(PIXTYPE) image::MaskedImage<PIXTYPE, image::MaskPixel, image::VariancePixel>
718 #define IMAGE(PIXTYPE) image::Image<PIXTYPE>
721 #define INSTANTIATE(DESTIMAGEPIXELT, SRCIMAGEPIXELT) \
722 template int warpCenteredImage( \
723 IMAGE(DESTIMAGEPIXELT) & destImage, IMAGE(SRCIMAGEPIXELT) const &srcImage, \
724 lsst::geom::LinearTransform const &linearTransform, lsst::geom::Point2D const ¢erPosition, \
725 WarpingControl const &control, IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
726 NL template int warpCenteredImage( \
727 MASKEDIMAGE(DESTIMAGEPIXELT) & destImage, MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, \
728 lsst::geom::LinearTransform const &linearTransform, lsst::geom::Point2D const ¢erPosition, \
729 WarpingControl const &control, MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
730 NL template int warpImage(IMAGE(DESTIMAGEPIXELT) & destImage, IMAGE(SRCIMAGEPIXELT) const &srcImage, \
731 geom::TransformPoint2ToPoint2 const &srcToDest, WarpingControl const &control, \
732 IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
733 NL template int warpImage(MASKEDIMAGE(DESTIMAGEPIXELT) & destImage, \
734 MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, \
735 geom::TransformPoint2ToPoint2 const &srcToDest, WarpingControl const &control, \
736 MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
737 NL template int warpImage(IMAGE(DESTIMAGEPIXELT) & destImage, geom::SkyWcs const &destWcs, \
738 IMAGE(SRCIMAGEPIXELT) const &srcImage, geom::SkyWcs const &srcWcs, \
739 WarpingControl const &control, IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
740 NL template int warpImage(MASKEDIMAGE(DESTIMAGEPIXELT) & destImage, geom::SkyWcs const &destWcs, \
741 MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, geom::SkyWcs const &srcWcs, \
742 WarpingControl const &control, \
743 MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
744 NL template int warpExposure(EXPOSURE(DESTIMAGEPIXELT) & destExposure, \
745 EXPOSURE(SRCIMAGEPIXELT) const &srcExposure, WarpingControl const &control, \
746 EXPOSURE(DESTIMAGEPIXELT)::MaskedImageT::SinglePixel padValue);
table::Key< std::string > name
#define INSTANTIATE(FROMSYS, TOSYS)
#define LSST_EXCEPT(type,...)
#define LOGL_DEBUG(logger, message...)
#define LSST_ARCHIVE_ASSERT(EXPR)
An assertion macro used to validate the structure of an InputArchive.
Implementation of the Photometric Calibration class.
A 2-dimensional celestial WCS that transform pixels to ICRS RA/Dec, using the LSST standard for pixel...
lsst::geom::SpherePoint pixelToSky(lsst::geom::Point2D const &pixel) const
Compute sky position(s) from pixel position(s)
lsst::geom::Point2D skyToPixel(lsst::geom::SpherePoint const &sky) const
Compute pixel position(s) from sky position(s)
std::string toString(std::string const &="") const override
Return string representation.
Kernel::Pixel operator()(double x) const override
Solve bilinear equation.
BilinearWarpingKernel & operator=(const BilinearWarpingKernel &)=delete
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
LanczosWarpingKernel(int order)
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.
std::shared_ptr< RecordT > addNew()
Create a new record, add it to the end of the catalog, and return a pointer to it.
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.
bool contains(Point2I const &point) const noexcept
T emplace_back(T... args)
void swap(CameraSys &a, CameraSys &b)
std::shared_ptr< TransformPoint2ToPoint2 > makeTransform(lsst::geom::AffineTransform const &affine)
Wrap an lsst::geom::AffineTransform as a Transform.
std::shared_ptr< TransformPoint2ToPoint2 > makeWcsPairTransform(SkyWcs const &src, SkyWcs const &dst)
A Transform obtained by putting two SkyWcs objects "back to back".
Transform< Point2Endpoint, Point2Endpoint > TransformPoint2ToPoint2
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.
std::int32_t MaskPixel
default type for Masks and MaskedImage Masks
double indexToPosition(double ind)
Convert image index to image position.
bool imagesOverlap(ImageBase< T1 > const &image1, ImageBase< T2 > const &image2)
Return true if the pixels for two images or masks overlap in memory.
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
A base class for image defects.
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