54 template <
typename OutImageT,
typename InImageT>
55 void assertDimensionsOK(OutImageT
const& convolvedImage, InImageT
const& inImage,
57 if (convolvedImage.getDimensions() != inImage.getDimensions()) {
59 os <<
"convolvedImage dimensions = ( " << convolvedImage.getWidth() <<
", " 60 << convolvedImage.getHeight() <<
") != (" << inImage.getWidth() <<
", " << inImage.getHeight()
61 <<
") = inImage dimensions";
64 if (inImage.getWidth() < kernel.
getWidth() || inImage.getHeight() < kernel.
getHeight()) {
66 os <<
"inImage dimensions = ( " << inImage.getWidth() <<
", " << inImage.getHeight()
68 <<
") = kernel dimensions in width and/or height";
74 <<
") smaller than (1, 1) in width and/or height";
119 template <
typename OutPixelT,
typename ImageIterT,
typename KernelIterT,
typename KernelPixelT>
120 inline OutPixelT kernelDotProduct(
121 ImageIterT imageIter,
122 KernelIterT kernelIter,
125 OutPixelT outPixel(0);
126 for (
int x = 0;
x < kWidth; ++
x, ++imageIter, ++kernelIter) {
127 KernelPixelT kVal = *kernelIter;
129 outPixel +=
static_cast<OutPixelT
>((*imageIter) * kVal);
141 template <
typename OutImageT,
typename InImageT>
148 LOGL_DEBUG(
"TRACE3.afw.math.convolve.basicConvolve",
149 "generic basicConvolve: dispatch to DeltaFunctionKernel basicConvolve");
150 basicConvolve(convolvedImage, inImage, *dynamic_cast<math::DeltaFunctionKernel const*>(&kernel),
154 LOGL_DEBUG(
"TRACE3.afw.math.convolve.basicConvolve",
155 "generic basicConvolve: dispatch to SeparableKernel basicConvolve");
156 basicConvolve(convolvedImage, inImage, *dynamic_cast<math::SeparableKernel const*>(&kernel),
161 "TRACE3.afw.math.convolve.basicConvolve",
162 "generic basicConvolve: dispatch to spatially varying LinearCombinationKernel basicConvolve");
163 basicConvolve(convolvedImage, inImage, *dynamic_cast<math::LinearCombinationKernel const*>(&kernel),
170 LOGL_DEBUG(
"TRACE2.afw.math.convolve.basicConvolve",
171 "generic basicConvolve: using linear interpolation");
176 LOGL_DEBUG(
"TRACE2.afw.math.convolve.basicConvolve",
"generic basicConvolve: using brute force");
181 template <
typename OutImageT,
typename InImageT>
186 assertDimensionsOK(convolvedImage, inImage, kernel);
188 int const mImageWidth = inImage.getWidth();
189 int const mImageHeight = inImage.getHeight();
190 int const cnvWidth = mImageWidth + 1 - kernel.
getWidth();
191 int const cnvHeight = mImageHeight + 1 - kernel.
getHeight();
192 int const cnvStartX = kernel.
getCtrX();
193 int const cnvStartY = kernel.
getCtrY();
194 int const inStartX = kernel.
getPixel().getX();
195 int const inStartY = kernel.
getPixel().getY();
197 LOGL_DEBUG(
"TRACE2.afw.math.convolve.basicConvolve",
"DeltaFunctionKernel basicConvolve");
199 for (
int i = 0; i < cnvHeight; ++i) {
200 typename InImageT::x_iterator inPtr = inImage.x_at(inStartX, i + inStartY);
201 for (
typename OutImageT::x_iterator cnvPtr = convolvedImage.x_at(cnvStartX, i + cnvStartY),
202 cnvEnd = cnvPtr + cnvWidth;
203 cnvPtr != cnvEnd; ++cnvPtr, ++inPtr) {
209 template <
typename OutImageT,
typename InImageT>
215 LOGL_DEBUG(
"TRACE2.afw.math.convolve.basicConvolve",
216 "basicConvolve for LinearCombinationKernel: spatially invariant; using brute force");
226 refKernelPtr = kernel.
clone();
230 refKernelPtr = kernel.
clone();
233 LOGL_DEBUG(
"TRACE2.afw.math.convolve.basicConvolve",
234 "basicConvolve for LinearCombinationKernel: using interpolation");
237 LOGL_DEBUG(
"TRACE2.afw.math.convolve.basicConvolve",
238 "basicConvolve for LinearCombinationKernel: maxInterpolationError < 0; using brute " 246 template <
typename OutImageT,
typename InImageT>
251 typedef KernelVector::const_iterator KernelIterator;
252 typedef typename InImageT::const_x_iterator InXIterator;
253 typedef typename InImageT::const_xy_locator InXYLocator;
254 typedef typename OutImageT::x_iterator OutXIterator;
255 typedef typename OutImageT::y_iterator OutYIterator;
256 typedef typename OutImageT::SinglePixel OutPixel;
258 assertDimensionsOK(convolvedImage, inImage, kernel);
263 KernelVector kernelXVec(kernel.
getWidth());
264 KernelVector kernelYVec(kernel.
getHeight());
267 LOGL_DEBUG(
"TRACE2.afw.math.convolve.basicConvolve",
268 "SeparableKernel basicConvolve: kernel is spatially varying");
270 for (
int cnvY = goodBBox.
getMinY(); cnvY <= goodBBox.
getMaxY(); ++cnvY) {
271 double const rowPos = inImage.indexToPosition(cnvY,
image::Y);
273 InXYLocator inImLoc = inImage.xy_at(0, cnvY - goodBBox.
getMinY());
274 OutXIterator cnvXIter = convolvedImage.row_begin(cnvY) + goodBBox.
getMinX();
276 ++cnvX, ++inImLoc.x(), ++cnvXIter) {
277 double const colPos = inImage.indexToPosition(cnvX,
image::X);
283 *cnvXIter = math::convolveAtAPoint<OutImageT, InImageT>(inImLoc, kernelXVec, kernelYVec);
285 *cnvXIter = *cnvXIter / kSum;
299 LOGL_DEBUG(
"TRACE2.afw.math.convolve.basicConvolve",
300 "SeparableKernel basicConvolve: kernel is spatially invariant");
303 KernelIterator
const kernelXVecBegin = kernelXVec.begin();
304 KernelIterator
const kernelYVecBegin = kernelYVec.begin();
311 int const yPrefillEnd = buffer.getHeight() - 1;
312 for (; yInd < yPrefillEnd; ++yInd) {
313 OutXIterator bufXIter = buffer.x_at(0, yInd);
314 OutXIterator
const bufXEnd = buffer.x_at(goodBBox.
getWidth(), yInd);
315 InXIterator inXIter = inImage.x_at(0, yInd);
316 for (; bufXIter != bufXEnd; ++bufXIter, ++inXIter) {
317 *bufXIter = kernelDotProduct<OutPixel, InXIterator, KernelIterator, KernelPixel>(
318 inXIter, kernelXVecBegin, kernel.
getWidth());
323 int inY = yPrefillEnd;
324 int bufY = yPrefillEnd;
328 InXIterator inXIter = inImage.x_at(0, inY);
329 OutXIterator bufXIter = buffer.x_at(0, bufY);
330 OutXIterator cnvXIter = convolvedImage.x_at(goodBBox.
getMinX(), cnvY);
331 for (
int bufX = 0; bufX < goodBBox.
getWidth(); ++bufX, ++cnvXIter, ++bufXIter, ++inXIter) {
334 *bufXIter = kernelDotProduct<OutPixel, InXIterator, KernelIterator, KernelPixel>(
335 inXIter, kernelXVecBegin, kernel.
getWidth());
337 OutYIterator bufYIter = buffer.y_at(bufX, 0);
338 *cnvXIter = kernelDotProduct<OutPixel, OutYIterator, KernelIterator, KernelPixel>(
339 bufYIter, kernelYVecBegin, kernel.
getHeight());
344 if (cnvY >= goodBBox.
getMaxY())
break;
350 std::rotate(kernelYVec.begin(), kernelYVec.end() - 1, kernelYVec.end());
355 template <
typename OutImageT,
typename InImageT>
363 typedef typename KernelImage::const_x_iterator KernelXIterator;
364 typedef typename KernelImage::const_xy_locator KernelXYLocator;
365 typedef typename InImageT::const_x_iterator InXIterator;
366 typedef typename InImageT::const_xy_locator InXYLocator;
367 typedef typename OutImageT::x_iterator OutXIterator;
368 typedef typename OutImageT::SinglePixel OutPixel;
370 assertDimensionsOK(convolvedImage, inImage, kernel);
372 int const inImageWidth = inImage.getWidth();
373 int const inImageHeight = inImage.getHeight();
374 int const kWidth = kernel.
getWidth();
376 int const cnvWidth = inImageWidth + 1 - kernel.
getWidth();
377 int const cnvHeight = inImageHeight + 1 - kernel.
getHeight();
378 int const cnvStartX = kernel.
getCtrX();
379 int const cnvStartY = kernel.
getCtrY();
380 int const cnvEndX = cnvStartX + cnvWidth;
381 int const cnvEndY = cnvStartY + cnvHeight;
384 KernelXYLocator
const kernelLoc = kernelImage.xy_at(0, 0);
387 LOGL_DEBUG(
"TRACE4.afw.math.convolve.convolveWithBruteForce",
388 "convolveWithBruteForce: kernel is spatially varying");
390 for (
int cnvY = cnvStartY; cnvY != cnvEndY; ++cnvY) {
391 double const rowPos = inImage.indexToPosition(cnvY,
image::Y);
393 InXYLocator inImLoc = inImage.xy_at(0, cnvY - cnvStartY);
394 OutXIterator cnvXIter = convolvedImage.x_at(cnvStartX, cnvY);
395 for (
int cnvX = cnvStartX; cnvX != cnvEndX; ++cnvX, ++inImLoc.x(), ++cnvXIter) {
396 double const colPos = inImage.indexToPosition(cnvX,
image::X);
398 KernelPixel kSum = kernel.
computeImage(kernelImage,
false, colPos, rowPos);
399 *cnvXIter = math::convolveAtAPoint<OutImageT, InImageT>(inImLoc, kernelLoc, kWidth, kHeight);
401 *cnvXIter = *cnvXIter / kSum;
406 LOGL_DEBUG(
"TRACE4.afw.math.convolve.convolveWithBruteForce",
407 "convolveWithBruteForce: kernel is spatially invariant");
411 for (
int inStartY = 0, cnvY = cnvStartY; inStartY < cnvHeight; ++inStartY, ++cnvY) {
412 KernelXIterator kernelXIter = kernelImage.x_at(0, 0);
413 InXIterator inXIter = inImage.x_at(0, inStartY);
414 OutXIterator cnvXIter = convolvedImage.x_at(cnvStartX, cnvY);
415 for (
int x = 0;
x < cnvWidth; ++
x, ++cnvXIter, ++inXIter) {
416 *cnvXIter = kernelDotProduct<OutPixel, InXIterator, KernelXIterator, KernelPixel>(
417 inXIter, kernelXIter, kWidth);
419 for (
int kernelY = 1, inY = inStartY + 1; kernelY < kHeight; ++inY, ++kernelY) {
420 KernelXIterator kernelXIter = kernelImage.x_at(0, kernelY);
421 InXIterator inXIter = inImage.x_at(0, inY);
422 OutXIterator cnvXIter = convolvedImage.x_at(cnvStartX, cnvY);
423 for (
int x = 0;
x < cnvWidth; ++
x, ++cnvXIter, ++inXIter) {
424 *cnvXIter += kernelDotProduct<OutPixel, InXIterator, KernelXIterator, KernelPixel>(
425 inXIter, kernelXIter, kWidth);
436 #define IMAGE(PIXTYPE) image::Image<PIXTYPE> 437 #define MASKEDIMAGE(PIXTYPE) image::MaskedImage<PIXTYPE, image::MaskPixel, image::VariancePixel> 440 #define INSTANTIATE_IM_OR_MI(IMGMACRO, OUTPIXTYPE, INPIXTYPE) \ 441 template void basicConvolve(IMGMACRO(OUTPIXTYPE)&, IMGMACRO(INPIXTYPE) const &, math::Kernel const&, \ 442 math::ConvolutionControl const&); \ 443 NL template void basicConvolve(IMGMACRO(OUTPIXTYPE)&, IMGMACRO(INPIXTYPE) const &, \ 444 math::DeltaFunctionKernel const&, math::ConvolutionControl const&); \ 445 NL template void basicConvolve(IMGMACRO(OUTPIXTYPE)&, IMGMACRO(INPIXTYPE) const &, \ 446 math::LinearCombinationKernel const&, math::ConvolutionControl const&); \ 447 NL template void basicConvolve(IMGMACRO(OUTPIXTYPE)&, IMGMACRO(INPIXTYPE) const &, \ 448 math::SeparableKernel const&, math::ConvolutionControl const&); \ 449 NL template void convolveWithBruteForce(IMGMACRO(OUTPIXTYPE)&, IMGMACRO(INPIXTYPE) const &, \ 450 math::Kernel const&, math::ConvolutionControl const&); 452 #define INSTANTIATE(OUTPIXTYPE, INPIXTYPE) \ 453 INSTANTIATE_IM_OR_MI(IMAGE, OUTPIXTYPE, INPIXTYPE) \ 454 INSTANTIATE_IM_OR_MI(MASKEDIMAGE, OUTPIXTYPE, INPIXTYPE) int getHeight() const
Return the Kernel's height.
int getCtrX() const
Return x index of kernel's center.
int getCtrY() const
Return y index of kernel's center.
Parameters to control convolution.
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 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...
#define LOGL_DEBUG(logger, message...)
Log a debug-level message using a varargs/printf style interface.
LSST DM logging module built on log4cxx.
bool isSpatiallyVarying() const
Return true iff the kernel is spatially varying (has a spatial function)
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) ...
A base class for image defects.
int getMaxY() const noexcept
A kernel that is a linear combination of fixed basis kernels.
int getMaxInterpolationDistance() const
int getWidth() const noexcept
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.
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.
int getMaxX() const noexcept
int getWidth() const
Return the Kernel's width.
lsst::geom::Extent2I const getDimensions() const
Return the Kernel's dimensions (width, height)
unsigned int getNKernelParameters() const
Return the number of kernel parameters (0 if none)
int getMinX() const noexcept
#define LSST_EXCEPT(type,...)
Create an exception with a given type.
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 getNSpatialParameters() const
Return the number of spatial parameters (0 if not spatially varying)
Reports invalid arguments.
lsst::geom::Point2I getPixel() const
bool getDoNormalize() const
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...
Kernels are used for convolution with MaskedImages and (eventually) Images.
An integer coordinate rectangle.
A class to represent a 2-dimensional array of pixels.
#define IS_INSTANCE(A, B)
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.
A kernel that has only one non-zero pixel (of value 1)
int getMinY() const noexcept
#define INSTANTIATE(FROMSYS, TOSYS)