phenotypic.measure#

Feature extraction from detected fungal colonies.

Computes per-colony and grid-level metrics describing growth, morphology, and appearance on agar plates. Measurements span bounds, size/integrated intensity, shape descriptors, texture, color, and grid-level statistics such as spatial spread and linear gradients. Results are returned as pandas DataFrames ready for analysis.

class phenotypic.measure.MeasureBounds[source]#

Bases: MeasureFeatures

Extract spatial boundaries and centroids of detected microbial colonies.

This class computes the bounding box coordinates and centroid position of each detected colony in the image. These measurements form the foundation for shape analysis, grid alignment assessment, and spatial statistics in colony phenotyping workflows.

Intuition: Every detected colony occupies a region in the image; understanding its bounds and center is essential for downstream analyses. The bounding box defines the minimal rectangular region containing the colony, while centroids enable distance-based metrics (e.g., grid alignment, spread measurements) and are used to relate colonies to expected well positions in arrayed assays.

Use cases (agar plates): - Establish the spatial footprint of each detected colony for morphological analysis. - Compute centroids for aligning colonies to grid positions in high-throughput assays. - Enable region-of-interest (ROI) extraction for downstream intensity, color, or texture measurements. - Assess colony positioning relative to plate edges to detect spreading beyond well boundaries. - Support grid refinement operations by providing predicted centroid positions for outlier filtering.

Caveats: - Bounding box depends entirely on accurate object segmentation/masking; poor or over-segmented masks

yield misleading bounds.

  • Bounding box is axis-aligned; it may include empty space for rotated or crescent-shaped colonies.

  • Centroid assumes the colony is a connected component; fragmented or multi-component objects will have unreliable centroid positions.

  • Boundaries are computed in image pixel coordinates; they must be scaled or transformed if results are used with data in different coordinate systems (e.g., physical microns).

Returns:
pd.DataFrame: Object-level spatial data with columns:
  • Label: Unique object identifier.

  • CenterRR, CenterCC: Centroid row and column coordinates.

  • MinRR, MinCC: Minimum (top-left) row and column of bounding box.

  • MaxRR, MaxCC: Maximum (bottom-right) row and column of bounding box.

Examples:
Extract colony boundaries for a plate image
from phenotypic import Image
from phenotypic.detect import OtsuDetector
from phenotypic.measure import MeasureBounds

# Load image and detect colonies
image = Image.from_image_path("colony_plate.jpg")
detector = OtsuDetector()
image = detector.operate(image)

# Extract boundaries
boundsizer = MeasureBounds()
bounds = boundsizer.operate(image)
print(bounds.head())
# Output: Label, CenterRR, CenterCC, MinRR, MinCC, MaxRR, MaxCC
Use boundaries to extract colony ROIs
# Extract a region for each colony for detailed analysis
bounds = boundsizer.operate(image)
for idx, row in bounds.iterrows():
    min_rr, max_rr = int(row['BBOX_MinRR']), int(row['BBOX_MaxRR'])
    min_cc, max_cc = int(row['BBOX_MinCC']), int(row['BBOX_MaxCC'])
    colony_roi = image.rgb[min_rr:max_rr, min_cc:max_cc]
    # Process ROI independently (e.g., color analysis, morphology)
Category: BBOX#

Name

Description

CenterRR

The row coordinate of the center of the bounding box.

MinRR

The smallest row coordinate of the bounding box.

MaxRR

The largest row coordinate of the bounding box.

CenterCC

The column coordinate of the center of the bounding box.

MinCC

The smallest column coordinate of the bounding box.

MaxCC

The largest column coordinate of the bounding box.

__del__()#

Automatically stop tracemalloc when the object is deleted.

measure(image, include_meta=False)#

Execute the measurement operation on a detected-object image.

This is the main public API method for extracting measurements. It handles: input validation, parameter extraction via introspection, calling the subclass-specific _operate() method, optional metadata merging, and exception handling.

How it works (for users):

  1. Pass your processed Image (with detected objects) to measure()

  2. The method calls your subclass’s _operate() implementation

  3. Results are validated and returned as a pandas DataFrame

  4. If include_meta=True, image metadata (filename, grid info) is merged in

How it works (for developers):

When you subclass MeasureFeatures, you only implement _operate(). This measure() method automatically:

  • Extracts __init__ parameters from your instance (introspection)

  • Passes matched parameters to _operate() as keyword arguments

  • Validates the Image has detected objects (objmap)

  • Wraps exceptions in OperationFailedError with context

  • Merges grid/object metadata if requested

Parameters:
  • image (Image) – A PhenoTypic Image object with detected objects (must have non-empty objmap from a prior detection operation).

  • include_meta (bool, optional) – If True, merge image metadata columns (filename, grid position, etc.) into the results DataFrame. Defaults to False.

Returns:

Measurement results with structure:

  • First column: OBJECT.LABEL (integer IDs from image.objmap[:])

  • Remaining columns: Measurement values (float, int, or string)

  • One row per detected object

If include_meta=True, additional metadata columns are prepended before OBJECT.LABEL (e.g., Filename, GridRow, GridCol).

Return type:

pd.DataFrame

Raises:

OperationFailedError – If _operate() raises any exception, it is caught and re-raised as OperationFailedError with details including the original exception type, message, image name, and operation class. This provides consistent error handling across all measurers.

Notes

  • This method is the main entry point; do not override in subclasses

  • Subclasses implement _operate() only, not this method

  • Automatic memory profiling is available via logging configuration

  • Image must have detected objects (image.objmap should be non-empty)

Examples

Basic measurement extraction
from phenotypic import Image
from phenotypic.measure import MeasureSize
from phenotypic.detect import OtsuDetector

# Load and detect
image = Image.from_image_path('plate.jpg')
image = OtsuDetector().operate(image)

# Extract measurements
measurer = MeasureSize()
df = measurer.measure(image)
print(df.head())
Include metadata in measurements
# With image metadata (filename, grid info)
df_with_meta = measurer.measure(image, include_meta=True)
print(df_with_meta.columns)
# Output: ['Filename', 'GridRow', 'GridCol', 'OBJECT.LABEL',
#          'Area', 'IntegratedIntensity', ...]
class phenotypic.measure.MeasureColor(white_chroma_max: float = 4.0, chroma_min: float = 8.0, include_XYZ: bool = False)[source]#

Bases: MeasureFeatures

Measure color characteristics of colonies across multiple perceptual color spaces.

This class extracts quantitative color statistics from segmented colonies using CIE XYZ, chromaticity (xy), CIE Lab (perceptually uniform), and HSV (hue-saturation-value) color spaces. For each color space, it computes intensity-independent statistical features (min, Q1, mean, median, Q3, max, standard deviation, coefficient of variation) per colony, plus chroma estimates in Lab space.

Intuition: Colony color provides phenotypic information about pigmentation, sporulation, metabolic products, and stress responses. Measuring color in multiple spaces captures different aspects: XYZ and xy are standardized for illuminant-independent comparisons, Lab is perceptually uniform (equal Euclidean distances reflect equal perceived color differences), and HSV separates hue (pigment type) from saturation and brightness. Colony-level color variation (e.g., std dev) indicates uneven growth, zonation, or heterogeneous populations.

Use cases (agar plates): - Distinguish pigmented colonies (e.g., red/yellow carotenoid-producing bacteria, dark melanin)

from colorless ones; stratify phenotypes by pigmentation profile.

  • Detect sectoring and growth heterogeneity via high color variance within single colonies.

  • Use chromaticity (xy) or hue to identify mixed cultures or secondary growth on a plate.

  • Enable image-based selection of colonies with specific pigmentation traits (e.g., high-chroma red vs pale).

  • Assess whether color measurements cluster by genotype or growth condition for cross-plate comparisons.

