LSST Applications g0b6bd0c080+a72a5dd7e6,g1182afd7b4+2a019aa3bb,g17e5ecfddb+2b8207f7de,g1d67935e3f+06cf436103,g38293774b4+ac198e9f13,g396055baef+6a2097e274,g3b44f30a73+6611e0205b,g480783c3b1+98f8679e14,g48ccf36440+89c08d0516,g4b93dc025c+98f8679e14,g5c4744a4d9+a302e8c7f0,g613e996a0d+e1c447f2e0,g6c8d09e9e7+25247a063c,g7271f0639c+98f8679e14,g7a9cd813b8+124095ede6,g9d27549199+a302e8c7f0,ga1cf026fa3+ac198e9f13,ga32aa97882+7403ac30ac,ga786bb30fb+7a139211af,gaa63f70f4e+9994eb9896,gabf319e997+ade567573c,gba47b54d5d+94dc90c3ea,gbec6a3398f+06cf436103,gc6308e37c7+07dd123edb,gc655b1545f+ade567573c,gcc9029db3c+ab229f5caf,gd01420fc67+06cf436103,gd877ba84e5+06cf436103,gdb4cecd868+6f279b5b48,ge2d134c3d5+cc4dbb2e3f,ge448b5faa6+86d1ceac1d,gecc7e12556+98f8679e14,gf3ee170dca+25247a063c,gf4ac96e456+ade567573c,gf9f5ea5b4d+ac198e9f13,gff490e6085+8c2580be5c,w.2022.27
LSST Data Management Base Package
MaskedImage Locators

(Return to Images)

(You might be interested to compare this example with the discussion of Image locators ; apart from an include file and a typedef, the only difference is the use of ImageT::Pixel(y, 0x1, 10) as the assigned pixel value instead of y).

Iterators provide access to an image, pixel by pixel. You often want access to neighbouring pixels (e.g. computing a gradient, or smoothing). Let's consider the problem of smoothing with a

1 2 1
2 4 2
1 2 1

