File size: 10,260 Bytes
572d772
 
 
aabdc18
 
 
572d772
 
 
aabdc18
 
572d772
 
 
b3d8920
572d772
aabdc18
 
 
 
 
572d772
b3d8920
 
 
 
572d772
b3d8920
 
 
 
572d772
aabdc18
572d772
aabdc18
 
 
 
 
 
 
 
b3d8920
aabdc18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b3d8920
aabdc18
b3d8920
 
 
 
 
aabdc18
b3d8920
 
 
aabdc18
 
b3d8920
 
aabdc18
 
 
b3d8920
aabdc18
b3d8920
 
 
 
aabdc18
 
 
 
 
b3d8920
aabdc18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9a071df
 
 
 
b3d8920
aabdc18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b3d8920
aabdc18
 
 
b3d8920
9a071df
aabdc18
9a071df
 
 
 
aabdc18
9a071df
 
 
aabdc18
 
 
 
 
 
 
 
 
 
b3d8920
 
572d772
 
 
aabdc18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
572d772
b3d8920
 
 
 
 
 
 
 
 
 
 
 
 
 
572d772
 
 
aabdc18
 
572d772
 
 
aabdc18
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
---
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 `<name>_masks.npy`, `<name>_probabilities.npy`, and
`<name>_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/<name>/model.safetensors`: exported model weights.
- `members/<name>/config.yml`: architecture and protocol config consumed by
  `from_pretrained`.
- `members/<name>/label_schema.json`: ordered output labels.
- `members/<name>/preprocessing.json`: public preprocessing contract.
- `members/<name>/checksums.json`: SHA256 checksums of the bundle files.
- `members/<name>/onnx/flexray-<name>-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.