Caveats: - Color measurements are highly sensitive to illumination, camera white balance, and exposure settings;

normalize and calibrate your imaging setup before comparing colors across plates or experiments.

  • Lab and HSV assume RGB input is correctly gamma-corrected and linearized; use image.gray or image.enh_gray if raw RGB is uncalibrated.

  • High saturation and brightness variance within a colony can indicate shadow regions, uneven lighting, or non-uniform mycelial depth; interpret texture variance alongside color variance.

  • Chroma estimates use simplified arithmetic; for critical applications, use reference color charts or spectrophotometry to validate color classifications.

  • XYZ inclusion is optional and slow; enable only if standardized color space analysis is essential.

Args:
white_chroma_max (float, optional): Chroma threshold below which a colony is classified as

“white” (achromatic). Used to filter Lab chroma calculations. Defaults to 4.0.

chroma_min (float, optional): Minimum chroma value to retain in analysis; colonies below this

are sometimes treated as colorless. Defaults to 8.0.

include_XYZ (bool, optional): Whether to compute CIE XYZ measurements (slower, less common).

Defaults to False.

Returns:
pd.DataFrame: Object-level color statistics with columns organized by color space:
  • ColorXYZ: X, Y, Z tristimulus values (if include_XYZ=True).

  • Colorxy: Chromaticity coordinates x, y (perceptual color without brightness).

  • ColorLab: L* (lightness), a* (green-red), b* (blue-yellow), and chroma estimates.

  • ColorHSV: Hue (angle, color identity), Saturation (intensity of color), Brightness (luminosity).

For each channel: Min, Q1, Mean, Median, Q3, Max, StdDev, CoeffVar.

Examples:
Measure colony color to detect pigmented mutants
from phenotypic import Image
from phenotypic.detect import OtsuDetector
from phenotypic.measure import MeasureColor

# Load image of colonies (may include pigmented and non-pigmented strains)
image = Image.from_image_path("mixed_pigment_plate.jpg")
detector = OtsuDetector()
image = detector.operate(image)

# Measure color
measurer = MeasureColor(include_XYZ=False)
colors = measurer.operate(image)

# Identify pigmented colonies by hue and saturation
pigmented = colors[colors['ColorHSV_SaturationMean'] > 15]
print(f"Found {len(pigmented)} pigmented colonies")
Use Lab color space for perceptually uniform analysis
# Measure using Lab space (perceptually uniform)
measurer = MeasureColor()
colors = measurer.operate(image)

# Chroma estimates reflect perceived "colorfulness"
bright_red = colors[
    (colors['ColorLab_L*Mean'] > 50) &
    (colors['ColorLab_ChromaEstimatedMean'] > 20)
]
print(f"Bright red colonies: {len(bright_red)}")
Category: ColorXYZ#

Name

Description

CieXMin

The minimum X value of the object in CIE XYZ color space

CieXQ1

The lower quartile (Q1) X value of the object in CIE XYZ color space

CieXMean

The mean X value of the object in CIE XYZ color space

CieXMedian

The median X value of the object in CIE XYZ color space

CieXQ3

The upper quartile (Q3) X value of the object in CIE XYZ color space

CieXMax

The maximum X value of the object in CIE XYZ color space

CieXStdDev

The standard deviation of the X value of the object in CIE XYZ color space

CieXCoeffVar

The coefficient of variation of the X value of the object in CIE XYZ color space

CieYMin

The minimum Y value of the object in CIE XYZ color space

CieYQ1

The lower quartile (Q1) Y value of the object in CIE XYZ color space

CieYMean

The mean Y value of the object in CIE XYZ color space

CieYMedian

The median Y value of the object in CIE XYZ color space

CieYQ3

The upper quartile (Q3) Y value of the object in CIE XYZ color space

CieYMax

The maximum Y value of the object in CIE XYZ color space

CieYStdDev

The standard deviation of the Y value of the object in CIE XYZ color space

CieYCoeffVar

The coefficient of variation of the Y value of the object in CIE XYZ color space

CieZMin

The minimum Z value of the object in CIE XYZ color space

CieZQ1

The lower quartile (Q1) Z value of the object in CIE XYZ color space

CieZMean

The mean Z value of the object in CIE XYZ color space

CieZMedian

The median Z value of the object in CIE XYZ color space

CieZQ3

The upper quartile (Q3) Z value of the object in CIE XYZ color space

CieZMax

The maximum Z value of the object in CIE XYZ color space

CieZStdDev

The standard deviation of the Z value of the object in CIE XYZ color space

CieZCoeffVar

The coefficient of variation of the Z value of the object in CIE XYZ color space

Category: Colorxy#

Name

Description

xMin

The minimum chromaticity x coordinate of the object

xQ1

The lower quartile (Q1) chromaticity x coordinate of the object

xMean

The mean chromaticity x coordinate of the object

xMedian

The median chromaticity x coordinate of the object

xQ3

The upper quartile (Q3) chromaticity x coordinate of the object

xMax

The maximum chromaticity x coordinate of the object

xStdDev

The standard deviation of the chromaticity x coordinate of the object

xCoeffVar

The coefficient of variation of the chromaticity x coordinate of the object

yMin

The minimum chromaticity y coordinate of the object

yQ1

The lower quartile (Q1) chromaticity y coordinate of the object

yMean

The mean chromaticity y coordinate of the object

yMedian

The median chromaticity y coordinate of the object

yQ3

The upper quartile (Q3) chromaticity y coordinate of the object

yMax

The maximum chromaticity y coordinate of the object

yStdDev

The standard deviation of the chromaticity y coordinate of the object

yCoeffVar

The coefficient of variation of the chromaticity y coordinate of the object

Category: ColorLab#

Name

Description

L*Min

The minimum L* value of the object

L*Q1

The lower quartile (Q1) L* value of the object

L*Mean

The mean L* value of the object

L*Median

The median L* value of the object

L*Q3

The upper quartile (Q3) L* value of the object

L*Max

The maximum L* value of the object

L*StdDev

The standard deviation of the L* value of the object

L*CoeffVar

The coefficient of variation of the L* value of the object

a*Min

The minimum a* value of the object

a*Q1

The lower quartile (Q1) a* value of the object

a*Mean

The mean a* value of the object

a*Median

The median a* value of the object

a*Q3

The upper quartile (Q3) a* value of the object

a*Max

The maximum a* value of the object

a*StdDev

The standard deviation of the a* value of the object

a*CoeffVar

The coefficient of variation of the a* value of the object

b*Min

The minimum b* value of the object

b*Q1

The lower quartile (Q1) b* value of the object

b*Mean

The mean b* value of the object

b*Median

The median b* value of the object

b*Q3

The upper quartile (Q3) b* value of the object

b*Max

The maximum b* value of the object

b*StdDev

The standard deviation of the b* value of the object

b*CoeffVar

The coefficient of variation of the b* value of the object

ChromaEstimatedMean

The mean chroma estimation of the object calculated using \(\(sqrt{a^{*}_{mean}^2 + b^{*}_{mean})^2}\)

ChromaEstimatedMedian

The median chroma estimation of the object using \(\sqrt({a*_{median}^2 + b*_{median})^2}\)

Category: ColorHSV#

Name

Description

HueMin

The minimum hue of the object

HueQ1

The lower quartile (Q1) hue of the object

HueMean

The mean hue of the object

HueMedian

The median hue of the object

HueQ3

The upper quartile (Q3) hue of the object

HueMax

The maximum hue of the object

HueStdDev

The standard deviation of the hue of the object

HueCoeffVar

The coefficient of variation of the hue of the object

SaturationMin

The minimum saturation of the object

SaturationQ1

The lower quartile (Q1) saturation of the object

SaturationMean

