Image cutouts

Image cutouts, or postage stamps, are useful for quick visual characterization of the detected transients or artefacts. We have a set of functions inside stdpipe.cutouts sub-module that ease these tasks.

They all work with the cutout structure returned by stdpipe.cutouts.get_cutout() that contains the stamps of requested size around the object from various image planes (science image, template, background map, mask, footprint map, …), as well as some meta-information.

# Create and display the cutouts from an image and its mask

for i,cand in enumerate(candidates):
    # Create the cutout from image based on the candidate
    cutout = cutouts.get_cutout(image, cand, 20, mask=mask, header=header)

    # We may directly download the template image for this cutout
    # from HiPS server - same scale and orientation, but different PSF shape!..
    cutout['template'] = templates.get_hips_image('PanSTARRS/DR1/r',
             header=cutout['header'], get_header=False)

    # We do not have difference image, so it will only display original one, template and mask
    plots.plot_cutout(cutout, planes=['image', 'template', 'mask'],
             qq=[0.5, 99.9], stretch='linear')
    plt.show()
# Create and display the cutouts from a full set of image subtraction results

for i,cand in enumerate(candidates):
    print('Candidate %d with mag = %.2f +/- %.2f at x/y = %.1f %.1d and RA/Dec = %.4f %.4f' %
             (i, cand['mag_calib'], cand['mag_calib_err'], cand['x'], cand['y'], cand['ra'], cand['dec']))

    cutout = cutouts.get_cutout(image, cand, 20, mask=mask|tmask, diff=diff, template=tmpl,
             convolved=conv, err=ediff, header=header, filename=filename, time=time)

    plots.plot_cutout(cutout, ['image', 'template', 'convolved', 'diff', 'mask'],
             qq=[0.5, 99.5], stretch='linear')

    plt.show()

    # Also, store the cutout!
    cutouts.write_cutout(cutout, 'candidates/cutout_%03d.fits' % i)
stdpipe.cutouts.get_cutout(image, candidate, radius, header=None, wcs=None, time=None, filename=None, name=None, **kwargs)[source]

Create a cutout postage stamp from one or more image planes.

The candidate may be a row from astropy.table.Table or a dict with at least x and y keys. The cutout is centered so the object falls inside the central pixel; size is 2*ceil(radius) + 1 pixels.

Parameters:
imagendarray

Primary (science) image plane.

candidatetable row or dict

Object record containing at least x and y pixel coordinates.

radiusfloat

Cutout half-size in pixels.

headerastropy.io.fits.Header, optional

Header of the original image. Copied and adjusted to represent the cutout WCS; stored as cutout['header'].

wcsastropy.wcs.WCS, optional

WCS of the original image. Adjusted and stored as cutout['wcs']. Takes precedence over header for WCS.

timeastropy.time.Time, datetime, or str, optional

Observation timestamp; stored in cutout['meta']['time'].

filenamestr, optional

Source image filename; stored in cutout['meta']['filename'].

namestr, optional

Object name; stored in cutout['meta']['name']. If omitted and candidate has ra / dec, a J2000 name is constructed.

**kwargs

Additional 2D arrays interpreted as extra image planes (e.g. mask, diff, template, convolved, err).

Returns:
dict

Cutout dictionary with at least:

  • image — primary image plane

  • mask, diff, template, etc. — extra planes if provided

  • header — adjusted FITS header, if provided

  • wcs — adjusted WCS, if provided

  • meta — dict with all candidate fields plus time, filename, and name

Saving and loading the cutouts

Cutouts may be easily written to multi-extension FITS images, and restored from them later using the following functions:

stdpipe.cutouts.write_cutout(cutout, filename)[source]

Store a cutout as a multi-extension FITS file.

Each image plane becomes a named FITS extension; metadata is stored as keywords in the primary header.

Parameters:
cutoutdict

Cutout structure as returned by get_cutout().

filenamestr

Output FITS filename.

stdpipe.cutouts.load_cutout(filename)[source]

Restore a cutout from a multi-extension FITS file.

Parameters:
filenamestr

Path to a FITS file written by write_cutout().

Returns:
dict

Cutout structure as returned by get_cutout().

Plotting the cutouts

There is a dedicated plotting routine that helps quickly visualize the information contained in it, including its different image planes - stdpipe.plots.plot_cutout()

stdpipe.plots.plot_cutout(cutout, planes=['image', 'template', 'diff', 'mask'], fig=None, axs=None, mark_x=None, mark_y=None, mark_r=5.0, mark_r2=None, mark_r3=None, mark_color='red', mark_lw=2, mark_ra=None, mark_dec=None, r0=None, show_title=True, title=None, additional_title=None, **kwargs)[source]

Display image planes from a cutout structure in a single row.

Parameters:
cutoutdict

Cutout structure as returned by stdpipe.cutouts.get_cutout().

planeslist of str, optional

Names of cutout planes to show (in order).

figmatplotlib.figure.Figure, optional

Figure to draw into; a new figure is created if not provided.

axslist of matplotlib.axes.Axes, optional

Axes to draw into; must be the same length as planes.

mark_xfloat, optional

X coordinate (in cutout pixels) of the circular overlay mark.

