53template <
typename OutImageT,
typename InImageT>
54void assertDimensionsOK(OutImageT
const& convolvedImage, InImageT
const& inImage,
56 if (convolvedImage.getDimensions() != inImage.getDimensions()) {
58 os <<
"convolvedImage dimensions = ( " << convolvedImage.getWidth() <<
", "
59 << convolvedImage.getHeight() <<
") != (" << inImage.getWidth() <<
", " << inImage.getHeight()
60 <<
") = inImage dimensions";
63 if (inImage.getWidth() < kernel.
getWidth() || inImage.getHeight() < kernel.
getHeight()) {
65 os <<
"inImage dimensions = ( " << inImage.getWidth() <<
", " << inImage.getHeight()
67 <<
") = kernel dimensions in width and/or height";
73 <<
") smaller than (1, 1) in width and/or height";
118template <
typename OutPixelT,
typename ImageIterT,
typename KernelIterT,
typename KernelPixelT>
119inline OutPixelT kernelDotProduct(
120 ImageIterT imageIter,
121 KernelIterT kernelIter,
124 OutPixelT outPixel(0);
125 for (
int x = 0;
x < kWidth; ++
x, ++imageIter, ++kernelIter) {
126 KernelPixelT kVal = *kernelIter;
128 outPixel +=
static_cast<OutPixelT
>((*imageIter) * kVal);
140template <
typename OutImageT,
typename InImageT>
147 LOGL_DEBUG(
"TRACE3.lsst.afw.math.convolve.basicConvolve",
148 "generic basicConvolve: dispatch to DeltaFunctionKernel basicConvolve");
153 LOGL_DEBUG(
"TRACE3.lsst.afw.math.convolve.basicConvolve",
154 "generic basicConvolve: dispatch to SeparableKernel basicConvolve");
160 "TRACE3.afw.math.convolve.basicConvolve",
161 "generic basicConvolve: dispatch to spatially varying LinearCombinationKernel basicConvolve");
169 LOGL_DEBUG(
"TRACE2.lsst.afw.math.convolve.basicConvolve",
170 "generic basicConvolve: using linear interpolation");
175 LOGL_DEBUG(
"TRACE2.lsst.afw.math.convolve.basicConvolve",
"generic basicConvolve: using brute force");
180template <
typename OutImageT,
typename InImageT>
185 assertDimensionsOK(convolvedImage, inImage, kernel);
187 int const mImageWidth = inImage.getWidth();
188 int const mImageHeight = inImage.getHeight();
189 int const cnvWidth = mImageWidth + 1 - kernel.
getWidth();
190 int const cnvHeight = mImageHeight + 1 - kernel.
getHeight();
191 int const cnvStartX = kernel.
getCtr().getX();
192 int const cnvStartY = kernel.
getCtr().getY();
193 int const inStartX = kernel.
getPixel().getX();
194 int const inStartY = kernel.
getPixel().getY();
196 LOGL_DEBUG(
"TRACE2.lsst.afw.math.convolve.basicConvolve",
"DeltaFunctionKernel basicConvolve");
198 for (
int i = 0; i < cnvHeight; ++i) {
199 typename InImageT::x_iterator inPtr = inImage.x_at(inStartX, i + inStartY);
200 for (
typename OutImageT::x_iterator cnvPtr = convolvedImage.x_at(cnvStartX, i + cnvStartY),
201 cnvEnd = cnvPtr + cnvWidth;
202 cnvPtr != cnvEnd; ++cnvPtr, ++inPtr) {
208template <
typename OutImageT,
typename InImageT>
214 LOGL_DEBUG(
"TRACE2.lsst.afw.math.convolve.basicConvolve",
215 "basicConvolve for LinearCombinationKernel: spatially invariant; using brute force");
225 refKernelPtr = kernel.
clone();
229 refKernelPtr = kernel.
clone();
232 LOGL_DEBUG(
"TRACE2.lsst.afw.math.convolve.basicConvolve",
233 "basicConvolve for LinearCombinationKernel: using interpolation");
236 LOGL_DEBUG(
"TRACE2.lsst.afw.math.convolve.basicConvolve",
237 "basicConvolve for LinearCombinationKernel: maxInterpolationError < 0; using brute "
245template <
typename OutImageT,
typename InImageT>
250 using KernelIterator = KernelVector::const_iterator;
251 using InXIterator =
typename InImageT::const_x_iterator;
252 using InXYLocator =
typename InImageT::const_xy_locator;
253 using OutXIterator =
typename OutImageT::x_iterator;
254 using OutYIterator =
typename OutImageT::y_iterator;
255 using OutPixel =
typename OutImageT::SinglePixel;
257 assertDimensionsOK(convolvedImage, inImage, kernel);
262 KernelVector kernelXVec(kernel.
getWidth());
263 KernelVector kernelYVec(kernel.
getHeight());
266 LOGL_DEBUG(
"TRACE2.lsst.afw.math.convolve.basicConvolve",
267 "SeparableKernel basicConvolve: kernel is spatially varying");
269 for (
int cnvY = goodBBox.
getMinY(); cnvY <= goodBBox.
getMaxY(); ++cnvY) {
270 double const rowPos = inImage.indexToPosition(cnvY,
image::Y);
272 InXYLocator inImLoc = inImage.xy_at(0, cnvY - goodBBox.
getMinY());
273 OutXIterator cnvXIter = convolvedImage.row_begin(cnvY) + goodBBox.
getMinX();
275 ++cnvX, ++inImLoc.x(), ++cnvXIter) {
276 double const colPos = inImage.indexToPosition(cnvX,
image::X);
282 *cnvXIter = math::convolveAtAPoint<OutImageT, InImageT>(inImLoc, kernelXVec, kernelYVec);
284 *cnvXIter = *cnvXIter / kSum;
298 LOGL_DEBUG(
"TRACE2.lsst.afw.math.convolve.basicConvolve",
299 "SeparableKernel basicConvolve: kernel is spatially invariant");
302 KernelIterator
const kernelXVecBegin = kernelXVec.begin();
303 KernelIterator
const kernelYVecBegin = kernelYVec.begin();
310 int const yPrefillEnd = buffer.getHeight() - 1;
311 for (; yInd < yPrefillEnd; ++yInd) {
312 OutXIterator bufXIter = buffer.x_at(0, yInd);
313 OutXIterator
const bufXEnd = buffer.x_at(goodBBox.
getWidth(), yInd);
314 InXIterator inXIter = inImage.x_at(0, yInd);
315 for (; bufXIter != bufXEnd; ++bufXIter, ++inXIter) {
316 *bufXIter = kernelDotProduct<OutPixel, InXIterator, KernelIterator, KernelPixel>(
317 inXIter, kernelXVecBegin, kernel.
getWidth());
322 int inY = yPrefillEnd;
323 int bufY = yPrefillEnd;
327 InXIterator inXIter = inImage.x_at(0, inY);
328 OutXIterator bufXIter = buffer.x_at(0, bufY);
329 OutXIterator cnvXIter = convolvedImage.x_at(goodBBox.
getMinX(), cnvY);
330 for (
int bufX = 0; bufX < goodBBox.
getWidth(); ++bufX, ++cnvXIter, ++bufXIter, ++inXIter) {
333 *bufXIter = kernelDotProduct<OutPixel, InXIterator, KernelIterator, KernelPixel>(
334 inXIter, kernelXVecBegin, kernel.
getWidth());
336 OutYIterator bufYIter = buffer.y_at(bufX, 0);
337 *cnvXIter = kernelDotProduct<OutPixel, OutYIterator, KernelIterator, KernelPixel>(
338 bufYIter, kernelYVecBegin, kernel.
getHeight());
343 if (cnvY >= goodBBox.
getMaxY())
break;
349 std::rotate(kernelYVec.begin(), kernelYVec.end() - 1, kernelYVec.end());
354template <
typename OutImageT,
typename InImageT>
362 using KernelXIterator =
typename KernelImage::const_x_iterator;
363 using KernelXYLocator =
typename KernelImage::const_xy_locator;
364 using InXIterator =
typename InImageT::const_x_iterator;
365 using InXYLocator =
typename InImageT::const_xy_locator;
366 using OutXIterator =
typename OutImageT::x_iterator;
367 using OutPixel =
typename OutImageT::SinglePixel;
369 assertDimensionsOK(convolvedImage, inImage, kernel);
371 int const inImageWidth = inImage.getWidth();
372 int const inImageHeight = inImage.getHeight();
373 int const kWidth = kernel.
getWidth();
375 int const cnvWidth = inImageWidth + 1 - kernel.
getWidth();
376 int const cnvHeight = inImageHeight + 1 - kernel.
getHeight();
377 int const cnvStartX = kernel.
getCtr().getX();
378 int const cnvStartY = kernel.
getCtr().getY();
379 int const cnvEndX = cnvStartX + cnvWidth;
380 int const cnvEndY = cnvStartY + cnvHeight;
383 KernelXYLocator
const kernelLoc = kernelImage.xy_at(0, 0);
386 LOGL_DEBUG(
"TRACE4.lsst.afw.math.convolve.convolveWithBruteForce",
387 "convolveWithBruteForce: kernel is spatially varying");
389 for (
int cnvY = cnvStartY; cnvY != cnvEndY; ++cnvY) {
390 double const rowPos = inImage.indexToPosition(cnvY,
image::Y);
392 InXYLocator inImLoc = inImage.xy_at(0, cnvY - cnvStartY);
393 OutXIterator cnvXIter = convolvedImage.x_at(cnvStartX, cnvY);
394 for (
int cnvX = cnvStartX; cnvX != cnvEndX; ++cnvX, ++inImLoc.x(), ++cnvXIter) {
395 double const colPos = inImage.indexToPosition(cnvX,
image::X);
397 KernelPixel kSum = kernel.
computeImage(kernelImage,
false, colPos, rowPos);
398 *cnvXIter = math::convolveAtAPoint<OutImageT, InImageT>(inImLoc, kernelLoc, kWidth, kHeight);
400 *cnvXIter = *cnvXIter / kSum;
405 LOGL_DEBUG(
"TRACE4.lsst.afw.math.convolve.convolveWithBruteForce",
406 "convolveWithBruteForce: kernel is spatially invariant");
410 for (
int inStartY = 0, cnvY = cnvStartY; inStartY < cnvHeight; ++inStartY, ++cnvY) {
411 KernelXIterator kernelXIter = kernelImage.x_at(0, 0);
412 InXIterator inXIter = inImage.x_at(0, inStartY);
413 OutXIterator cnvXIter = convolvedImage.x_at(cnvStartX, cnvY);
414 for (
int x = 0;
x < cnvWidth; ++
x, ++cnvXIter, ++inXIter) {
415 *cnvXIter = kernelDotProduct<OutPixel, InXIterator, KernelXIterator, KernelPixel>(
416 inXIter, kernelXIter, kWidth);
418 for (
int kernelY = 1, inY = inStartY + 1; kernelY < kHeight; ++inY, ++kernelY) {
419 KernelXIterator kernelXIter = kernelImage.x_at(0, kernelY);
420 InXIterator inXIter = inImage.x_at(0, inY);
421 OutXIterator cnvXIter = convolvedImage.x_at(cnvStartX, cnvY);
422 for (
int x = 0;
x < cnvWidth; ++
x, ++cnvXIter, ++inXIter) {
423 *cnvXIter += kernelDotProduct<OutPixel, InXIterator, KernelXIterator, KernelPixel>(
424 inXIter, kernelXIter, kWidth);
435#define IMAGE(PIXTYPE) image::Image<PIXTYPE>
436#define MASKEDIMAGE(PIXTYPE) image::MaskedImage<PIXTYPE, image::MaskPixel, image::VariancePixel>
439#define INSTANTIATE_IM_OR_MI(IMGMACRO, OUTPIXTYPE, INPIXTYPE) \
440 template void basicConvolve(IMGMACRO(OUTPIXTYPE)&, IMGMACRO(INPIXTYPE) const &, math::Kernel const&, \
441 math::ConvolutionControl const&); \
442 NL template void basicConvolve(IMGMACRO(OUTPIXTYPE)&, IMGMACRO(INPIXTYPE) const &, \
443 math::DeltaFunctionKernel const&, math::ConvolutionControl const&); \
444 NL template void basicConvolve(IMGMACRO(OUTPIXTYPE)&, IMGMACRO(INPIXTYPE) const &, \
445 math::LinearCombinationKernel const&, math::ConvolutionControl const&); \
446 NL template void basicConvolve(IMGMACRO(OUTPIXTYPE)&, IMGMACRO(INPIXTYPE) const &, \
447 math::SeparableKernel const&, math::ConvolutionControl const&); \
448 NL template void convolveWithBruteForce(IMGMACRO(OUTPIXTYPE)&, IMGMACRO(INPIXTYPE) const &, \
449 math::Kernel const&, math::ConvolutionControl const&);
451#define INSTANTIATE(OUTPIXTYPE, INPIXTYPE) \
452 INSTANTIATE_IM_OR_MI(IMAGE, OUTPIXTYPE, INPIXTYPE) \
453 INSTANTIATE_IM_OR_MI(MASKEDIMAGE, OUTPIXTYPE, INPIXTYPE)
#define IS_INSTANCE(A, B)
#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.
A class to represent a 2-dimensional array of pixels.
Parameters to control convolution.
int getMaxInterpolationDistance() const
bool getDoNormalize() const
A kernel that has only one non-zero pixel (of value 1)
lsst::geom::Point2I getPixel() const
Kernels are used for convolution with MaskedImages and (eventually) Images.
lsst::geom::Extent2I const getDimensions() const
Return the Kernel's dimensions (width, height)
int getHeight() const
Return the Kernel's height.
lsst::geom::Point2I getCtr() const
Return index of kernel's center.
unsigned int getNKernelParameters() const
Return the number of kernel parameters (0 if none)
int getNSpatialParameters() const
Return the number of spatial parameters (0 if not spatially varying)
lsst::geom::Box2I shrinkBBox(lsst::geom::Box2I const &bbox) const
Given a bounding box for an image one wishes to convolve with this kernel, return the bounding box fo...
int getWidth() const
Return the Kernel's width.
bool isSpatiallyVarying() const
Return true iff the kernel is spatially varying (has a spatial function)
double computeImage(lsst::afw::image::Image< Pixel > &image, bool doNormalize, double x=0.0, double y=0.0) const
Compute an image (pixellized representation of the kernel) in place.
A kernel that is a linear combination of fixed basis kernels.
std::shared_ptr< Kernel > refactor() const
Refactor the kernel as a linear combination of N bases where N is the number of parameters for the sp...
std::shared_ptr< Kernel > clone() const override
Return a pointer to a deep copy of this kernel.
A kernel described by a pair of functions: func(x, y) = colFunc(x) * rowFunc(y)
double computeVectors(std::vector< Pixel > &colList, std::vector< Pixel > &rowList, bool doNormalize, double x=0.0, double y=0.0) const
Compute the column and row arrays in place, where kernel(col, row) = colList(col) * rowList(row)
An integer coordinate rectangle.
int getMinY() const noexcept
int getMinX() const noexcept
int getWidth() const noexcept
int getMaxX() const noexcept
int getMaxY() const noexcept
Reports invalid arguments.
void convolveWithInterpolation(OutImageT &outImage, InImageT const &inImage, lsst::afw::math::Kernel const &kernel, ConvolutionControl const &convolutionControl)
Convolve an Image or MaskedImage with a spatially varying Kernel using linear interpolation.
void basicConvolve(OutImageT &convolvedImage, InImageT const &inImage, lsst::afw::math::Kernel const &kernel, lsst::afw::math::ConvolutionControl const &convolutionControl)
Low-level convolution function that does not set edge pixels.
void convolveWithBruteForce(OutImageT &convolvedImage, InImageT const &inImage, lsst::afw::math::Kernel const &kernel, lsst::afw::math::ConvolutionControl const &convolutionControl)
Convolve an Image or MaskedImage with a Kernel by computing the kernel image at every point.