autods_pet.imaging#

Imaging subpackage: DICOM, normalization, registration, segmentation.

autods_pet.imaging.compute_suvbw(pet_bqml, weight_kg, dose_bq)[source]#

Convert a PET image from Bq/mL to SUV body-weight.

Parameters:
  • pet_bqml (Image) – PET image in Bq/mL units.

  • weight_kg (float) – Patient body weight in kg.

  • dose_bq (float) – Effective injected dose in Bq (after decay correction if needed).

Returns:

PET image in SUVbw units (float64).

Return type:

Image

autods_pet.imaging.decay_dose(dose_bq, half_life_s, elapsed_s)[source]#

Apply radioactive decay: dose * exp(-lambda * t).

Parameters:
  • dose_bq (float) – Initial dose in Bq.

  • half_life_s (float) – Radionuclide half-life in seconds.

  • elapsed_s (float) – Time elapsed since injection in seconds.

Returns:

Decayed dose in Bq.

Return type:

float

Examples

>>> from autods_pet.imaging.normalization import decay_dose
>>> round(decay_dose(370e6, 6586.2, 3600), 1)
253314180.2
autods_pet.imaging.dicom_series_to_nifti(dicom_files, output_path)[source]#

Convert a DICOM series to a NIfTI file.

Uses SimpleITK’s ImageSeriesReader with GDCM-based spatial ordering to ensure correct slice order.

Parameters:
  • dicom_files (Sequence[str | Path]) – File paths belonging to a single DICOM series.

  • output_path (str | Path) – Where to write the NIfTI file.

Returns:

The loaded image (also written to output_path).

Return type:

Image

autods_pet.imaging.effective_dose(total_dose_bq, half_life_s, elapsed_s, decay_correction)[source]#

Determine the effective reference dose depending on the DICOM DecayCorrection tag.

Parameters:
  • total_dose_bq (float) – Injected radionuclide dose in Bq.

  • half_life_s (float) – Radionuclide half-life in seconds.

  • elapsed_s (float) – Seconds between injection and acquisition.

  • decay_correction (str) – DICOM DecayCorrection value (e.g. "NONE", "START", "ADMIN").

Returns:

Dose in Bq to use as the SUV denominator.

Return type:

float

Notes

The denominator must be the activity present in the patient at the same reference time the pixel values are corrected to:

ADMIN (and the non-standard YES/CORRECTED) means the pixels are decay corrected back to the injection time, so the raw injected dose is already the activity at that reference time.

START means the pixels are decay corrected to the acquisition start time, the dominant Siemens and GE whole-body FDG convention. The reference activity is then the injected dose decayed from injection to acquisition.

NONE (and unknown values, handled conservatively the same way) means the pixels hold the activity measured at acquisition, so the dose must likewise be decayed to that time.

autods_pet.imaging.extract_ct_tags(dicom_path)[source]#

Extract CT metadata tags from a single DICOM file.

Returns:

CT-relevant tags. Missing tags are set to None.

Return type:

dict[str, Any]

Parameters:

dicom_path (str | Path)

autods_pet.imaging.extract_patient_weight(dicom_path)[source]#

Extract patient weight (kg) from a DICOM file.

Returns:

Weight in kg, or None if the tag is absent or zero.

Return type:

float | None

Parameters:

dicom_path (str | Path)

autods_pet.imaging.extract_pet_tags(dicom_path)[source]#

Extract PET metadata tags from a single DICOM file.

Handles nested RadiopharmaceuticalInformationSequence with top-level fallback for dose and half-life tags.

Returns:

Keys match the metadata expected by normalize_pet(): PatientID, StudyDate, AcquisitionTime, Units, DecayCorrection, RadiopharmaceuticalStartTime, RadiopharmaceuticalStartDateTime, RadionuclideTotalDose, RadionuclideHalfLife, SeriesInstanceUID, StudyInstanceUID. Missing tags are set to None.

Return type:

dict[str, Any]

Parameters:

dicom_path (str | Path)

Notes

RadiopharmaceuticalStartDateTime (0018,1078) carries the full date and time, so it survives multi-day protocols and is the only injection timestamp some Enhanced PET objects populate. It is read from the nested sequence first and from the top level as a fallback.

autods_pet.imaging.find_dicom_files(patient_dir, size_threshold_kb=100)[source]#

Recursively find DICOM files above a size threshold.

Matching is case-insensitive on the .dcm extension and also accepts extensionless (or oddly named) files that carry the Part 10 DICM magic, both of which are common in PACS exports.

Parameters:
  • patient_dir (str | Path) – Root directory to search.

  • size_threshold_kb (int (default: 100)) – Minimum file size in KB. Files smaller than this are skipped (e.g. tiny overlay or presentation-state files).

Returns:

Sorted list of matching file paths.

Return type:

list[Path]

autods_pet.imaging.find_series_by_modality(patient_dir, size_threshold_kb=100)[source]#

