astroalign

repository·master·Indexed 41 days ago

https://github.com/quatrope/astroalign

A Python module for the astrometric alignment of stellar astronomical images, specifically designed for cases where World Coordinate System (WCS) information is unavailable. It uses 3-point asterism matching to deduce affine transformations, providing robustness against varying fields of view and atmospheric conditions. Key functions include `aa.register()` for image alignment and `aa.find_transform()` for extracting transformation matrices and star correspondences.

Tokens
4.6K
Snippets
17
Records
19
Agent score
51%

What's inside astroalign

  1. What is Astroalign and how does it work?

    master

    Astroalign is a Python module designed to register (align) two stellar astronomical images, particularly when World Coordinate System (WCS) information is unavailable.

    Instead of using generic point-feature matching (which often fails on stellar images due to the lack of stable structure), Astroalign uses asterism matching. It identifies similar 3-point asterisms (triangles) in both images to estimate the affine transformation between them.

    Key characteristics:

    • Robustness: Because it relies on geometric patterns (triangles), it is less affected by differences in point-spread function (PSF), seeing, or atmospheric conditions.
    • Limitations: It may struggle with images of extended objects (few point-like sources), very crowded fields, or images containing a large number of hot pixels.
    • Hot Pixel Mitigation: If hot pixels cause incorrect registration, it is recommended to use a cosmic-ray remover like CCDProc's cosmic-ray remover before using Astroalign.
  2. Selecting regions with masking

    master

    You can restrict the source search to specific regions by masking out undesired areas in your images before passing them to astroalign.register. This is useful for:

    • Restricting search to overlapping fields of view when images have different scales.
    • Masking out objects below the horizon.
    • Excluding extended objects like galaxies or dense globular clusters.
    • Defining a Region of Interest (ROI) when you know the approximate location of the source image within the target image.
  3. Propagate masks for `NDData`, `CCDData`, and Masked Arrays

    master

    When working with objects that have both .data and .mask properties (like Astropy NDData or CCDData), you can transform both simultaneously using the propagate_mask=True argument in aa.register or aa.apply_transform.

    This ensures that the transformation's footprint (the area with no valid pixel information) is automatically added to the existing mask of the object.

    from astropy.nddata import NDData
    import numpy as np
    from skimage.transform import SimilarityTransform
    import astroalign as aa
    
    # Setup NDData with data and a mask
    nd = NDData([[0, 1], [2, 3]], [[True, False], [False, False]])
    
    # Define a transform
    transf = SimilarityTransform(rotation=np.pi/2., translation=(1, 0))
    
    # Apply transform with mask propagation
    aligned_image, footprint = aa.apply_transform(transf, nd, nd, propagate_mask=True)
    
    # Create a new object with the combined mask
    new_nd = NDData(aligned_image, mask=footprint)
  4. Handle RGB and RGBA images

    master

    Astroalign supports color images provided the channel dimension is the last axis in the array (the default for pillow and scikit-image). The transformation is calculated based on the mean average of all channels.

    When working with pillow images, you may need to convert the resulting array back to an unsigned 8-bit integer format for compatibility.

    from PIL import Image
    import astroalign as aa
    
    source = Image.open("source.jpg")
    target = Image.open("target.jpg")
    registered, footprint = aa.register(source, target)
    
    # Convert back to pillow image if necessary:
    # Pillow may require array to be unsigned 8-bit integer format.
    registered = Image.fromarray(registered.astype("uint8"))
  5. Masking out undesired regions using NumPy masked arrays

    master

    To restrict the search to a specific Region of Interest (ROI), create a boolean mask where True represents the areas to be ignored and False represents the ROI. You can then pass a NumPy masked array to aa.register.

    In the example below, we define an ROI corresponding to rows 300–900 and columns 1000–1700 across all color channels. We set the mask to False for the ROI (meaning these pixels are not masked/ignored) and True everywhere else.

    import numpy as np
    import astroalign as aa
    
    # Create a mask of all True (everything is masked/ignored)
    mask = np.ones_like(target, dtype="bool")
    
    # Set the ROI to False (not masked/included in search)
    mask[300:900, 1000:1700, :] = False
    
    # Create the masked array
    target_masked = np.ma.array(target, mask=mask)
    
    # Perform registration using the masked target
    registered, footprint = aa.register(source, target_masked)
    
    # Optional: Overwrite the target with the registered source in the footprint pixels
    target[~footprint] = registered[~footprint]
  6. Mask the aligned image with a fill value

    master

    To fill the regions where the transformation produced no pixel information (the footprint) with a specific value, you can either use the returned footprint manually or use the fill_value argument in aa.register.

    # Option 1: Manual masking
    registered_image, footprint = aa.register(source, target)
    registered_image[footprint] = -99999.99
    
    # Option 2: Using the fill_value argument
    registered_image, footprint = aa.register(source, target, fill_value=-99999.99)
  7. Detect faint stars by adjusting detection threshold

    master

    If stars in your image are too faint to pass the default $5\sigma$ threshold, you can lower the detection sensitivity by adjusting the detection_sigma keyword argument. This argument is available in both aa.register() and aa.find_transform().

    import astroalign as aa
    # Lower the detection threshold to 2 sigma
    registered_image, footprint = aa.register(source, target, detection_sigma=2)
  8. Limit the number of stars used for registration

    master

    When working with fields containing very few stars (e.g., 3 to 6), you can prevent astroalign from picking up noisy structures by restricting the number of stars selected for the transformation. Use the max_control_points keyword argument in aa.register() or aa.find_transform() to set a maximum limit. Note that at least 3 stars are required to find a transformation.

    import astroalign as aa
    # Restrict to exactly 3 stars to avoid noise
    registered_image, footprint = aa.register(source, target, max_control_points=3)
  9. Avoid hot pixels and CCD artifacts during registration

    master

    Persistent CCD defects like hot or dead pixels can be mistaken for legitimate sources, potentially resulting in an identity transformation. There are two ways to handle this:

    1. Pre-cleaning: Use a tool like ccdproc.cosmicray_lacosmic to clean the image before passing it to astroalign.
    2. Increase minimum area: Increase the min_area keyword argument (which defaults to 5) to require a larger number of connected pixels for a source to be considered valid. This helps ignore single-pixel artifacts.

    Both aa.register() and aa.find_transform() support the min_area parameter.

    # Option 1: Clean with ccdproc first
    from ccdproc import cosmicray_lacosmic as lacosmic
    clean_source, mask = lacosmic(myimage)
    registered_image, footprint = aa.register(clean_source, clean_target, min_area=9)
    
    # Option 2: Increase min_area to ignore small artifacts
    import astroalign as aa
    registered_image, footprint = aa.register(source, target, min_area=9)
  10. Apply a transformation to a set of points using `aa.matrix_transform`

    master

    To apply a known transformation matrix to a set of coordinates, use aa.matrix_transform. This is a convenience wrapper around scikit-image.transform.matrix_transform.

    import astroalign as aa
    
    # src is an array of (x, y) points
    # tform is a SimilarityTransform object from find_transform or estimate_transform
    dst_calc = aa.matrix_transform(src, tform.params)