phenotypic.abc_.GridCorrector#

class phenotypic.abc_.GridCorrector[source]#

Bases: ImageCorrector, GridOperation, ABC

Apply whole-image transformations (rotation, alignment, perspective) to GridImage objects.

GridCorrector is a type-safe wrapper around ImageCorrector that enforces GridImage input and output types. It is specialized for grid-aware image corrections on arrayed plate images.

Purpose

Use GridCorrector when implementing transformations that modify entire GridImage objects while respecting their grid structure. Like ImageCorrector, it updates all image components (rgb, gray, enh_gray, objmask, objmap) together to maintain synchronization. The difference is that it requires GridImage input and output, making explicit that your transformation works in the context of grid-structured plate images.

What GridCorrector modifies

GridCorrector operations modify ALL image components simultaneously:

  • Color data: rgb, gray (pixel coordinates change due to rotation/perspective)

  • Preprocessed data: enh_gray (enhanced grayscale also rotates/transforms)

  • Detection results: objmask, objmap (colony masks and labels transform identically)

  • Grid structure: Grid rotation angle and alignment state (optional, depends on operation)

This ensures that a rotated colony mask aligns perfectly with the rotated rgb and gray data.

GridImage vs Image

  • Image: Generic image with optional, unvalidated grid information.

  • GridImage: Specialized Image subclass with validated grid structure (row/column layout, well positions, grid alignment angle). Typically used after GridFinder detects the grid structure.

When to use GridCorrector vs ImageCorrector

  • ImageCorrector: Transformation works on any Image. Examples: rotation, perspective correction for individual (non-gridded) images. Use when grid structure is irrelevant.

  • GridCorrector: Transformation assumes or modifies grid structure. Examples: aligning colonies to grid rows/columns, rotating to match grid axes, per-well perspective correction. Use when the transformation is grid-aware or affects well-level alignment.

Typical Use Cases

  • Grid alignment: Rotate the entire image so detected colonies align with grid rows and columns. Improves downstream grid-based analysis. Example: GridAligner rotates to make colony rows parallel to image axes.

  • Perspective correction: Correct camera tilt or lens distortion that skews the grid.

  • Plate reorientation: Rotate plate image to canonical orientation for consistent analysis.

  • Color calibration per well: Apply per-well color correction that respects grid boundaries.

Implementation Pattern

Inherit from GridCorrector and implement _operate() as normal:

from phenotypic.abc_ import GridCorrector
from phenotypic import GridImage

class GridAligner(GridCorrector):
    '''Rotate GridImage to align colonies with grid rows/columns.'''

    def __init__(self, axis: int = 0):
        super().__init__()
        self.axis = axis

    @staticmethod
    def _operate(image: GridImage, axis: int = 0) -> GridImage:
        # image is guaranteed to be GridImage
        # Rotate all components together
        rotation_angle = calculate_grid_rotation(image, axis)
        image.rotate(angle_of_rotation=rotation_angle, mode='edge')
        return image

Critical Implementation Detail

Ensure ALL image components are transformed identically:

@staticmethod
def _operate(image: GridImage, **kwargs) -> GridImage:
    # Apply transformation to rgb/gray
    angle = kwargs.get('angle', 0)
    image.rotate(angle_of_rotation=angle, mode='edge')

    # The image.rotate() method automatically handles:
    # - Rotating enh_gray identically
    # - Rotating objmask and objmap with same angle
    # - Updating grid rotation state if applicable

    return image

Interpolation Considerations

When rotating or warping:

  • Color data (rgb, gray): Use smooth interpolation (order=1+) to preserve colony edges

  • Detection data (objmask, objmap): Use nearest-neighbor interpolation (order=0) to preserve discrete object labels (must remain integers)

  • Enhanced grayscale: Use same interpolation as color data for consistency

Notes

  • GridCorrector has no integrity checks (@validate_operation_integrity), by design. All components are intentionally modified together; there is nothing to validate.

  • Grid rotation angle and alignment state may be updated after the transformation. Downstream grid-aware operations will work with the updated grid structure.

  • GridImage must have valid grid structure before correction. Use GridFinder or specify grid manually before applying GridCorrector.

  • Output is always GridImage (type-safe). Attempting to apply to plain Image raises error.

Examples

GridAligner: rotate to align colonies with grid axes
from phenotypic import GridImage, Image
from phenotypic.detect import RoundPeaksDetector
from phenotypic.correction import GridAligner

# Load and detect colonies
image = Image.from_image_path('plate.jpg')
image = RoundPeaksDetector().operate(image)

# Create GridImage with grid structure
grid_image = GridImage(image)
grid_image.detect_grid()

# Align entire image to grid rows/columns
aligner = GridAligner(axis=0)  # Align rows horizontally
aligned = aligner.apply(grid_image)

# All components (rgb, gray, masks, map) rotated together
# Grid structure updated to reflect rotation
print(f"Rotation angle: {aligned.grid.rotation_angle}")
Custom perspective correction (conceptual)
from phenotypic.abc_ import GridCorrector
from phenotypic import GridImage

class GridPerspectiveCorrector(GridCorrector):
    """Correct camera tilt or lens distortion on grid plate."""

    def __init__(self, tilt_angle: float):
        super().__init__()
        self.tilt_angle = tilt_angle

    @staticmethod
    def _operate(image: GridImage, tilt_angle: float) -> GridImage:
        # Apply perspective transform to all components
        # (Implementation depends on specific correction needed)
        # image.apply_perspective(...) or similar
        return image

# Usage: correct skewed plate image
corrector = GridPerspectiveCorrector(tilt_angle=10.0)
corrected = corrector.apply(grid_image)

Methods

__init__

apply

Calculates the optimal rotation angle and applies it to a grid image for alignment along the specified axis.

dispose_widgets

Drop references to the UI widgets.

sync_widgets_from_state

Push internal state into widgets.

widget

Return (and optionally display) the root widget.

apply(image: GridImage, inplace=False) GridImage[source]#

Calculates the optimal rotation angle and applies it to a grid image for alignment along the specified axis.

The method performs alignment of a GridImage object along either nrows or columns based on the specified axis. It calculates the linear regression slope and intercept for the axis, determines geometric properties of the grid vertices, and computes rotation angles needed to align the image. The optimal angle is found by minimizing the error across all computed angles, and the image is rotated accordingly.

Raises:

ValueError – If the axis is not 0 (row-wise) or 1 (column-wise).

Parameters:

image (ImageGridHandler) – The arr grid image object to be aligned.

Returns:

The rotated grid image object after alignment.

Return type:

ImageGridHandler

__del__()#

Automatically stop tracemalloc when the object is deleted.

__getstate__()#

Prepare the object for pickling by disposing of any widgets.

This ensures that UI components (which may contain unpickleable objects like input functions or thread locks) are cleaned up before serialization.

Note

This method modifies the object state by calling dispose_widgets(). Any active widgets will be detached from the object.

dispose_widgets() None#

Drop references to the UI widgets.

Return type:

None

sync_widgets_from_state() None#

Push internal state into widgets.

Return type:

None

widget(image: Image | None = None, show: bool = False) Widget#

Return (and optionally display) the root widget.

Parameters:
  • image (Image | None) – Optional image to visualize. If provided, visualization controls will be added to the widget.

  • show (bool) – Whether to display the widget immediately. Defaults to False.

Returns:

The root widget.

Return type:

ipywidgets.Widget

Raises:

ImportError – If ipywidgets or IPython are not installed.