The mean saturation of the object

SaturationMedian

The median saturation of the object

SaturationQ3

The upper quartile (Q3) saturation of the object

SaturationMax

The maximum saturation of the object

SaturationStdDev

The standard deviation of the saturation of the object

SaturationCoeffVar

The coefficient of variation of the saturation of the object

BrightnessMin

The minimum brightness of the object

BrightnessQ1

The lower quartile (Q1) brightness of the object

BrightnessMean

The mean brightness of the object

BrightnessMedian

The median brightness of the object

BrightnessQ3

The upper quartile (Q3) brightness of the object

BrightnessMax

The maximum brightness of the object

BrightnessStdDev

The standard deviation of the brightness of the object

BrightnessCoeffVar

The coefficient of variation of the brightness of the object

Parameters:
__del__()#

Automatically stop tracemalloc when the object is deleted.

measure(image, include_meta=False)#

Execute the measurement operation on a detected-object image.

This is the main public API method for extracting measurements. It handles: input validation, parameter extraction via introspection, calling the subclass-specific _operate() method, optional metadata merging, and exception handling.

How it works (for users):

  1. Pass your processed Image (with detected objects) to measure()

  2. The method calls your subclass’s _operate() implementation

  3. Results are validated and returned as a pandas DataFrame

  4. If include_meta=True, image metadata (filename, grid info) is merged in

How it works (for developers):

When you subclass MeasureFeatures, you only implement _operate(). This measure() method automatically:

  • Extracts __init__ parameters from your instance (introspection)

  • Passes matched parameters to _operate() as keyword arguments

  • Validates the Image has detected objects (objmap)

  • Wraps exceptions in OperationFailedError with context

  • Merges grid/object metadata if requested

Parameters:
  • image (Image) – A PhenoTypic Image object with detected objects (must have non-empty objmap from a prior detection operation).

  • include_meta (bool, optional) – If True, merge image metadata columns (filename, grid position, etc.) into the results DataFrame. Defaults to False.

Returns:

Measurement results with structure:

  • First column: OBJECT.LABEL (integer IDs from image.objmap[:])

  • Remaining columns: Measurement values (float, int, or string)

  • One row per detected object

If include_meta=True, additional metadata columns are prepended before OBJECT.LABEL (e.g., Filename, GridRow, GridCol).

Return type:

pd.DataFrame

Raises:

OperationFailedError – If _operate() raises any exception, it is caught and re-raised as OperationFailedError with details including the original exception type, message, image name, and operation class. This provides consistent error handling across all measurers.

Notes

  • This method is the main entry point; do not override in subclasses

  • Subclasses implement _operate() only, not this method

  • Automatic memory profiling is available via logging configuration

  • Image must have detected objects (image.objmap should be non-empty)

Examples

Basic measurement extraction
from phenotypic import Image
from phenotypic.measure import MeasureSize
from phenotypic.detect import OtsuDetector

# Load and detect
image = Image.from_image_path('plate.jpg')
image = OtsuDetector().operate(image)

# Extract measurements
measurer = MeasureSize()
df = measurer.measure(image)
print(df.head())
Include metadata in measurements
# With image metadata (filename, grid info)
df_with_meta = measurer.measure(image, include_meta=True)
print(df_with_meta.columns)
# Output: ['Filename', 'GridRow', 'GridCol', 'OBJECT.LABEL',
#          'Area', 'IntegratedIntensity', ...]
class phenotypic.measure.MeasureGridLinRegStats(section_num: int | None = None)[source]#

Bases: GridMeasureFeatures

Evaluate grid alignment quality for arrayed microbial colonies using linear regression.

This class measures how well detected colonies align to expected grid positions in arrayed phenotyping assays. It computes row-wise and column-wise linear regression fits across the grid, then quantifies each colony’s deviation from the predicted position using residual error.

Intuition: In high-throughput microbial phenotyping, 96-well and 384-well plates have expected regular grid patterns. Deviations indicate off-grid growth, misdetections, plate warping, or imaging artifacts. High residual errors flag problematic detections that may need refinement or filtering before downstream analysis.

Use cases (agar plates): - Identify colonies that grew outside their designated grid position (e.g., spreading into

adjacent wells on a plate).

  • Detect systematic alignment issues (e.g., rotation, shear) across the plate to validate grid detection quality.

  • Filter or weight colonies by detection confidence based on deviation from expected position (used by refinement operations to select most plausible colony per grid cell).

  • Quantify plate warping or uneven agar surface by analyzing residual error patterns across rows/columns.

Caveats: - Regression assumes a linear relationship; severely warped plates may not fit the linear model well. - Residual errors are sensitive to grid detection accuracy; incorrect grid estimates propagate to

inflated residuals for correctly detected colonies.

  • Threshold selection for outlier filtering is application-dependent; conservatively use the 95th percentile residual error within a plate for robust quality control.

Args:
section_num (Optional[int], optional): Grid section number to restrict measurements to.

If None, measurements are computed across the entire grid. Defaults to None.

Attributes:

section_num (Optional[int]): Section number for targeted grid region analysis.

Returns:
pd.DataFrame: Measurement results indexed by object label. Includes per-object metrics:
  • RowM, RowB: Row regression slope and intercept (1 value per row).

  • ColM, ColB: Column regression slope and intercept (1 value per column).

  • PredRR, PredCC: Predicted row and column centroids from regression.

  • ResidualError: Euclidean distance between actual and predicted centroid.

Examples:
Measure grid alignment for an arrayed plate
from phenotypic import GridImage
from phenotypic.detect import OtsuDetector
from phenotypic.measure import MeasureGridLinRegStats

# Load a plate image with grid information
grid_image = GridImage.from_image_path("plate_96well.jpg", grid_shape=(8, 12))

# Detect colonies
detector = OtsuDetector()
grid_image = detector.operate(grid_image)

# Measure grid alignment quality
measurer = MeasureGridLinRegStats()
results = measurer.operate(grid_image)

# Identify off-grid colonies
outliers = results[results['GridLinReg_ResidualError'] > 10.0]
print(f"Found {len(outliers)} colonies with high misalignment")
Measure alignment within a single section/well
# Measure only colonies in section 5 (useful for troubleshooting
# a specific well or region)
measurer = MeasureGridLinRegStats(section_num=5)
section_results = measurer.operate(grid_image)
# Results contain grid stats and residual errors only for section 5
Category: GRID_LINREG_STATS#

Name

Description

RowM

Slope of row-wise linear regression fit across column positions. Measures systematic drift in row alignment. Values near 0 indicate horizontal rows; non-zero values suggest rotational misalignment or systematic row curvature across the plate.

RowB

Intercept of row-wise linear regression fit. Represents the expected row coordinate when column position is 0. Combined with slope, defines the expected row trend line for quality assessment and position prediction.

ColM

Slope of column-wise linear regression fit across row positions. Measures systematic drift in column alignment. Values near 0 indicate vertical columns; non-zero values suggest rotational misalignment or systematic column curvature across the plate.

ColB

Intercept of column-wise linear regression fit. Represents the expected column coordinate when row position is 0. Combined with slope, defines the expected column trend line for quality assessment and position prediction.

PredRR

Predicted row coordinate from column-wise linear regression. Uses the column position and column regression parameters (ColM, ColB) to estimate where the row coordinate should be if the grid were perfectly aligned. Used for calculating residual errors and detecting misaligned colonies.

PredCC

Predicted column coordinate from row-wise linear regression. Uses the row position and row regression parameters (RowM, RowB) to estimate where the column coordinate should be if the grid were perfectly aligned. Used for calculating residual errors and detecting misaligned colonies.

ResidualError

