Crop Tool

Crop, straighten and rotate an image as a single geometric transform, then re-solve the astrometric calibration so the result stays plate-solved.

1. Using the tool

ControlEffect (and how it feeds the transform)
Drag on imageDraws/forms the crop rectangle; handles resize, drag-inside moves, rotation handle sets the angle.
Aspect RatioConstrains the rectangle to a fixed ratio (reshaped about its centre).
Margins (px) - T/R/B/LTrims an exact pixel count per edge; the cleanest way to remove a known stacking border.
AngleRotation in degrees baked into the transform; output is axis-aligned to the rotated rectangle.
Zoom +/-, 100%, FitPreview navigation only.
Toggle AutostretchScreen-only stretch for framing faint data; never written into the result.
ApplyExecutes the crop and the WCS re-solve.
Load Previous CropRecalls the last rectangle, position and angle to reuse identical framing.
Batch Crop (all open)Applies the same transform to every open image - meaningful for a registered set that shares framing; each image's own WCS is re-solved independently.

Multi-Image Overlap

The ⊞ Multi-Image Overlap panel composites several open images so you can see their common coverage and choose a crop valid for all of them (mosaics, dithered sets). Blend sets how contributors combine; Colorize tints each so you can see which image covers which region. The composite is a framing aid only — the crop still applies to the real image data, and each image's WCS is re-solved on apply.

Typical workflow: open your registered set → Multi-Image Overlap → Apply Composite Preview to see the shared region → draw a crop inside it → Back to Single Image → Batch Crop (all open).

2. Technical details

The rest of this page documents exactly what the tool computes internally, including how the World Coordinate System (WCS) and its SIP distortion model are preserved.

2.1 The geometry is one projective transform

Everything you set in the dialog — the crop rectangle, the rotation Angle, the aspect-ratio constraint and the per-edge pixel Margins — collapses into a single 3×3 homography matrix M that maps a source pixel to an output pixel in homogeneous coordinates:

[ x' w ] [ m00 m01 m02 ] [ x ] [ y' w ] = [ m10 m11 m12 ] [ y ] [ w ] [ m20 m21 m22 ] [ 1 ] output pixel = ( x'w / w , y'w / w )

A pure crop is a translation; rotation adds the usual sine/cosine block; rescale adds a diagonal factor. Representing all of it as one matrix means the WCS update below is identical whether you cropped, rotated, rescaled, or did all three at once — there is no special-case code per operation.

2.2 How the WCS is preserved

SASpro does not try to algebraically edit the old WCS (shift CRPIX, rotate the CD matrix, rotate SIP coefficients). That approach is fragile the moment rotation and SIP distortion are combined. Instead the tool re-fits a brand-new WCS from resampled sky positions, which is exact up to a measured residual. The algorithm (wcs_update.py → update_wcs_after_crop):

2.2.1 — Build the original WCS

The source FITS header is parsed with astropy as a TAN or TAN–SIP WCS (relax=True). If the header is 3-D (e.g. a CTYPE3 axis), the axis-3 cards (CRPIX3, CD3_, PC3_, PV3_, …) are stripped so the solve is strictly 2-D.

2.2.2 — Invert the transform and sample the output

The matrix is inverted, H = M⁻¹ (output → source), and a dense grid of up to 40×40 points is laid over the output image at pixel centres 0.5 … N−0.5. Each output grid point (x,y) is mapped back to a source pixel through the inverse homography, with the perspective divide:

x_{\mathrm{src}}=\frac{H_{00}x+H_{01}y+H_{02}}{H_{20}x+H_{21}y+H_{22}}
y_{\mathrm{src}}=\frac{H_{10}x+H_{11}y+H_{12}}{H_{20}x+H_{21}y+H_{22}}

2.2.3 — Source pixels → true sky

Those source pixels are pushed through the original WCS with its full distortion model via pixel_to_world. This is the crucial step: the celestial position of every grid point is computed with the original SIP + TAN, so the (output-pixel ↔ RA/Dec) correspondences carry the real distortion, not an approximation of it.

2.2.4 — Fit a new forward WCS

astropy's fit_wcs_from_points solves a fresh WCS from those correspondences:

The forward SIP convention it fits is the standard Shupe (2005) model: with u = x − CRPIX1, v = y − CRPIX2, the distortion polynomials are

f(u,v)=\sum A_{pq}\,u^{p}v^{q}\,,\qquad g(u,v)=\sum B_{pq}\,u^{p}v^{q}\qquad(p+q\leq A\_ORDER)

which are applied and projected as

(\xi,\eta)=\mathrm{CD}\cdot(u+f(u,v),\;v+g(u,v))

followed by a TAN deprojection about (CRVAL1, CRVAL2) to give (RA, Dec).

2.2.5 — Reconstruct the inverse SIP (AP/BP)

fit_wcs_from_points only fits the forward terms A/B, not the inverse AP/BP used for sky→pixel. SASpro rebuilds the inverse on the same grid. For each sample it asks the new forward solution where the sky point lands, (x_fwd, y_fwd) = world_to_pixel(sky), and forms CRPIX-relative residuals:

u_obs = x - CRPIX1 v_obs = y - CRPIX2 (target grid) u_fwd = x_fwd - CRPIX1 v_fwd = y_fwd - CRPIX2 (forward prediction) du = u_obs - u_fwd dv = v_obs - v_fwd (what AP/BP must add back)

It then least-squares-fits two 2-D polynomials up to the SIP order to those residuals, AP(u_fwd, v_fwd) ≈ du and BP(·) ≈ dv, via a monomial design matrix and numpy.linalg.lstsq. Non-negligible coefficients (|c| > 1e-30) are written as AP_p_q / BP_p_q with matching AP_ORDER/BP_ORDER, so pixel↔sky round-trips stay accurate in both directions.

2.2.6 — Clean header rewrite

The new cards are written into a copy of the original header only after every old WCS keyword is stripped — CTYPE, CUNIT, CDELT, CRPIX, CRVAL, PC, CD, PV, PS, LONPOLE, LATPOLE, RADESYS, EQUINOX, A_*, B_*, AP_*, BP_*, WCSAXES, MJDREF. This guarantees no stale distortion term from the pre-crop solution can survive alongside the new one. NAXIS1/2 are set to the output size, and the live wcs object plus wcs_header in the document metadata are replaced so downstream RA/Dec read-outs use the new solution immediately.

2.3 Fit quality

After fitting, the grid is round-tripped (new pixel → sky) and compared to the true sky positions from step 2.2.3. The angular separations give an RMS and the 50th/95th percentiles, in arc-seconds, recorded in the document's __wcs_debug__ summary along with before/after CRVAL, CRPIX, pixel scale and rotation. Pixel scale and field rotation are read back from the CD matrix as:

\mathrm{scale}_x=3600\,\sqrt{\mathrm{CD}_{11}^{2}+\mathrm{CD}_{21}^{2}}\quad[\mathrm{arcsec/px}]
\theta=\mathrm{atan2}(-\mathrm{CD}_{21},\;\mathrm{CD}_{11})\quad[\deg]

A well-behaved crop/rotate typically round-trips to well under the pixel scale; large residuals indicate the source solution was marginal or the header could not be coerced to 2-D.

2.4 Limitations