Bits & Flames bitsandflames/fyron

BOA Visualization

fyron.visualization provides medical imaging figures for clinical review, reports, and publication figures. It includes BOA segmentation collages, CT/MR Curate preview preparation, and aligned CT/MR volume-difference heatmaps.

For the combined BOA workflow overview, see BOA. For feature-table extraction from the same folders, see BOA Extraction. For local image I/O and normalization, see Imaging.

Use this module when the clinical question benefits from seeing anatomy and segmentation context together. The collage helpers are designed for review boards, quality control, and report figures where several patients or segmentation layers need to be compared in one visual artifact.

Before building a final figure, confirm orientation, slice position, windowing, and label colors on a small subset. A visually clean collage is only useful if each row represents the intended patient, slice, and segmentation layer.

Install

bash
uv add "fyron[visualization]"

This installs OpenCV for rendering. NIfTI I/O uses the imaging stack already included with Fyron.

Imports

python
from fyron.visualization import (
    SegmentationLayer,
    create_boa_segmentation_collage,
    prepare_curate_image_previews,
    plot_volume_difference_heatmap,
)

API Contract

TopicContract
Input shapeBOA folder paths for collages; CT/MR image directories for Curate previews; NIfTI paths, SimpleITK.Image, or NumPy volumes for difference heatmaps.
Required columns/filesCollages need CT and segmentation files; heatmaps need two same-shape and same-geometry volumes.
Return shapeCollage helpers write an image and return metadata; heatmaps return (fig, axes, table).
Saved artifactsPNG collage/review images, Curate preview folders, CSV manifests, volume-difference figures, and slice-level difference summary tables.
Failure modesMissing files, incompatible geometry, unsupported orientation, invalid slice selection, failed image loading, or misleading comparisons without prior alignment.

Function Parameter Reference

The tables below distinguish required inputs from optional configuration through the Required column.

SegmentationLayer

ParameterRequiredDefaultDescription
kindyesnoneOne of body_regions, tissues, or total.
display_modenolayer defaultct, ct_overlay, or segmentation.
titlenodefault layer titleColumn title.
filenamenodefault BOA filenameOverride mask filename.
opacitynoglobal opacityPer-layer overlay opacity.

create_boa_segmentation_collage

ParameterRequiredDefaultDescription
boa_foldersyesnoneOne BOA output folder per patient.
output_pathyesnoneDestination image path.
orientationno"axial"axial, sagittal, or coronal.
segmentation_layersnodefault layersStrings or SegmentationLayer objects.
patient_labelsnoPatient A, ...Row labels.
slice_indexnocenterAbsolute slice index.
slice_indicesnoNoneMultiple absolute slices.
slice_fractionnoNoneRelative slice position in [0, 1].
ct_filenamenoauto-detectExplicit CT file in each folder.
window_centerno40.0CT window center.
window_widthno400.0CT window width.
segmentation_opacityno0.45Global overlay opacity.
overlay_alphanoNoneDeprecated alias for opacity.
panel_widthno320Width of each panel in pixels.
row_label_prefixnoorientation labelPrefix for multi-slice labels.
crop_body_regionnoNoneBOA region id/name for automatic crop.
body_region_padding_mmno15.0Crop padding in millimetres.
body_region_slicesno"best"best, center, or all.

Returns a metadata dict and writes the image to output_path.

prepare_curate_image_previews

ParameterRequiredDefaultDescription
input_diryesnoneDirectory containing CT/MRI volumes, DICOM series, or 2-D images.
output_diryesnoneCurate-ready folder for PNG previews.
recursivenoTrueScan nested folders.
include_dicomnoTrueDiscover DICOM series below the input directory.
include_image_filesnoTrueRead NIfTI/MHA/NRRD and common 2-D raster files.
panel_widthno384Output PNG width.
window_center, window_widthnoNoneOptional fixed CT-style window.
percentile_clipno(1.0, 99.0)Robust auto-windowing for CT/MRI when no fixed window is passed.
overwritenoFalseOverwrite existing PNG previews.
manifest_csvnooutput_dir/manifest.csvPreview manifest path.

Returns a metadata dict and writes PNG previews plus a CSV manifest.

plot_volume_difference_heatmap

