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 colBand = 0, endBand = edgeColList.
size(); colBand < endBand; ++colBand) {
551 int const endCol = edgeColList[colBand];
554 auto rightSrcPosList = localDestToParentSrc->applyForward(endColPosList);
555 srcPosView[-1] = rightSrcPosList[0];
556 for (
int colBand = 1, endBand = edgeColList.
size(); colBand < endBand; ++colBand) {
557 int const prevEndCol = edgeColList[colBand - 1];
558 int const endCol = edgeColList[colBand];
562 (rightSrcPosList[colBand] - leftSrcPos) * invWidthList[colBand];
564 for (
int col = prevEndCol + 1; col <= endCol; ++col) {
565 srcPosView[col] = srcPosView[col - 1] + xDeltaSrcPos;
570 while (endRow < maxRow) {
573 int prevEndRow = endRow;
575 if (endRow > maxRow) {
578 assert(endRow - prevEndRow > 0);
579 double interpInvHeight = 1.0 /
static_cast<double>(endRow - prevEndRow);
584 for (
int colBand = 0, endBand = edgeColList.
size(); colBand < endBand; ++colBand) {
585 int endCol = edgeColList[colBand];
588 auto bottomSrcPosList = localDestToParentSrc->applyForward(destRowPosList);
589 for (
int colBand = 0, endBand = edgeColList.
size(); colBand < endBand; ++colBand) {
590 int endCol = edgeColList[colBand];
591 yDeltaSrcPosList[colBand] =
592 (bottomSrcPosList[colBand] - srcPosView[endCol]) * interpInvHeight;
595 for (
int row = prevEndRow + 1; row <= endRow; ++row) {
596 typename DestImageT::x_iterator destXIter = destImage.row_begin(row);
597 srcPosView[-1] += yDeltaSrcPosList[0];
598 for (
int colBand = 1, endBand = edgeColList.
size(); colBand < endBand; ++colBand) {
601 int const prevEndCol = edgeColList[colBand - 1];
602 int const endCol = edgeColList[colBand];
611 for (
int col = prevEndCol + 1; col <= endCol; ++col, ++destXIter) {
614 double relativeArea = computeRelativeArea(srcPos, leftSrcPos, srcPosView[col]);
616 srcPosView[col] = srcPos;
619 destXIter, srcPos, relativeArea,
634 destPosList.
reserve(1 + destWidth);
635 for (
int col = -1; col < destWidth; ++col) {
638 auto prevSrcPosList = localDestToParentSrc->applyForward(destPosList);
640 for (
int row = 0; row < destHeight; ++row) {
642 for (
int col = -1; col < destWidth; ++col) {
645 auto srcPosList = localDestToParentSrc->applyForward(destPosList);
647 typename DestImageT::x_iterator destXIter = destImage.row_begin(row);
648 for (
int col = 0; col < destWidth; ++col, ++destXIter) {
650 auto srcPos = srcPosList[col + 1];
651 double relativeArea =
652 computeRelativeArea(srcPos, prevSrcPosList[col], prevSrcPosList[col + 1]);
654 if (warpAtOnePoint(destXIter, srcPos, relativeArea,
661 swap(srcPosList, prevSrcPosList);
665 return numGoodPixels;
668 template <
typename DestImageT,
typename SrcImageT>
672 typename DestImageT::SinglePixel padValue) {
674 if ((destImage.getWidth() != srcImage.getWidth()) || (destImage.getHeight() != srcImage.getHeight()) ||
675 (destImage.getXY0() != srcImage.getXY0())) {
677 errStream <<
"src and dest images must have same size and xy0.";
682 SrcImageT srcImageCopy(srcImage,
true);
683 srcImageCopy.setXY0(0, 0);
684 destImage.setXY0(0, 0);
696 static float t = 0.0;
697 float t_before = 1.0*clock()/CLOCKS_PER_SEC;
698 int n =
warpImage(destImage, srcImageCopy, affTran, control, padValue);
699 float t_after = 1.0*clock()/CLOCKS_PER_SEC;
700 float dt = t_after - t_before;
702 std::cout <<srcImage.getWidth()<<
"x"<<srcImage.getHeight()<<
": "<< dt <<
" "<< t <<
std::endl;
704 int n =
warpImage(destImage, srcImageCopy, *affineTransform22, control, padValue);
708 destImage.setXY0(srcImage.getXY0());
718 #define EXPOSURE(PIXTYPE) image::Exposure<PIXTYPE, image::MaskPixel, image::VariancePixel>
719 #define MASKEDIMAGE(PIXTYPE) image::MaskedImage<PIXTYPE, image::MaskPixel, image::VariancePixel>
720 #define IMAGE(PIXTYPE) image::Image<PIXTYPE>
723 #define INSTANTIATE(DESTIMAGEPIXELT, SRCIMAGEPIXELT) \
724 template int warpCenteredImage( \
725 IMAGE(DESTIMAGEPIXELT) & destImage, IMAGE(SRCIMAGEPIXELT) const &srcImage, \
726 lsst::geom::LinearTransform const &linearTransform, lsst::geom::Point2D const ¢erPosition, \
727 WarpingControl const &control, IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
728 NL template int warpCenteredImage( \
729 MASKEDIMAGE(DESTIMAGEPIXELT) & destImage, MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, \
730 lsst::geom::LinearTransform const &linearTransform, lsst::geom::Point2D const ¢erPosition, \
731 WarpingControl const &control, MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
732 NL template int warpImage(IMAGE(DESTIMAGEPIXELT) & destImage, IMAGE(SRCIMAGEPIXELT) const &srcImage, \
733 geom::TransformPoint2ToPoint2 const &srcToDest, WarpingControl const &control, \
734 IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
735 NL template int warpImage(MASKEDIMAGE(DESTIMAGEPIXELT) & destImage, \
736 MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, \
737 geom::TransformPoint2ToPoint2 const &srcToDest, WarpingControl const &control, \
738 MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
739 NL template int warpImage(IMAGE(DESTIMAGEPIXELT) & destImage, geom::SkyWcs const &destWcs, \
740 IMAGE(SRCIMAGEPIXELT) const &srcImage, geom::SkyWcs const &srcWcs, \
741 WarpingControl const &control, IMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
742 NL template int warpImage(MASKEDIMAGE(DESTIMAGEPIXELT) & destImage, geom::SkyWcs const &destWcs, \
743 MASKEDIMAGE(SRCIMAGEPIXELT) const &srcImage, geom::SkyWcs const &srcWcs, \
744 WarpingControl const &control, \
745 MASKEDIMAGE(DESTIMAGEPIXELT)::SinglePixel padValue); \
746 NL template int warpExposure(EXPOSURE(DESTIMAGEPIXELT) & destExposure, \
747 EXPOSURE(SRCIMAGEPIXELT) const &srcExposure, WarpingControl const &control, \
748 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.
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