Skip to main content

ga_metrics

Shared GA metrics computation primitives.

These pure functions are used by both ga_calculation_with_fovea and ga_calculation_without_fovea steps. Extracted here so that neither step package depends on the other.

Module

Functions

compact_bscan_index

def compact_bscan_index(bscan_index: float, na_bscan_indices: tuple[int, ...])> float:

Shift an input-space B-scan coordinate into compacted index space.

parse_bscan_predictions gives column_masks a row only for a B-scan that carried model output, so its first axis is compacted. A coordinate that arrived in the original input index space — a fovea landmark's slice, for instance — therefore sits one row too high for every dropped frame below it, which is slice_thickness of physical error per dropped frame once the displacement is scaled.

A coordinate whose own B-scan was dropped maps onto the next surviving row. That is the closest surviving sample to a frame that was never imaged; the alternative, landing it half-way between neighbours, would invent a position for tissue no B-scan observed.

Arguments

  • bscan_index: The B-scan coordinate in the original (uncompacted) input index space.
  • na_bscan_indices: The dropped input indices, from ParsedBScanPredictions.na_bscan_indices.

Returns The corresponding coordinate in compacted index space.

compute_ga_metrics_for_scan

def compute_ga_metrics_for_scan(    bscan_predictions: tuple[str, ...],    slice_thickness: float,    pixel_spacing_column: float,    all_segmentation_labels: dict[str, int],    ga_area_include_segmentations: list[str],    ga_area_exclude_segmentations: list[str],    n_scan_biomarker_thresholds: Mapping[str, float] | None = None,    include_raw_pathology_probabilities: bool = False,)> GAMetrics:

Compute GA metrics for a single scan (without fovea).

This encapsulates the per-file computation loop body from _WorkerSide.run() (lines 268-343 in the original).

Arguments

  • bscan_predictions: The raw per-B-scan prediction JSON strings.
  • slice_thickness: The distance between B-scans in mm.
  • pixel_spacing_column: The pixel spacing along the B-scan column axis.
  • all_segmentation_labels: All segmentation label names to class index.
  • ga_area_include_segmentations: Segmentation labels to include when computing the GA area column mask.
  • ga_area_exclude_segmentations: Segmentation labels to exclude when computing the GA area column mask.
  • n_scan_biomarker_thresholds: Per-biomarker >= probability threshold used to compute n_scan_run_lengths. Keys define which N-scan biomarkers are evaluated; when None, every label in N_SCAN_BIOMARKER_LABELS defaults to 0.5.
  • include_raw_pathology_probabilities: Whether to include the raw per-B-scan pathology probability arrays on the returned metrics.

Raises

  • Exception: Propagates any exception from prediction parsing or metric computation so that the caller can handle/skip.

compute_n_scan_run_lengths

def compute_n_scan_run_lengths(    class_probabilities_by_bscan: Mapping[str, NDArray[Any]],    thresholds: Mapping[str, float],)> dict[str, int]:

Longest consecutive-B-scan run over the threshold, per biomarker.

For each requested biomarker, mask the per-B-scan probabilities at (>=) its threshold and return the length of the longest consecutive True run.

warning

The input must be B-scan-indexedParsedBScanPredictions. class_probabilities_by_bscan, not class_probabilities. The latter is appended once per reported detection, so a biomarker seen on B-scans 0, 2 and 4 yields [p, p, p] there and would be misread as a run of 3. ParsedBScanPredictions rejects a misaligned mapping on construction, so this only bites a caller that assembles the mapping by hand.

Arguments

  • class_probabilities_by_bscan: Per-biomarker probability arrays indexed by B-scan, with 0.0 where the biomarker was not reported on a B-scan (as built by parse_bscan_predictions).
  • thresholds: Per-biomarker >= probability threshold. Keys define which biomarkers to evaluate; the result is dense over these keys.

Returns A {biomarker: longest_run} map. A biomarker whose array is absent, empty, or has no B-scan meeting the threshold maps to 0.

convert_nan_to_zero

def convert_nan_to_zero(value: Any)> float:

Convert NaN values to 0.

extract_bscan_predictions

def extract_bscan_predictions(ga_inferences: Any)> tuple[str, ...]:

Extract ordered B-scan prediction strings from a GA inferences dict.

