Alternate Image Types#
AstroPhot operates in the tangent plane space and so must have a mapping between the pixels and the sky that it can use to properly perform integration within every pixel. Aside from the standard ap.TargetImage used to store regular data with a linear mapping between pixel space and the tangent plane, there are two more image types ap.SIPTargetImage and ap.CMOSTargetImage which are explained below.
import astrophot as ap
import torch
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
SIP Target Image#
The ap.SIPTargetImage object stores data for a pixel array that is distorted using Simple-Image-Polynomial distortions. This is a non-linear polynomial transformation that is used to account for optical effects in images that result in the sky being non-linearly projected onto the pixel grid used to collect data. AstroPhot follows the WCS standard when it comes to SIP distortions and can read the SIP coefficients directly from an image. AstroPhot can also save a SIP distortion model to a FITS image. Internally the SIP coefficients are stored in image.sipA, image.sipB, image.sipAP and image.SIPBP which are formatted as dictionaries with the keys as tuples of two integers giving the powers and the value as the coefficient. For example in a FITS file the header line A_1_2 = 0.01 will translate to image.sipA = {(1,2): 0.01}.
Some particulars of the AstroPhot implementation. For the sake of efficiency, when a SIP image is created AstroPhot evaluates the SIP distortion at every pixel and stores that in a distortion map with the same size as the image. Afterwards, calling image.pixel_to_plane will not evaluate the SIP polynomial, but instead a bilinear interpolation of the distortion model will be used. This massively increases speed, but means that the distortion model is only accurate up to the bilinear interpolation accuracy, since most SIP distortions are quite smooth, this interpolation is extremely accurate. For queries beyond the borders of the image, AstroPhot will not extrapolate the SIP polynomials, instead the distortion amount at the pixel border is simply carried onwards. As second element of the AstroPhot implementation is that if a backwards model (AP and BP) is not provided, then AstroPhot will use linear algebra to determine the backwards model. This is taken from the very clever code written by Shu Liu and Lei Hi that you can find here.
For the most part, once you define a ap.SIPTargetImage you can use it like a regular ap.TargetImage object.
target = ap.SIPTargetImage(
data=torch.randn(128, 256),
sipA={(0, 1): 1e-3, (1, 0): -1e-3, (1, 1): 1e-4, (2, 0): -5e-5, (0, 2): -5e-4},
sipB={(0, 1): 1e-3, (1, 0): -1e-3, (1, 1): -1e-3, (2, 0): 1e-4, (0, 2): 2e-3},
)
fig, ax = plt.subplots(figsize=(10, 5))
ap.plots.target_image(fig, ax, target)
ax.set_title("SIP Target Image")
plt.show()
Because the pixels are distorted on the sky, this means that the amount of area on the sky for each pixel is different. One would expect a pixel that projects to a larger area to collect more light than one that gets squished smaller. A uniform source observed through a telescope with SIP distortions will therefore produce a non-uniform image. As such, AstroPhot tracks the projected area of each pixel to ensure its calculations are accurate. Here is what that pixel area map looks like for the above image. As you can see, the parts which get stretched out then correspond to larger areas, and the parts that get squished correspond to smaller areas.
plt.imshow(target.pixel_area_map.T, cmap="inferno", origin="lower")
plt.colorbar(label="Pixel Area (arcsec$^2$)")
plt.title("Pixel Area Map")
plt.show()
CMOS Target Image#
A CMOS sensor is an alternative technology from a CCD for collecting light in an optical system. While it has certain advantages, one challenge with CMOS sensors is that only a sub region of each pixel is actually sensitive to light, the rest holding per-pixel electronics. This means there are gaps in the true placement of the CMOS pixels on the sky. Currently AstroPhot implements this by ensuring that the models are only sampled and integrated in the appropriate pixel areas. However, this treatment is not appropriate for certain PSF convolution modes and so the ap.CMOSTargetImage is under active development. Expect some changes in the future as we ensure it is viable for all model types. Currently, sky models, point source models, and un-convolved galaxy models should all work accurately. Adding convolved galaxy models is set for future work.
target = ap.CMOSTargetImage(
data=torch.randn(128, 256),
subpixel_loc=(-0.1, -0.1),
subpixel_scale=0.8,
)
fig, ax = plt.subplots(figsize=(10, 5))
ap.plots.target_image(fig, ax, target)
ax.set_title("CMOS Target Image")
plt.show()
There is no visible difference when plotting the data as compressing every pixel in an image like above would make it hard to see what is happening. Below we plot what a single pixel truly looks like in the CMOS target representation.
fig, ax = plt.subplots(figsize=(5, 5))
r1 = Rectangle((-0.5, -0.5), 1, 1, facecolor="grey", label="Pixel Area")
ax.add_patch(r1)
r2 = Rectangle((-0.5, -0.5), 0.8, 0.8, facecolor="blue", label="Subpixel Area")
ax.add_patch(r2)
ax.scatter([-0.1], [-0.1], color="red", label="subpixel_loc")
ax.axvline(0.33, 0, 0.8, color="orange", linewidth=2, label="subpixel_scale")
ax.set_xlim(-0.5, 0.5)
ax.set_ylim(-0.5, 0.5)
ax.set_title("CMOS Pixel Representation")
ax.legend(loc="upper left")
plt.show()
Where the blue subpixel area is actually sensitive to light. Note that pixel indexing places (0,0) at the center of the pixel and every pixel has size 1, so for the first pixel show here the pixel coordinates range from -0.5 to +0.5 on both axes. This is also the representation used to define a ap.CMOSTargetImage where subpixel_loc gives the pixel coordinates of the center of the subpixel and subpixel_scale gives the side length of the subpixel.