phenotypic.util package#
A module for useful utility operations and functions that don’t fit into a specific category.
- class phenotypic.util.BackgroundMetrics[source]#
Bases:
TypedDictBackground uniformity metrics.
- clear() None. Remove all items from D.#
- copy() a shallow copy of D#
- classmethod fromkeys(iterable, value=None, /)#
Create a new dictionary with keys from iterable and values set to value.
- get(key, default=None, /)#
Return the value for key if key is in the dictionary, else default.
- items() a set-like object providing a view on D's items#
- keys() a set-like object providing a view on D's keys#
- pop(k[, d]) v, remove specified key and return the corresponding value.#
If the key is not found, return the default if given; otherwise, raise a KeyError.
- popitem()#
Remove and return a (key, value) pair as a 2-tuple.
Pairs are returned in LIFO (last-in, first-out) order. Raises KeyError if the dict is empty.
- setdefault(key, default=None, /)#
Insert key with a value of default if key is not in the dictionary.
Return the value for key if key is in the dictionary, else default.
- update([E, ]**F) None. Update D from mapping/iterable E and F.#
If E is present and has a .keys() method, then does: for k in E.keys(): D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]
- values() an object providing a view on D's values#
- class phenotypic.util.ContrastMetrics[source]#
Bases:
TypedDictContrast-related metrics from detection matrix analysis.
- clear() None. Remove all items from D.#
- copy() a shallow copy of D#
- classmethod fromkeys(iterable, value=None, /)#
Create a new dictionary with keys from iterable and values set to value.
- get(key, default=None, /)#
Return the value for key if key is in the dictionary, else default.
- items() a set-like object providing a view on D's items#
- keys() a set-like object providing a view on D's keys#
- pop(k[, d]) v, remove specified key and return the corresponding value.#
If the key is not found, return the default if given; otherwise, raise a KeyError.
- popitem()#
Remove and return a (key, value) pair as a 2-tuple.
Pairs are returned in LIFO (last-in, first-out) order. Raises KeyError if the dict is empty.
- setdefault(key, default=None, /)#
Insert key with a value of default if key is not in the dictionary.
Return the value for key if key is in the dictionary, else default.
- update([E, ]**F) None. Update D from mapping/iterable E and F.#
If E is present and has a .keys() method, then does: for k in E.keys(): D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]
- values() an object providing a view on D's values#
- class phenotypic.util.ImageMetricsCalculator(detect_mat: numpy.ndarray)[source]#
Bases:
objectComputes image quality metrics from detection matrices.
This class extracts noise, contrast, structure, and background metrics used to assess image quality for colony detection. It provides the computational core shared by both the static matplotlib and the interactive Plotly diagnostics visualizations.
- Parameters:
detect_mat (np.ndarray) – 2D grayscale detection matrix (typically
image.detect_mat[:]).
- bit_depth#
Image bit depth (8 or 16) inferred from max intensity.
Examples
>>> from phenotypic.data import load_synth_yeast_plate >>> from phenotypic.util.image_metrics import ImageMetricsCalculator >>> image = load_synth_yeast_plate() >>> calc = ImageMetricsCalculator(image.detect_mat[:]) >>> noise = calc.compute_noise_metrics() >>> print(f"SNR: {noise['snr']:.2f}") SNR: ... >>> contrast = calc.compute_contrast_metrics() >>> print(f"RMS contrast: {contrast['rms_contrast']:.3f}") RMS contrast: ...
- static generate_interpretation(section: Literal['noise', 'contrast', 'structure', 'background'], metrics: dict[str, Any]) str[source]#
Generate human-readable interpretation text.
- static generate_recommendations(noise: NoiseMetrics, contrast: ContrastMetrics, structure: StructureMetrics, background: BackgroundMetrics) list[str][source]#
Generate actionable preprocessing recommendations.
- Parameters:
noise (NoiseMetrics) – Noise metrics from compute_noise_metrics().
contrast (ContrastMetrics) – Contrast metrics from compute_contrast_metrics().
structure (StructureMetrics) – Structure metrics from compute_structure_metrics().
background (BackgroundMetrics) – Background metrics from compute_background_metrics().
- Returns:
List of recommendation strings.
- Return type:
- compute_all(structure_sigma: float = 1.5, ridge_scales: list[float] | None = None, ridge_method: Literal['meijering', 'frangi', 'hessian'] = 'meijering', background_sigma: float = 50.0, include_non_serializable: bool = False) dict[str, Any][source]#
Compute all metrics and return comprehensive dict.
- Parameters:
structure_sigma (float) – Sigma for structure tensor computation.
ridge_scales (list[float] | None) – Scales for ridge detection.
ridge_method (Literal['meijering', 'frangi', 'hessian']) – Ridge detection algorithm.
background_sigma (float) – Sigma for background estimation.
include_non_serializable (bool) – If False, removes numpy arrays from output.
- Returns:
bit_depth, noise, contrast, structure, background, quality_scores, interpretations, recommendations.
- Return type:
Dict with keys
- compute_background_metrics(sigma: float = 50.0) BackgroundMetrics[source]#
Compute background uniformity metrics.
- Parameters:
sigma (float) – Sigma for background estimation (Gaussian smoothing).
- Returns:
Dictionary with nonuniformity_ratio, mean_gradient, and background_estimate.
- Return type:
- compute_contrast_metrics() ContrastMetrics[source]#
Compute contrast-related metrics (parameter-free).
- Returns:
Dictionary with rms_contrast, michelson, dynamic_range, p1, and p99.
- Return type:
- compute_local_contrast(img: np.ndarray | None = None, window_size: int = 15) np.ndarray[source]#
Compute local Weber contrast map.
- Parameters:
img (np.ndarray | None) – Input image array. If None, uses the stored detection matrix.
window_size (int) – Size of local window.
- Returns:
Local contrast map.
- Return type:
np.ndarray
- compute_local_variance(img: np.ndarray | None = None, window_size: int = 15) np.ndarray[source]#
Compute local variance map.
- Parameters:
img (np.ndarray | None) – Input image array. If None, uses the stored detection matrix.
window_size (int) – Size of local window.
- Returns:
Local variance map.
- Return type:
np.ndarray
- compute_noise_metrics() NoiseMetrics[source]#
Compute noise-related metrics (parameter-free).
- Returns:
Dictionary with SNR, sigma_mad, and correlation_length.
- Return type:
- compute_psd(img: np.ndarray | None = None) tuple[np.ndarray, np.ndarray][source]#
Compute radially averaged power spectral density.
- Parameters:
img (np.ndarray | None) – Input image array. If None, uses the stored detection matrix.
- Returns:
Tuple of (frequencies, radial_psd).
- Return type:
tuple[np.ndarray, np.ndarray]
- compute_quality_scores(noise: NoiseMetrics, contrast: ContrastMetrics, structure: StructureMetrics, background: BackgroundMetrics) QualityScores[source]#
Compute normalized 0-1 quality scores for radar chart.
- Parameters:
noise (NoiseMetrics) – Noise metrics from compute_noise_metrics().
contrast (ContrastMetrics) – Contrast metrics from compute_contrast_metrics().
structure (StructureMetrics) – Structure metrics from compute_structure_metrics().
background (BackgroundMetrics) – Background metrics from compute_background_metrics().
- Returns:
Dictionary of quality scores (SNR, Contrast, Coherence, Uniformity, Sharpness), all normalized to [0, 1].
- Return type:
- compute_structure_metrics(sigma: float = 1.5, scales: list[float] | None = None, ridge_method: Literal['meijering', 'frangi', 'hessian'] = 'meijering') StructureMetrics[source]#
Compute structure tensor and ridge detection metrics.
- Parameters:
sigma (float) – Sigma for structure tensor computation.
scales (list[float] | None) – List of scales for multiscale ridge detection. Defaults to [0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 5.0].
ridge_method (Literal['meijering', 'frangi', 'hessian']) – Method for ridge detection: “meijering” (default), “frangi”, or “hessian”.
- Returns:
Dictionary with mean_coherence, optimal_scale, peak_response, ridge_responses, scales, ridge_method, and coherence_map.
- Return type:
- class phenotypic.util.NoiseMetrics[source]#
Bases:
TypedDictNoise-related metrics from detection matrix analysis.
- clear() None. Remove all items from D.#
- copy() a shallow copy of D#
- classmethod fromkeys(iterable, value=None, /)#
Create a new dictionary with keys from iterable and values set to value.
- get(key, default=None, /)#
Return the value for key if key is in the dictionary, else default.
- items() a set-like object providing a view on D's items#
- keys() a set-like object providing a view on D's keys#
- pop(k[, d]) v, remove specified key and return the corresponding value.#
If the key is not found, return the default if given; otherwise, raise a KeyError.
- popitem()#
Remove and return a (key, value) pair as a 2-tuple.
Pairs are returned in LIFO (last-in, first-out) order. Raises KeyError if the dict is empty.
- setdefault(key, default=None, /)#
Insert key with a value of default if key is not in the dictionary.
Return the value for key if key is in the dictionary, else default.
- update([E, ]**F) None. Update D from mapping/iterable E and F.#
If E is present and has a .keys() method, then does: for k in E.keys(): D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]
- values() an object providing a view on D's values#
- class phenotypic.util.QualityScores[source]#
Bases:
TypedDictNormalized 0-1 quality scores for radar chart visualization.
- clear() None. Remove all items from D.#
- copy() a shallow copy of D#
- classmethod fromkeys(iterable, value=None, /)#
Create a new dictionary with keys from iterable and values set to value.
- get(key, default=None, /)#
Return the value for key if key is in the dictionary, else default.
- items() a set-like object providing a view on D's items#
- keys() a set-like object providing a view on D's keys#
- pop(k[, d]) v, remove specified key and return the corresponding value.#
If the key is not found, return the default if given; otherwise, raise a KeyError.
- popitem()#
Remove and return a (key, value) pair as a 2-tuple.
Pairs are returned in LIFO (last-in, first-out) order. Raises KeyError if the dict is empty.
- setdefault(key, default=None, /)#
Insert key with a value of default if key is not in the dictionary.
Return the value for key if key is in the dictionary, else default.
- update([E, ]**F) None. Update D from mapping/iterable E and F.#
If E is present and has a .keys() method, then does: for k in E.keys(): D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]
- values() an object providing a view on D's values#
- class phenotypic.util.StructureMetrics[source]#
Bases:
TypedDictStructure tensor and ridge detection metrics.
- clear() None. Remove all items from D.#
- copy() a shallow copy of D#
- classmethod fromkeys(iterable, value=None, /)#
Create a new dictionary with keys from iterable and values set to value.
- get(key, default=None, /)#
Return the value for key if key is in the dictionary, else default.
- items() a set-like object providing a view on D's items#
- keys() a set-like object providing a view on D's keys#
- pop(k[, d]) v, remove specified key and return the corresponding value.#
If the key is not found, return the default if given; otherwise, raise a KeyError.
- popitem()#
Remove and return a (key, value) pair as a 2-tuple.
Pairs are returned in LIFO (last-in, first-out) order. Raises KeyError if the dict is empty.
- setdefault(key, default=None, /)#
Insert key with a value of default if key is not in the dictionary.
Return the value for key if key is in the dictionary, else default.
- update([E, ]**F) None. Update D from mapping/iterable E and F.#
If E is present and has a .keys() method, then does: for k in E.keys(): D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]
- values() an object providing a view on D's values#
- phenotypic.util.cone_to_hsv(cone: numpy.ndarray) numpy.ndarray[source]#
Inverse of
hsv_to_cone(); returns H,S,V in [0,1].- Parameters:
cone (numpy.ndarray)
- Return type:
- phenotypic.util.decode_well_position(well, n_rows: int = 8, order: str = 'column') ndarray[source]#
Decode microplate well identifiers to 0-based linear indices.
Supports scalar strings, Python lists, NumPy arrays, Pandas Series, and Polars Series as input. Output is always a NumPy int32 array of the same length, or a scalar int32 for scalar input. The well position is distinct from the GRID labels. These are in relation to the actual original well plate, and may have an offset in comparison to the GRID positions from a GridImage
Row encoding follows the ANSI/SBS microplate convention: single letters A-Z map to rows 0-25; double letters AA-AZ to 26-51, etc. This covers 96-well (A-H), 384-well (A-P), and 1536-well (A-AF) formats.
Column-major index formula:
(col - 1) * n_rows + row_idx(0-based).- Parameters:
well – Well label(s) of the form
<letter(s)><digits>, e.g."A1","B11","AA3". Case-insensitive. Leading/trailing whitespace is stripped. Acceptsstr,list[str],np.ndarray,pandas.Series, orpolars.Series.n_rows (int) – Number of rows in the plate. Defaults to 8 (96-well). Use 16 for 384-well, 32 for 1536-well.
order (str) – Linear indexing order.
"column"(default) uses column-major (Fortran) order."row"is reserved but not yet implemented.
- Returns:
np.ndarray of dtype int32 containing 0-based linear indices. Invalid or unparseable labels produce
-1. Returns a scalar int32 when a single string is passed.- Raises:
NotImplementedError – If
order="row"is requested.- Return type:
Examples
>>> decode_well_position("A1") array(0, dtype=int32) >>> decode_well_position(["A1", "B1", "A2"], n_rows=8) array([ 0, 1, 8], dtype=int32) >>> decode_well_position(["A11", "H12"], n_rows=8) array([80, 95], dtype=int32)
- phenotypic.util.delta_e2000_spread(deltas: numpy.ndarray) tuple[float, float, float][source]#
Return (median, mean, P95) of a ΔE2000 distance array; NaNs if empty.
- Parameters:
deltas (numpy.ndarray)
- Return type:
- phenotypic.util.generate_output_key(df: DataFrame | DataFrame) pandas.DataFrame[source]#
Generate a column-description key for recognized output columns.
- Parameters:
df (DataFrame | DataFrame) – A pandas or polars measurements DataFrame.
- Returns:
A pandas DataFrame with
column_headeranddescriptioncolumns, preserving the input column order and omitting columns that are not backed by a publicMeasurementInfomember.- Raises:
TypeError – If df is not a pandas or polars DataFrame.
- Return type:
- phenotypic.util.geometric_median(points: ndarray, eps: float = 1e-06, method: Literal['cohen', 'weiszfeld'] = 'cohen', matrix_free: bool | None = None, matrix_free_threshold: int = 100, verbose: bool = True, **kwargs) Tuple[ndarray, Dict][source]#
Compute geometric median of a set of points.
Main interface supporting both Cohen et al. (2016) nearly-linear time algorithm and classical Weiszfeld algorithm.
- Parameters:
points (ndarray) – Data points, shape (n, d)
eps (float) – Target accuracy for (1 + eps)-approximation
method (Literal['cohen', 'weiszfeld']) – Algorithm to use: - ‘cohen’: Cohen et al. (2016) O(nd log³(n/ε)) algorithm [default] - ‘weiszfeld’: Classical Weiszfeld O(?) algorithm
matrix_free (bool | None) – For Cohen method, whether to use matrix-free Hessian. If None, automatically decides based on dimension.
matrix_free_threshold (int) – Dimension threshold for matrix-free mode
verbose (bool) – Whether to print progress information
**kwargs – Additional method-specific arguments
- Returns:
Geometric median point, shape (d,) info: Dictionary with algorithm statistics:
’iterations’: Number of iterations performed
’objective’: Final objective value f(x)
’converged’: Whether algorithm converged
’method’: Algorithm used
Additional method-specific statistics
- Return type:
median
- Raises:
ValueError – If method is invalid or points array has wrong shape
Examples
>>> # Cohen method (recommended for large problems) >>> points = np.random.randn(10000, 50) >>> median, info = geometric_median(points, method='cohen', eps=0.01) >>> print(f"Converged: {info['converged']}") >>> print(f"Objective: {info['objective']:.6f}")
>>> # Weiszfeld method (simple, good for small problems) >>> points = np.random.randn(100, 3) >>> median, info = geometric_median(points, method='weiszfeld', eps=1e-6)
>>> # Force matrix-free for high-dimensional problems >>> points = np.random.randn(1000, 500) >>> median, info = geometric_median(points, method='cohen', ... matrix_free=True, eps=0.1)
References
Cohen, M. B., Lee, Y. T., Miller, G., Pachocki, J., & Sidford, A. (2016). Geometric median in nearly linear time. STOC 2016.
- phenotypic.util.hsv_to_cone(hsv: numpy.ndarray) numpy.ndarray[source]#
Embed HSV (H,S,V in [0,1]) into Cartesian cone coords (S*V*cosθ, S*V*sinθ, V).
- Parameters:
hsv (numpy.ndarray)
- Return type:
- phenotypic.util.lab_to_srgb_hex(lab: numpy.ndarray) str[source]#
Convert a single CIE L*a*b* (D65) color to an sRGB
#RRGGBBstring.Returns
""if any coordinate is NaN (e.g. an empty object).- Parameters:
lab (numpy.ndarray)
- Return type:
- phenotypic.util.medoid_ciede2000(lab_points: numpy.ndarray, max_pixels: int = 1000, seed: int = 0, chunk_size: int = 128) tuple[TypeAliasForwardRef('numpy.ndarray'), TypeAliasForwardRef('numpy.ndarray')][source]#
ΔE2000 medoid center and per-pixel ΔE2000 distances to it.
The medoid (real pixel minimizing total ΔE2000) is selected from a seeded subsample of at most
max_pixels; the returned distances are computed from the chosen medoid to all input pixels.The total-distance (“row sum”) used to pick the medoid is accumulated in candidate blocks of
chunk_sizerows rather than materializing the fullm x mpairwise matrix. Peak memory is thereforeO(chunk_size * m)instead ofO(m^2)(CIEDE2000 allocates many intermediate arrays the size of its broadcast grid). The result is bit-identical to the full-matrix form for anychunk_size– chunking bounds memory, not accuracy.- Parameters:
lab_points (numpy.ndarray) – (N, 3) CIE L*a*b* pixel vectors.
max_pixels (int) – Subsample cap for medoid selection.
seed (int) – RNG seed for reproducible subsampling.
chunk_size (int) – Number of candidate rows scored per block; caps peak memory.
- Returns:
(center (3,), all_deltas (N,)). center is all-NaN and all_deltas empty when
lab_pointsis empty.- Return type:
tuple[TypeAliasForwardRef(‘numpy.ndarray’), TypeAliasForwardRef(‘numpy.ndarray’)]
- phenotypic.util.robust_color_center(points: numpy.ndarray, max_iter: int = 50, tol: float = 0.0001) numpy.ndarray[source]#
Euclidean geometric median of
points(N, D), as a bare (D,) array.Reuses
phenotypic.util.geometric_median(Weiszfeld). Returns all-NaN for empty input and the sole point forN == 1(the underlying solver requiresN >= 1and a defined centroid).- Parameters:
points (numpy.ndarray) – (N, D) coordinates (Lab pixels, or HSV cone coordinates).
max_iter (int) – Weiszfeld iteration cap.
tol (float) – Convergence tolerance (forwarded as
eps).
- Returns:
(D,) geometric-median coordinate.
- Return type:
- phenotypic.util.split_measurements(df: DataFrame | DataFrame) dict[str, DataFrame | DataFrame][source]#
Split a measurements table into producer-specific data frames.
Columns backed by public
MeasureFeaturesandSetAnalyzerMeasurementInfoenums define each split. Columns that do not belong to any producer-owned enum are preserved in every split as context columns.- Parameters:
df (DataFrame | DataFrame) – A pandas or polars measurements DataFrame.
- Returns:
Mapping of producer class name to a same-type DataFrame containing all context columns plus that producer’s recognized measurement columns. Returns an empty mapping when no producer-owned measurement columns are present.
- Raises:
TypeError – If df is not a pandas or polars DataFrame.
- Return type:
Submodules#
- phenotypic.util.image_metrics module
BackgroundMetricsBackgroundMetrics.clear()BackgroundMetrics.copy()BackgroundMetrics.fromkeys()BackgroundMetrics.get()BackgroundMetrics.items()BackgroundMetrics.keys()BackgroundMetrics.pop()BackgroundMetrics.popitem()BackgroundMetrics.setdefault()BackgroundMetrics.update()BackgroundMetrics.values()BackgroundMetrics.background_estimateBackgroundMetrics.mean_gradientBackgroundMetrics.nonuniformity_ratio
ContrastMetricsContrastMetrics.clear()ContrastMetrics.copy()ContrastMetrics.fromkeys()ContrastMetrics.get()ContrastMetrics.items()ContrastMetrics.keys()ContrastMetrics.pop()ContrastMetrics.popitem()ContrastMetrics.setdefault()ContrastMetrics.update()ContrastMetrics.values()ContrastMetrics.dynamic_rangeContrastMetrics.michelsonContrastMetrics.p1ContrastMetrics.p99ContrastMetrics.rms_contrast
ImageMetricsCalculatorImageMetricsCalculator.bit_depthImageMetricsCalculator.generate_interpretation()ImageMetricsCalculator.generate_recommendations()ImageMetricsCalculator.compute_all()ImageMetricsCalculator.compute_background_metrics()ImageMetricsCalculator.compute_contrast_metrics()ImageMetricsCalculator.compute_local_contrast()ImageMetricsCalculator.compute_local_variance()ImageMetricsCalculator.compute_noise_metrics()ImageMetricsCalculator.compute_psd()ImageMetricsCalculator.compute_quality_scores()ImageMetricsCalculator.compute_structure_metrics()ImageMetricsCalculator.bit_depthImageMetricsCalculator.max_intensity
NoiseMetricsNoiseMetrics.clear()NoiseMetrics.copy()NoiseMetrics.fromkeys()NoiseMetrics.get()NoiseMetrics.items()NoiseMetrics.keys()NoiseMetrics.pop()NoiseMetrics.popitem()NoiseMetrics.setdefault()NoiseMetrics.update()NoiseMetrics.values()NoiseMetrics.correlation_lengthNoiseMetrics.sigma_madNoiseMetrics.snr
QualityScoresQualityScores.clear()QualityScores.copy()QualityScores.fromkeys()QualityScores.get()QualityScores.items()QualityScores.keys()QualityScores.pop()QualityScores.popitem()QualityScores.setdefault()QualityScores.update()QualityScores.values()QualityScores.CoherenceQualityScores.ContrastQualityScores.SNRQualityScores.SharpnessQualityScores.Uniformity
StructureMetricsStructureMetrics.clear()StructureMetrics.copy()StructureMetrics.fromkeys()StructureMetrics.get()StructureMetrics.items()StructureMetrics.keys()StructureMetrics.pop()StructureMetrics.popitem()StructureMetrics.setdefault()StructureMetrics.update()StructureMetrics.values()StructureMetrics.coherence_mapStructureMetrics.mean_coherenceStructureMetrics.optimal_scaleStructureMetrics.peak_responseStructureMetrics.ridge_methodStructureMetrics.ridge_responsesStructureMetrics.scales