mark_yfloat, optional

Y coordinate (in cutout pixels) of the circular overlay mark.

mark_rfloat, optional

Radius of the overlay mark in pixels.

mark_r2float, optional

Radius of a secondary dashed overlay circle.

mark_r3float, optional

Radius of a tertiary dashed overlay circle.

mark_colorcolor, optional

Color of the overlay mark.

mark_lwfloat, optional

Line width of the overlay mark.

mark_rafloat, optional

RA of the overlay mark; overrides mark_x / mark_y.

mark_decfloat, optional

Dec of the overlay mark; overrides mark_x / mark_y.

r0float, optional

Gaussian smoothing sigma applied to image, template, and diff planes.

show_titlebool, optional

If True (default), display a title above the cutout row.

titlestr, optional

Title text. Auto-generated from cutout metadata if not provided.

additional_titlestr, optional

Text appended to the auto-generated title.

**kwargs

Additional keyword arguments passed to imshow() for each plane.

Cutout-level rejection of subtraction artefacts

The difference image often contains the characteristic artefacts (“dipoles”) due to slight sub-pixel displacement of the object between the science image and the template (e.g. due to imperfect astrometric alignment, or due to large proper motion for the templates acquired long ago). Also, the intensity of the object may also be slightly different on the science image - e.g. due to slightly different photometric band - that leads to unshifted positive or negative differences. In principle, such cases may be automatically detected and - if appropriate - rejected by a simple adjustment procedure based solely on the information we already have inside typical cutout. The adjustment here means optimizing slight (sub-pixel) shifts between the image and (convolved) template, as well as slight scaling of the template, in order to minimize the residuals. We have a dedicated routine, stdpipe.cutouts.adjust_cutout(), that tries to perform such optimization, and may be used inside the pipelines like in the following example:

for i,cand in enumerate(candidates):
    print('Candidate %d with mag = %.2f +/- %.2f at x/y = %.1f %.1d and RA/Dec = %.4f %.4f' %
             (i, cand['mag_calib'], cand['mag_calib_err'], cand['x'], cand['y'], cand['ra'], cand['dec']))

    # Produce the cutout with all necessary planes
    cutout = cutouts.get_cutout(image, cand, 20, mask=mask|tmask, diff=diff, template=tmpl,
             convolved=conv, err=ediff, header=header, filename=filename, time=time)

    # Try to adjust the position and scale of the cutout to see whether it disappears or not
    # Here we use only inner 2*fwhm x 2*fwhm part for fitting
    # and limit possible shifts and scale to 1 pixel and 2.0, correspondingly
    if cutouts.adjust_cutout(cutout, inner=2*fwhm, max_shift=1, max_scale=2.0,
             fit_bg=True, normalize=False, verbose=True):
        # The optimization converged - now we may check its actual adjustment
        # and decide whether the transient disappeared or not
        # E.g. we see whether final chi2 is at least 3 times smaller than original one
        # and its corresponding p-value is worse than 1e-5
        # and the fitted scale is between 0.8 and 1.2
        if ((cutout['meta']['adjust_pval'] > 1e-5 or
             cutout['meta']['adjust_chi2'] < 0.3*cutout['meta']['adjust_chi2_0']) and
            cutout['meta']['adjust_scale'] > 0.8 and cutout['meta']['adjust_scale'] < 1.2):
            # Well, it really "disappeared"
            print("The candidate is no more significant"
            cutout['meta']['adjusted'] = True

    # We may now plot the results, including the results of the adjustment routine
    # It will be shown in `adjusted` plane, if the optimization converged
    plots.plot_cutout(cutout, ['image', 'template', 'convolved', 'diff', 'adjusted', 'mask'],
             qq=[0.5, 99.5], stretch='linear')
stdpipe.cutouts.adjust_cutout(cutout, max_shift=2, max_scale=1.1, inner=None, normalize=False, fit_bg=False, verbose=False)[source]

Fit a positional and flux-scaling adjustment to minimize the image–template residual.

Optimizes a shift (dx, dy), scale, and optionally background levels to minimize the chi-squared residual between cutout['image'] and cutout['convolved']. On success, adds a cutout['adjusted'] plane with the optimized difference.

Parameters:
cutoutdict

Cutout structure as returned by get_cutout(). Must contain image, convolved, and err planes.

max_shiftfloat, optional

Maximum allowed positional shift in pixels (symmetric bound).

max_scalefloat, optional

Maximum allowed flux scale factor; scale is bounded to (1/max_scale, max_scale).

innerint, optional

If set, only the central inner × inner pixel box is used for optimization.

normalizebool, optional

If True, divide the adjusted plane by the err plane.

fit_bgbool, optional

If True, fit background levels as free parameters. If False, estimate backgrounds from the cutout using SExtractor-mode (2.5*median 1.5*mean) and hold them fixed.

verbosebool or callable, optional

Whether to show verbose messages. May be boolean or a print-like callable.

Returns:
bool

True if optimization succeeded, False otherwise.

Notes

On success the following keys are added to cutout['meta']: adjust_chi2_0, adjust_chi2, adjust_df, adjust_pval, adjust_dx, adjust_dy, adjust_scale, adjust_bg, adjust_tbg.