goatpy.auto_align
=================

.. py:module:: goatpy.auto_align

.. autoapi-nested-parse::

   auto_align.py
   =============
   Auto-registration of a MALDI imzML dataset against a whole-slide H&E image,
   or against a multichannel IHC TIFF / OME-TIFF image.

   Key design decisions
   --------------------
   1.  Registration is performed at MALDI native pixel size (default 10 um/px)
       so the H&E thumbnail is tiny and peak RAM stays manageable.

   2.  Matching uses normalised grayscale cross-correlation (TM_CCOEFF_NORMED)
       on the TIC image vs inverted H&E/IHC intensity.

   3.  A two-pass rotation search (coarse 0-360, then fine) finds the correct
       slide orientation without assuming any starting angle.

   4.  Registration, canvas building, and annotation transforms all use
       cv2.warpAffine with the same analytically derived affine matrix.
       This guarantees that best_idx, canvas pixels, and annotation coordinates
       all live in the same coordinate system.

   IHC support
   -----------
   When ``ihc_path`` is supplied to ``load_and_align``, the function loads a
   multichannel TIFF or OME-TIFF (any format readable by tifffile) instead of
   an H&E image.  Registration uses a single channel selected by ``ihc_channel``
   (default: the channel with the highest mean intensity, which is usually the
   DAPI / nuclear stain — a good proxy for tissue shape).  The full multichannel
   canvas is stored in ``sdata.images["ihc_image"]``; the channel used for
   registration is stored in ``sdata.images["ihc_reg_channel"]`` for inspection.

   Coordinate system
   -----------------
   All three steps share the same affine matrix M_stored:

       M_stored maps: reg-resolution image coords -> canvas coords

       _match_at_rotation  uses cv2.warpAffine(M_stored) to build the search canvas
       _build_affine_and_canvas uses cv2.warpAffine(M_stored) to build output canvas
       _transform_geojson  applies M_up @ M_stored @ M_scale to annotation vertices

   Because the same matrix is used everywhere, best_idx from matchTemplate is
   directly valid as the MALDI placement offset in the output canvas.



Functions
---------

.. autoapisummary::

   goatpy.auto_align.load_and_align


Module Contents
---------------

.. py:function:: load_and_align(imzml_path: str, he_path: Optional[str] = None, peaks_path: Optional[str] = None, geojson_path: Optional[Union[str, pathlib.Path]] = None, geojson_shapes_key: str = 'Annotations', geojson_classification_key: str = 'classification', maldi_pixel_um: Optional[float] = None, he_pixel_um: Optional[float] = None, spectra_chunk_size: int = 10, coarse_rotation_step: int = 15, fine_rotation_range: float = 5.0, fine_rotation_step: float = 1.0, buffer_px: int = 150, img_upscaling: int = 10, tol: float = 0.1, reduce_func: Any = sum, ihc_path: Optional[str] = None, ihc_pixel_um: Optional[float] = None, ihc_channel: Optional[int] = None, ihc_invert: bool = False) -> spatialdata.SpatialData

   Load a MALDI imzML dataset and an H&E or IHC image, auto-register them,
   and return a merged SpatialData object.

   Exactly one of ``he_path`` or ``ihc_path`` must be supplied.  When
   ``ihc_path`` is given the function loads a multichannel TIFF / OME-TIFF
   (any format readable by ``tifffile``) and uses a single channel for
   registration.  The full multichannel canvas is stored in
   ``sdata.images["ihc_image"]``; the H&E slot is left empty.

   :param imzml_path: Path to the .imzML file.
   :type imzml_path: str
   :param he_path: Path to an H&E image (SVS, NDPI, TIFF, PNG, …).
                   Supply either ``he_path`` or ``ihc_path``, not both.
   :type he_path: str or None
   :param peaks_path: Path to peaks CSV.  Uses bundled PEAKS.csv when None.
   :type peaks_path: str or None
   :param geojson_path: Optional QuPath GeoJSON annotation export.
   :type geojson_path: str, Path, or None
   :param geojson_shapes_key:
   :type geojson_shapes_key: str, default "annotations"
   :param geojson_classification_key:
   :type geojson_classification_key: str, default "classification"
   :param maldi_pixel_um: MALDI pixel size in µm.  Auto-read from imzML when None.
   :type maldi_pixel_um: float or None
   :param he_pixel_um: H&E native pixel size in µm.  Auto-read from metadata when None.
                       Also used as the pixel size for IHC when ``ihc_pixel_um`` is None.
   :type he_pixel_um: float or None
   :param spectra_chunk_size:
   :type spectra_chunk_size: int
   :param coarse_rotation_step:
   :type coarse_rotation_step: int
   :param fine_rotation_range:
   :type fine_rotation_range: float
   :param fine_rotation_step:
   :type fine_rotation_step: float
   :param buffer_px:
   :type buffer_px: int
   :param img_upscaling:
   :type img_upscaling: int
   :param tol:
   :type tol: float
   :param reduce_func:
   :type reduce_func: callable
   :param IHC parameters:
   :param --------------:
   :param ihc_path: Path to a multichannel IHC TIFF / OME-TIFF.
                    Supply either ``he_path`` or ``ihc_path``, not both.
   :type ihc_path: str or None
   :param ihc_pixel_um: Native IHC pixel size in µm.  Auto-read from OME-XML / TIFF tags
                        when None.  Falls back to ``he_pixel_um`` and then to 0.2527 µm/px.
   :type ihc_pixel_um: float or None
   :param ihc_channel: Index of the channel to use for registration.
                       None (default) → auto-select the channel with the highest mean
                       intensity (typically the nuclear stain, which captures tissue shape).
   :type ihc_channel: int or None
   :param ihc_invert: Invert the registration channel before matching.
                      H&E images are automatically inverted (tissue is dark → bright after
                      inversion, matching the bright MALDI TIC signal).
                      For brightfield IHC, set ``ihc_invert=True``.
                      For fluorescence IHC, leave as False (tissue is already bright).
   :type ihc_invert: bool, default False

   :returns: images['he_image']             -- H&E canvas (if he_path supplied)
             images['ihc_image']            -- multichannel IHC canvas (if ihc_path supplied)
             shapes['pixels']               -- one square per MALDI pixel
             shapes[geojson_shapes_key]     -- annotations (if geojson_path given)
             points['centroids']            -- centroid of each MALDI pixel
             tables['maldi_adata']          -- AnnData with ion intensities
   :rtype: SpatialData with


