Source code for phenotypic.abc_._grid_measure
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from phenotypic import GridImage
import pandas as pd
from phenotypic.abc_ import MeasureFeatures
from phenotypic.tools.exceptions_ import GridImageInputError, OutputValueError
from phenotypic.tools.funcs_ import validate_measure_integrity
from abc import ABC
[docs]
class GridMeasureFeatures(MeasureFeatures, ABC):
"""Extract feature measurements from detected colonies in GridImage objects.
GridMeasureFeatures is a type-safe wrapper around MeasureFeatures that enforces
GridImage input type. It is to MeasureFeatures what GridOperation is to ImageOperation:
a specialization for grid-aware (arrayed plate) analysis.
**Purpose**
Use GridMeasureFeatures when implementing measurement operations that extract
quantitative metrics from colonies in grid-structured agar plate images. Like
MeasureFeatures, it returns pandas DataFrames with one row per detected colony.
The only difference is that it requires GridImage input, making explicit that your
measurement may leverage grid structure (well positions, row/column layout) if desired.
**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). Suitable for 96-well, 384-well, or other
arrayed plate formats.
**When to use GridMeasureFeatures vs MeasureFeatures**
- **MeasureFeatures:** Measurement works equally well on any Image (with or without
grid). Examples: colony size, color composition, morphology metrics that are
computed globally. Use when grid structure is irrelevant.
- **GridMeasureFeatures:** Measurement leverages grid structure or assumes well-level
organization. Examples: per-well growth metrics, grid-aligned morphology, measurements
that depend on row/column position. Use when grid structure is essential or enhances
the measurement.
**Implementation Pattern**
Inherit from GridMeasureFeatures and implement ``_operate()`` as normal:
.. code-block:: python
from phenotypic.abc_ import GridMeasureFeatures
import pandas as pd
class GridMeasureWellOccupancy(GridMeasureFeatures):
'''Measure fraction of well area occupied by colonies.'''
def _operate(self, image: GridImage) -> pd.DataFrame:
# image is guaranteed to be GridImage with grid structure
# Implement your grid-aware measurement here
results = pd.DataFrame(...)
return results
**Typical Use Cases**
- Per-well phenotypic analysis where well position matters
- Grid-based filtering (e.g., "measure only colonies in the center wells")
- Well-normalized metrics (e.g., colony area relative to well size)
- Multi-well experiments where you need to track which well each measurement came from
**Notes**
- The ``measure()`` method is inherited from MeasureFeatures; the only difference
is input type validation.
- Returns pandas.DataFrame with one row per detected object, first column is
OBJECT.LABEL (matching image.objmap labels).
- GridImage must have valid grid structure set before measuring. Typically set
by GridFinder or GridCorrector operations in the pipeline.
- All helper methods from MeasureFeatures (mean, median, sum, etc.) are available.
Examples:
.. dropdown:: Grid-aware measurement of colony size per well
.. code-block:: python
from phenotypic import GridImage
from phenotypic.abc_ import GridMeasureFeatures
from phenotypic.tools.constants_ import OBJECT
import pandas as pd
class MeasureWellOccupancy(GridMeasureFeatures):
\"\"\"Measure total area occupied in each well.\"\"\"
def _operate(self, image: GridImage) -> pd.DataFrame:
# Use grid accessor to calculate per-well metrics
area = self._calculate_sum(image.objmask[:], image.objmap[:])
well_info = image.grid.info() # Get well assignments
# Combine area with well location
results = pd.DataFrame({
'WellArea': area,
})
results.insert(0, OBJECT.LABEL, image.objects.labels2series())
return results
# Usage
from phenotypic import Image
from phenotypic.detect import OtsuDetector
image = Image.from_image_path('plate.jpg')
image = OtsuDetector().operate(image)
grid_image = GridImage(image)
grid_image.detect_grid() # Establish grid structure
measurer = MeasureWellOccupancy()
df = measurer.measure(grid_image) # Returns grid-aware measurements
"""
[docs]
@validate_measure_integrity()
def measure(self, image: GridImage) -> pd.DataFrame:
from phenotypic import GridImage
if not isinstance(image, GridImage):
raise GridImageInputError()
output = super().measure(image)
if not isinstance(output, pd.DataFrame):
raise OutputValueError("pandas.DataFrame")
return output