LSSTApplications  10.0+286,10.0+36,10.0+46,10.0-2-g4f67435,10.1+152,10.1+37,11.0,11.0+1,11.0-1-g47edd16,11.0-1-g60db491,11.0-1-g7418c06,11.0-2-g04d2804,11.0-2-g68503cd,11.0-2-g818369d,11.0-2-gb8b8ce7
LSSTDataManagementBasePackage
How to display images

How to display images

Displaying images in the LSST framework

In April 2015 we restructured the image display code to be backend agnostic, while still only supporting a virtualDevice (which does nothing), and ds9 (http://hea-www.harvard.edu/RD/ds9) for the present. At the same time we made the interface object oriented — opening a display returns an object (the old functional interface with a frame argument is supported for backward compatibility).

Although ds9 has a number of drawbacks (slow; slightly unstable API; awkward to use with firewalls) it also as a number of advantages: Supported by the CfA; ubiquitous in astronomy; support for WCS; support for multiple frames.

The basic functionality is in lsst.afw.display, and the workhorse routine is mtv. The simplest use is simply to setup afw and display_ds9, start ds9 (you may have to install it yourself) and python, and type:

import lsst.afw.display as afwDisplay
import lsst.afw.image as afwImage
im = afwImage.ImageF("myFile.fits")
disp = afwDisplay.getDisplay()
disp.mtv(im)

Rather than saving disp in a variable, you can say

afwDisplay.getDisplay().mtv(im)

and if the display doesn't exist it'll be created for you. You can say e.g.

disp = afwDisplay.getDisplay(2, "virtualDisplay")

to create a rather useless display named "2" using the do-nothing virtualDisplay device; you're more likely to specify "ds9" or (soon) "firefly".

You can display images in multiple frames, either by explicitly specifying the frame, or by setting the default with afwDisplay.setDefaultFrame(). If you are using the ds9 backend and there's a strong desire for multiple instances of ds9 this could be supported (by choosing different tcp ports); file a ticket if this is really important for you. ds9 uses integers as its `frame' identifiers, but other display devices may use other types (e.g. strings).

See the manual at lsst.afw.display.interface.Display for all the supported commands, and the iPython notebook at examples/imageDisplay.ipynb for examples (we'll provide an nbviewer link once this is pushed to github).

Whenever you're writing more than a few glyphs we strongly recommend that you turn on buffering (it's certainly necessary when you're using the ds9 backend). The simplest way to do this is by using python's with statement:

with disp.Buffering():
for source in sourceList:
disp.dot("o", source.getXAstrom(), source.getYAstrom())

You can explicitly flush the buffer at anytime with disp.flush()

How afwDisplay handles different Image types

The mtv command handles all of the LSST image types:

Image

The pixels are displayed on your image display. Images don't have a Wcs, so no astronomical WCS information is available, but we do support WCSA and WCSB; the former is 0-indexed pixel coordinates allowing for the Image's XY0; the latter is 0-indexed pixel coordinates relative to the bottom left pixel being (0, 0).

mtv accepts an optional wcs argument, which allows you to provide an astronomical Wcs (but if you have an DecoratedImage or Exposure this is done for you)

DecoratedImage

Displayed like Image, but with the default WCS set from the DecoratedImage's Wcs

Mask

Overlay the current display with the Mask.

If you're using the ds9 backend there's a bug that prevents you from displaying a pure Mask, but you can use the isMask argument to force the Mask to be treated as a 16-bit image. Also, the Mask display isn't as fast as you might like, as ds9's current API requires us to send each mask plane as a separate 16-bit image (I have asked Bill Joy at CfA to change this). Each bitplane may be given a separate colour; you can inspect the current mapping with getMaskPlaneColor or set it with setMaskPlaneColor. If a mask plane has no defined colour, one will be chosen for you.

You may vary the mask's transparency, either via the GUI or with e.g. setMaskTransparency(50).

MaskedImage

The image-plane pixels are displayed, overlaid with the mask. If you want to look at the variance too, you'll need to say something like:

mi = afwImage.MaskedImageF("mi.fits")
disp.mtv(mi)
afwDisplay.getDisplay(1).mtv(mi.getVariance())
afwDisplay.getDisplay(1).mtv(mi.getMask())

Exposure
Displayed like MaskedImage, but with the default WCS set from the MaskedImage's Wcs

How to Configure the Display Device

You won't need to configure the display code at all if you're happy with ds9, as it is the default-default backend when available (i.e. if you have display_ds9 setup).

At some point we may add a configuration mechanism using environment variables and/or dot files, but for the present the recommended way to set your preferred display environment is by using python's startup file. E.g. if you have the environment variable $PYTHONSTARTUP set to ~/.pythonrc, you'd edit ~.pythonrc to say something like:

try:
import lsst.afw.display as afwDisplay
except ImportError:
afwDisplay = None
if afwDisplay:
try:
afwDisplay.setDefaultBackend("myDevice") # or "ds9"
except RuntimeError as e:
print e
afwDisplay.setDefaultMaskTransparency(75)

How to build a mosaic image

There are facilities to build mosaics of images in lsst.afw.display.utils (n.b. these are mosaics in the sense of a tiled floor; we're not building astrophysical mosaics projected on the sky.)

The basic class is Mosaic:

m = Mosaic()
m.setGutter(5)
m.setBackground(10)
m.setMode("square") # the default
mosaic = m.makeMosaic(im1, im2, im3) # build the mosaic
display = afwDisplay.getDisplay()
display.mtv(mosaic) # display it
m.drawLabels(["Label 1", "Label 2", "Label 3"], display) # label the panels
# alternative way to build a mosaic
images = [im1, im2, im3]
labels = ["Label 1", "Label 2", "Label 3"]
mosaic = m.makeMosaic(images)
display.mtv(mosaic)
m.drawLabels(labels, display)
# Yet another way to build a mosaic (no need to build the images/labels lists)
for i in range(len(images)):
m.append(images[i], labels[i])
mosaic = m.makeMosaic()
display.mtv(mosaic)
m.drawLabels(display=display)

You can return the (ix, iy)th (or nth) bounding box with getBBox()

See lsst.afw.display.utils.Mosaic for more details; there are examples near the end of the iPython notebook at examples/imageDisplay.ipynb (we'll provide an nbviewer link once this is pushed to github).

How to use ds9 through a firewall

If you're using ds9 you may want to use it through a firewall; here's how.

On your home machine, type

export XPA_PORT="DS9:ds9 22345 22346"
# ^^^^^ ^^^^^
# Choose any 2 consecutive numbers over 4095
ssh -N -f lsstXXX.ncsa.uiuc.edu -R 22345:localhost:22345 -R 22346:localhost:22346 > /dev/null 2>&1
ds9 &

(setenv XPA_PORT "DS9:ds9 22345 22346" for csh users, of course)

On lsstXXX.ncsa.uiuc.edu, set XPA_PORT to the same value, setup display_ds9 and afw, start python, import lsst.afw.display as afwDisplay and proceed:

export XPA_PORT="DS9:ds9 22345 22346"
python
>>> import lsst.afw.display as afwDisplay
>>> afwDisplay.setBackend("ds9") # the default, so probably not needed
>>> afwDisplay.getDisplay().erase()

xpa afficianados will note that I'm bypassing the xpa name server; it needs another set of 2 or 3 ports tunnelled, and setting up ACLs.

Here's a script (in afw/examples) to run on your home machine that should simplify setting up/tearing down the ssh tunnels. It's not great, so improvements would/will be welcomed.