Arguments

  • ga_inferences: The value of the inferences_json (or inferences_json_ga) field from a merged row. Expected to be a dict keyed by B-scan index (numeric strings or ints).

Returns Tuple of prediction strings sorted by numeric B-scan index, or an empty tuple if the input is not a non-empty dict.

get_lesion_sizes

def get_lesion_sizes(    num_lesions: int,    labeled_array: NDArray[Any],    slice_thickness: float,    pixel_spacing_column: float,)> list[float]:

Calculate the size of each lesion in mm^2.

Arguments

  • num_lesions: Number of lesions in the image.
  • labeled_array: Numpy array of shape (num_bscans, num_cols) where each pixel is labelled with the lesion number it belongs to.
  • slice_thickness: Thickness of each B-scan in mm.
  • pixel_spacing_column: Spacing between columns in mm.

Returns List of lesion sizes in mm^2.

get_max_ga_bscan_index

def get_max_ga_bscan_index(column_masks_arr: NDArray[Any], ga_area: float)> int | None:

Return the index of the B-scan with the largest GA area.

Arguments

  • column_masks_arr: Numpy array mask of shape (num_bscans, num_cols).
  • ga_area: Total GA area in mm^2.

Returns Index of the B-scan with the largest GA area, or None if no GA.

get_missing_data_reason

def get_missing_data_reason(    slice_thickness: float,    pixel_spacing_column: float,    original_filename: str | float,    pixel_spacing_row: float | None = <object object>,)> str | None:

Check required per-row fields and return a skip reason if any are missing.

Arguments

  • slice_thickness: Value from the Slice Thickness metadata column. Will be float('nan') when the datasource has no value for this file.
  • pixel_spacing_column: Value from the Pixel Spacing Column metadata column. Will be float('nan') when the datasource has no value for this file.
  • original_filename: Value from the filename metadata column. Will be float('nan') when the left-join produced no match.
  • pixel_spacing_row: Optional value from the Pixel Spacing Row metadata column. Only the fluid-volume calculation requires this axis, so it defaults to the _UNSET sentinel (not checked) — GA callers pass nothing and are unaffected. When a value is supplied it is validated like the other fields, including a genuine None/NaN cell (reported as pixel_spacing_row).

Returns A "missing_data:<field>,..." string if one or more fields are NaN / null, or None if all fields are present.

get_shortest_distance_from_image_centre

def get_shortest_distance_from_image_centre(    column_masks_arr: NDArray[Any],    labeled_array: NDArray[Any],    num_lesions: int,    slice_thickness: float,    pixel_spacing_column: float,)> float:

Calculate the distance from the image centre to the nearest lesion.

Image centre is used as a proxy for the fovea.

Arguments

  • column_masks_arr: Numpy array mask of shape (num_bscans, num_cols).
  • labeled_array: Numpy array of shape (num_bscans, num_cols) where each pixel is labelled with the lesion number it belongs to.
  • num_lesions: Number of lesions in the image.
  • slice_thickness: Thickness of each B-scan in mm.
  • pixel_spacing_column: Spacing between columns in mm.

Returns Distance from the image centre to the nearest lesion in mm.

parse_bscan_predictions

def parse_bscan_predictions(    bscan_prediction_strs: tuple[str, ...],    slice_thickness: float,    pixel_spacing_column: float,    all_segmentation_labels: dict[str, int],    ga_area_include_segmentations: list[str],    ga_area_exclude_segmentations: list[str],)> ParsedBScanPredictions:

Parse raw B-scan prediction strings into columnar masks and probabilities.

This function was extracted from _WorkerSide._parse_bscan_predictions in ga_trial_calculation_algorithm_base.py. The only change is that configuration values (segmentation labels) are passed as explicit parameters instead of being read from self.

Arguments

  • bscan_prediction_strs: Tuple of JSON prediction strings, one per B-scan.
  • slice_thickness: Thickness of each B-scan in mm.
  • pixel_spacing_column: Spacing between columns in mm.
  • all_segmentation_labels: Mapping of segmentation class name → index.
  • ga_area_include_segmentations: Segmentations used to include GA area.
  • ga_area_exclude_segmentations: Segmentations used to exclude GA area.

Returns A ParsedBScanPredictions containing columnar masks, class probabilities, and class areas per B-scan.