Bits & Flames bitsandflames/fyron

BOA Extraction

fyron.boa_extraction converts Body-and-Organ-Analysis cohort folders into patient-level feature tables. Use it when a BOA run has produced folders like patient0001/, patient0002/, each containing measurement JSON files and NIfTI segmentations.

This module is designed for clinical data science workflows where imaging-derived features need to join cleanly with FHIR, outcome, survival, or ML tables. Every output column maps back to a body region, tissue, vertebral level, organ, or organ ratio.

For the combined BOA workflow overview, see BOA. For radiomics from the same masks, see BOA Radiomics. For visual review collages, see BOA Visualization.

Core Concepts

ConceptMeaning
case folderOne BOA output folder for one patient, study, or image series.
download_idCase identifier in Fyron outputs. Defaults to the case folder name unless passed explicitly.
bca-measurements.jsonBody-region/tissue measurement file used for body-composition features.
measurements_no_extremitiesBOA JSON branch used for body-region tissue volumes.
total-measurements.jsonTotal segmentation measurement file used for organ volume, HU, CNR, and ratio features.
tissues.nii.gz / body-regions.nii.gzSegmentation masks used for vertebral slice markers such as L3 SMA.
total.nii.gzTotal segmentation mask used by some workflows and visual checks.

API Contract

TopicContract
Input shapeBOA cohort root containing one case folder per patient, study, or image series.
Required columns/filesExpected BOA JSON files such as bca-measurements.json and optional segmentation NIfTI files for derived markers.
Return shapeFeature extraction returns a pandas.DataFrame keyed by download_id.
Saved artifactsCaller decides CSV/Parquet export; extraction itself returns tables and may record per-case errors.
Failure modesMissing BOA files, malformed JSON, missing segmentation masks, inconsistent case IDs, or no extractable measurements.

The body-region extractor deliberately reads tissue volumes from measurements_no_extremities:

text
aggregated -> <body_region> -> measurements_no_extremities -> <tissue> -> sum

This means columns such as abdominal_cavity_muscle_sum_ml and thoracic_cavity_vat_sum_ml exclude extremities according to the BOA JSON output. Total segmentation features are separate and come from total-measurements.json.

Research Decision: BOA Measurement Branches

DecisionPractical guidance
download_idKeep it as the stable join key between DICOM downloads, BOA folders, and clinical cohorts.
measurements_no_extremitiesUse this branch for body-composition tissue volumes when available.
total segmentationUse total-organ/body labels when the study question needs organ or region presence/volume.
derived markersTreat derived ratios and region summaries as features; record their source branch in the methods.
mergingMerge BOA features to clinical cohorts only after checking one row per download_id.

Expected Folder Layout

text
boa-cohort/
  patient0001/
    bca-measurements.json
    total-measurements.json
    tissues.nii.gz
    body-regions.nii.gz
    total.nii.gz
  patient0002/
    bca-measurements.json
    total-measurements.json
    tissues.nii.gz

The patient folder name becomes download_id by default.

If the folders were created from Fyron DICOM downloads, the folder name is often the deterministic DICOM download_id. Keep a clinical mapping table with patient ID, study UID, series UID, and download_id so BOA features can be merged back into the cohort.

Minimal Cohort Example

python
from fyron.boa_extraction import extract_boa_features

features = extract_boa_features(
    "/data/boa-cohort",
    include_body_regions=True,
    include_total=True,
    num_workers=8,
)

features.head()

Returned rows are keyed by download_id. Merge with clinical data using that key or a mapping table:

python
clinical = clinical.merge(features, on="download_id", how="left")

Imports

python
from fyron.boa_extraction import (
    DEFAULT_BODY_REGION_DERIVED_MARKERS,
    DEFAULT_VERTEBRAL_SLICE_LABELS,
    discover_boa_cases,
    extract_body_region_features,
    extract_total_segmentation_features,
    extract_experimental_aortic_calcification_features,
    extract_experimental_cohort_aortic_calcification_features,
    extract_cohort_body_region_features,
    extract_cohort_total_features,
    extract_boa_features,
)

Function Parameter Reference

discover_boa_cases

ParameterRequiredDefaultDescription
cohort_folderyesnoneRoot folder containing patient folders.
measurement_filenameno"bca-measurements.json"File used to identify completed BOA cases.

Returns a sorted list of patient folders that contain the measurement file.

extract_body_region_features

