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:
MeasureFeaturesExtract 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
CenterRRThe row coordinate of the center of the bounding box.
MinRRThe smallest row coordinate of the bounding box.
MaxRRThe largest row coordinate of the bounding box.
CenterCCThe column coordinate of the center of the bounding box.
MinCCThe smallest column coordinate of the bounding box.
MaxCCThe 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):
Pass your processed Image (with detected objects) to measure()
The method calls your subclass’s _operate() implementation
Results are validated and returned as a pandas DataFrame
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:
- 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:
MeasureFeaturesMeasure 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
CieXMinThe minimum X value of the object in CIE XYZ color space
CieXQ1The lower quartile (Q1) X value of the object in CIE XYZ color space
CieXMeanThe mean X value of the object in CIE XYZ color space
CieXMedianThe median X value of the object in CIE XYZ color space
CieXQ3The upper quartile (Q3) X value of the object in CIE XYZ color space
CieXMaxThe maximum X value of the object in CIE XYZ color space
CieXStdDevThe standard deviation of the X value of the object in CIE XYZ color space
CieXCoeffVarThe coefficient of variation of the X value of the object in CIE XYZ color space
CieYMinThe minimum Y value of the object in CIE XYZ color space
CieYQ1The lower quartile (Q1) Y value of the object in CIE XYZ color space
CieYMeanThe mean Y value of the object in CIE XYZ color space
CieYMedianThe median Y value of the object in CIE XYZ color space
CieYQ3The upper quartile (Q3) Y value of the object in CIE XYZ color space
CieYMaxThe maximum Y value of the object in CIE XYZ color space
CieYStdDevThe standard deviation of the Y value of the object in CIE XYZ color space
CieYCoeffVarThe coefficient of variation of the Y value of the object in CIE XYZ color space
CieZMinThe minimum Z value of the object in CIE XYZ color space
CieZQ1The lower quartile (Q1) Z value of the object in CIE XYZ color space
CieZMeanThe mean Z value of the object in CIE XYZ color space
CieZMedianThe median Z value of the object in CIE XYZ color space
CieZQ3The upper quartile (Q3) Z value of the object in CIE XYZ color space
CieZMaxThe maximum Z value of the object in CIE XYZ color space
CieZStdDevThe standard deviation of the Z value of the object in CIE XYZ color space
CieZCoeffVarThe coefficient of variation of the Z value of the object in CIE XYZ color space
Category: Colorxy# Name
Description
xMinThe minimum chromaticity x coordinate of the object
xQ1The lower quartile (Q1) chromaticity x coordinate of the object
xMeanThe mean chromaticity x coordinate of the object
xMedianThe median chromaticity x coordinate of the object
xQ3The upper quartile (Q3) chromaticity x coordinate of the object
xMaxThe maximum chromaticity x coordinate of the object
xStdDevThe standard deviation of the chromaticity x coordinate of the object
xCoeffVarThe coefficient of variation of the chromaticity x coordinate of the object
yMinThe minimum chromaticity y coordinate of the object
yQ1The lower quartile (Q1) chromaticity y coordinate of the object
yMeanThe mean chromaticity y coordinate of the object
yMedianThe median chromaticity y coordinate of the object
yQ3The upper quartile (Q3) chromaticity y coordinate of the object
yMaxThe maximum chromaticity y coordinate of the object
yStdDevThe standard deviation of the chromaticity y coordinate of the object
yCoeffVarThe coefficient of variation of the chromaticity y coordinate of the object
Category: ColorLab# Name
Description
L*MinThe minimum L* value of the object
L*Q1The lower quartile (Q1) L* value of the object
L*MeanThe mean L* value of the object
L*MedianThe median L* value of the object
L*Q3The upper quartile (Q3) L* value of the object
L*MaxThe maximum L* value of the object
L*StdDevThe standard deviation of the L* value of the object
L*CoeffVarThe coefficient of variation of the L* value of the object
a*MinThe minimum a* value of the object
a*Q1The lower quartile (Q1) a* value of the object
a*MeanThe mean a* value of the object
a*MedianThe median a* value of the object
a*Q3The upper quartile (Q3) a* value of the object
a*MaxThe maximum a* value of the object
a*StdDevThe standard deviation of the a* value of the object
a*CoeffVarThe coefficient of variation of the a* value of the object
b*MinThe minimum b* value of the object
b*Q1The lower quartile (Q1) b* value of the object
b*MeanThe mean b* value of the object
b*MedianThe median b* value of the object
b*Q3The upper quartile (Q3) b* value of the object
b*MaxThe maximum b* value of the object
b*StdDevThe standard deviation of the b* value of the object
b*CoeffVarThe coefficient of variation of the b* value of the object
ChromaEstimatedMeanThe mean chroma estimation of the object calculated using \(\(sqrt{a^{*}_{mean}^2 + b^{*}_{mean})^2}\)
ChromaEstimatedMedianThe median chroma estimation of the object using \(\sqrt({a*_{median}^2 + b*_{median})^2}\)
Category: ColorHSV# Name
Description
HueMinThe minimum hue of the object
HueQ1The lower quartile (Q1) hue of the object
HueMeanThe mean hue of the object
HueMedianThe median hue of the object
HueQ3The upper quartile (Q3) hue of the object
HueMaxThe maximum hue of the object
HueStdDevThe standard deviation of the hue of the object
HueCoeffVarThe coefficient of variation of the hue of the object
SaturationMinThe minimum saturation of the object
SaturationQ1The lower quartile (Q1) saturation of the object
SaturationMeanThe mean saturation of the object
SaturationMedianThe median saturation of the object
SaturationQ3The upper quartile (Q3) saturation of the object
SaturationMaxThe maximum saturation of the object
SaturationStdDevThe standard deviation of the saturation of the object
SaturationCoeffVarThe coefficient of variation of the saturation of the object
BrightnessMinThe minimum brightness of the object
BrightnessQ1The lower quartile (Q1) brightness of the object
BrightnessMeanThe mean brightness of the object
BrightnessMedianThe median brightness of the object
BrightnessQ3The upper quartile (Q3) brightness of the object
BrightnessMaxThe maximum brightness of the object
BrightnessStdDevThe standard deviation of the brightness of the object
BrightnessCoeffVarThe coefficient of variation of the brightness 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):
Pass your processed Image (with detected objects) to measure()
The method calls your subclass’s _operate() implementation
Results are validated and returned as a pandas DataFrame
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:
- 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:
GridMeasureFeaturesEvaluate 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
RowMSlope 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.
RowBIntercept 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.
ColMSlope 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.
ColBIntercept 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.
PredRRPredicted 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.
PredCCPredicted 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.
ResidualErrorEuclidean 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:
GridMeasureFeaturesQuantify 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
ObjectSpreadSum 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:
MeasureFeaturesMeasure 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
IntegratedIntensityThe sum of the object’s pixels
MinimumIntensityThe minimum intensity of the object
MaximumIntensityThe maximum intensity of the object
MeanIntensityThe mean intensity of the object
MedianIntensityThe median intensity of the object
StandardDeviationIntensityThe standard deviation of the object
CoefficientVarianceIntensityThe coefficient of variation of the object
LowerQuartileIntensityThe lower quartile intensity of the object
UpperQuartileIntensityThe upper quartile intensity of the object
InterquartileRangeIntensityThe 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):
Pass your processed Image (with detected objects) to measure()
The method calls your subclass’s _operate() implementation
Results are validated and returned as a pandas DataFrame
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:
- 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:
MeasureFeaturesMeasure 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
AreaTotal 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.
PerimeterTotal 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.
CircularityCalculated 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.
ConvexAreaArea 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.
MedianRadiusMedian 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.
MeanRadiusAverage 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.
MaxRadiusMaximum 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.
MinFeretDiameterMinimum 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.
MaxFeretDiameterMaximum 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.
EccentricityMeasure 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.
SolidityRatio 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.
ExtentRatio 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.
BboxAreaArea 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.
MajorAxisLengthLength 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.
MinorAxisLengthLength 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.
CompactnessCalculated 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.
OrientationAngle (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):
Pass your processed Image (with detected objects) to measure()
The method calls your subclass’s _operate() implementation
Results are validated and returned as a pandas DataFrame
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:
- 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:
MeasureFeaturesMeasure 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
AreaTotal number of pixels occupied by the microbial colony.Larger areas typically indicate more robust growth or longer incubation times.
IntegratedIntensityThe 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):
Pass your processed Image (with detected objects) to measure()
The method calls your subclass’s _operate() implementation
Results are validated and returned as a pandas DataFrame
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:
- 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:
MeasureFeaturesMeasure 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.
- __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):
Pass your processed Image (with detected objects) to measure()
The method calls your subclass’s _operate() implementation
Results are validated and returned as a pandas DataFrame
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:
- 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', ...]