ParameterRequiredDefaultDescription
volume_a, volume_byesnoneNIfTI path, SimpleITK.Image, or NumPy array in (z, y, x) order.
data_typeno"ct_hu""ct_hu", "ct_uint16", or "mr".
orientationno"axial"axial, sagittal, or coronal.
slice_indexnocenterOne absolute slice index.
slice_indicesnoNoneMultiple absolute slices.
slice_fractionnoNoneOne relative slice position in [0, 1].
label_a, label_bnoVolume A, Volume BColumn labels.
window_center, window_widthno40.0, 400.0CT display window.
rescale_slope, rescale_interceptno1.0, -1024.0Stored CT value to HU conversion for ct_uint16.
mr_normalization, mr_percentilenopercentile, 99.0MR scaling before difference calculation.
diff_vmaxnoautoSymmetric absolute limit for the difference map.
diff_percentileno99.0Percentile used when diff_vmax is automatic.
diff_cmapnofyron_diverging_blue_redMatplotlib or Fyron diverging colormap.
figsize, title, save_pathnoNoneLayout and optional export settings.

Returns (fig, axes, table). The table has one row per rendered slice with difference summaries such as mean difference, mean absolute difference, RMSE, min/max difference, and the symmetric heatmap limit.

What The Collage Shows

The collage layout is optimized for comparing patients and mask layers:

  • columns represent segmentation layers, such as body regions, tissues, and organs,
  • rows represent patients or selected slices,
  • patient labels are displayed on the row axis,
  • CT backgrounds can be windowed and overlaid with segmentation colors,
  • voxel spacing is respected when sizing panels,
  • optional body-region cropping keeps anatomy framed consistently.

Expected BOA Folder

Each patient folder should contain a CT NIfTI and one or more mask files:

FileMeaning
CT NIfTIAuto-detected as the largest non-mask NIfTI unless ct_filename is passed
body-regions.nii.gzBOA body region labels
tissues.nii.gztissue labels
total.nii.gzorgan / TotalSegmentator-style labels

The CT and mask arrays must have matching shapes.

Basic Multi-Patient Collage

python
result = create_boa_segmentation_collage(
    boa_folders=["/data/patient_a", "/data/patient_b"],
    output_path="figures/boa_axial.png",
    orientation="axial",
    segmentation_layers=["body_regions", "tissues", "total"],
    patient_labels=["Patient A", "Patient B"],
    slice_fraction=0.5,
)

result["output_path"]
result["image_shape"]

The function returns metadata including output path, patient paths, layers used, orientation, image shape, and selected slice indices.

Curate Preview Preparation

Use prepare_curate_image_previews when a folder of CT/MRI volumes should be turned into PNGs that can be dropped directly into Fyron Curate. 3-D volumes produce middle axial, coronal, and sagittal slices. 2-D images such as X-rays or topograms produce one preview.

python
from fyron.visualization import prepare_curate_image_previews

result = prepare_curate_image_previews(
    input_dir="/data/ct_exports",
    output_dir="curate_previews",
    recursive=True,
    panel_width=384,
)

result["manifest_csv"]
result["images_written"]

The equivalent CLI command is:

bash
fyron dataset-curate-previews --input-dir /data/ct_exports --output-dir curate_previews

After the previews are written, launch the review app and drop the generated folder into the Add cohort dialog:

bash
fyron curate

The generated manifest.csv is for traceability; Curate indexes the PNG files themselves and preserves their relative folder paths.

Layer Configuration

Use strings for common defaults:

python
create_boa_segmentation_collage(
    ["/data/patient_a"],
    "boa_default.png",
    segmentation_layers=["body_regions", "tissues", "total"],
)

Use SegmentationLayer for display modes and opacity:

python
create_boa_segmentation_collage(
    ["/data/patient_a"],
    "boa_custom.png",
    segmentation_layers=[
        SegmentationLayer("body_regions", display_mode="ct_overlay", opacity=0.55),
        SegmentationLayer("tissues", display_mode="ct_overlay", opacity=0.4),
        SegmentationLayer("total", display_mode="segmentation"),
    ],
    slice_index=90,
)

Display modes:

ModeUse
ctCT only, useful as an anatomical reference
ct_overlayCT with semi-transparent segmentation
segmentationsegmentation colors without CT background

Slice Selection

Choose one absolute slice:

python
create_boa_segmentation_collage(
    ["/data/patient_a"],
    "slice_90.png",
    orientation="axial",
    slice_index=90,
)

Choose relative position per patient:

python
create_boa_segmentation_collage(
    ["/data/patient_a", "/data/patient_b"],
    "middle_slice.png",
    orientation="axial",
    slice_fraction=0.5,
)