ParameterRequiredDefaultDescription
case_folder_or_measurementsyesnonePatient folder or direct path to bca-measurements.json.
body_regionsnoabdominal cavity, thoracic cavity, L1-L5Keys read from aggregated.
tissuesnobone, muscle, TAT, IMAT, EAT, PAT, VAT, SATTissue measurements read per body region.
vertebraenoL1-L6, T1-T12Vertebral levels used for SMA slices.
measurement_filenameno"bca-measurements.json"Measurement filename when a folder is passed.
sma_segmentation_filenameno("tissues.nii.gz", "body-regions.nii.gz")Mask file or fallback list used for vertebral skeletal muscle area.
sma_labelno2Label value counted as muscle.
slice_levelsno("l3",)Vertebral levels used for richer axial area markers.
slice_tissue_labelsnoSMA/IMAT/VAT/SAT defaultsTissue-label map used for axial markers.
height_mnoNoneOptional height in meters for explicit height-normalized *_cm2_per_m2 outputs. Area-based SMI, IMATI, VATI, and SATI do not require height.
include_derived_markersnoTrueWhether to calculate body-region ratio markers.
derived_markersnodefault ratio marker setAdjustable ratio definitions.
download_idnofolder nameExplicit case identifier.
include_smanoTrueWhether to calculate <vertebra>_sma columns.
missing_valuenonumpy.nanValue used when JSON keys or masks are missing.

Returns one feature dictionary or None when the measurement JSON is missing or malformed.

Default Body-Region Derived Markers

The body-region extractor now calculates configurable volumetric markers from the BOA JSON tissue volumes.

Output suffixFormulaDefault regions
sarcopenia_index_simuscle / boneall requested body regions
myosteatosis_fat_index_mfi100 * imat / tatall requested body regions
visceral_fat_index_vfivat / satabdominal_cavity
eat_pat_ratioeat / patthoracic_cavity, ventral_cavity when available

Output columns include the body-region prefix:

text
abdominal_cavity_sarcopenia_index_si
abdominal_cavity_myosteatosis_fat_index_mfi
abdominal_cavity_visceral_fat_index_vfi
thoracic_cavity_eat_pat_ratio
ventral_cavity_eat_pat_ratio

Default L3 Slice Markers

When tissues.nii.gz or another segmentation file is available, Fyron can calculate axial area markers at configured vertebral levels, usually L3.

OutputMeaning
l3_smaBackwards-compatible skeletal muscle area in cm².
l3_sma_cm2Skeletal muscle area in cm².
l3_imat_area_cm2Intramuscular adipose tissue area in cm².
l3_vat_area_cm2Visceral adipose tissue area in cm².
l3_sat_area_cm2Subcutaneous adipose tissue area in cm².
l3_smiArea-based skeletal muscle marker in cm²; no height required.
l3_imatiArea-based intramuscular adipose tissue marker in cm²; no height required.
l3_vatiArea-based visceral adipose tissue marker in cm²; no height required.
l3_satiArea-based subcutaneous adipose tissue marker in cm²; no height required.
l3_smi_cm2_per_m2Height-normalized skeletal muscle area, only when height_m is passed.
l3_imati_cm2_per_m2Height-normalized IMAT area, only when height_m is passed.
l3_vati_cm2_per_m2Height-normalized VAT area, only when height_m is passed.
l3_sati_cm2_per_m2Height-normalized SAT area, only when height_m is passed.

The default tissue labels are adjustable:

python
DEFAULT_VERTEBRAL_SLICE_LABELS
# {"sma": 2, "imat": 4, "vat": 7, "sat": 8}

extract_total_segmentation_features

ParameterRequiredDefaultDescription
case_folderyesnonePatient folder containing total-measurements.json.
labelsnocommon BOA/TotalSegmentator organsOrgan names to extract from segmentations.total.
ratiosnospleen/liverOrgan volume ratios to compute.
measurement_filenameno"total-measurements.json"Total measurements filename.
download_idnofolder nameExplicit case identifier.
round_digitsno2Decimal rounding for numeric outputs.
missing_valuenonumpy.nanValue used when metrics are missing.

Outputs <organ>_cnr, <organ>_total, <organ>_hu, and ratio columns such as spleen_liver_ratio.

extract_experimental_aortic_calcification_features

This extractor is experimental. It belongs in fyron.boa_extraction because it derives tabular features from BOA-style CT masks, but aorta and iliac mask names can differ between segmentation pipelines. Always validate the masks and label IDs on a small sample before using the output in a study.

