thickness_calculation
Shared per-file thickness-calculation runner.
Ports the per-file orchestration loop of the legacy abstract base
_BaseThicknessWorkerSide.run()
(federated/algorithms/ophthalmology/base_thickness_calculation_algorithm.py,
lines 134-273) into a single free function generic over the metric type.
Both the CST/CRT step and the GCC step share this entire orchestration — they
differ only in the per-file calculate_metric closure they pass in. The
metric-specific maths lives in each step's functions.py; the pure
retinal-layer primitives live in thickness_metrics.py. This module is the
only one that touches the datasource, so it is kept separate from the pure-math
module.
Module
Functions
run_thickness_calculation
def run_thickness_calculation( datasource: BaseSource, layer_predictions: pd.DataFrame, center_predictions: pd.DataFrame, filenames: list[str], *, center_landmark_type: str, metric_name: str, calculate_metric: Callable[[pd.Series, pd.Series], T | str | None],) ‑> dict[str, typing.Union[~T, str, NoneType]]:Calculate a per-file thickness metric from model predictions.
Ported from _BaseThicknessWorkerSide.run(). Per-instance configuration
previously read from self (the center-landmark type, the metric name
used in logs, and the _calculate_metric hook) is passed as explicit
parameters so both the CST and GCC steps reuse this unchanged.
An uncomputable file is recorded as a reason string rather than a silent
None: no datasource metadata row yields "missing_data:metadata" and
a raised exception yields "calculation_error:<detail>". A per-file
calculate_metric hook may also return its own reason string. Callers
persist a metric object to metrics_json and a reason string to error.
Arguments
datasource: File-iterable datasource providing DICOM metadata for each file (slice thickness, pixel spacing, etc.).layer_predictions: DataFrame with retinal layer segmentation predictions (onePixel_Data_*_predictioncolumn per B-scan) and an_original_filenamecolumn.center_predictions: DataFrame with predictions for the landmark centre (usually fovea or macular centre). Should containcentral_sliceandlandmarkscolumns plus_original_filename.filenames: List of files the results correspond to. If not provided (None), filenames are taken fromlayer_predictions.center_landmark_type: Nature of the center landmark ("fovea"/"macula"). Used as the merge suffix for the center predictions.metric_name: Name of the metric being calculated. Used only for log and error text.calculate_metric: Per-file metric hook, called ascalculate_metric(layer_pred, file_row). Returns the metric object, or a reason string when it cannot be computed.
Returns
Dictionary mapping each original filename to its metric object, or a
reason string (missing_data:* / calculation_error:*) when the
file could not be computed.
widen_predictions
def widen_predictions(df: pd.DataFrame) ‑> pandas.core.frame.DataFrame:Expand the packed inferences_json dict column into wide columns.
The cache stores each row's predictions as a single packed dict under
inferences_json; the thickness runner (ported from the v8 base) reads
per-field wide columns (Pixel_Data_*_prediction for retinal layers,
central_slice/landmarks for the fovea/macula centre). Mirrors the
store's CollectedModelInferenceQueryResult.as_df(). Returns df
unchanged when it has no packed column.
Shared by the CST and GCC calculation steps (both call it on their two
RAW cached frames before handing them to run_thickness_calculation).