kernel (the code's in maskedImage2.cc):

Start by including MaskedImage.h, defining a namespace for clarity:

#include "lsst/geom.h"
namespace image = lsst::afw::image;
using ImageT = image::MaskedImage<int>;
int main() {
afw::table::Key< afw::table::Array< ImagePixelT > > image
A class to manipulate images, masks, and variance as a single object.
Definition: MaskedImage.h:73
Backwards-compatibility support for depersisting the old Calib (FluxMag0/FluxMag0Err) objects.

Declare a MaskedImage

Set the image (but not the mask or variance) to a ramp

for (int y = 0; y != in.getHeight(); ++y) {
for (ImageT::xy_locator ptr = in.xy_at(0, y), end = in.xy_at(in.getWidth(), y); ptr != end;
++ptr.x()) {
*ptr = ImageT::Pixel(y, 0x1, 10);
}
int end
uint64_t * ptr
Definition: RangeSet.cc:88
int y
Definition: SpanSet.cc:48
float Pixel
Typedefs to be used for pixel values.
Definition: common.h:37
}

That didn't gain us much, did it? The code's a little messier than using x_iterator. But now we can add code to calculate the smoothed image. First make an output image, and copy the input pixels:

ImageT out(in.getDimensions()); // Make an output image the same size as the input image
out.assign(in);

(we didn't need to copy all of them, just the ones around the edge that we won't smooth, but this is an easy way to do it).

Now do the smoothing:

for (int y = 1; y != in.getHeight() - 1; ++y) {
for (ImageT::xy_locator ptr = in.xy_at(1, y), end = in.xy_at(in.getWidth() - 1, y),
optr = out.xy_at(1, y);
ptr != end; ++ptr.x(), ++optr.x()) {
*optr = ptr(-1, -1) + 2 * ptr(0, -1) + ptr(1, -1) + 2 * ptr(-1, 0) + 4 * ptr(0, 0) +
2 * ptr(1, 0) + ptr(-1, 1) + 2 * ptr(0, 1) + ptr(1, 1);
}
}

(N.b. you don't really want to do this; not only is this kernel separable into 1 2 1 in first the x then the y directions, but lsst::afw::math can do convolutions for you).

Here's a faster way to do the same thing (the use of an Image::Ptr is just for variety)

std::shared_ptr<ImageT> out2(new ImageT(in.getDimensions()));
out2->assign(in);
using xy_loc = ImageT::const_xy_locator;
for (int y = 1; y != in.getHeight() - 1; ++y) {
// "dot" means "cursor location" in emacs
xy_loc dot = in.xy_at(1, y), end = in.xy_at(in.getWidth() - 1, y);
xy_loc::cached_location_t nw = dot.cache_location(-1, -1);
xy_loc::cached_location_t n = dot.cache_location(0, -1);
xy_loc::cached_location_t ne = dot.cache_location(1, -1);
xy_loc::cached_location_t w = dot.cache_location(-1, 0);
xy_loc::cached_location_t c = dot.cache_location(0, 0);
xy_loc::cached_location_t e = dot.cache_location(1, 0);
xy_loc::cached_location_t sw = dot.cache_location(-1, 1);
xy_loc::cached_location_t s = dot.cache_location(0, 1);
xy_loc::cached_location_t se = dot.cache_location(1, 1);
for (ImageT::x_iterator optr = out2->row_begin(y) + 1; dot != end; ++dot.x(), ++optr) {
*optr = dot[nw] + 2 * dot[n] + dot[ne] + 2 * dot[w] + 4 * dot[c] + 2 * dot[e] + dot[sw] +
2 * dot[s] + dot[se];
}
def dot(symb, c, r, frame=None, size=2, ctype=None, origin=afwImage.PARENT, *args, **kwargs)
Definition: ds9.py:100
double w
Definition: CoaddPsf.cc:69
}

The xy_loc::cached_location_t variables remember relative positions.

We can rewrite this to move setting nw, se etc. out of the loop:

xy_loc pix11 = in.xy_at(1, 1);
xy_loc::cached_location_t nw = pix11.cache_location(-1, -1);
xy_loc::cached_location_t n = pix11.cache_location(0, -1);
xy_loc::cached_location_t ne = pix11.cache_location(1, -1);
xy_loc::cached_location_t w = pix11.cache_location(-1, 0);
xy_loc::cached_location_t c = pix11.cache_location(0, 0);
xy_loc::cached_location_t e = pix11.cache_location(1, 0);
xy_loc::cached_location_t sw = pix11.cache_location(-1, 1);
xy_loc::cached_location_t s = pix11.cache_location(0, 1);
xy_loc::cached_location_t se = pix11.cache_location(1, 1);
for (int y = 1; y != in.getHeight() - 1; ++y) {
// "dot" means "cursor location" in emacs
xy_loc dot = in.xy_at(1, y), end = in.xy_at(in.getWidth() - 1, y);
for (ImageT::x_iterator optr = out2->row_begin(y) + 1; dot != end; ++dot.x(), ++optr) {
*optr = dot[nw] + 2 * dot[n] + dot[ne] + 2 * dot[w] + 4 * dot[c] + 2 * dot[e] + dot[sw] +
2 * dot[s] + dot[se];
}
}

You may have noticed that that kernel isn't normalised. We could change the coefficients, but that'd slow things down for integer images (such as the one here); but we can normalise after the fact by making an Image that shares pixels with the central part of out2 and manipulating it via overloaded operator/=

{
ImageT center = ImageT(
*out2,
center /= 16;
}
An integer coordinate rectangle.
Definition: Box.h:55

N.b. you can use the iterator embedded in the locator directly if you really want to, e.g.

for (int y = 0; y != in.getHeight(); ++y) {
for (ImageT::xy_x_iterator ptr = in.xy_at(0, y).x(), end = in.xy_at(in.getWidth(), y).x(); ptr != end;
++ptr) {
*ptr = 0;
}
}

Note that this isn't quite the same x_iterator as before, due to the need to make the x_iterator move the underlying xy_locator.

Finally write some output files and close out main():

out.writeFits("foo.fits");
out2->writeFits("foo2.fits");
return 0;
}