Source code for phenotypic.measure._measure_size
from __future__ import annotations
from typing import TYPE_CHECKING
from phenotypic.tools.constants_ import OBJECT
if TYPE_CHECKING:
from phenotypic import Image
import pandas as pd
import numpy as np
from phenotypic.abc_ import MeasurementInfo, MeasureFeatures
class SIZE(MeasurementInfo):
"""The labels and descriptions of the size measurements."""
@classmethod
def category(cls):
return "Size"
AREA = (
"Area",
"Total number of pixels occupied by the microbial colony."
"Larger areas typically indicate more robust growth or longer incubation times.",
)
INTEGRATED_INTENSITY = (
"IntegratedIntensity",
r"The sum of the object\'s grayscale pixels. Calculated as"
r"$\sum{pixel values}*area$",
)
[docs]
class MeasureSize(MeasureFeatures):
"""Measure basic size metrics of detected microbial colonies.
This class extracts two fundamental size metrics: Area (colony pixel count) and Integrated Intensity
(total grayscale brightness). These measurements are extracted from shape and intensity analyses but
are provided as a lightweight convenience class for quick size assessment without full morphological
or intensity statistical analysis.
**Intuition:** Colony size and brightness are primary phenotypic traits in high-throughput screening.
Area directly reflects biomass and growth extent on the agar surface. Integrated Intensity approximates
optical density without requiring cell suspension, making it useful for time-course growth tracking.
Together, these metrics enable rapid size-based sorting and quality filtering.
**Use cases (agar plates):**
- Quickly assess colony size distribution on a plate for quality control.
- Track colony growth kinetics over time via area measurements at multiple time points.
- Estimate total biomass via integrated intensity (sum of all pixel values).
- Filter colonies by minimum size threshold to exclude aborted or contaminating growth.
- Rank colonies by size for automated picking or downstream processing.
**Caveats:**
- Area is measured in pixels; it must be scaled by pixel-to-micron conversion factors if physical
dimensions are needed.
- Integrated intensity is sensitive to imaging conditions (lighting, exposure, camera gain); absolute
values are not comparable across different imaging setups without normalization.
- Both metrics depend on accurate segmentation; over- or under-segmentation distorts size estimates.
- For size-normalized intensity analysis (e.g., intensity per unit area), use MeasureIntensity.mean
instead of integrated intensity, or divide integrated intensity by area.
- Area does not distinguish between a single solid colony and multiple touching fragments; use
MeasureGridSpread or object count metrics for multi-object detection in grid sections.
Returns:
pd.DataFrame: Object-level size measurements with columns:
- Label: Unique object identifier.
- Area: Number of pixels occupied by the colony.
- IntegratedIntensity: Sum of grayscale pixel values in the colony (proxy for biomass/OD).
Examples:
.. dropdown:: Quick size assessment and filtering
.. code-block:: python
from phenotypic import Image
from phenotypic.detect import OtsuDetector
from phenotypic.measure import MeasureSize
# Load and detect colonies
image = Image.from_image_path("colony_plate.jpg")
detector = OtsuDetector()
image = detector.operate(image)
# Measure size
sizer = MeasureSize()
sizes = sizer.operate(image)
# Filter colonies by size (exclude very small or very large)
min_area, max_area = 50, 5000 # pixels
good_colonies = sizes[
(sizes['Size_Area'] >= min_area) &
(sizes['Size_Area'] <= max_area)
]
print(f"Colonies within size range: {len(good_colonies)}/{len(sizes)}")
.. dropdown:: Track colony growth over time
.. code-block:: python
# Load images from multiple time points
import pandas as pd
from phenotypic import Image
from phenotypic.detect import OtsuDetector
from phenotypic.measure import MeasureSize
detector = OtsuDetector()
sizer = MeasureSize()
# Simulate time-series measurements
growth_data = []
for timepoint_h in [0, 6, 12, 24, 48]:
img_path = f"plate_t{timepoint_h}h.jpg"
image = Image.from_image_path(img_path)
image = detector.operate(image)
sizes = sizer.operate(image)
sizes['TimePoint_h'] = timepoint_h
growth_data.append(sizes)
# Combine and analyze
growth_df = pd.concat(growth_data, ignore_index=True)
# Track individual colonies and compute growth rate (simplified)
avg_area = growth_df.groupby('TimePoint_h')['Size_Area'].mean()
print("Average colony area over time:")
print(avg_area)
"""
def _operate(self, image: Image) -> pd.DataFrame:
# Create empty numpy arrays to store measurements
measurements = {
str(feature): np.zeros(shape=image.num_objects)
for feature in SIZE
if feature != SIZE.CATEGORY
}
# Calculate integrated intensity using the sum calculation method from base class
intensity_matrix = image.gray[:].copy()
objmap = image.objmap[:].copy()
measurements[SIZE.AREA] = self._calculate_sum(
array=image.objmask[:], objmap=objmap
)
measurements[SIZE.INTEGRATED_INTENSITY] = self._calculate_sum(
array=intensity_matrix, objmap=objmap
)
measurements = pd.DataFrame(measurements)
measurements.insert(
loc=0, column=OBJECT.LABEL, value=image.objects.labels2series()
)
return measurements
MeasureSize.__doc__ = SIZE.append_rst_to_doc(MeasureSize)