ParameterRequiredDefaultDescription
case_folder_or_masks_diryesnonePatient folder or mask folder.
ct_pathnoauto-detect common CT filenamesCT NIfTI in HU. Pass this explicitly when the CT is outside the mask folder.
masks_dirnocase_folder_or_masks_dirFolder containing BOA masks.
download_idnofolder nameExplicit case identifier.
hu_thresholdno130.0HU threshold used to count calcified voxels.
prefixno"aortic_calcification"Prefix for all output columns.
aorta_mask_filenamesno("aorta.nii.gz",)Binary aorta masks tried first.
total_mask_filenamesno("total.nii.gz",)Fallback multiclass total masks.
total_aorta_labelsno(52,)Label IDs counted as aorta in total.nii.gz; verify against your local label map.
thoracic_mask_filenameno"thoracic_cavity.nii.gz"Optional binary thoracic mask.
abdominal_mask_filenameno"abdominal_cavity.nii.gz"Optional binary abdominal mask.
body_region_mask_filenameno"body-regions.nii.gz"Multiclass body-region fallback.
body_region_labelsnothoracic 3, abdominal 4Body-region labels used when separate masks are absent.
iliac_globno"*iliac*.nii.gz"Glob used to discover iliac masks.
include_iliacnoTrueAdd iliac-extension and total-aortoiliac features.
missing_valuenonumpy.nanValue for unavailable optional regions.

Returns one flat feature dictionary or None when CT or aorta inputs are unavailable.

Generated feature groups include:

text
aortic_calcification_total_aorta_calcified_volume_ml
aortic_calcification_total_aorta_calcium_voxel_count
aortic_calcification_total_aorta_calcium_max_hu
aortic_calcification_total_aorta_calcium_mean_hu
aortic_calcification_total_aorta_normalized_calcified_volume
aortic_calcification_thoracic_aorta_*
aortic_calcification_abdominal_aorta_*
aortic_calcification_iliac_extension_*
aortic_calcification_total_aortoiliac_*

Batch Helpers

FunctionPurpose
extract_cohort_body_region_features(cohort_folder_or_cases, num_workers=1, show_progress=True, **kwargs)Body-region and SMA features for many patients.
extract_cohort_total_features(cohort_folder_or_cases, num_workers=1, show_progress=True, **kwargs)Organ features for many patients.
extract_experimental_cohort_aortic_calcification_features(cohort_folder_or_cases, num_workers=1, show_progress=True, **kwargs)Experimental aortic calcification features for many patients.
extract_boa_features(cohort_folder_or_cases, include_body_regions=True, include_total=True, ...)Extract both feature families and outer-merge on download_id.

cohort_folder_or_cases may be a cohort root folder, a single patient folder, or a list of patient folders. Set num_workers above 1 for multiprocessing.

Return Values

Output familyExample columnsSource
Body-region volumesabdominal_cavity_muscle_sum_ml, thoracic_cavity_vat_sum_mlbca-measurements.json measurements_no_extremities
Derived ratiosabdominal_cavity_sarcopenia_index_si, thoracic_cavity_eat_pat_ratiocomputed from body-region volume columns
Vertebral slice markersl3_sma_cm2, l3_imat_area_cm2, l3_vatisegmentation masks and image spacing
Total organ metricsliver_total, liver_hu, liver_cnrtotal-measurements.json
Organ ratiosspleen_liver_ratiocomputed from total organ metrics
Experimental calcificationaortic_calcification_total_aorta_calcified_volume_mlCT volume and vascular masks

CLI Workflow

Use the CLI for cohort-wide extraction jobs that should produce a single auditable CSV.

bash
fyron boa-extract \
  --cohort-folder /data/boa-cohort \
  --output boa_features.csv \
  --include-body-regions \
  --include-total \
  --include-aortic-experimental \
  --num-workers 8

Flags map to feature families:

FlagAdds
--include-body-regionstissue volumes, derived ratios, L3 SMA/IMATI/VATI markers
--include-totalorgan volumes, HU, CNR, organ ratios
--include-aortic-experimentalexperimental aortic/iliac calcification features

Discover Completed Cases

python
from fyron.boa_extraction import discover_boa_cases

cases = discover_boa_cases("/data/boa-cohort")
len(cases)
cases[:3]

Extract One Patient

python
from fyron.boa_extraction import (
    extract_body_region_features,
    extract_total_segmentation_features,
)

case = "/data/boa-cohort/patient0001"

body_row = extract_body_region_features(
    case,
    include_sma=True,
    sma_segmentation_filename=("tissues.nii.gz", "body-regions.nii.gz"),
    sma_label=2,
    slice_levels=("l3",),
)

