103.5. Image display with Firefly#
103.5. Image display with Firefly¶
For the Rubin Science Platform at data.lsst.cloud.
Data Release: Data Preview 1
Container Size: large
LSST Science Pipelines version: r29.2.0
Last verified to run: 2025-09-30
Repository: github.com/lsst/tutorial-notebooks
Learning objective: How to use the Firefly interactive image display tool.
LSST data products: deep_coadd
Packages: lsst.daf.butler
, lsst.afw.display
, firefly_client
Credit: Originally developed by the Rubin Community Science team. Please consider acknowledging them if this notebook is used for the preparation of journal articles, software releases, or other notebooks.
Get Support: Everyone is encouraged to ask questions or raise issues in the Support Category of the Rubin Community Forum. Rubin staff will respond to all questions posted there.
1. Introduction¶
Firefly is an interactive web user interface for astronomers that provides a table display, FITS/HiPS image visualization, and customizable 2D plots. All three highly interactive components can be used together on the same data set.
Firefly forms the basis of the Portal Aspect. From Notebooks, the Firefly FITS image visualization panel is recommended for displaying and manipulating images, as demonstrated in this tutorial.
Firefly development by IPAC at Caltech has been supported by NASA, principally through IRSA, and by the National Science Foundation, through the Vera C. Rubin Observatory. Firefly is open-source software, available on GitHub (Caltech-IPAC/firefly) and DockerHub (ipac/firefly).
Related tutorials: The 100-level series on the Butler demonstrates how to find and retrieve images.
1.1. Import packages¶
Import the Butler
module from the lsst.daf.butler
package, the display
module from the lsst.afw
package (for image display), and the firefly_client.plot
module (for FireflyClient).
import numpy as np
from lsst.daf.butler import Butler
import lsst.afw.display as afwDisplay
import firefly_client.plot as ffplt
1.2. Define parameters and functions¶
Instantiate the butler.
butler = Butler("dp1", collections="LSSTComCam/DP1")
assert butler is not None
2. Start Firefly¶
Set afwDisplay
to use Firefly, and define afw_display
to show images in frame 1. Firefly will open in a new tab.
afwDisplay.setDefaultBackend("firefly")
afw_display = afwDisplay.Display(frame=1)
2.1. Set up a side-by-side view¶
To set up the JupyterLab main work area for side-by-side viewing of notebook and Firefly, click on the "Firefly Viewer" tab and drag it to the right, then down to the middle of the screen. A blue box will appear indicating the new position of the tab. Release the mouse button.
Figure 1: Side-by-side view of a Jupyter notebook and the Firefly viewer.
3. Display an image¶
Define an RA, Dec, and band (filter). These coordinates are near the center of the Extended Chandra Deep Field South (ECDFS).
ra = 53.076
dec = -28.110
band = 'r'
Define a query string using the coordinates and band as search constraints.
Query the butler for matching deep_coadd
images and retrieve the first on the list.
query = "band.name = :band AND patch.region OVERLAPS POINT(:ra, :dec)"
bind = {'band': band, 'ra': ra, 'dec': dec}
refs = butler.query_datasets("deep_coadd", where=query,
bind=bind, order_by='patch')
deep_coadd = butler.get(refs[0])
Display the retrieved image in Firefly.
afw_display.image(deep_coadd)
The displayed image should look like this.
Figure 2: A deep coadd in the default display mode: with the mask overlay.
afw_display.setMaskTransparency(100)
The displayed image should look like this.
Figure 3: A deep coadd without the mask.
Reset mask transparency to 50% transparent.
afw_display.setMaskTransparency(50)
Print the list of mask keys and color.
mask = deep_coadd.getMask()
for mask_key, bit in mask.getMaskPlaneDict().items():
print('{} ({}): {}'.format(mask_key, bit,
afw_display.getMaskPlaneColor(mask_key)))
BAD (0): red CLIPPED (11): blue CR (3): magenta CROSSTALK (12): cyan DETECTED (5): blue DETECTED_NEGATIVE (6): cyan EDGE (4): yellow INEXACT_PSF (13): magenta INTRP (2): green ITL_DIP (14): yellow NOT_DEBLENDED (15): orange NO_DATA (8): orange REJECTED (16): red SAT (1): green SENSOR_EDGE (17): green STREAK (10): green SUSPECT (7): yellow UNMASKEDNAN (18): blue VIGNETTED (9): red
Click on the layers icon (red arrow) to open the layers pop-up box in the Firefly display and see the mask names and colors.
Figure 4: The layers overlay with all masks toggled to display.
4.2. Select mask layers to display¶
Set all mask layers to 100% transparent.
afw_display.setMaskTransparency(100)
Set the DETECTED mask layer, indicating pixels that are part of a detected object, to 80% transparent to show only that layer.
afw_display.setMaskTransparency(80, 'DETECTED')
Re-display all masks by setting all mask layers to 50% transparent.
afw_display.setMaskTransparency(50)
Open the layers pop-up box again and untoggle all but the DETECTED mask layer.
Figure 5: The layers overlay with all masks toggled off except DETECTED.
Click the X in the upper right corner of the window to dismiss it.
4.3. Change the mask colors¶
The Wikipedia page for X11 color names has a table with color swatches and their hexadecimal format.
Set all masks to be transparent and then only the DETECTED mask to be 80% transparent.
afw_display.setMaskTransparency(100)
afw_display.setMaskTransparency(80, 'DETECTED')
Change the DETECTED mask plane color to "Deep Pink", #FF1493
.
afw_display.setMaskPlaneColor('DETECTED', '#FF1493')
Re-display the deep_coadd
to see the new mask color.
afw_display.image(deep_coadd)
Change the mask color to cyan from within Firefly.
Click on the color button for the DETECTED mask layer (red arrow) to open the color picker, and choose cyan (Hex 50E3C2).
Figure 6: The color for the DETECTOR mask has been reset to cyan.
Click "OK" to dismiss the color picker, and then the X in the upper right corner of the Layers pop-up window to dismiss it.
Notice: Modifications to the displayed masked colors made via afw_display
commands will be mirrored in the Firefly overlay display panel.
However, changes performed in the Firefly overlay display panel might not be mirrored in the afw_display
variables.
Notice that the default scale (white-grey-black) is a linear stretch range from 1% to 99% pixel values.
Figure 7: The stretch drop down menu in the Firefly display, with the default scale setting.
Control the colorbar scaling algorithm with the display’s scale method.
Set the scale to asinh stretch with explicit minimum (black) and maximum (white) values.
afw_display.scale("asinh", -5, 100)
Use an automatic algorithm like zscale
(or minmax
) to select the white and black thresholds.
afw_display.scale("asinh", "zscale")
Option to reset the stretch back to the default from the command line.
# afw_display.scale("linear", 1, 99)
5.2. Zoom in and out¶
Use the zoom method to zoom in by a factor of 4.
afw_display.zoom(4)
The result should look like this.
Figure 8: Zoom in to 4x scale.
Zoom out to a factor of 1.
afw_display.zoom(1)
The result should look like this.
Figure 9: Zoom out to 1x scale.
Reset to full-frame by clicking on the icon marked with the red arrow in Figure 9.
5.3. Plot markers¶
Use the dataId
for the deep_coadd
image to retrieve the object
table for the tract.
dataId = refs[0].dataId
Retrieve the patch
and the objects' x and y coordinates.
use_columns = ['objectId', 'patch',
'r_centroid_x', 'r_centroid_y']
objects = butler.get('object', tract=dataId.get('tract'),
parameters={'columns': use_columns})
Identify objects in the patch of the displayed deep_coadd
image.
tx = np.where(objects['patch'] == dataId.get('patch'))[0]
print(len(tx))
11123
Use buffering to display orange circles at the location of every object.
with afw_display.Buffering():
for i in tx:
afw_display.dot('o', objects[i]['r_centroid_x'],
objects[i]['r_centroid_y'],
size=20, ctype='orange')
The result should look like this.
Figure 10: Objects in the patch plotted as orange circles.
Why are no objects near the edges marked?
The deep_coadd
images are per-patch, and patches and deep_coadd
images overlap at their edges. The object
table is by tract
, and has no duplicates -- there is only one row per detected object. For every object the patch
column is the patch for which they are closest to the center. The stars and galaxies near the edges of the displayed deep_coadd
image are listed as belonging to the adjacent patch.
Erase the markers.
afw_display.erase()
5.4. Draw lines¶
Draw a line on the image with pixel coordinates. The coordinates are in zero based image pixel rather than FITS image pixel. Click the "EQ-J2000:" at bottom left (purple arrow in Figure 11) to switch to "Zero Based Image Pixel (0 Based Pix)", and pixel values instead of sky coordinates will appear when the mouse is hovered over the image.
Draw a red line between two stars.
afw_display.line([[12847, 3376], [12869, 3795]], ctype='red')
The result should look like this.
Figure 11: A red line drawn on the image.
Draw other two more lines in purple to make a triangle.
afw_display.line([[12847, 3376], [12510, 3610], [12869, 3795]], ctype='purple')
Erase the lines and set the mask to transparent.
afw_display.erase()
afw_display.setMaskTransparency(100)
5.5. Open multiple frames¶
Get another deep_coadd
image.
deep_coadd_new = butler.get(refs[1])
Set up another frame. Display the new image in the new frame, so that two images are displayed side-by-side.
afw_display2 = afwDisplay.Display(frame=2)
afw_display2.image(deep_coadd_new)
The result should look like this, after setting the left image to full-frame (by clicking on the icon marked with the red arrow in Figure 9).
Figure 12: Displaying two images side by side.
Close the two images by moving the cursor to the cross (x) at the top right of each image and then clicking it.
7. Use the FireflyClient¶
Get the FireflyClient
object.
fc = afw_display.getClient()
Use the same FireflyClient
instance in the firefly_client.plot module.
ffplt.use_client(fc)
Upload the table of selected objects
in the deep_coadd
, which was used in Section 5.3. above, to Firefly.
The catalog is shown in an interactive table viewer, which is essentially and instance of the Portal Aspect launched within the Notebook Aspect.
tbl_id = ffplt.upload_table(objects[tx], title='Object Catalog')
Make a scatter plot using the coordinates. Plots are rendered by plotly
.
ffplt.scatter(x_col='r_centroid_x',
y_col='r_centroid_y',
size=3,
color='blue',
title='r_centroid_x vs. r_centroid_y',
xlabel='r_centroid_x',
ylabel='r_centroid_y')
Find the result in the "Pinned Chart" tab in the left-side panel.
The highlighted point in the scatter plot corresponds to the highlighted row in the table. Click another point or row for another object.
The result should look like this.
Figure 13: Using
FireflyClient
.
Find more tutorials for the Portal Aspect (FireflyClient
) in the documentation for Data Preview 1.