|
| 1 | +{ |
| 2 | + "cells": [ |
| 3 | + { |
| 4 | + "cell_type": "markdown", |
| 5 | + "metadata": { |
| 6 | + "slideshow": { |
| 7 | + "slide_type": "slide" |
| 8 | + } |
| 9 | + }, |
| 10 | + "source": [ |
| 11 | + "<img align=\"left\" src = https://project.lsst.org/sites/default/files/Rubin-O-Logo_0.png width=250> \n", |
| 12 | + "<b>Displaying images using the astronomical framework library</b> <br>\n", |
| 13 | + "Last verified to run on <b>TBD</b> with LSST Science Pipelines release <b>TBD</b> <br>\n", |
| 14 | + "Contact author: Alex Drlica-Wagner <br>\n", |
| 15 | + "Credit: Brant Robertson and the LSST Stack Club <br>\n", |
| 16 | + "Target audience: All DP0 delegates. <br>\n", |
| 17 | + "Container Size: medium <br>\n", |
| 18 | + "<br>\n", |
| 19 | + "Questions welcome at <a href=\"https://community.lsst.org/c/support/dp0\">community.lsst.org/c/support/dp0</a> <br>\n", |
| 20 | + "Find DP0 documentation and resources at <a href=\"https://dp0-1.lsst.io\">dp0-1.lsst.io</a> <br>\n", |
| 21 | + "\n", |
| 22 | + "## **Learning Objectives:**\n", |
| 23 | + "\n", |
| 24 | + "This tutorial is designed to help users get a brief feel for the `lsst.afw.display` library that enables the visual inspection of data. The [`lsst.afw` library](https://github.com/lsst/afw) provides an \"Astronomical Framework\" (afw) while the `lsst.daf.*` libraries (see, e.g., [daf_base](https://github.com/lsst/daf_base)) provides a Data Access Framework (daf). Both libraries are used in this tutorial, with the `lsst.daf.persistence` library used to access a calibrated exposure (calexp) and the `lsst.afw.display` library used to show the exposure image on the screen.\n", |
| 25 | + "\n", |
| 26 | + "**Credit:** This tutorial is based on the [`AFW_Display_Demo.ipynb`](https://github.com/LSSTScienceCollaborations/StackClub/blob/master/Visualization/AFW_Display_Demo.ipynb) notebook originally written by Brant Robertson and maintained by the LSST Stack Club. More examples of the use of `lsst.afw.display` can be found in the [Stack ](https://pipelines.lsst.io/getting-started/display.html).\n", |
| 27 | + "\n", |
| 28 | + "### Set Up" |
| 29 | + ] |
| 30 | + }, |
| 31 | + { |
| 32 | + "cell_type": "code", |
| 33 | + "execution_count": null, |
| 34 | + "metadata": {}, |
| 35 | + "outputs": [], |
| 36 | + "source": [ |
| 37 | + "# What version of the Stack are we using?\n", |
| 38 | + "! echo $HOSTNAME\n", |
| 39 | + "! eups list -s | grep lsst_distrib" |
| 40 | + ] |
| 41 | + }, |
| 42 | + { |
| 43 | + "cell_type": "markdown", |
| 44 | + "metadata": { |
| 45 | + "slideshow": { |
| 46 | + "slide_type": "subslide" |
| 47 | + } |
| 48 | + }, |
| 49 | + "source": [ |
| 50 | + "### 1. Import Common Python Libraries\n", |
| 51 | + "\n", |
| 52 | + "The [`matplotlib`](https://matplotlib.org/), [`numpy`](http://www.numpy.org/), and [`astropy`](http://www.astropy.org/) libraries are widely used Python libraries for plotting, scientific computing, and astronomical data analysis. We will use these packages in common ways below, including the `matplotlib.pyplot` plotting sublibrary. We also import the [`warnings` library](https://docs.python.org/2/library/warnings.html) to prevent some routine warning messages from printing to the screen." |
| 53 | + ] |
| 54 | + }, |
| 55 | + { |
| 56 | + "cell_type": "code", |
| 57 | + "execution_count": null, |
| 58 | + "metadata": {}, |
| 59 | + "outputs": [], |
| 60 | + "source": [ |
| 61 | + "#allow for matplotlib to create inline plots in our notebook\n", |
| 62 | + "%matplotlib inline \n", |
| 63 | + "import numpy as np #imports numpy with the alias np\n", |
| 64 | + "import matplotlib.pyplot as plt #imports matplotlib.pyplot as plt\n", |
| 65 | + "import warnings #imports the warnings library" |
| 66 | + ] |
| 67 | + }, |
| 68 | + { |
| 69 | + "cell_type": "markdown", |
| 70 | + "metadata": {}, |
| 71 | + "source": [ |
| 72 | + "And let the kernel know that we're happy not to have some useful warnings printed during this tutorial." |
| 73 | + ] |
| 74 | + }, |
| 75 | + { |
| 76 | + "cell_type": "code", |
| 77 | + "execution_count": null, |
| 78 | + "metadata": {}, |
| 79 | + "outputs": [], |
| 80 | + "source": [ |
| 81 | + "warnings.simplefilter(\"ignore\", category=FutureWarning) #prevent some helpful but ancillary warning messages from printing during some LSST DM Release calls\n", |
| 82 | + "warnings.simplefilter(\"ignore\", category=UserWarning) #prevent some helpful but ancillary warning messages from printing during some LSST DM Release calls" |
| 83 | + ] |
| 84 | + }, |
| 85 | + { |
| 86 | + "cell_type": "markdown", |
| 87 | + "metadata": {}, |
| 88 | + "source": [ |
| 89 | + "As a last preparatory task, we set the parameters of `matplotlib.pyplot` to give us a large default size for an image." |
| 90 | + ] |
| 91 | + }, |
| 92 | + { |
| 93 | + "cell_type": "code", |
| 94 | + "execution_count": null, |
| 95 | + "metadata": {}, |
| 96 | + "outputs": [], |
| 97 | + "source": [ |
| 98 | + "plt.rcParams['figure.figsize'] = (8.0, 8.0) #set a large default size for our images" |
| 99 | + ] |
| 100 | + }, |
| 101 | + { |
| 102 | + "cell_type": "markdown", |
| 103 | + "metadata": {}, |
| 104 | + "source": [ |
| 105 | + "### 1. Load the LSST Science Pipelines\n", |
| 106 | + "\n", |
| 107 | + "First, we load the `lsst.afw.display` library to gain access to the image visualization routines we'd like to use." |
| 108 | + ] |
| 109 | + }, |
| 110 | + { |
| 111 | + "cell_type": "code", |
| 112 | + "execution_count": null, |
| 113 | + "metadata": {}, |
| 114 | + "outputs": [], |
| 115 | + "source": [ |
| 116 | + "import lsst.afw.display as afwDisplay #load lsst.afw.display to gain access to image visualization routines.\n", |
| 117 | + "from lsst.daf.butler import Butler #load the Butler, which provides programmatic access to LSST data products." |
| 118 | + ] |
| 119 | + }, |
| 120 | + { |
| 121 | + "cell_type": "markdown", |
| 122 | + "metadata": {}, |
| 123 | + "source": [ |
| 124 | + "### 2. Load the Data to Visualize\n", |
| 125 | + "\n", |
| 126 | + "To display an image, we must first load some data. These data have been processed with the LSST Science Pipelines, and are organized in a structure that enables us to access them through the `Butler`. For more information on the `Butler`, see [lsst.daf.butler](https://pipelines.lsst.io/modules/lsst.daf.butler/index.html).\n", |
| 127 | + "\n", |
| 128 | + "The DP0.1 data set contains simulated images from the LSST DESC Data Challenge 2 (DC2). These data are available in the data directory `/datasets/DC2/DR6/Run2.2i/patched/2021-02-10/rerun/run2.2i-calexp-v1/`. We access a single image from a specific visit (`512055`), raft (`R20`), and detector (`76`). This happens to be an i-band exposure.\n", |
| 129 | + "\n", |
| 130 | + "Once we define a string that contains the data directory, we start the `Butler` instance using the `lsst.daf.persistence` library alias `dafPersist` and its `Butler` class. The `Butler` object is initialized with a string containing the data directory we wish to access. Running the cell may take a few moments.\n", |
| 131 | + "\n", |
| 132 | + "With the `Butler` instance now generated using our data directory, we can retrieve the desired calibrated exposure by telling the butler which filter, CCD, and visit we wish to view. To do this, we definie dictionary with the required information." |
| 133 | + ] |
| 134 | + }, |
| 135 | + { |
| 136 | + "cell_type": "code", |
| 137 | + "execution_count": null, |
| 138 | + "metadata": {}, |
| 139 | + "outputs": [], |
| 140 | + "source": [ |
| 141 | + "# Note that the keys are slightly different for DC2/LSSTCam \n", |
| 142 | + "# You can view all the keys by creating the butler and calling: print(butler.getKeys('calexp'))\n", |
| 143 | + "dataId = {'visit': 227982, 'raftName': 'R31', 'detector': 129}\n", |
| 144 | + "repo = 's3://butler-us-central1-dp01' \n", |
| 145 | + "collection='2.2i/runs/DP0.1'\n", |
| 146 | + "butler = Butler(repo,collections=collection)\n", |
| 147 | + " \n", |
| 148 | + "# Retrieve the data using the `butler` instance and its function `get()`\n", |
| 149 | + "calexp = butler.get('calexp', **dataId)" |
| 150 | + ] |
| 151 | + }, |
| 152 | + { |
| 153 | + "cell_type": "markdown", |
| 154 | + "metadata": {}, |
| 155 | + "source": [ |
| 156 | + "### 3.1: Use AFWDisplay to Visualize the Image\n", |
| 157 | + "\n", |
| 158 | + "Now, with a `Butler` instance defined and a calibrated exposure retrieved, we can use [`lsst.afw.display`](https://github.com/lsst/afw/tree/master/python/lsst/afw/display) to visualize the data. The next task is to let AFWDisplay know that we want it to enroll `matplotlib` as our default display backend. To do this, we use the `setDefaultBackend()` function. Remember that we made an alias to `lsst.afw.display` called `afwDisplay`, so we'll use that to call `setDefaultBackend()`." |
| 159 | + ] |
| 160 | + }, |
| 161 | + { |
| 162 | + "cell_type": "code", |
| 163 | + "execution_count": null, |
| 164 | + "metadata": {}, |
| 165 | + "outputs": [], |
| 166 | + "source": [ |
| 167 | + "afwDisplay.setDefaultBackend('matplotlib') # Use lsst.afw.display with the matplotlib backend" |
| 168 | + ] |
| 169 | + }, |
| 170 | + { |
| 171 | + "cell_type": "markdown", |
| 172 | + "metadata": {}, |
| 173 | + "source": [ |
| 174 | + "We are now set to display the image. To do this, we:\n", |
| 175 | + "\n", |
| 176 | + "1. First create a `matplotlib.pyplot` figure using `plt.figure()` -- this will be familiar to anyone with experience using `matplotlib`.\n", |
| 177 | + "2. Then create an alias to the `lsst.afw.display.Display` method that will allow us to display the data to the screen. This alias will be called `afw_display`.\n", |
| 178 | + "3. Before showing the data on the screen, we have to decide how to apply an image stretch given the data. The algorithm we'll use is `asinh` familiar from SDSS images, with a range of values set by `zscale`. To do this, we use the `scale()` function provided by `lsst.afw.display`. See the `scale()` function definition in the [`interface.py` file of the lsst.afw.display library](https://github.com/lsst/afw/blob/master/python/lsst/afw/display/interface.py).\n", |
| 179 | + "4. Finally, we can display the image. Do do this, we provide the `mtv()` method the `image` member of our calibrated image retrieved by the `butler`. We can then use `plt.show()` to display our figure.\n", |
| 180 | + "\n", |
| 181 | + "All these tasks are best done within the same notebook cell." |
| 182 | + ] |
| 183 | + }, |
| 184 | + { |
| 185 | + "cell_type": "code", |
| 186 | + "execution_count": null, |
| 187 | + "metadata": {}, |
| 188 | + "outputs": [], |
| 189 | + "source": [ |
| 190 | + "plt.figure() #create a matplotlib.pyplot figure\n", |
| 191 | + "display = afwDisplay.Display() #get an alias to the lsst.afw.display.Display() method\n", |
| 192 | + "display.scale('asinh', 'zscale') #set the image stretch algorithm and range\n", |
| 193 | + "display.mtv(calexp.image) #load the image into the display\n", |
| 194 | + "plt.show() #show the corresponding pyplot figure" |
| 195 | + ] |
| 196 | + }, |
| 197 | + { |
| 198 | + "cell_type": "markdown", |
| 199 | + "metadata": {}, |
| 200 | + "source": [ |
| 201 | + "Often you may want to plot two images side-by-side. This can be done by creating matplotlib subplots." |
| 202 | + ] |
| 203 | + }, |
| 204 | + { |
| 205 | + "cell_type": "code", |
| 206 | + "execution_count": null, |
| 207 | + "metadata": {}, |
| 208 | + "outputs": [], |
| 209 | + "source": [ |
| 210 | + "fig,ax = plt.subplots(1,2,figsize=(14,7))\n", |
| 211 | + "\n", |
| 212 | + "plt.sca(ax[0]) # set the first axes as current\n", |
| 213 | + "display1 = afwDisplay.Display(frame=fig)\n", |
| 214 | + "display1.scale('linear', 'zscale')\n", |
| 215 | + "display1.mtv(calexp.image)\n", |
| 216 | + "\n", |
| 217 | + "plt.sca(ax[1]) # set the second axes as current\n", |
| 218 | + "display2 = afwDisplay.Display(frame=fig)\n", |
| 219 | + "display2.mtv(calexp.mask)\n", |
| 220 | + "\n", |
| 221 | + "plt.tight_layout()\n", |
| 222 | + "plt.show()" |
| 223 | + ] |
| 224 | + }, |
| 225 | + { |
| 226 | + "cell_type": "markdown", |
| 227 | + "metadata": {}, |
| 228 | + "source": [ |
| 229 | + "It is also possible to plot the mask on top of the image using the `calexp.maskedImage`." |
| 230 | + ] |
| 231 | + }, |
| 232 | + { |
| 233 | + "cell_type": "code", |
| 234 | + "execution_count": null, |
| 235 | + "metadata": {}, |
| 236 | + "outputs": [], |
| 237 | + "source": [ |
| 238 | + "fig = plt.figure()\n", |
| 239 | + "display = afwDisplay.Display()\n", |
| 240 | + "display.scale('linear', 'zscale')\n", |
| 241 | + "display.mtv(calexp.maskedImage)\n", |
| 242 | + "plt.show()" |
| 243 | + ] |
| 244 | + }, |
| 245 | + { |
| 246 | + "cell_type": "markdown", |
| 247 | + "metadata": {}, |
| 248 | + "source": [ |
| 249 | + "**Congratulations!** We've plotted an image using `lsst.afw.display`!" |
| 250 | + ] |
| 251 | + }, |
| 252 | + { |
| 253 | + "cell_type": "markdown", |
| 254 | + "metadata": {}, |
| 255 | + "source": [ |
| 256 | + "### 3.2: Use AFWDisplay to Visualize the Image and Mask Plane\n", |
| 257 | + "\n", |
| 258 | + "The `calexp` returned by the butler contains more than just the image pixel values (see the Stack Club [calexp tutorial](https://github.com/LSSTScienceCollaborations/StackClub/blob/master/Basics/Calexp_guided_tour.ipynb) for more details). One other component is the mask plane associated with the image. `AFWDisplay` provides a nice pre-packaged interface for overplotting the mask associated with an image. A mask is composed of a set of \"mask planes\", 2D binary bit maps corresponding to pixels that are masked for various reasons (see [here](https://pipelines.lsst.io/v/DM-11392/getting-started/display.html#interpreting-displayed-mask-colors) for more details)." |
| 259 | + ] |
| 260 | + }, |
| 261 | + { |
| 262 | + "cell_type": "markdown", |
| 263 | + "metadata": {}, |
| 264 | + "source": [ |
| 265 | + "We'll follow the same steps as above to display the image, but we'll add a few modifications\n", |
| 266 | + "\n", |
| 267 | + "1. We explicitly set the transparency of the overplotted mask (0 = transparent, 1 = opaque)\n", |
| 268 | + "2. We explicitly set the color of the 'DETECTED' mask plane to 'blue' (i.e. all pixels associated with detected objects).\n", |
| 269 | + "3. We pass the full `calexp` object to `mtv` instead of just the image plane." |
| 270 | + ] |
| 271 | + }, |
| 272 | + { |
| 273 | + "cell_type": "code", |
| 274 | + "execution_count": null, |
| 275 | + "metadata": {}, |
| 276 | + "outputs": [], |
| 277 | + "source": [ |
| 278 | + "plt.figure() #create a matplotlib.pyplot figure\n", |
| 279 | + "afw_display = afwDisplay.Display() #get an alias to the lsst.afw.display.Display() method\n", |
| 280 | + "afw_display.scale('asinh', 'zscale') #set the image stretch algorithm and range\n", |
| 281 | + "afw_display.setMaskTransparency(0.4) #set the transparency of the mask plane (1 = opaque)\n", |
| 282 | + "afw_display.setMaskPlaneColor('DETECTED','blue') #set the color for a single plane in the mask\n", |
| 283 | + "afw_display.mtv(calexp) #load the image and mask plane into the display\n", |
| 284 | + "plt.show() #show the corresponding pyplot figure" |
| 285 | + ] |
| 286 | + }, |
| 287 | + { |
| 288 | + "cell_type": "markdown", |
| 289 | + "metadata": {}, |
| 290 | + "source": [ |
| 291 | + "The `afw_display` object contains more information about the mask planes that can be accessed" |
| 292 | + ] |
| 293 | + }, |
| 294 | + { |
| 295 | + "cell_type": "code", |
| 296 | + "execution_count": null, |
| 297 | + "metadata": {}, |
| 298 | + "outputs": [], |
| 299 | + "source": [ |
| 300 | + "print(\"Mask plane bit definitions:\\n\", afw_display.getMaskPlaneColor()) # Print the colors associated to each plane in the mask\n", |
| 301 | + "print(\"\\nMask plane methods:\\n\")\n", |
| 302 | + "help(afw_display.setMaskPlaneColor)" |
| 303 | + ] |
| 304 | + }, |
| 305 | + { |
| 306 | + "cell_type": "markdown", |
| 307 | + "metadata": {}, |
| 308 | + "source": [ |
| 309 | + "### 4. More Information about lsst.afw.display\n", |
| 310 | + "\n", |
| 311 | + "To get some more information about `lsst.afw.display`, we can print the method list to see what's available. The next cell will print `lsst.afw.display` methods to the screen." |
| 312 | + ] |
| 313 | + }, |
| 314 | + { |
| 315 | + "cell_type": "code", |
| 316 | + "execution_count": null, |
| 317 | + "metadata": {}, |
| 318 | + "outputs": [], |
| 319 | + "source": [ |
| 320 | + "method_list = [func for func in dir(display) if callable(getattr(display, func))]\n", |
| 321 | + "print(method_list)" |
| 322 | + ] |
| 323 | + }, |
| 324 | + { |
| 325 | + "cell_type": "markdown", |
| 326 | + "metadata": {}, |
| 327 | + "source": [ |
| 328 | + "If you'd like to learn more about any given function, please see the [`lsst.afw.display` source code](https://github.com/lsst/afw/tree/master/python/lsst/afw/display).\n", |
| 329 | + "\n", |
| 330 | + "You can also read the API documentation about the above functions using the Jupyter notebook `help()` function:" |
| 331 | + ] |
| 332 | + }, |
| 333 | + { |
| 334 | + "cell_type": "code", |
| 335 | + "execution_count": null, |
| 336 | + "metadata": {}, |
| 337 | + "outputs": [], |
| 338 | + "source": [ |
| 339 | + "help(display.scale)" |
| 340 | + ] |
| 341 | + }, |
| 342 | + { |
| 343 | + "cell_type": "code", |
| 344 | + "execution_count": null, |
| 345 | + "metadata": {}, |
| 346 | + "outputs": [], |
| 347 | + "source": [ |
| 348 | + "help(display.mtv)" |
| 349 | + ] |
| 350 | + }, |
| 351 | + { |
| 352 | + "cell_type": "markdown", |
| 353 | + "metadata": {}, |
| 354 | + "source": [ |
| 355 | + "### Additional Documentation\n", |
| 356 | + "\n", |
| 357 | + "If you'd like some more information on `lsst.afw.display`, please have a look at the following websites:\n", |
| 358 | + "\n", |
| 359 | + "* [Info on image indexing conventions.](https://pipelines.lsst.io/modules/lsst.afw.image/indexing-conventions.html) \n", |
| 360 | + "* [afw.display Doxygen website](http://doxygen.lsst.codes/stack/doxygen/x_masterDoxyDoc/namespacelsst_1_1afw_1_1display.html) \n", |
| 361 | + "* [afw.display GitHub website](https://github.com/RobertLuptonTheGood/afw/tree/master/python/lsst/afw/display) \n", |
| 362 | + "* [Getting Started on Image Display (pipelines.lsst.io)](https://pipelines.lsst.io/getting-started/display.html)" |
| 363 | + ] |
| 364 | + } |
| 365 | + ], |
| 366 | + "metadata": { |
| 367 | + "celltoolbar": "Slideshow", |
| 368 | + "kernelspec": { |
| 369 | + "display_name": "LSST", |
| 370 | + "language": "python", |
| 371 | + "name": "lsst" |
| 372 | + }, |
| 373 | + "language_info": { |
| 374 | + "codemirror_mode": { |
| 375 | + "name": "ipython", |
| 376 | + "version": 3 |
| 377 | + }, |
| 378 | + "file_extension": ".py", |
| 379 | + "mimetype": "text/x-python", |
| 380 | + "name": "python", |
| 381 | + "nbconvert_exporter": "python", |
| 382 | + "pygments_lexer": "ipython3", |
| 383 | + "version": "3.8.8" |
| 384 | + }, |
| 385 | + "livereveal": { |
| 386 | + "scroll": true, |
| 387 | + "start_slideshow_at": "selected" |
| 388 | + } |
| 389 | + }, |
| 390 | + "nbformat": 4, |
| 391 | + "nbformat_minor": 4 |
| 392 | +} |
0 commit comments