Source code for phenotypic.refine._center_deviation_reducer

from __future__ import annotations

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from phenotypic import Image

from scipy.spatial.distance import euclidean

from phenotypic.abc_ import ObjectRefiner
from phenotypic.tools.constants_ import OBJECT, BBOX


[docs] class CenterDeviationReducer(ObjectRefiner): """Keep the object closest to center and remove off-center detections. Intuition: For isolated-colony images (e.g., single-spot captures or per-grid-cell crops), the true colony is typically near the center. Spurious blobs from glare, dust, or agar texture may appear off-center. This operation keeps only the object whose centroid is closest to the image center, removing all others. Why this is useful for agar plates: When imaging a grid of pinned colonies, per-cell crops may contain extra detections (ringing, condensation, halo). Selecting the most centered object stabilizes downstream phenotyping by focusing on the intended colony in each crop. Use cases: - Single-colony crops from a grid plate where occasional debris is picked up near edges. - Automated pipelines that assume one colony per field-of-view. Caveats: - If the true colony is notably off-center (misalignment, drift), this method can remove it and keep a distractor. - Not suitable for multi-colony fields; it will drop all but one object. Attributes: (No public attributes) Examples: >>> from phenotypic.refine import CenterDeviationReducer >>> op = CenterDeviationReducer() >>> image = op.apply(image, inplace=True) # doctest: +SKIP """ 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