spacr.qt.mask_engine
====================

.. py:module:: spacr.qt.mask_engine

.. autoapi-nested-parse::

   Pure-Python backend for the Qt make-masks screen.

   Mirrors the image + mask I/O and label-mutation helpers from
   `spacr.gui_elements.ModifyMaskApp`, without touching Tk. The Qt screen
   above this reads/writes `self.image` and `self.mask` directly and calls
   these helpers for the non-brush operations (fill / relabel / invert /
   remove small).









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

.. py:data:: IMAGE_EXTS
   :value: ('.png', '.jpg', '.jpeg', '.tif', '.tiff', '.bmp')


.. py:function:: list_images(folder: str) -> List[str]

   Return filenames of image files in `folder`, sorted, or [].


.. py:function:: load_image_and_mask(folder: str, filename: str) -> Tuple[numpy.ndarray, numpy.ndarray]

   Load an image and its accompanying mask (from `folder/masks/`).

   - Multi-channel images are collapsed to grayscale via BT.601 weights.
   - Missing masks are created as zeros of the image shape.
   - Both are returned as uint16 / uint8 arrays (image / mask).


.. py:function:: save_mask(folder: str, filename: str, mask: numpy.ndarray) -> str

   Relabel connected components and write to <folder>/masks/<name>.tif.

   Returns the absolute save path.


.. py:function:: normalize_uint16(image: numpy.ndarray, lower_pct: float = 1.0, upper_pct: float = 99.9) -> numpy.ndarray

   Return image clipped + rescaled to its dtype's full range.


.. py:function:: overlay_mask(image: numpy.ndarray, mask: numpy.ndarray, alpha: float = 0.5) -> numpy.ndarray

   Blend a colorized label mask onto a grayscale image, uint8 RGB.


.. py:function:: paint_disk(mask: numpy.ndarray, cx: int, cy: int, radius: int, value: int = 255) -> None

   In-place stamp a filled square (radius half-width) at (cx, cy).


.. py:function:: paint_line(mask: numpy.ndarray, x0: int, y0: int, x1: int, y1: int, radius: int, value: int = 255) -> None

   In-place stamp a line of disks between two points (Bresenham).


.. py:function:: fill_holes(mask: numpy.ndarray) -> numpy.ndarray

   Fill holes inside True regions; returns a relabeled mask.


.. py:function:: relabel_objects(mask: numpy.ndarray) -> numpy.ndarray

   Return a mask whose connected components are labeled 1..N.


.. py:function:: clear_mask(mask: numpy.ndarray) -> numpy.ndarray

   Return an all-zero array shaped like ``mask``.


.. py:function:: invert_mask(mask: numpy.ndarray) -> numpy.ndarray

   Return the mask with foreground/background flipped and relabeled.


.. py:function:: remove_small_objects(mask: numpy.ndarray, min_area: int) -> numpy.ndarray

   Drop connected components with area < min_area (in pixels).


.. py:function:: erase_object_at(mask: numpy.ndarray, x: int, y: int) -> numpy.ndarray

   Zero out the object under (x, y). No-op if no object there.


.. py:function:: magic_wand(image: numpy.ndarray, mask: numpy.ndarray, seed_x: int, seed_y: int, tolerance: float, max_pixels: int = 100000, action: str = 'add') -> numpy.ndarray

   BFS flood-fill from (seed_x, seed_y) filling pixels whose intensity
   is within `tolerance` (L2 distance) of the seed. Writes 255 (add) or
   0 (erase) into the returned mask copy.


.. py:class:: MaskHistory(capacity: int = 20)

   Bounded undo/redo stack of mask arrays. Deep-copies on push so
   callers can mutate in place without corrupting older snapshots.


   .. py:attribute:: capacity


   .. py:method:: clear() -> None

      Discard every snapshot from both the undo and redo stacks.



   .. py:method:: push(mask: numpy.ndarray) -> None

      Store a deep-copy of ``mask`` and drop any redo history.



   .. py:method:: can_undo() -> bool

      Return True when at least one prior snapshot is available to undo to.



   .. py:method:: can_redo() -> bool

      Return True when the redo stack has a snapshot to restore.



   .. py:method:: undo() -> Optional[numpy.ndarray]

      Pop the top snapshot, save it to the redo stack, and return the
      previous snapshot (i.e. one step back). None if not possible.



   .. py:method:: redo() -> Optional[numpy.ndarray]

      Restore the most-recently-undone snapshot, or ``None`` if empty.



