Even though Hubble has a small field of view, satellites are commonly captured in images. The cosmic ray rejection algorithm in Astrodrizzle is not well suited to eliminate satellite trails, and the affected adjacent pixels that make up their wings leave ugly blemishes in stacked images.
To fix this problem, the pixels around satellite trails need to be marked as bad in the affected images. There are several ways to do this. The ACS Team has developed an algorithm to automatically detect and mask satellite trails. This is the easiest and most convenient way. Masks can also be made manually using DS9 regions. While not as convenient, making masks manually allows you to mask not only satellites, but also any other anomalies with odd shapes (e.g. dragon's breath, glint, blooming).
Both methods are explained below.
import os import shutil from astropy.io import fits from astroquery.mast import Observations from astropy.visualization import astropy_mpl_style,LogStretch,ImageNormalize,LinearStretch from IPython.display import Image import matplotlib.pyplot as plt import pyregion import acstools from acstools.satdet import detsat, make_mask, update_dq from drizzlepac.astrodrizzle import AstroDrizzle as adriz %matplotlib inline
The following tasks in the acstools package can be run with TEAL: acs2d acs_destripe acs_destripe_plus acsccd acscte acscteforwardmodel acsrej acssum calacs PixCteCorr is no longer supported. Please use acscte. The following task in the stsci.skypac package can be run with TEAL: skymatch
ModuleNotFoundErrorTraceback (most recent call last) ModuleNotFoundError: No module named 'numpy.core._multiarray_umath'
Coordinate transformation and image resampling library NOT found! Please check the installation of this package to insure C code was built successfully.
ImportErrorTraceback (most recent call last) /opt/conda/envs/notebooks_env/lib/python3.6/site-packages/drizzlepac/ablot.py in <module> 22 try: ---> 23 from . import cdriz 24 except ImportError: ImportError: numpy.core.multiarray failed to import During handling of the above exception, another exception occurred: ImportErrorTraceback (most recent call last) <ipython-input-1-7f458ad5fc29> in <module> 11 import acstools 12 from acstools.satdet import detsat, make_mask, update_dq ---> 13 from drizzlepac.astrodrizzle import AstroDrizzle as adriz 14 15 get_ipython().run_line_magic('matplotlib', 'inline') /opt/conda/envs/notebooks_env/lib/python3.6/site-packages/drizzlepac/__init__.py in <module> 19 from .version import * 20 ---> 21 from . import ablot 22 from . import adrizzle 23 from . import astrodrizzle /opt/conda/envs/notebooks_env/lib/python3.6/site-packages/drizzlepac/ablot.py in <module> 26 print('\n Coordinate transformation and image resampling library NOT found!') 27 print('\n Please check the installation of this package to insure C code was built successfully.') ---> 28 raise ImportError 29 30 from .version import * ImportError:
#Searching for the observsation results = Observations.query_criteria(obs_id='JC8MB7020', obstype='all') #Downloading previews and FLC files jpg_download = Observations.download_products(results['obsid'], mrp_only=False, extension=['jpg']) flc_download = Observations.download_products(results['obsid'], productSubGroupDescription=['FLC'], mrp_only=False)
# Cleaning up directories after downloading from MAST for file in jpg_download['Local Path']: if 'drc' in file: os.rename(file, os.path.basename(file)) for file in flc_download['Local Path']: os.rename(file, os.path.basename(file)) shutil.rmtree('mastDownload')
The image below shows the combined drizzled image from this association. The satellite trail can be seen going across the image from left to right, just above the center of the image.
The bright satellite trail that caused this came from the image jc8mb7cq_flc.fits. The figure below shows the top chip which is referred to as SCI,2 (or extension 4).
plt.style.use(astropy_mpl_style) img = fits.getdata('jc8mb7tcq_flc.fits', ext=4) norm1 = ImageNormalize(img, vmin=100, vmax=200, stretch=LinearStretch()) plt.figure(figsize=(16, 16)) plt.imshow(img, norm=norm1, cmap='gray_r', origin='lower') plt.colorbar(orientation='horizontal')
The ACS Team developed an algorithm to automically detect and mask satellite trails in ACS images (ISR ACS 2016-01). The module is called
satdet and can be found in the
acstools package. The 'readthedocs' page for the software can be found here: Satellite Trails Detection.
The first command below runs the detection algorithm on the top chip only (extension 4) and generates some diagnostic plots. Note that the images are shown upside down from the figure above.
results, errors = detsat('jc8mb7tcq_flc.fits', chips=, n_processes=4, plot=True, verbose=False)
The diagnostic plots can be used to verify that the satellite was properly detected. Changing parameters to adjust this task is beyond the scope of this notebook, but please consult the package documentation indicated above for instructions on how to do this.
Assuming that the satellite trail was properly detected, masks can be made to flag the satellite in the data quality array (DQ) of the image. Once this information is in the DQ array, AstroDrizzle knows to ignore the flagged pixels when making the combined image. The function
update_dq is used to flag pixels in the DQ array of SCI,2 (extension 6) using the default flag value of 16384.
If the satellite were instead on the bottom chip (SCI,1 or extension 1), the
update_dq function would instead be used to modify extension 3. More detail on the ACS file structure may be found in the ACS Data Handbook.
trail_coords = results[('jc8mb7tcq_flc.fits', 4)] trail_segment = trail_coords trail_segment mask = make_mask('jc8mb7tcq_flc.fits', 4, trail_segment, plot=True, verbose=True) update_dq('jc8mb7tcq_flc.fits', 6, mask, verbose=True)
With the satellite masked, the images can be drizzled again. For brevity, only the top chip (SCI,2) of the image stack will be drizzled together to make a combined product. This is controlled in
AstroDrizzle via the
adriz('*flc.fits', output='automatic', runfile='', context=False, group='4', build=True, num_cores=1, preserve=False, clean=True, driz_sep_bits='64,16', final_bits='64,16')
The final drizzled product shows that the bright satellite trail and its wings have been removed. A second, fainter satellite can be seen from a different image in the stack, and this will be masked in the steps below.
img = fits.getdata('automatic_drc.fits', ext=1) norm1 = ImageNormalize(img, vmin=-0.01, vmax=0.02, stretch=LinearStretch()) plt.figure(figsize=(20, 20)) plt.imshow(img, norm=norm1, cmap='gray_r', origin='lower') plt.colorbar(orientation='horizontal')
While the automatic detection algorithm flagged and masked the large satellite trail, the image above shows a second trail from a different image in the stack.
To get rid of this trail, we will demonstrate how a DS9 regions can be used. The example image displayed below shows a region around a satellite trail.
This region was saved in image coordinates. NOTE THAT REGIONS SAVED IN SKY COORDINATES WILL NOT WORK FOR THIS EXAMPLE. Below is the contents of the region file.
# Region file format: DS9 version 4.1 global color=green dashlist=8 3 width=1 font="helvetica 10 normal roman" select=1 highlite=1 dash=0 fixed=0 edit=1 move=1 delete=1 include=1 source=1 image polygon(1476.9255,1816.4415,1545.7465,1818.5921,2825.3869,485.1853,2765.1685,480.88399)
pyregion package will be used to make masks out of region files. For details on how to use this package go here. (This package will eventually be superseded by the astropy affiliated
# Reading region file reg_file = pyregion.parse('''# Region file format: DS9 version 4.1 global color=green dashlist=8 3 width=1 font="helvetica 10 normal roman" select=1 highlite=1 dash=0 fixed=0 edit=1 move=1 delete=1 include=1 source=1 image polygon(1476.9255,1816.4415,1545.7465,1818.5921,2825.3869,485.1853,2765.1685,480.88399)''') # Making mask out of region file and masking DQ array with fits.open('jc8mb7t5q_flc.fits', mode='update') as hdu: dq = hdu.data mask = reg_file.get_mask(shape=dq.shape) dq[mask] |= 16384 norm1 = ImageNormalize(img, vmin=0, vmax=1000, stretch=LinearStretch()) plt.figure(figsize=(16, 10)) plt.imshow(dq, norm=norm1, cmap='gray_r', origin='lower') plt.title('DQ array of jc8mb7t5q_flc.fits showing masked pixels', fontsize=20)
With the satellite masked, the full set of images can be drizzled once more.
adriz('*flc.fits', output='manual', runfile='', context=False, group='4', build=True, num_cores=1, preserve=False, clean=True, driz_sep_bits='16, 64', final_bits='16, 64')
The new drizzled product shows that the second satellite trail and its wings have been removed.
img = fits.getdata('manual_drc.fits', ext=1) norm1 = ImageNormalize(img, vmin=-0.01, vmax=0.02, stretch=LinearStretch()) plt.figure(figsize=(20, 20)) plt.imshow(img, norm=norm1, cmap='gray_r', origin='lower') plt.colorbar(orientation='horizontal')