Euclidean distance between the actual colony centroid and the predicted position from linear regression. Quantifies how far each colony deviates from the expected grid pattern. High values indicate misdetections, off-grid growth, or local plate warping. Used by refinement operations to filter outliers and select the most plausible colony per grid cell.

Parameters:

section_num (Optional[int])

__del__()#

Automatically stop tracemalloc when the object is deleted.

measure(image)#

Processes an arr image to calculate and organize grid-based boundaries and centroids using coordinates. This function implements a two-pass approach to refine row and column boundaries with exact precision, ensuring accurate grid labeling and indexing. The function dynamically computes boundary intervals and optimally segments the arr space into grids based on specified nrows and columns.

Parameters:

image (Image) – The arr image to be analyzed and processed.

Returns:

A DataFrame containing the grid results including boundary intervals, grid indices, and section numbers corresponding to the segmented arr image.

Return type:

pd.DataFrame

class phenotypic.measure.MeasureGridSpread[source]#

Bases: GridMeasureFeatures

Quantify spatial distribution of colonies within grid sections of arrayed assays.

This class measures colony clustering and spread within each grid cell (well) of a high-throughput microbial phenotyping plate. It computes the sum of squared pairwise distances between all colony pairs in each section, revealing whether multiple colonies are dispersed within a well or tightly clustered near the center.

Intuition: In ideal arrayed assays, each well contains a single localized colony at the expected position. High ObjectSpread values indicate multiple colonies, fragmented growth, or spread beyond the well boundary. This metric helps identify sections with detection ambiguity or atypical growth patterns that may compromise phenotypic measurements.

Use cases (agar plates): - Detect over-segmentation: Multiple detected objects in a single well instead of one cohesive colony. - Identify spreading or invasive growth: Colonies extending far beyond their designated grid position

into adjacent areas.

  • Flag wells with questionable data quality for manual review or exclusion from downstream analysis.

  • Assess plate quality: Systematic high spread values across the plate suggest uneven agar surface, condensation issues, or poor inoculation technique.

  • Prioritize sections for refinement operations (e.g., object merging or filtering).

Caveats: - ObjectSpread depends on colony density and size; small plates with tight spacing have inherently

different baselines than larger assays.

  • Spread values are not normalized by well area; compare only within the same plate type and grid.

  • Touching or overlapping colonies may register as a single large object (low spread) or two smaller objects (high spread) depending on detection algorithm performance. Use in conjunction with object count and boundary metrics for robust quality assessment.

  • The metric is sensitive to very small or very large colonies; outliers in position or size can disproportionately inflate spread values.

Returns:
pd.DataFrame: Section-level statistics with columns:
  • Section numbers (index from grid).

  • Count: Number of colonies detected in each section.

  • ObjectSpread: Sum of squared pairwise Euclidean distances between colonies.

    Sorted in descending order by ObjectSpread.

Examples:
Measure colony spread across a plate
from phenotypic import GridImage
from phenotypic.detect import OtsuDetector
from phenotypic.measure import MeasureGridSpread

# Load a plate with grid
grid_image = GridImage.from_image_path("plate_384well.jpg", grid_shape=(16, 24))

# Detect colonies
detector = OtsuDetector()
grid_image = detector.operate(grid_image)

# Measure spread per well
spreader = MeasureGridSpread()
spread_results = spreader.operate(grid_image)

# Find wells with high spread (potential over-segmentation)
high_spread = spread_results.nlargest(10, 'GridSpread_ObjectSpread')
print(f"Top 10 problematic wells:")
print(high_spread)
Identify over-segmented wells
# Flag wells with both multiple detections AND high spread
multi_obj = spread_results[spread_results['count'] > 1]
high_spread_multi = multi_obj[
    multi_obj['GridSpread_ObjectSpread'] > spread_results['GridSpread_ObjectSpread'].quantile(0.75)
]
print(f"Wells needing refinement: {list(high_spread_multi.index)}")
Category: GRID_SPREAD#

Name

Description

ObjectSpread

Sum of squared pairwise Euclidean distances between all unique colony pairs within a grid section. Quantifies spatial dispersion of colonies in a grid cell. Higher values indicate greater spread from the section center, suggesting over-segmentation, multi-detections, or colonies growing beyond expected boundaries. Used to identify problematic grid sections requiring refinement or quality review.

__del__()#

Automatically stop tracemalloc when the object is deleted.

measure(image)#

Processes an arr image to calculate and organize grid-based boundaries and centroids using coordinates. This function implements a two-pass approach to refine row and column boundaries with exact precision, ensuring accurate grid labeling and indexing. The function dynamically computes boundary intervals and optimally segments the arr space into grids based on specified nrows and columns.

Parameters:

image (Image) – The arr image to be analyzed and processed.

Returns:

A DataFrame containing the grid results including boundary intervals, grid indices, and section numbers corresponding to the segmented arr image.

Return type:

pd.DataFrame

class phenotypic.measure.MeasureIntensity[source]#

Bases: MeasureFeatures

Measure grayscale intensity statistics of detected microbial colonies.

This class computes quantitative intensity metrics from the grayscale representation of each detected colony, including integrated intensity (total brightness), percentiles (min, Q1, median, Q3, max), and variability measures (standard deviation, coefficient of variation). These statistics reflect colony optical density, biomass, and internal heterogeneity on agar plates.

Intuition: Colony brightness in grayscale images correlates with biomass accumulation, cell density, and optical density. A low average intensity indicates sparse or translucent growth, while high intensity suggests dense mycelial mat or concentrated cells. Intensity variance within a colony reveals sectoring, differential growth rates, or uneven mycelial coverage. Integrated intensity (sum of all pixels) scales with both biomass and area, making it useful for growth kinetics tracking.

Use cases (agar plates): - Track colony growth over time via integrated intensity measurements (approximates optical density

without requiring liquid suspension).

  • Detect metabolically stressed or slow-growing colonies via low mean intensity.

  • Identify sectored, mutant, or chimeric colonies by high intensity variance within a single object.

  • Assess pigmentation or mycelial density differences between wild-type and mutant strains.

  • Enable automated colony picking strategies based on intensity thresholds (e.g., select only colonies above a biomass threshold).

Caveats: - Intensity depends critically on imaging conditions (lighting, exposure, camera gain); standardize

these settings across plates and experiments for reliable comparisons.

  • Grayscale conversion to luminance (Y channel) may not capture all visual information from colored agar or pigmented colonies; use enhanced grayscale (enh_gray) for better contrast, or measure color separately.

  • Integrated intensity mixes area and brightness; normalize by area or use intensity density (mean) for size-independent comparisons.

  • Shadows or uneven lighting on the plate cause local intensity artifacts; preprocessing with background subtraction or illumination correction may improve quality.

  • Outliers (very bright or very dark pixels from debris, bubble, or focus issues) can inflate standard deviation; use robust statistics (IQR) for noise-resistant variability assessment.

Returns:
pd.DataFrame: Object-level intensity statistics with columns:
  • Label: Unique object identifier.

  • IntegratedIntensity: Sum of all grayscale pixel values in the colony.

  • MinimumIntensity, MaximumIntensity: Range of intensity values.

  • MeanIntensity, MedianIntensity: Central tendency (mean more sensitive to outliers).

  • LowerQuartileIntensity, UpperQuartileIntensity: 25th and 75th percentiles (robust measures).

  • InterquartileRangeIntensity: Q3 - Q1 (robust measure of spread).

  • StandardDeviationIntensity: Sample standard deviation.

  • CoefficientVarianceIntensity: Normalized variability (std dev / mean, unitless).

Examples:
Measure colony biomass via intensity
from phenotypic import Image
from phenotypic.detect import OtsuDetector
from phenotypic.measure import MeasureIntensity

# Load and process plate image
image = Image.from_image_path("colony_plate_t24h.jpg")
detector = OtsuDetector()
image = detector.operate(image)

