Visualization & Plotting#
- property Image.plot: PlotAccessor#
The plot property provides quality-of-life visualizations for pipeline development,
specifically designed for colony detection parameter tuning on arrayed microbe cultures
on agar plates. These methods help understand how morphological operations, size filtering,
and spatial patterns affect detection results.
All methods support flexible data requirements, automatically detecting whether labeled objects (objmap) or binary masks (objmask) are available.
Morphological Progression Analysis#
Purpose: Visualize how morphological operations (erosion, dilation, opening, closing) affect colony boundaries as kernel size increases.
Use Case: Identify critical transition points where small colonies vanish, distinct colonies merge, or unexpected artifacts appear. This is essential for selecting optimal morphological parameters in colony detection pipelines.
Output: Grid of images with color-coded boundary overlays for each kernel size.
Interpretation Guide:
Small colonies vanishing: Operation too aggressive → reduce kernel size or use gentler preprocessing
Colonies merging unexpectedly: Kernel too large → reduce size or improve initial detection
Stable transitions: Minimal boundary changes between consecutive sizes → robust parameter region
Edge artifacts: Boundary behavior differs at image edges → consider cropping or border removal
Domain Context (Agar Plates):
For arrayed microbial cultures, morphological operations are used to:
Remove agar texture artifacts (opening with small disk)
Merge colony fragments from uneven growth (closing)
Remove uncertain edge pixels (erosion)
Recover faint boundaries (dilation)
By observing boundary evolution across kernel sizes, you can empirically determine appropriate parameters for your imaging conditions, colony size distribution, and inter-colony spacing.
Example:
from phenotypic import Image
from phenotypic.detect import OtsuDetector
# Load and detect colonies
img = Image.imread('plate.jpg')
detector = OtsuDetector()
detected = detector.apply(img)
# Visualize opening operation across kernel sizes
fig, axes = detected.plot.morph_progression(
operation='opening',
kernel_sizes=[1, 3, 5, 7, 9, 11, 13, 15],
shape='disk'
)
fig.suptitle('GrayOpening: Colony Boundary Changes')
# Compare with closing
fig2, axes2 = detected.plot.morph_progression(
operation='closing',
kernel_sizes=[1, 3, 5, 7, 9, 11],
shape='disk'
)
fig2.suptitle('Closing: Gap Filling Progression')
Structural Response Curves#
Purpose: Quantify how object metrics (count, total area, mean size) respond to morphological kernel size changes. Provides data-driven parameter selection by identifying stable plateaus (robust) vs. steep slopes (sensitive).
Use Case: Select morphological parameters that balance noise removal with colony preservation. Stable regions (low derivative) indicate robust parameter choices.
Output: Line plot with metric vs. kernel size; optional derivative curve showing sensitivity.
Interpretation Guide:
Plateaus (low derivative): Stable, robust parameters insensitive to small adjustments
Steep slopes (high derivative): Parameter-sensitive zones where small changes dramatically affect results
For noise removal: Select kernel where count stabilizes after initial drop
For fragment merging: Select kernel where count drops to expected value without area explosion
Domain Context (Colony Phenotyping):
The structural response curve reveals trade-offs in detection pipelines:
GrayOpening for noise removal: Count drops sharply at small sizes (dust removed), then plateaus (real colonies preserved)
Closing for fragment merging: Area increases while count decreases (fragments merge into single colonies)
Optimal kernel: First stable plateau after transition region
Example:
detected = detector.apply(img)
# Plot count response to opening
fig, ax = detected.plot.structural_response_curve(
operation='opening',
kernel_range=(1, 20),
metric='count',
show_derivative=True
)
ax.set_title('Colony Count vs. GrayOpening Kernel Size')
# Find optimal kernel: first plateau after noise removal
# Look for region where derivative approaches zero
Boundary Displacement Analysis#
Purpose: Visualize spatial sensitivity to morphological parameters as a heatmap showing where boundary displacement is highest. Identifies illumination-dependent detection and edge effects.
Use Case: Detect systematic biases in detection due to uneven illumination, vignetting, or edge artifacts.
Output: Heatmap showing boundary displacement magnitude at each location; statistics panel with displacement metrics.
Interpretation Guide:
High displacement at edges: Edge effects or border artifacts → apply border removal
High displacement following lighting patterns: Vignetting or illumination gradient → apply flat-field correction
Uniform displacement: Parameter-sensitive but systematic → acceptable if magnitude is small
Clustered displacement: Specific biological/technical issues (small colonies, contamination, touching colonies)
Domain Context (Agar Plates):
Boundary displacement patterns reveal imaging artifacts common in plate scanners:
Radial gradient: Lens vignetting or uneven illumination from center to edges
Corners darker: Extreme vignetting requiring illumination correction
Edge displacement: Boundary effects from detection algorithms
Random clusters: Small/faint colonies more sensitive to parameters
Example:
detected = detector.apply(img)
# Visualize displacement from opening
fig, ax = detected.plot.boundary_displacement(
operation='opening',
kernel_sizes=[3, 7],
reference_size=1,
cmap='hot'
)
fig.suptitle('Boundary Sensitivity to GrayOpening')
# High displacement indicates regions requiring correction
Object Size Distribution#
Purpose: Comprehensive size analysis with 6-panel visualization showing histogram, CDF, sensitivity curve, and filtered previews at predetermined thresholds.
Use Case: Select minimum/maximum size thresholds to remove dust, debris, and artifacts while preserving real colonies.
Output: 6-panel figure with histogram+KDE, CDF, sensitivity curve, and 3 threshold previews (5th, 50th, and 95th percentiles by default).
Interpretation Guide (each panel):
Histogram: Bimodal distributions suggest distinct populations (colonies vs. debris)
CDF: Curves separating reveal transition from artifact-dominated to colony-dominated sizes
Sensitivity: Plateaus indicate robust threshold regions where small changes don’t affect count
Previews: Green (accepted objects), red (rejected) for visual threshold validation
Domain Context (Agar Plates):
Typical colony size distributions:
50-5000 pixels: Real colonies at standard plate scanner resolution
<50 pixels: Usually dust, agar texture, or condensation
>5000 pixels: Merged colonies, large mutants, or contamination
Bimodal: Distinct artifact and colony populations
Log-normal: Typical for exponential growth with size variation
Example:
detected = detector.apply(img)
# Static 6-panel analysis
fig, axes = detected.plot.size_distribution(
thresholds=None, # Auto-select 5th, 50th, 95th percentiles
log_scale=True
)
fig.suptitle('Colony Size Distribution Analysis')
Size Viewer (Interactive)#
Purpose: Interactive size analysis with real-time threshold adjustment using widgets for live preview and parameter tuning.
Use Case: Fine-tune size thresholds interactively to balance artifact removal with colony preservation.
Output: Base 6-panel visualization plus ipywidgets slider for threshold selection with live preview updates.
Requirements: ipywidgets package installed and Jupyter notebook environment.
Interpretation Guide: Same as static version, but with real-time feedback from slider adjustments.
Domain Context (Agar Plates): Same size distribution patterns as static version, but allows interactive exploration of threshold effects.
Example:
# Interactive mode (requires ipywidgets + Jupyter)
fig, axes, widgets = detected.plot.size_distribution_interactive()
# Use slider to adjust threshold and see immediate effect
# on preview images. Find value that removes artifacts
# while preserving all real colonies.
Spatial Size Distribution Map#
Purpose: Pseudo-color map showing each object colored by its size relative to a center value (median, mean, percentile, or absolute). Detects position effects, illumination gradients, and contamination.
Use Case: Identify systematic spatial biases in colony growth or detection: radial gradients (temperature), row/column patterns (pipetting errors), clusters (contamination).
Output: Pseudo-color image with diverging colormap (blue = smaller, red = larger); colorbar with annotations; statistics on spatial variation.
Interpretation Guide (Color Patterns):
Uniform color: Homogeneous colony sizes (ideal)
Radial gradient (center vs. edges): Temperature gradient, illumination gradient, evaporation effects
Row/column pattern: Systematic pipetting volume gradient, plate tilt, incubator shelf effect
Random patches: Contamination clusters or local growth defects
Edge effects: Detection artifacts from image boundaries
Mode Selection:
median (default): Robust to outliers, best for general use
mean: Sensitive to outliers, use with symmetric distributions
percentile: Highlight specific deviations (e.g., value=75 emphasizes smallest 25%)
absolute: Compare to biological expectation from pilot experiments
Domain Context (High-Throughput Screening):
Spatial patterns reveal technical and biological effects:
Temperature: Incubator edges cooler → slower growth → smaller colonies at periphery
Evaporation: Plate edges dry faster → osmotic stress → smaller colonies
Pipetting: Volume gradient across multichannel pipette → dose-response pattern
Contamination: Localized clusters of abnormally large colonies
Returns: Metadata dictionary with spatial statistics for programmatic analysis.
Example:
detected = detector.apply(img)
# Visualize size relative to median
fig, ax, metadata = detected.plot.spatial_size_map(
mode='median',
robust=True,
cmap='RdBu_r'
)
ax.set_title('Colony Size Deviation from Median')
# Check spatial variation magnitude
print(f"CV: {metadata['cv']:.2f}")
print(f"Range: {metadata['value_range']}")
Size-Intensity Correlation#
Purpose: Scatter plot correlating object size (x-axis) with mean intensity (y-axis), with optional color-coding by secondary feature. Distinguishes true colonies from dust, condensation, merged objects via feature relationships.
Use Case: Identify artifact types based on size-intensity relationships and derive multi-feature filtering rules.
Output: Scatter plot with optional marginal histograms; log-log regression line with confidence band; color-coded by secondary feature.
Interpretation Guide (Regression Slope in Log-Log Space):
Slope ≈ 1: Integrated density constant (typical for uniform colonies)
Slope ≈ 0: Intensity independent of size (threshold-dominated detection)
Slope < 0: Artifacts! Small bright spots are dust, condensation, or optical defects
Slope > 1: 3D growth effects, merged colonies, or saturated bright objects
Outlier Patterns:
Small + very bright: Dust, lens artifacts, condensation → filter by size AND intensity
Large + very dim: Over-segmentation, shadows, plate edges → raise threshold or apply border removal
High intensity_std (color): Merged/heterogeneous objects → use watershed segmentation
Low solidity (color): Irregular shapes (merged, debris, artifacts with holes)
High eccentricity (color): Elongated objects (merged in rows, spreading phenotypes)
Color-by Options:
intensity_std: High values indicate heterogeneous/merged objects
solidity: Low values indicate irregular shapes (merged, touching, debris)
eccentricity: High values indicate elongated objects (merged, spreading)
Domain Context (Colony Quality Control):
Feature correlations reveal detection issues:
Negative slope: Optical artifacts dominating small size range
Positive slope > 1.5: Merged colonies or 3D growth (use watershed)
Outliers above trend: Bright mutants or contamination
Outliers below trend: Faint colonies near detection threshold
Cluster separation: Distinct artifact and colony populations in feature space
Example:
detected = detector.apply(img)
# Basic size-intensity scatter
fig, ax, regression = detected.plot.size_scatter(
color_by='intensity_std',
show_regression=True,
show_marginals=True
)
ax.set_title('Colony Feature Space')
# Interpret slope
slope = regression['slope']
if slope < 0:
print("Warning: Negative slope suggests optical artifacts")
elif slope > 1.5:
print("Warning: High slope suggests merged colonies")
# Identify outliers for manual inspection
# High intensity_std (red points) = heterogeneous/merged