Source code for phenotypic.refine._center_deviation_reducer
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from phenotypic._core._image import Image
from scipy.spatial.distance import euclidean
from phenotypic.abc_ import ObjectRefiner
from phenotypic.tools_.measurement_info_ import BBOX
[docs]
class CenterDeviationReducer(ObjectRefiner):
"""Retain only the object whose centroid is closest to the image center.
Computes the Euclidean distance from each object's centroid to the image
center and keeps the single nearest object, removing all others. Useful
for per-cell crops where the intended colony sits near the center and
peripheral detections are artifacts.
Returns:
Image: Input image with ``objmap`` reduced to the single most centered
object.
Best For:
- Single-colony crops from grid plates where debris appears near edges.
- Automated pipelines that assume one colony per field-of-view.
- Post-crop cleanup in conjunction with grid-based workflows.
Consider Also:
- :class:`GridSectionLargest` when the largest object per grid cell is
more reliable than the most centered.
- :class:`SmallObjectRemover` when peripheral artifacts are simply
smaller than the true colony.
- :class:`ReduceMultipleGridObjects` for grid-aware multi-detection
reduction using positional regression.
See Also:
:doc:`/how_to/notebooks/refine_noisy_boundaries` for boundary cleanup
workflows.
:doc:`/explanation/refinement_strategies` for an overview of
refinement approaches.
"""
def _operate(self, image: Image):
img_center_cc = image.shape[1] // 2
img_center_rr = image.shape[0] // 2
bound_info = image.objects.info()
# Add a column to the bound info for center deviation
bound_info.loc[:, "Measurement_CenterDeviation"] = bound_info.apply(
lambda row: euclidean(
u=[row[str(BBOX.CENTER_CC)], row[str(BBOX.CENTER_RR)]],
v=[img_center_cc, img_center_rr],
),
axis=1,
)
# Get the label of the obj w/ the least deviation
obj_to_keep = bound_info.loc[:, "Measurement_CenterDeviation"].idxmin()
# Get a working copy of the object map
objmap = image.objmap[:]
# Set Image object map to new other_image
image.objmap[objmap != obj_to_keep] = 0
return image