# Measure intensity to estimate biomass
measurer = MeasureIntensity()
intensity = measurer.operate(image)

# Track colonies by integrated intensity (proxy for biomass)
high_biomass = intensity[intensity['Intensity_IntegratedIntensity'] > 100000]
print(f"Colonies with high biomass (>100k): {len(high_biomass)}")
Identify heterogeneous or sectored colonies
# Measure intensity variance to detect sectoring
intensity = measurer.operate(image)

# Colonies with high variance may be sectored or chimeric
sectored = intensity[
    intensity['Intensity_CoefficientVarianceIntensity'] >
    intensity['Intensity_CoefficientVarianceIntensity'].quantile(0.75)
]
print(f"Potentially sectored colonies: {list(sectored.index)}")
Category: INTENSITY#

Name

Description

IntegratedIntensity

The sum of the object’s pixels

MinimumIntensity

The minimum intensity of the object

MaximumIntensity

The maximum intensity of the object

MeanIntensity

The mean intensity of the object

MedianIntensity

The median intensity of the object

StandardDeviationIntensity

The standard deviation of the object

CoefficientVarianceIntensity

The coefficient of variation of the object

LowerQuartileIntensity

The lower quartile intensity of the object

UpperQuartileIntensity

The upper quartile intensity of the object

InterquartileRangeIntensity

The interquartile range of the object

__del__()#

Automatically stop tracemalloc when the object is deleted.

measure(image, include_meta=False)#

Execute the measurement operation on a detected-object image.

This is the main public API method for extracting measurements. It handles: input validation, parameter extraction via introspection, calling the subclass-specific _operate() method, optional metadata merging, and exception handling.

How it works (for users):

  1. Pass your processed Image (with detected objects) to measure()

  2. The method calls your subclass’s _operate() implementation

  3. Results are validated and returned as a pandas DataFrame

  4. If include_meta=True, image metadata (filename, grid info) is merged in

How it works (for developers):

When you subclass MeasureFeatures, you only implement _operate(). This measure() method automatically:

  • Extracts __init__ parameters from your instance (introspection)

  • Passes matched parameters to _operate() as keyword arguments

  • Validates the Image has detected objects (objmap)

  • Wraps exceptions in OperationFailedError with context

  • Merges grid/object metadata if requested

Parameters:
  • image (Image) – A PhenoTypic Image object with detected objects (must have non-empty objmap from a prior detection operation).

  • include_meta (bool, optional) – If True, merge image metadata columns (filename, grid position, etc.) into the results DataFrame. Defaults to False.

Returns:

Measurement results with structure:

  • First column: OBJECT.LABEL (integer IDs from image.objmap[:])

  • Remaining columns: Measurement values (float, int, or string)

  • One row per detected object

If include_meta=True, additional metadata columns are prepended before OBJECT.LABEL (e.g., Filename, GridRow, GridCol).

Return type:

pd.DataFrame

Raises:

OperationFailedError – If _operate() raises any exception, it is caught and re-raised as OperationFailedError with details including the original exception type, message, image name, and operation class. This provides consistent error handling across all measurers.

Notes

  • This method is the main entry point; do not override in subclasses

  • Subclasses implement _operate() only, not this method

  • Automatic memory profiling is available via logging configuration

  • Image must have detected objects (image.objmap should be non-empty)

Examples

Basic measurement extraction
from phenotypic import Image
from phenotypic.measure import MeasureSize
from phenotypic.detect import OtsuDetector

# Load and detect
image = Image.from_image_path('plate.jpg')
image = OtsuDetector().operate(image)

# Extract measurements
measurer = MeasureSize()
df = measurer.measure(image)
print(df.head())
Include metadata in measurements
# With image metadata (filename, grid info)
df_with_meta = measurer.measure(image, include_meta=True)
print(df_with_meta.columns)
# Output: ['Filename', 'GridRow', 'GridCol', 'OBJECT.LABEL',
#          'Area', 'IntegratedIntensity', ...]
class phenotypic.measure.MeasureShape[source]#

Bases: MeasureFeatures

Measure morphological characteristics of detected microbial colonies.

This class extracts comprehensive geometric metrics from colony shapes, including area, perimeter, circularity, convex hull properties, radius-based measures, Feret diameters, elongation (eccentricity), and ellipse fitting parameters. These measurements quantify colony morphology, growth patterns, and spatial organization on agar plates.

Intuition: Colony shape encodes biological and environmental information. Regular circular colonies indicate healthy, isotropic growth under uniform conditions. Irregular, elongated, or filamentous morphologies suggest mutations, directional growth (chemotaxis), nutrient stress, or environmental gradients on the agar surface. Shape measures are used to classify colony types, assess fitness, and detect phenotypic variants in high-throughput screening.

Use cases (agar plates): - Distinguish colony morphotypes: smooth circular (wild-type) vs wrinkled, branching, or invasive

(mutant phenotypes).

  • Assess growth symmetry via eccentricity and orientation; colonies with high eccentricity may indicate motility, chemotaxis, or unidirectional stress.

  • Detect invasive or spreading growth via low solidity (indented periphery) or high convex area relative to actual area.

  • Enable morphological clustering and classification for automated strain identification or phenotypic screening.

  • Measure colony compactness to predict growth kinetics: compact colonies often have higher growth rates than sprawling ones under nutrient limitation.

Caveats: - Shape measurements depend entirely on segmentation quality; poor thresholding or edge detection

yield misleading morphology metrics.

  • Perimeter is sensitive to pixel-level noise; small variations in boundary can inflate perimeter and reduce circularity. Consider smoothing or filtering for robust estimates.

  • Feret diameters and convex hull computation are sensitive to boundary artifacts; outlier or misdetected pixels at the edge disproportionately affect these metrics.

  • Radius-based measures (mean, median, max radius) depend on centroid accuracy; off-center centroids from irregular shapes can yield biased radius values.

  • Eccentricity ranges 0–1 (circle to line); values near 0 and 1 are rare for biological objects. Interpret eccentricity alongside aspect ratio and orientation for robust shape classification.

Returns:
pd.DataFrame: Object-level morphological measurements with columns:
  • Label: Unique object identifier.

  • Area: Number of pixels in the colony.

  • Perimeter: Boundary length in pixels.

  • Circularity: 4π·Area/Perimeter² (1.0 = perfect circle; <1 = irregular).

  • Compactness: Perimeter²/(4π·Area) (inverse of circularity; >1 for irregular shapes).

  • ConvexArea: Area of convex hull (smallest convex polygon containing the colony).

  • Solidity: Area/ConvexArea (1.0 = convex; <1 = indented/spreading).

  • Extent: Area/BboxArea (1.0 = fills bounding box; <1 = spread out).

  • BboxArea: Area of axis-aligned bounding rectangle.

  • MeanRadius, MedianRadius, MaxRadius: Distance from centroid to edge (robust size measures).

  • MinFeretDiameter, MaxFeretDiameter: Minimum/maximum caliper diameters (orientation-independent).

  • MajorAxisLength, MinorAxisLength: Axes of best-fit ellipse.

  • Eccentricity: Ellipse elongation (0 = circle; 1 = line).

  • Orientation: Angle of major axis (radians, –π/2 to π/2).

Examples:
Measure colony morphology for phenotypic classification
from phenotypic import Image
from phenotypic.detect import OtsuDetector
from phenotypic.measure import MeasureShape

# Load plate with multiple morphotype colonies
image = Image.from_image_path("morphotype_plate.jpg")
detector = OtsuDetector()
image = detector.operate(image)

# Measure morphology
shaper = MeasureShape()
shapes = shaper.operate(image)