Choose multiple slices:

python
create_boa_segmentation_collage(
    ["/data/patient_a"],
    "multi_slice.png",
    orientation="coronal",
    slice_indices=[80, 100, 120],
)

Body-Region Cropping

Cropping is useful when the clinical figure should focus on one anatomical region.

python
create_boa_segmentation_collage(
    ["/data/patient_a", "/data/patient_b"],
    "abdomen.png",
    orientation="axial",
    crop_body_region="Abdominal Cavity",
    body_region_padding_mm=20,
    body_region_slices="best",
)

body_region_slices options:

OptionBehavior
bestslice with largest in-plane region area
centermiddle slice of the region extent
allevery slice containing the region

When crop_body_region is used, do not pass slice_index, slice_indices, or slice_fraction; slices are selected automatically.

CT Windowing And Opacity

python
create_boa_segmentation_collage(
    ["/data/patient_a"],
    "lung_window.png",
    window_center=-600,
    window_width=1500,
    segmentation_opacity=0.35,
)

Per-layer opacity overrides the global segmentation_opacity.

Output Metadata

python
result = create_boa_segmentation_collage(...)

{
    "output_path": result["output_path"],
    "patient_paths": result["patient_paths"],
    "slice_indices_used": result["slice_indices_used"],
    "orientation": result["orientation"],
    "image_shape": result["image_shape"],
}

Use this metadata to track exactly which slices and layers were used in a manuscript figure.

Volume Difference Heatmaps

Use plot_volume_difference_heatmap when two already-aligned volumes should be visually compared slice by slice. Typical use cases include GAN output review, reconstruction checks, registration QC after explicit resampling, and model-output comparison against a reference volume.

The figure always has three columns:

ColumnMeaning
Volume AReference or baseline volume.
Volume BComparison volume.
DifferenceVolume A - Volume B, shown with a zero-centered diverging heatmap.
python
from fyron.visualization import plot_volume_difference_heatmap

fig, axes, diff_table = plot_volume_difference_heatmap(
    "reference_ct.nii.gz",
    "generated_ct.nii.gz",
    data_type="ct_hu",
    orientation="axial",
    slice_indices=[70, 90, 110],
    label_a="Reference CT",
    label_b="Generated CT",
    window_center=40,
    window_width=400,
    title="CT difference review",
    save_path="figures/ct_difference.png",
)

diff_table.to_csv("figures/ct_difference_summary.csv", index=False)

CT HU, CT UInt16, And MR Inputs

Use data_type="ct_hu" when both arrays are already in Hounsfield units. Use data_type="ct_uint16" for stored integer CT values that need conversion to HU:

python
plot_volume_difference_heatmap(
    stored_ct_a,
    stored_ct_b,
    data_type="ct_uint16",
    rescale_slope=1.0,
    rescale_intercept=-1024.0,
)

Use data_type="mr" for MRI volumes. Fyron normalizes each volume to [0, 1] before computing the difference:

python
plot_volume_difference_heatmap(
    "baseline_mr.nii.gz",
    "followup_mr.nii.gz",
    data_type="mr",
    mr_normalization="percentile",
    mr_percentile=99,
)

Geometry Requirement

V1 is deliberately strict. Volumes must have the same shape. When both inputs carry SimpleITK/NIfTI geometry, spacing, origin, and direction must also match. Fyron does not auto-resample inside the plot function because interpolation choices are clinical assumptions. Resample explicitly before plotting and keep that operation in your provenance manifest.

Interpreting The Difference Map

The difference panel is centered at 0: one color direction means Volume A is brighter/higher, the other means Volume B is brighter/higher. For CT this is in HU. For MR this is normalized intensity. Use diff_vmax to lock a fixed scale across several cases or let Fyron estimate a robust symmetric scale from diff_percentile.

Troubleshooting

ProblemLikely causeFix
CT file not foundfolder contains no non-mask NIfTI or ambiguous filespass ct_filename
shape mismatchCT and masks are not in the same geometryresample/align masks before rendering
region crop emptybody-region label is absentchoose another region or inspect body-regions.nii.gz
image too small/largedefault panel_width is not right for the outputset panel_width
overlay too strongopacity too highlower segmentation_opacity or layer opacity
difference heatmap geometry errorvolumes are not already alignedresample explicitly before plotting
difference colors look saturatedautomatic diff_vmax is too small for outlierspass a fixed diff_vmax