Skip to content

Commit

Permalink
PS1-image nb, no mastDownload
Browse files Browse the repository at this point in the history
add PS1 image nb from another page.
exclude any mastDownload/ folders, we really don't want these bloating the repository
  • Loading branch information
ttdu authored Jul 6, 2023
2 parents 1913668 + fef13bc commit 56319ba
Show file tree
Hide file tree
Showing 4 changed files with 388 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.ipynb_checkpoints
.DS_Store
mastDownload

_build/
1 change: 1 addition & 0 deletions _toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ parts:
- caption: PanSTARRS
chapters:
- file: notebooks/PanSTARRS/PS1_DR2_TAP/PS1_DR2_TAP.ipynb
- file: notebooks/PanSTARRS/PS1_image/PS1_image.ipynb

- caption: TESS
chapters:
Expand Down
380 changes: 380 additions & 0 deletions notebooks/PanSTARRS/PS1_image/PS1_image.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,380 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='top'></a>\n",
"# Get Images from the PS1 Image Server\n",
"\n",
"The PanStarrs survey has imaged the entire night sky north of -30$^{\\circ}$. Although this makes makes it useful for many kinds of work, you may want to focus on one particular target or region. You might do this using the [PS1 image cutout web interface](http://ps1images.stsci.edu/cgi-bin/ps1cutouts). If you have many targets, a programmatic query would be better for this.\n",
"\n",
"This tutorial will demonstrate a way you can interface with the image server programmatically. To accomplish this, we'll follow this step-by-step workflow:\n",
"\n",
"* [Request a list of Available Images at Our Target Coordinates](#imlist)\n",
"* [Generate the Image URL from the Filename](#imurl)\n",
"* [Download the Images](#download)\n",
" * [JPEG Images: Monochrome and Color](#jpeg)\n",
" * [FITS Images](#fits)\n",
"\n",
"For more details on the services being used, see the <a href=\"https://outerspace.stsci.edu/x/ioOc\">PS1 Image Cutout Service documentation</a>.\n",
"\n",
"### Imports\n",
"We'll need to import a few packages to run our code.\n",
"* `requests`, `io`, and `PIL` to request and read JPG images from the image server\n",
"* `astropy` to read and visualize FITS files"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import numpy\n",
"import requests\n",
"import warnings\n",
"\n",
"from astropy.io import fits\n",
"from astropy.visualization import PercentileInterval, AsinhStretch\n",
"from astropy.table import Table\n",
"from io import BytesIO\n",
"from PIL import Image"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='imlist'></a>\n",
"## Getting a List of Images\n",
"Before we download any images, we should query for a list of observations that include our target. In this case, we're looking for the Crab Nebula, so we'll need to input the corresponding coordinates.\n",
"\n",
"To make this task a little easier, we'll create a helper function called `get_image_table()` that takes in our coordinates and returns a table of results. To do this, we are essentially generating a URL with our query embedded, then reading the resulting webpage."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def get_image_table(ra,dec,filters=\"grizy\"):\n",
" \"\"\"\n",
" Query ps1filenames.py service to get a list of images\n",
" \n",
" ra, dec = position in degrees\n",
" filters = string with filters to include. includes all by default\n",
" Returns a table with the results\n",
" \"\"\"\n",
" service = \"https://ps1images.stsci.edu/cgi-bin/ps1filenames.py\"\n",
" # The final URL appends our query to the PS1 image service\n",
" url = f\"{service}?ra={ra}&dec={dec}&filters={filters}\"\n",
" # Read the ASCII table returned by the url\n",
" table = Table.read(url, format='ascii')\n",
" return table"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"With the helper function defined, it's now straightforward to execute our query."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Crab Nebula Coordinates\n",
"ra = 83.633210\n",
"dec = 22.014460\n",
"\n",
"# Call our helper function\n",
"get_image_table(ra,dec)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Under the filter column, you'll notice that there are five available options. These five filters are often identified as \"grizy\" in the PanStarrs literature. By default, our `getimages()` function returns all available filters unless others are specified."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='imurl'></a>\n",
"## Getting the Image URL\n",
"You can request JPEG, PNG, or FITS images from the service. As before, doing so requires correctly formatting a URL and parsing the response. \n",
"\n",
"We can create a fairly sophisticated helper function to generate the URLs for us. We'll set our parameters to be the target coordinates, desired image size and format, PanStarrs filters, and whether we'd like a color image. This will result in a lengthy block of code, especially if we check for formatting errors, but it will be flexible enough to handle many varieties of image requests.\n",
"\n",
"**Note:** you can make this function much simpler by removing the error handling. However, it is usually harder to diagnose 'invalid request' errors from the PS1 server than these customized error messages."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def get_imurl(ra, dec, size=240, output_size=None, filters=\"grizy\", im_format=\"jpg\", color=False):\n",
" \"\"\"\n",
" Get URL for images in the table\n",
" \n",
" ra, dec = position in degrees\n",
" size = extracted image size in pixels (0.25 arcsec/pixel)\n",
" output_size = output (display) image size in pixels (default = size).\n",
" output_size has no effect for fits format images.\n",
" filters = string with filters to include. choose from \"grizy\"\n",
" format = data format (options are \"jpg\", \"png\" or \"fits\")\n",
" color = if True, creates a color image (only for jpg or png format).\n",
" Default is return a list of URLs for single-filter grayscale images. \n",
" Returns a string with the URL\n",
" \"\"\"\n",
" # Check for user input errors\n",
" if color and (im_format == \"fits\"):\n",
" raise ValueError(\"color images are available only for jpg or png formats\")\n",
" if im_format not in (\"jpg\",\"png\",\"fits\"):\n",
" raise ValueError(\"format must be one of jpg, png, fits\")\n",
" \n",
" # Call the original helper function to get the table\n",
" table = get_image_table(ra,dec,filters=filters)\n",
" url = (f\"https://ps1images.stsci.edu/cgi-bin/fitscut.cgi?\"\n",
" f\"ra={ra}&dec={dec}&size={size}&format={im_format}\")\n",
" \n",
" # Append an output size, if requested\n",
" if output_size:\n",
" url = url + f\"&output_size={output_size}\"\n",
" \n",
" # Sort filters from red to blue\n",
" flist = [\"yzirg\".find(x) for x in table['filter']]\n",
" table = table[numpy.argsort(flist)]\n",
" \n",
" if color:\n",
" # We need at least 3 filters to create a color image\n",
" if len(table) < 3:\n",
" raise ValueError(\"at least three filters are required for an RGB color image\")\n",
" # If more than 3 filters, pick 3 filters from the availble results\n",
" if len(table) > 3:\n",
" table = table[[0,len(table)//2,len(table)-1]]\n",
" # Create the red, green, and blue files for our image\n",
" for i, param in enumerate([\"red\",\"green\",\"blue\"]):\n",
" url = url + f\"&{param}={table['filename'][i]}\"\n",
" \n",
" else:\n",
" # If not a color image, only one filter should be given.\n",
" if len(table)>1:\n",
" warnings.warn('Too many filters for monochrome image. Using only 1st filter.')\n",
" # Use red for monochrome images\n",
" urlbase = url + \"&red=\"\n",
" url = []\n",
" filename = table[0]['filename']\n",
" url = urlbase+filename\n",
" return url"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Phew! That's a long function. Let's run a little sanity check below."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"get_imurl(ra, dec, im_format='png', filters='y', color=False)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Looks great! We expected a URL containing our query parameters, and that's exactly what was returned.\n",
"\n",
"Evidently, this is **not** something you want to generate by hand. Although it's harder to set up this helper function initially, it is easy to generate many such URLs for multiple targets of interest."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='download'></a>\n",
"## Downloading an Image\n",
"\n",
"Now that we have the image URL, we can get the image itself. FITS files require a little more effort than standard image files to process, but we can write our last helper function to handle that for us.\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def get_im(ra, dec, size=240, output_size=None, filters=\"g\", im_format=\"jpg\", color=False):\n",
" \"\"\"\n",
" Get image at a sky position. Depends on get_imurl\n",
" \n",
" ra, dec = position in degrees\n",
" size = extracted image size in pixels (0.25 arcsec/pixel)\n",
" output_size = output (display) image size in pixels (default = size).\n",
" output_size has no effect for fits format images.\n",
" filters = string with filters to include\n",
" format = data format (options are \"jpg\", \"png\")\n",
" Returns the image\n",
" \"\"\"\n",
" # For either format, we need the image URL\n",
" url = get_imurl(ra,dec,size=size,filters=filters,output_size=output_size,im_format=im_format,color=color)\n",
" if im_format == \"fits\":\n",
" fh = fits.open(url)\n",
" # The image is contained within the data unit\n",
" fits_im = fh[0].data\n",
" # Set contrast to something reasonable\n",
" transform = AsinhStretch() + PercentileInterval(99.5)\n",
" im = transform(fits_im)\n",
" else:\n",
" # JPEG is easy. Request the file, read the bytes\n",
" r = requests.get(url)\n",
" im = Image.open(BytesIO(r.content))\n",
" return im"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Done! We'll test out this function in the next section."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='jpeg'></a>\n",
"### JPEG Images: Monochrome and Color\n",
"\n",
"Let's try getting two images of the Crab Nebula: a single-band grayscale JPEG and color JPEG. We'll set the extracted region size as 1500 pixels = 375 arcsec. This should give us a good view of the Nebula."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Set image size\n",
"size = 1500\n",
"\n",
"# Greyscale image\n",
"gim = get_im(ra, dec, size=size, im_format='jpg', filters=\"i\", color=False)\n",
"# Color image\n",
"cim = get_im(ra, dec, size=size, im_format='jpg', filters=\"grz\", color=True)\n",
"\n",
"# Create the figure\n",
"plt.figure(1,(12,6))\n",
"\n",
"# Grey image subplot on left\n",
"plt.subplot(121)\n",
"plt.imshow(gim,origin=\"upper\",cmap=\"gray\")\n",
"plt.title('Crab Nebula PS1 i')\n",
"\n",
"# Color image subplot on right\n",
"plt.subplot(122)\n",
"plt.title('Crab Nebula PS1 grz')\n",
"plt.imshow(cim,origin=\"upper\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Whoops! Looks like our image runs off the edge of a [Skycell](https://outerspace.stsci.edu/display/PANSTARRS/PS1+Sky+tessellation+patterns). Our image was a little bit too large, which is what creates the white band at the bottom; there's no data to display.\n",
"\n",
"Other than the image size, this is a good example of how you might process data into both greyscale and color images."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"<a id='fits'></a>\n",
"### Load and display a FITS image\n",
"\n",
"Let's display a FITS image in the i-band to see if there are any differences from the JPEG version."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Get a new fits image in the 'i' band\n",
"fitsim = get_im(ra, dec, size=size, filters=\"i\", im_format=\"fits\")\n",
"\n",
"plt.figure(1,(12,6))\n",
"# As before, plot the grayscale JPG on the left\n",
"plt.subplot(121)\n",
"plt.imshow(gim, origin=\"upper\",cmap='afmhot')\n",
"plt.title('Crab Nebula PS1 i (jpeg)')\n",
"\n",
"# Now plot the greyscale FITS on the right\n",
"plt.subplot(122)\n",
"plt.imshow(fitsim, origin=\"lower\", cmap='afmhot')\n",
"plt.title('Crab Nebula PS1 i (fits)')"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Note that the $y$-axis is flipped in FITS image, compared to the JPEG image. You may actually prefer this, since it matches a typical $x$-$y$ coordinate system.\n",
"\n",
"Another subtle difference you might notice is the contrast in the images. The JPEG image is \"dimmer\" and does not look as saturated. We hard-coded the contrast for the FITS in our `get_im` function. Did we choose a reasonable value? Feel free to experiment.\n",
"\n",
"## About this Notebook\n",
"This notebook was developed by Rick White. Additional editing was provided by Thomas Dutkiewicz.\n",
"\n",
"**Last updated:** Jan 2022 <br>\n",
"\n",
"For support, please contact the Archive HelpDesk at archive@stsci.edu.\n",
"\n",
"***\n",
" <img style=\"float: right;\" src=\"https://raw.githubusercontent.com/spacetelescope/notebooks/master/assets/stsci_pri_combo_mark_horizonal_white_bkgd.png\" alt=\"Space Telescope Logo\" width=\"200px\"/>\n",
"\n",
"[Return to top of page](#top)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.4"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Loading

0 comments on commit 56319ba

Please sign in to comment.