Source code for phenotypic.refine._keep_section_largest
from __future__ import annotations
from typing import TYPE_CHECKING
import numpy as np
if TYPE_CHECKING:
from phenotypic._core._grid_image import GridImage
from phenotypic.abc_ import GridObjectRefiner
from phenotypic.measure import MeasureSize
from phenotypic.schema import SIZE, GRID
from phenotypic.schema import OBJECT
[docs]
class KeepSectionLargest(GridObjectRefiner):
"""Retain only the largest object by area within each grid section.
Measures the pixel area of every detected object via :class:`MeasureSize`,
groups objects by grid cell, and discards all but the largest per cell.
Fragments, debris, and secondary detections within each grid position are
removed, yielding at most one colony label per well. Requires a
``GridImage`` input so that grid section membership is known.
For an overview of grid refinement approaches, see
:doc:`/explanation/refinement_strategies`.
Best For:
- Pinned colony grids (96-well, 384-well) where the genuine colony is
consistently the largest detection in each cell and smaller objects
are condensation, debris, or satellite colonies.
- Quick post-detection cleanup that does not require grid inference
because the grid layout is already embedded in the ``GridImage``.
- Pipelines that produce multi-detection grid cells and need a simple,
parameter-free reduction to one object per position.
Consider Also:
- :class:`GridAlignmentRefiner` for grid-aware selection with
configurable strategies (dominant, centered, regularized) and
optional grid inference for plain ``Image`` inputs.
- :class:`KeepNearestCenter` when the most spatially centered object
is a more reliable proxy for the true colony than pixel area.
- :class:`ReduceSectionsByLine` for regression-based selection using
expected grid positions when area is not a reliable discriminant.
Returns:
Image: Input image with ``objmap`` reduced to the single largest
object per grid section; all other objects are set to background.
See Also:
:doc:`/how_to/notebooks/refine_noisy_boundaries` for grid-based
refinement workflows on real plate images.
"""
def _operate(self, image: GridImage) -> GridImage:
size_table = MeasureSize().measure(image, include_meta=True)
max_idx = size_table.groupby(
by=GRID.ROW_MAJOR_IDX,
observed=True
)[SIZE.AREA].idxmax()
max_size_labels = size_table.loc[max_idx, OBJECT.LABEL].to_numpy()
# Drop objects not the largest
nonmax_mask = ~np.isin(image.objmap[:], max_size_labels)
image.objmap[nonmax_mask] = 0
return image