# Classify morphotypes by circularity and solidity
smooth_round = shapes[
    (shapes['Shape_Circularity'] > 0.8) &
    (shapes['Shape_Solidity'] > 0.95)
]
invasive = shapes[shapes['Shape_Solidity'] < 0.85]

print(f"Smooth/round colonies: {len(smooth_round)}")
print(f"Invasive/spreading colonies: {len(invasive)}")
Detect elongated or directional growth
# Use eccentricity and max radius to find elongated colonies
shapes = shaper.operate(image)

elongated = shapes[shapes['Shape_Eccentricity'] > 0.7]
print(f"Highly elongated colonies: {len(elongated)}")

# Visualize growth directionality
import numpy as np
for idx, row in elongated.iterrows():
    angle = np.degrees(row['Shape_Orientation'])
    aspect = row['Shape_MajorAxisLength'] / row['Shape_MinorAxisLength']
    print(f"Colony {row['OBJECT_Label']}: angle={angle:.1f}°, aspect={aspect:.2f}")
Category: SHAPE#

Name

Description

Area

Total number of pixels occupied by the microbial colony. Represents colony biomass and growth extent on agar plates. Larger areas typically indicate more robust growth or longer incubation times.

Perimeter

Total length of the colony’s outer boundary in pixels. Measures colony edge complexity and surface irregularity. Smooth, circular colonies have shorter perimeters relative to their area compared to irregular or filamentous colonies.

Circularity

Calculated as \(\frac{4\pi*\text{Area}}{\text{Perimeter}^2}\). Measures how closely a colony approximates a perfect circle (value = 1). Values < 1 indicate irregular colony morphology, which may result from genetic mutations, environmental stress, or mixed microbial populations on agar plates.

ConvexArea

Area of the smallest convex polygon that completely contains the colony. Represents the colony’s “filled-in” appearance if all indentations and holes were removed. Useful for detecting colony spreading patterns or invasive growth characteristics.

MedianRadius

Median distance from colony center to edge across all directions. Provides a robust measure of typical colony size that is less sensitive to outliers than mean radius. Particularly useful for colonies with uneven growth or sectoring.

MeanRadius

Average distance from colony center to edge across all directions. Represents overall colony expansion rate. In arrayed growth assays, this correlates with microbial fitness and growth kinetics under controlled conditions.

MaxRadius

Maximum distance from colony center to edge across all directions. Represents the furthest extent of colony growth from its center. In arrayed microbial assays, this measurement helps identify asymmetric growth patterns or colonies extending toward neighboring positions.

MinFeretDiameter

Minimum caliper diameter - the shortest distance between two parallel tangent lines touching opposite sides of the colony. Represents the narrowest dimension of the colony regardless of orientation. Useful for detecting elongated or irregular colony morphologies and measuring colony width.

MaxFeretDiameter

Maximum caliper diameter - the longest distance between two parallel tangent lines touching opposite sides of the colony. Represents the maximum dimension of the colony regardless of orientation. Often exceeds major axis length for irregular shapes and helps quantify maximum colony extent.

Eccentricity

Measure of colony elongation, ranging from 0 (perfect circle) to 1 (highly elongated). Values near 0 indicate compact, radially symmetric growth typical of healthy bacterial colonies, while higher values may suggest directional growth, motility, or environmental gradients on the agar surface.

Solidity

Ratio of actual colony area to its convex hull area (Area/ConvexArea). Values near 1 indicate compact, solid colonies with minimal indentations. Lower values (< 0.9) may indicate invasive growth, colony spreading, or the presence of clearing zones around colonies.

Extent

Ratio of colony area to its bounding box area (ObjectArea/BboxArea). Measures how efficiently the colony fills its allocated space. Compact colonies have higher extent values, while spread-out or irregular colonies have lower values.

BboxArea

Area of the smallest rectangle that completely contains the colony. Represents the total spatial footprint of the colony including any empty space. In high-throughput assays, this helps assess colony positioning and potential interference with neighboring colonies.

MajorAxisLength

Length of the longest axis of the ellipse that best fits the colony shape. Represents the maximum colony dimension. In arrayed microbial growth, this measurement helps identify colonies that have grown beyond their intended grid positions.

MinorAxisLength

Length of the shortest axis of the ellipse that best fits the colony shape. Represents the minimum colony dimension. Together with major axis length, this helps characterize colony aspect ratio and growth anisotropy.

Compactness

Calculated as \(\frac{\text{Perimeter}^2}{4\pi*\text{Area}}\). Inverse of circularity (ranges from 1 for perfect circles to higher values for irregular shapes). Measures colony shape complexity - compact, circular colonies have values near 1, while irregular or filamentous colonies have much higher values.

Orientation

Angle (in radians) between the colony’s major axis and the horizontal axis. Measures colony alignment and growth directionality. Random orientations are typical for most bacterial colonies, while consistent orientations may indicate environmental gradients or mechanical stresses during plating.

__del__()#

Automatically stop tracemalloc when the object is deleted.

measure(image, include_meta=False)#

Execute the measurement operation on a detected-object image.

This is the main public API method for extracting measurements. It handles: input validation, parameter extraction via introspection, calling the subclass-specific _operate() method, optional metadata merging, and exception handling.

How it works (for users):

  1. Pass your processed Image (with detected objects) to measure()

  2. The method calls your subclass’s _operate() implementation

  3. Results are validated and returned as a pandas DataFrame

  4. If include_meta=True, image metadata (filename, grid info) is merged in

How it works (for developers):

When you subclass MeasureFeatures, you only implement _operate(). This measure() method automatically:

  • Extracts __init__ parameters from your instance (introspection)

  • Passes matched parameters to _operate() as keyword arguments

  • Validates the Image has detected objects (objmap)

  • Wraps exceptions in OperationFailedError with context

  • Merges grid/object metadata if requested

Parameters:
  • image (Image) – A PhenoTypic Image object with detected objects (must have non-empty objmap from a prior detection operation).

  • include_meta (bool, optional) – If True, merge image metadata columns (filename, grid position, etc.) into the results DataFrame. Defaults to False.

Returns:

Measurement results with structure:

  • First column: OBJECT.LABEL (integer IDs from image.objmap[:])

  • Remaining columns: Measurement values (float, int, or string)

  • One row per detected object

If include_meta=True, additional metadata columns are prepended before OBJECT.LABEL (e.g., Filename, GridRow, GridCol).

Return type:

pd.DataFrame

Raises:

OperationFailedError – If _operate() raises any exception, it is caught and re-raised as OperationFailedError with details including the original exception type, message, image name, and operation class. This provides consistent error handling across all measurers.

Notes

  • This method is the main entry point; do not override in subclasses

  • Subclasses implement _operate() only, not this method

  • Automatic memory profiling is available via logging configuration

  • Image must have detected objects (image.objmap should be non-empty)

Examples

Basic measurement extraction
from phenotypic import Image
from phenotypic.measure import MeasureSize
from phenotypic.detect import OtsuDetector

# Load and detect
image = Image.from_image_path('plate.jpg')
image = OtsuDetector().operate(image)

# Extract measurements
measurer = MeasureSize()
df = measurer.measure(image)
print(df.head())
Include metadata in measurements
# With image metadata (filename, grid info)
df_with_meta = measurer.measure(image, include_meta=True)
print(df_with_meta.columns)
# Output: ['Filename', 'GridRow', 'GridCol', 'OBJECT.LABEL',
#          'Area', 'IntegratedIntensity', ...]
class phenotypic.measure.MeasureSize[source]#

Bases: 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:
Quick size assessment and filtering
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)}")
Track colony growth over time
# 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)
Category: SIZE#

Name

Description

Area

Total number of pixels occupied by the microbial colony.Larger areas typically indicate more robust growth or longer incubation times.

IntegratedIntensity

