# Spatial SUV Characterization Utilities for exploratory analysis of spatial SUV distributions in PET NIfTI images. The main goal is to investigate whether high-SUV spatial patterns contain biomarker information beyond simple scalar summaries such as SUV percentiles. --- ## Repository structure ```text . ├── data/ │ ├── raw/ # raw PET and segmentation NIfTI files │ └── gen/ # generated tables and outputs ├── notebooks/ # exploratory notebooks ├── notes/ # methodological notes ├── shiny_app/ # interactive Shiny app ├── spatial_suv_charact/ # Python package ├── pyproject.toml └── README.md ```` --- ## Package modules ```text metadata.py -> dataframe / patient metadata utilities image_io.py -> NIfTI loading and segmentation application suv_stats.py -> simple SUV distribution features spatial_features.py -> spatial tail-region features plotting.py -> Plotly visualization ``` --- ## Installation From the repository root: ```bash pip install -e . ``` If needed: ```bash pip install -r spatial_suv_charact/requirements.txt ``` --- ## Basic usage ```python from pathlib import Path from spatial_suv_charact.metadata import ( get_meta_data, flag_corrupted_files, flag_AE_patients, ) from spatial_suv_charact.image_io import get_processed_image from spatial_suv_charact.suv_stats import get_suv_percentiles from spatial_suv_charact.spatial_features import compute_tail_spatial_features from spatial_suv_charact.plotting import plot_suv_pdf_plotly, plot_hot_voxels_plotly ``` Collect metadata: ```python DATA_RAW = Path("data/raw") df_meta = get_meta_data(DATA_RAW) df_meta = flag_corrupted_files(df_meta) df_meta = flag_AE_patients(df_meta) ``` Load one processed image: ```python patient_id = "NIX-LJU-D2002-IRAE-A001" organ = "Lung" visit = 0 row, processed_image = get_processed_image( df_meta, patient_id=patient_id, organ=organ, visit=visit, ) ``` Compute SUV percentiles: ```python probs = (80, 90, 95) df_perc = get_suv_percentiles( processed_image, percentiles=probs, ) ``` Plot SUV distribution: ```python fig, suv_percentiles = plot_suv_pdf_plotly( processed_image, percentiles=probs, bins=100, log_x=True, min_suv=0.1, ) fig.show() ``` Compute spatial features: ```python features = compute_tail_spatial_features( image=processed_image, percentiles=probs, component_connectivity=26, contrast_connectivity=26, min_component_voxels=3, compute_spread=True, compute_local_contrast=True, compute_sphericity=True, crop_to_roi=True, image_id=(patient_id, organ, visit), ) features ``` --- ## Spatial features For a percentile threshold (q), the high-SUV tail region is $$ R_q = {v : SUV(v) \ge Q_q}, $$ where $Q_q$ is the $q$-th percentile of SUV values inside the processed image. The package computes features such as: * tail mean, median, max and coefficient of variation, * tail excess above percentile threshold, * number of connected components, * largest-component fraction, * component entropy, * local contrast, * spatial spread, * SUV-weighted spatial spread, * optional largest-component sphericity. These features are intended to describe intensity, heterogeneity, fragmentation, and spatial organization of high-SUV uptake. --- ## Shiny app The Shiny app is in: ```text shiny_app/app.py ``` Run from the repository root: ```bash shiny run --reload shiny_app/app.py ``` The app allows interactive inspection of: * metadata table rows, * SUV distribution plots, * hot-voxel plots, * computed spatial features. --- ## Notes Methodological notes on testing candidate spatial metrics are in: ```text notes/testing_spatial_metric.md ``` --- ## Development notes Recommended cleanup before committing: ```bash find . -type d -name "__pycache__" -prune -exec rm -rf {} + find . -type f -name "*.pyc" -delete ``` Suggested `.gitignore` entries: ```gitignore __pycache__/ *.pyc .ipynb_checkpoints/ data/raw/ *.nii *.nii.gz .DS_Store ``` Generated outputs in `data/gen/` can be ignored or committed selectively, depending on size and reproducibility needs. --- ## Status This project is exploratory and intended for biomarker discovery. Candidate metrics should be validated carefully, especially when the number of AE cases is small.