Find the primary CT and PT DICOM series in a patient directory.

Groups .dcm files by (Modality, SeriesInstanceUID), then picks the series with the most files for each modality.

Note

This function reads DICOM headers for every .dcm file sequentially (O(n) disk reads, no parallelism). For directories with 1000+ files on network-attached storage this may be slow. Consider parallel header reads for high-throughput deployments.

Returns:

{"CT": [Path, ...], "PT": [Path, ...]}. Empty lists for modalities that are not found.

Return type:

dict[str, list[Path]]

Parameters:
  • patient_dir (str | Path)

  • size_threshold_kb (int)

autods_pet.imaging.parse_dicom_date(dstr)[source]#

Parse a DICOM DA string (YYYYMMDD) into a datetime.date.

Parameters:

dstr (str) – DICOM DA-format string (e.g. "20230415").

Return type:

date

Raises:

ValueError – If dstr is too short or contains invalid month/day.

Examples

>>> from autods_pet.imaging.normalization import parse_dicom_date
>>> parse_dicom_date("20230415")
datetime.date(2023, 4, 15)
autods_pet.imaging.parse_dicom_time(tstr)[source]#

Parse a DICOM TM string into a datetime.time.

Handles missing leading zeros, fractional seconds, and hours >= 24 (wrapped modulo 24). Out-of-range minutes and seconds are clamped to 59 with a warning, so a leap second (ss == 60, permitted by the DICOM TM value representation) or a malformed field does not abort a run mid-cohort.

Parameters:

tstr (str) – DICOM TM-format string (e.g. "143012.123456").

Return type:

time

Raises:

ValueError – If tstr is empty.

Examples

>>> from autods_pet.imaging.normalization import parse_dicom_time
>>> parse_dicom_time("143012.000000")
datetime.time(14, 30, 12)
>>> parse_dicom_time("090000")
datetime.time(9, 0)
>>> parse_dicom_time("143060")  # leap second
datetime.time(14, 30, 59)
autods_pet.imaging.resolve_patient_folder(basepath, patient_id)[source]#

Case-insensitive lookup for a patient subfolder.

Returns the resolved Path or None if no match is found.

Return type:

Path | None

Parameters:
autods_pet.imaging.rigid_register_pet_to_ct(ct, pet, log_to_console=False, report_path=None)[source]#

Rigidly register pet onto the ct grid.

Parameters:
  • ct (Image) – CT image (fixed / reference frame).

  • pet (Image) – PET image (moving).

  • log_to_console (bool (default: False)) – If True, print Elastix iteration logs to stdout.

  • report_path (Path | None (default: None)) – If set, Elastix writes TransformParameters.0.txt to report_path.parent and it is renamed to report_path.

Returns:

PET resampled onto the CT grid (float64).

Return type:

Image

autods_pet.imaging.run_totalsegmentator(ct_path, output_dir, task='total', fast=False, output_filename=None)[source]#

Run TotalSegmentator on a CT image.

Uses the --ml flag to produce a single multilabel NIfTI file named TOTSEG_FILENAME inside output_dir.

Note

Paths are passed as subprocess arguments (list form, no shell). This mitigates OS command injection (CWE-78), but path traversal (CWE-22) is not validated. If this function is ever exposed via a network service, add explicit path validation.

Parameters:
  • ct_path (Path) – Path to the input CT NIfTI file.

  • output_dir (Path) – Directory where the segmentation output will be written.

  • task (str (default: 'total')) – TotalSegmentator task name (default "total").

  • fast (bool (default: False)) – If True, use the --fast flag for lower resolution but faster inference.

  • output_filename (str | None (default: None)) – Custom filename for the output segmentation file. When None (default), uses TOTSEG_FILENAME.

Returns:

Path to the output segmentation file.

Return type:

Path

autods_pet.imaging.seconds_between(injection, acquisition, max_uptake_hours=6.0)[source]#

Seconds elapsed from injection to acquisition, handling midnight wrap.

If the elapsed time is slightly negative (0 to -max_uptake_hours), assumes the acquisition crossed midnight and adds 24 h. If more negative than -max_uptake_hours, still corrects but logs a warning.

Parameters:
  • injection (datetime) – Radiopharmaceutical injection time.

  • acquisition (datetime) – PET acquisition time.

  • max_uptake_hours (float (default: 6.0)) – Maximum expected injection-to-scan interval in hours. Negative elapsed times within this window are assumed to be midnight wraparounds. The default of 6 h is appropriate for standard 18F-FDG protocols (typical uptake ~60 min). Increase for tracers with longer uptake periods (e.g. 68Ga-DOTATATE).

Returns:

Elapsed seconds (always >= 0 after midnight correction).

Return type:

float

Examples

>>> from datetime import datetime
>>> from autods_pet.imaging.normalization import seconds_between
>>> seconds_between(datetime(2023, 1, 1, 9, 0), datetime(2023, 1, 1, 10, 0))
3600.0