The sum of the object's grayscale pixels. Calculated as$sum{pixel values}*area$

__del__()#

Automatically stop tracemalloc when the object is deleted.

measure(image, include_meta=False)#

Execute the measurement operation on a detected-object image.

This is the main public API method for extracting measurements. It handles: input validation, parameter extraction via introspection, calling the subclass-specific _operate() method, optional metadata merging, and exception handling.

How it works (for users):

  1. Pass your processed Image (with detected objects) to measure()

  2. The method calls your subclass’s _operate() implementation

  3. Results are validated and returned as a pandas DataFrame

  4. If include_meta=True, image metadata (filename, grid info) is merged in

How it works (for developers):

When you subclass MeasureFeatures, you only implement _operate(). This measure() method automatically:

  • Extracts __init__ parameters from your instance (introspection)

  • Passes matched parameters to _operate() as keyword arguments

  • Validates the Image has detected objects (objmap)

  • Wraps exceptions in OperationFailedError with context

  • Merges grid/object metadata if requested

Parameters:
  • image (Image) – A PhenoTypic Image object with detected objects (must have non-empty objmap from a prior detection operation).

  • include_meta (bool, optional) – If True, merge image metadata columns (filename, grid position, etc.) into the results DataFrame. Defaults to False.

Returns:

Measurement results with structure:

  • First column: OBJECT.LABEL (integer IDs from image.objmap[:])

  • Remaining columns: Measurement values (float, int, or string)

  • One row per detected object

If include_meta=True, additional metadata columns are prepended before OBJECT.LABEL (e.g., Filename, GridRow, GridCol).

Return type:

pd.DataFrame

Raises:

OperationFailedError – If _operate() raises any exception, it is caught and re-raised as OperationFailedError with details including the original exception type, message, image name, and operation class. This provides consistent error handling across all measurers.

Notes

  • This method is the main entry point; do not override in subclasses

  • Subclasses implement _operate() only, not this method

  • Automatic memory profiling is available via logging configuration

  • Image must have detected objects (image.objmap should be non-empty)

Examples

Basic measurement extraction
from phenotypic import Image
from phenotypic.measure import MeasureSize
from phenotypic.detect import OtsuDetector

# Load and detect
image = Image.from_image_path('plate.jpg')
image = OtsuDetector().operate(image)

# Extract measurements
measurer = MeasureSize()
df = measurer.measure(image)
print(df.head())
Include metadata in measurements
# With image metadata (filename, grid info)
df_with_meta = measurer.measure(image, include_meta=True)
print(df_with_meta.columns)
# Output: ['Filename', 'GridRow', 'GridCol', 'OBJECT.LABEL',
#          'Area', 'IntegratedIntensity', ...]
class phenotypic.measure.MeasureTexture(scale: int | List[int] = 5, quant_lvl: Literal[8, 16, 32, 64] = 32, enhance: bool = False, warn: bool = False)[source]#

Bases: MeasureFeatures

Measure colony surface texture using Haralick features from gray-level co-occurrence matrices.

This class extracts second-order texture features (Haralick features) from colony grayscale images, quantifying surface roughness, regularity, and directional patterns. Features are computed at specified pixel offsets (scales) and across four directional angles (0°, 45°, 90°, 135°), then averaged.

Intuition: Colony texture reflects mycelial structure, growth patterns, and physiological state. Smooth, glabrous colonies have low texture contrast and entropy. Wrinkled, powdery, or sporulated colonies exhibit high contrast and energy. Radial growth patterns show angular correlation; random growth shows low correlation. Texture metrics capture morphological complexity beyond area and perimeter, enabling fine-grained phenotypic discrimination.

Use cases (agar plates): - Distinguish wild-type smooth colonies from rough/wrinkled mutants (e.g., Bacillus subtilis biofilm

morphologies, Pseudomonas aeruginosa rough variants).

  • Detect sporulation and powdery growth via high contrast and entropy.

  • Assess mycelial organization in fungi: organized radial hyphae (high correlation) vs diffuse cottony growth (low correlation).

  • Identify growth stress or nutrient depletion via texture changes within the same strain over time.

  • Enable multi-feature clustering combining size, shape, color, and texture for robust phenotyping.

Caveats: - Haralick features depend on image quantization level (quant_lvl); lower levels (8) reduce texture

nuance but are faster; higher levels (64) capture detail but are sensitive to noise.

  • Scale parameter affects the neighborhood size; small scales (1-2 px) capture fine texture (mycelial threads), large scales (5-10 px) capture coarse patterns (overall wrinkles). No single scale is universal; use multiple scales and average or compare within-plate.

  • Texture metrics are sensitive to uneven illumination and shadow; preprocess with illumination correction or histogram equalization if images show strong gradients.

  • Enhancement (rescale_intensity) improves texture detail but can inflate contrast in low-variance regions (e.g., uniform smooth colonies); use judiciously and validate with manual inspection.

  • Computation is slow for large colonies and high quantization levels; optimize scale and quant_lvl for your specific assay.

Attributes:
scale (list[int]): Distance parameter(s) for Haralick co-occurrence matrix, typically 1–10 pixels.

Larger values capture coarse texture; smaller values capture fine detail.

quant_lvl (Literal[8, 16, 32, 64]): Gray-level quantization (number of bins). Lower values

(8, 16) reduce dimensionality and computation time; higher values (32, 64) preserve texture nuance but increase noise sensitivity.

enhance (bool): Whether to rescale intensity within each colony to [0,1] before Haralick

computation. Improves contrast in low-variance regions but can bias comparisons. Defaults to False.

warn (bool): Whether to issue warnings if Haralick computation fails for specific objects.

Failures typically occur with very small colonies or empty regions. Defaults to False.

Returns:
pd.DataFrame: Object-level texture measurements with columns:
  • Label: Unique object identifier.

  • Texture measurements by scale and direction: AngularSecondMoment-deg000-scale##, Contrast-deg045-scale##, …, Correlation-avg-scale##, etc.

  • 13 Haralick features × 4 angles = 52 directional columns per scale.

  • Final 13 columns: averages across angles for each feature at the given scale.

References:

[1] https://mahotas.readthedocs.io/en/latest/api.html#mahotas.features.haralick [2] R. M. Haralick, K. Shanmugam, and I. Dinstein, “Textural Features for Image Classification,”

IEEE Transactions on Systems, Man, and Cybernetics, vol. SMC-3, no. 6, pp. 610–621, Nov. 1973, doi: 10.1109/TSMC.1973.4309314.

Examples:
Measure texture to distinguish morphotypes
from phenotypic import Image
from phenotypic.detect import OtsuDetector
from phenotypic.measure import MeasureTexture

# Load plate with smooth and wrinkled colonies
image = Image.from_image_path("morphotype_plate.jpg")
detector = OtsuDetector()
image = detector.operate(image)

# Measure texture at a single scale with default quantization
measurer = MeasureTexture(scale=3, quant_lvl=32, enhance=False)
texture = measurer.operate(image)

# High contrast and energy indicate wrinkled/rough morphology
wrinkled = texture[
    texture['TextureGray_Contrast-avg-scale03'] > texture['TextureGray_Contrast-avg-scale03'].quantile(0.75)
]
print(f"Wrinkled colonies: {len(wrinkled)}")
Multi-scale texture analysis for fine/coarse features
# Use multiple scales to capture fine and coarse texture
measurer = MeasureTexture(scale=[1, 3, 5], quant_lvl=32, enhance=True, warn=False)
texture = measurer.operate(image)

# Compare entropy across scales to assess texture organization
# Fine texture (scale 1): high entropy -> many small features
# Coarse texture (scale 5): low entropy -> organized large structures
for scale in [1, 3, 5]:
    col = f'TextureGray_Entropy-avg-scale0{scale}'
    if col in texture.columns:
        avg_entropy = texture[col].mean()
        print(f"Scale {scale}px: avg entropy = {avg_entropy:.2f}")