total_row = extract_total_segmentation_features(case)

Use single-case extraction when validating a new BOA export format. Inspect body_row.keys() and compare a few values against the original JSON before launching a full cohort job.

Experimental Aortic Calcification Extraction

Use this when a BOA-style folder contains a CT volume in HU and vascular masks. Fyron first tries aorta.nii.gz; if that is missing, it can use total.nii.gz with configurable aorta labels. Thoracic and abdominal aortic burden is calculated by intersecting the aorta mask with thoracic/abdominal body-region masks when available. Iliac-extension burden is calculated from masks matching *iliac*.nii.gz.

python
from fyron.boa_extraction import extract_experimental_aortic_calcification_features

row = extract_experimental_aortic_calcification_features(
    case_folder_or_masks_dir="/data/boa-cohort/patient0001",
    ct_path="/data/boa-cohort/patient0001/ct.nii.gz",
    masks_dir="/data/boa-cohort/patient0001",
    hu_threshold=130,
    total_aorta_labels=(52,),
)

For a cohort:

python
from fyron.boa_extraction import extract_experimental_cohort_aortic_calcification_features

aortic = extract_experimental_cohort_aortic_calcification_features(
    "/data/boa-cohort",
    num_workers=8,
    show_progress=True,
)

aortic.to_csv("experimental_aortic_calcification_features.csv", index=False)

Before modeling, inspect several cases visually with the BOA visualization tools and check that aorta.nii.gz, total.nii.gz, and any iliac masks use the expected label convention.

Extract A Cohort Table

python
from fyron.boa_extraction import extract_boa_features

features = extract_boa_features(
    "/data/boa-cohort",
    num_workers=8,
    show_progress=True,
)

features.to_csv("boa_features.csv", index=False)

The returned DataFrame contains one row per download_id. Body-region columns follow this pattern:

text
abdominal_cavity_muscle_sum_ml
thoracic_cavity_vat_sum_ml
l3_sma
l3_sma_cm2
l3_imati
l3_vati
abdominal_cavity_sarcopenia_index_si
abdominal_cavity_myosteatosis_fat_index_mfi
abdominal_cavity_visceral_fat_index_vfi
thoracic_cavity_eat_pat_ratio

Total segmentation columns follow this pattern:

text
liver_total
liver_hu
liver_cnr
spleen_liver_ratio

Customize Labels And Feature Families

python
features = extract_boa_features(
    "/data/boa-cohort",
    body_region_kwargs={
        "body_regions": ["abdominal_cavity", "thoracic_cavity", "l3"],
        "tissues": ["bone", "muscle", "tat", "imat", "eat", "pat", "vat", "sat"],
        "vertebrae": ["l3"],
        "sma_segmentation_filename": ("tissues.nii.gz", "body-regions.nii.gz"),
        "sma_label": 2,
        "slice_levels": ("l3",),
        "slice_tissue_labels": {
            "sma": 2,
            "imat": 4,
            "vat": 7,
            "sat": 8,
        },
    },
    total_kwargs={
        "labels": {"spleen": 1, "liver": 5, "pancreas": 10},
        "ratios": [("spleen", "liver")],
    },
    num_workers=8,
)

This is useful when you want a compact feature table for modeling instead of the full default set.

Customize Derived Markers

Derived markers are plain dictionaries. This makes the feature set easy to adjust when a project uses different body-region names or wants only selected formulas. Use multiplier for scaled indices such as percent-style fat indices.

python
custom_markers = [
    {
        "name": "sarcopenia_index_si",
        "numerator": "muscle",
        "denominator": "bone",
        "regions": ["l3", "abdominal_cavity"],
    },
    {
        "name": "eat_pat_ratio",
        "numerator": "eat",
        "denominator": "pat",
        "regions": ["thoracic_cavity", "ventral_cavity"],
    },
    {
        "name": "myosteatosis_fat_index_mfi",
        "numerator": "imat",
        "denominator": "tat",
        "regions": None,
        "multiplier": 100,
    },
]

row = extract_body_region_features(
    case,
    body_regions=["thoracic_cavity", "ventral_cavity", "abdominal_cavity", "l3"],
    tissues=["bone", "muscle", "tat", "imat", "eat", "pat"],
    derived_markers=custom_markers,
)

Set include_derived_markers=False when you only want raw tissue volumes.

Join With Clinical Data

python
clinical = clinical.merge(features, on="download_id", how="left")

After joining, use fyron.cohort to validate patient identifiers and build survival columns, or use fyron.ml for classification workflows.