--- license: cc-by-nc-4.0 library_name: pytorch pipeline_tag: image-segmentation datasets: - VictorButoi/flexray-data tags: - medical-image-segmentation - x-ray - radiograph - anatomy - flexray --- # FleXray: `VictorButoi/flexray` - Website and in-browser demo: [victorbutoi.github.io/FleXray](https://victorbutoi.github.io/FleXray/) - Code: [github.com/VictorButoi/FleXray](https://github.com/VictorButoi/FleXray) - Data: [`VictorButoi/flexray-data`](https://huggingface.co/datasets/VictorButoi/flexray-data) - Tutorial: [Colab notebook](https://colab.research.google.com/drive/1MNIeN9LN-tY8wAifWSolxmFZi3Q0BlVX) - Paper: *FleXray: Flexible Full-Body X-ray Segmentation* (coming soon) FleXray is a single 2D UNet that segments anatomy from standard radiographs across body regions, projections, and acquisition settings. It predicts 60 anatomical structures (plus background) as independent sigmoid channels at 256 x 256 resolution. This repository holds the **flagship model** and the four sibling models of the FleXray ensemble, one bundle per `members/` subfolder (see [Repository layout](#repository-layout)). `flexify` and `FleXraySegmenter.from_pretrained` load the flagship by default. ## Quick start ```bash python -m pip install flexray flexify --input ./image.png --output-dir ./predictions ``` ```python from fxr.inference import FleXraySegmenter segmenter = FleXraySegmenter.from_pretrained("VictorButoi/flexray") prediction = segmenter.predict("./image.png", threshold=0.5) prediction.masks # uint8, CxHxW thresholded masks prediction.probabilities # float32, CxHxW sigmoid probabilities prediction.logits # float32, CxHxW raw scores ``` `flexify` writes `_masks.npy`, `_probabilities.npy`, and `_logits.npy` per image. Channel order follows `label_schema.json`. Pass `--binary LABEL` (for example `--binary femurs`) to write one label. See [docs/inference.md](https://github.com/VictorButoi/FleXray/blob/main/docs/inference.md) for the full CLI and Python API. ## The FleXray ensemble The flagship was trained with a 0.375 FluXray proportion in the training mix. Four sibling models share its architecture, label schema, preprocessing, and training recipe and differ only in that proportion: | Subfolder | FluXray proportion | Role | | --- | --- | --- | | `members/flux000` | 0.0 | ensemble member | | `members/flux025` | 0.25 | ensemble member | | **`members/flux0375`** | **0.375** | **flagship (loaded by default)** | | `members/flux050` | 0.5 | ensemble member | | `members/flux075` | 0.75 | ensemble member | `ensemble.json` at the repository root lists the flagship and the members. Because the members share one output space, they are averaged in probability space: ```bash flexify --ensemble --tta-samples 16 --input ./image.png --output-dir ./predictions flexify --subfolder members/flux000 --input ./image.png --output-dir ./predictions ``` ```python segmenter = FleXraySegmenter.from_pretrained("VictorButoi/flexray", ensemble=True) prediction = segmenter.predict("./image.png", tta_samples=16) member = FleXraySegmenter.from_pretrained( "VictorButoi/flexray", subfolder="members/flux000" ) ``` The website demo exposes the same choices as quality modes: **Low** runs the flagship once, **Normal** runs the flagship with 16-pass TTA, **High** runs the five-model ensemble once, and **X-High** runs the ensemble with 16-pass TTA. The members are also listed in [MODEL_ZOO.md](https://github.com/VictorButoi/FleXray/blob/main/MODEL_ZOO.md). ## Test-time augmentation We typically run FleXray with test-time augmentation (TTA) rather than a single forward pass; `--tta-samples 16` (or `predict(..., tta_samples=16)`) is the setting behind reported results and the demo's Normal / X-High modes. `tta_samples=N` runs one un-augmented pass plus `N - 1` randomly augmented passes and averages them in probability space (mean of sigmoid outputs, then converted back to logits). The augmentation chain is fixed in [`fxr.inference.tta`](https://github.com/VictorButoi/FleXray/blob/main/src/fxr/inference/tta.py) and the browser demo mirrors it exactly: | Transform | Probability | Range | | --- | --- | --- | | Horizontal flip (exactly inverted on the logits before merging) | 0.5 | - | | Gamma | 0.5 | gamma 0.9-1.1, gain 0.9-1.1 | | Intensity scale (additive) | 0.5 | -0.1 to 0.1 | | Brightness | 0.5 | 0.8-1.2 | | Sharpness | 0.5 | 0.6-1.4 | | Invert | 0.5 | - | | Contrast | 0.5 | 0.7-1.3 | The flip is the only geometric transform; intensity transforms do not move pixels and are not inverted. Augmented views are drawn from the global torch RNG (`torch.manual_seed` for reproducibility). With an ensemble, every view is drawn once and run through every member, so `M` members with `tta_samples=N` cost `M x N` forward passes (80 for the full ensemble at N=16). `tta_samples<=1` reproduces the plain single pass. ## Input contract `preprocessing.json` is applied automatically by the public loaders: - grayscale input (RGB is converted), any 8-bit or 16-bit PNG / JPEG / TIFF / BMP - per-image percentile min-max normalization to `[0, 1]` (0.5th / 99.5th percentiles, `eps = 1e-8`) - zero-pad to a square, then resize to 256 x 256 - outputs are `multilabel` sigmoid probabilities; masks use threshold 0.5 Outputs are at the 256 x 256 model resolution; the CLI and Python API do not resample back to the original image size. ## Output labels FleXray outputs 60 foreground masks (61 channels including `background`). The broader dataset/evaluation protocol also recognizes aggregate `lumbar_spine` and `thoracolumbar_spine` annotations; these are evaluated by combining the relevant per-vertebra outputs and are not checkpoint channels. Channel order is stored in each bundle's `label_schema.json`. - **Skull / shoulder girdle:** skull, scapulae, clavicles - **Upper limb:** humeri, radii, ulnae, carpals, metacarpals, phalanges - **Lower limb:** femurs, patellae, tibiae, fibulae, tarsals, metatarsals, toes - **Thorax:** rib_1 - rib_12, sternum - **Spine:** vertebra_c1 - c7, t1 - t12, l1 - l5, sacrum - **Pelvis:** hips - **Soft tissue:** lungs, heart, liver, spleen, kidneys Paired structures are merged (for example `femurs` covers both sides); laterality is not predicted. ## Architecture `fxr.models.UNet`, 2D, 1 input channel, 61 output channels; filters `[64, 128, 256, 512, 512, 720, 1024]`, 3 convolutions per block, residual blocks with instance norm, `align_corners=True` upsampling. The full architecture is in each bundle's `config.yml`. ## Training data The models were trained on three source types unified under the FleXray label protocol. For training mixture proportions, please refer to the paper: - **Real X-ray masks:** HandBones, FootBones, MURA forearm, and MURA humerus, with our own annotations. - **Generated FluXray images:** digitally reconstructed radiographs from the MOOSE CTs, generatively edited toward real X-ray appearance, with exact overlapping masks for every protocol structure. - **Online CT->DRR rendering:** MOOSE / ENHANCE-PET 1.6k, Shoulder-CT, HaN-Seg, PedsCT, RSNA cervical-spine fracture CTs, and ElbowCT, rendered to DRRs at random poses during training with per-label attenuation jitter. Training used AdamW (lr 3e-4, cosine schedule), a Dice + binary cross-entropy loss routed per dataset (partially labeled sources ignore unlabeled channels), and separate augmentation presets for CT-derived and X-ray inputs. The exact recipe is `fxr/configs/training/base.yml` in the code release. Every dataset's license, redistribution status, and download pointer is documented in the [`VictorButoi/flexray-data`](https://huggingface.co/datasets/VictorButoi/flexray-data) card. That repository ships the real X-ray sources whose licenses permit redistribution, already packed in the FleXray protocol, the MURA masks, and the FluXray database. ## Evaluation FleXray was evaluated on nine real-radiograph datasets held out from training (ElbowLat, HipRay, LowerLimbs, MendeleyCXR, MTDDH, DarwinCVD19, DeepFluoro, RAM-W600, VinDr-Rib), spanning lungs, ribs, peripheral bones, spine, and pelvis. Against generalist baselines (FluoroSAM, TotalSegmentator2D, PAXray) it achieves the highest macro Dice in every comparison (9 of 9). Per-dataset numbers and confidence intervals are in the paper; the benchmark figure is on the [project website](https://victorbutoi.github.io/FleXray/#results). Evaluation ignores ground-truth labels covering less than 0.1% of the image. ## Intended use and limitations Research use only. FleXray is **not a medical device** and is not cleared for clinical diagnosis, treatment planning, or patient-care decisions. - Targets conventional radiographs; dental and mammographic images are out of scope. - Predicts anatomy, not pathology. - No laterality (left/right) and no uncertainty estimates. - Performance on acquisition settings, views, or populations far from the training sources has not been validated. ## Repository layout - `README.md`: this card. - `ensemble.json`: the `flagship` subfolder and the `members` list with their FluXray proportions. - `members//model.safetensors`: exported model weights. - `members//config.yml`: architecture and protocol config consumed by `from_pretrained`. - `members//label_schema.json`: ordered output labels. - `members//preprocessing.json`: public preprocessing contract. - `members//checksums.json`: SHA256 checksums of the bundle files. - `members//onnx/flexray--256-fp16.onnx`: fp16 ONNX export (opset 18, sigmoid baked in) used by the in-browser demo; parity-checked against the PyTorch weights by `tools/export_web_demo.py`. ## Licenses - Code: MIT - Weights: CC-BY-NC-4.0 ## Citation ```bibtex @software{butoi2026flexray, title = {FleXray: Flexible Full-Body X-ray Segmentation}, author = {Butoi, Victor Ion and Gopalakrishnan, Vivek and Guttag, John V. and Dalca, Adrian V. and Dey, Neel}, year = {2026}, license = {MIT}, url = {https://github.com/VictorButoi/FleXray} } ``` Please also cite the source datasets listed in the [`flexray-data`](https://huggingface.co/datasets/VictorButoi/flexray-data) card for any dataset you use.