Category: TEXTURE#

Name

Description

AngularSecondMoment

Angular second moment (energy / uniformity). Measures the degree of local homogeneity

(Σ p(i,j)²). High values → uniform texture (e.g., smooth, yeast-like colonies with consistent mycelial density). Low values → heterogeneous surfaces (e.g., sectored, wrinkled, or mixed sporulation zones). Reflects colony surface regularity rather than brightness.

Contrast

Contrast (local intensity variation; Σ (i–j)² p(i,j)). High values indicate strong gray-level

differences (e.g., sharply defined rings, radial sectors, raised or folded regions). Low values indicate gradual tonal changes or uniformly pigmented colonies. Quantifies visual roughness and zonation amplitude.

Correlation

Linear gray-level correlation between neighboring pixels. Positive, high values suggest

structured spatial dependence (e.g., oriented radial hyphae or concentric patterns); near-zero values indicate uncorrelated, disordered growth (e.g., diffuse cottony mycelium). Sensitive to illumination gradients and directional GLCM computation.

HaralickVariance

GLCM variance (Σ (i–μ)² p(i,j)). Captures spread of co-occurring gray-level pairs, distinct

from raw intensity variance. High values → complex, multi-zone textures with variable hyphal/spore densities. Low values → consistent gray-level relationships and simpler colony surfaces.

InverseDifferenceMoment

Homogeneity (Σ p(i,j) / (1 + (i–j)²)). High values → smooth, locally uniform textures

(e.g., glabrous colonies, uniform aerial mycelium). Low values → abrupt gray-level changes (e.g., granular sporulation, wrinkled surfaces). Typically inversely correlated with Contrast.

SumAverage

Mean of gray-level sums (Σ k·p_{x+y}(k)). Reflects the average intensity combination of

neighboring pixels. In fungal colonies, can loosely parallel mean colony brightness when illumination and exposure are controlled, but remains a second-order rather than first-order intensity metric.

SumVariance

Variance of gray-level sum distribution. High values → heterogeneous brightness zones

(e.g., alternating dense/sparse or pigmented/non-pigmented regions). Low values → uniform tone across the colony. Often correlated with Contrast; use comparatively within one setup.

SumEntropy

Entropy of the gray-level sum distribution. High values → diverse brightness combinations

and irregular zonation. Low values → repetitive or periodic brightness patterns (e.g., evenly spaced rings). Indicates spatial unpredictability of summed intensities.

Entropy

Global GLCM entropy (–Σ p(i,j)·log p(i,j)). Measures total texture disorder and information

content. High values → complex, irregular colony surfaces (powdery, fuzzy, or sectored growth). Low values → simple, smooth, predictable patterns (glabrous or uniform colonies). Sensitive to gray-level quantization and image dynamic range.

DiffVariance

Variance of gray-level difference distribution. High values → mixture of smooth and textured

regions (e.g., smooth margins with wrinkled centers). Low values → consistent edge content. Highlights heterogeneity in edge magnitude across the colony.

DiffEntropy

Entropy of gray-level difference distribution. High values → irregular, unpredictable

intensity transitions (e.g., random sporulation or uneven mycelial networks). Low values → regular periodic transitions (e.g., concentric zonation). Reflects randomness of local contrast rather than its magnitude.

InfoCorrelation1

Information measure of correlation 1. Compares joint vs marginal entropies to quantify

mutual dependence between gray levels. Positive values → structured, predictable textures (e.g., organized radial growth); near-zero → independence between adjacent regions. Direction of sign varies with implementation.

InfoCorrelation2

Information measure of correlation 2 (√[1 – exp(–2 (H_xy2–H_xy))]). Always ≥ 0.

Values approaching 1 → strong spatial dependence and organized architecture (e.g., symmetric rings, radial structure). Values near 0 → random, independent patterns. Captures nonlinear organization missed by linear correlation.

Parameters:
  • scale (int | List[int])

  • quant_lvl (Literal[8, 16, 32, 64])

  • enhance (bool)

  • warn (bool)

__del__()#

Automatically stop tracemalloc when the object is deleted.

__init__(scale: int | List[int] = 5, quant_lvl: Literal[8, 16, 32, 64] = 32, enhance: bool = False, warn: bool = False)[source]#

Initializes an object with specific configurations for scale, quantization level, enhance, and warning behaviors. This constructor ensures that the ‘scale’ parameter is always stored as a list.

Parameters:
  • scale (int | List[int]) – A single integer or a list of integers representing the scale configuration. If a single integer is provided, it will be converted into a list containing that integer.

  • quant_lvl (Literal[8, 16, 32, 64]) – The quantization level. A higher level adds more computational complexity but captures more discrete texture changes. A higher value is not always more meaningful. Think of this like sensitivity to texture. Acceptable values are either 8, 16, 32, or 64.

  • enhance (bool) – A flag indicating whether to enhance the image before measuring texture. This can increase the amount of detail captured but can bias the measurements in cases where the relative variance between pixel intensities of an object is small.

  • warn (bool) – A flag indicating whether warnings should be issued.

measure(image, include_meta=False)#

Execute the measurement operation on a detected-object image.

This is the main public API method for extracting measurements. It handles: input validation, parameter extraction via introspection, calling the subclass-specific _operate() method, optional metadata merging, and exception handling.

How it works (for users):

  1. Pass your processed Image (with detected objects) to measure()

  2. The method calls your subclass’s _operate() implementation

  3. Results are validated and returned as a pandas DataFrame

  4. If include_meta=True, image metadata (filename, grid info) is merged in

How it works (for developers):

When you subclass MeasureFeatures, you only implement _operate(). This measure() method automatically:

  • Extracts __init__ parameters from your instance (introspection)

  • Passes matched parameters to _operate() as keyword arguments

  • Validates the Image has detected objects (objmap)

  • Wraps exceptions in OperationFailedError with context

  • Merges grid/object metadata if requested

Parameters:
  • image (Image) – A PhenoTypic Image object with detected objects (must have non-empty objmap from a prior detection operation).

  • include_meta (bool, optional) – If True, merge image metadata columns (filename, grid position, etc.) into the results DataFrame. Defaults to False.

Returns:

Measurement results with structure:

  • First column: OBJECT.LABEL (integer IDs from image.objmap[:])

  • Remaining columns: Measurement values (float, int, or string)

  • One row per detected object

If include_meta=True, additional metadata columns are prepended before OBJECT.LABEL (e.g., Filename, GridRow, GridCol).

Return type:

pd.DataFrame

Raises:

OperationFailedError – If _operate() raises any exception, it is caught and re-raised as OperationFailedError with details including the original exception type, message, image name, and operation class. This provides consistent error handling across all measurers.

Notes

  • This method is the main entry point; do not override in subclasses

  • Subclasses implement _operate() only, not this method

  • Automatic memory profiling is available via logging configuration

  • Image must have detected objects (image.objmap should be non-empty)

Examples

Basic measurement extraction
from phenotypic import Image
from phenotypic.measure import MeasureSize
from phenotypic.detect import OtsuDetector

# Load and detect
image = Image.from_image_path('plate.jpg')
image = OtsuDetector().operate(image)

# Extract measurements
measurer = MeasureSize()
df = measurer.measure(image)
print(df.head())
Include metadata in measurements
# With image metadata (filename, grid info)
df_with_meta = measurer.measure(image, include_meta=True)
print(df_with_meta.columns)
# Output: ['Filename', 'GridRow', 'GridCol', 'OBJECT.LABEL',
#          'Area', 'IntegratedIntensity', ...]