Views
No views yet
coronary-syntax-prediction/
├── backbone/ # Single-frame backbone (3D ResNet)
│ ├── dataset.py # SyntaxDataset for single-view training
│ ├── pl_model.py # SyntaxLightningModule (Lightning wrapper)
│ ├── pl_train.py # Training script for backbone
│ └── __init__.py
├── full_model/ # Multi-view RNN head + backbone
│ ├── rnn_dataset.py # SyntaxDataset for multi-view (RNN) training
│ ├── rnn_model.py # SyntaxLightningModule with RNN variants
│ ├── rnn_train.py # Training script for full model
│ └── __init__.py
├── inference/ # Inference & evaluation
│ ├── rnn_apply.py # Ensemble inference script
│ ├── metrics_visualization.py # Plotly-based metrics & visualization
│ └── __init__.py
├── backbone_weights/ # Backbone .pt checkpoints (5 folds × 2 arteries)
├── full_model_weights/ # Full model .pt checkpoints (5 folds × 2 arteries × variants)
├── scaling_coeffs/ # Calibration coefficients (a, b) per fold
├── requirements.txt
└── README.md1# Clone the repository
2git clone https://huggingface.co/MesserMMP/coronary-syntax-prediction
3cd coronary-syntax-prediction
4
5# Install dependencies
6pip install -r requirements.txtrequirements.txt for full list.1python backbone/pl_train.py \
2 -r /path/to/dataset_root \
3 --fold 0 \
4 -a right \
5 --num-classes 2 \
6 -b 50 \
7 -f 32 \
8 -v 256 256 \
9 --max-epochs 10 \
10 --num-workers 8 \
11 --devices 0 \
12 --precision bf16-mixed \
13 --logdir ./logs/backbone \
14 --use-weighted-sampler-r, --dataset-root: Path to dataset root (default: .)--fold: Fold number (0-4, default: 4)-a, --artery: left or right (default: right)-nc, --num-classes: Output units: 2 for (classification, regression) (default: 2)-b, --batch-size: Batch size (default: 50)-f, --frames-per-clip: Frames per clip (default: 32)-v, --video-size: Frame resolution H W (default: 256 256)--max-epochs: Epochs for full training (default: 10)--num-workers: DataLoader workers (default: 8)--devices: GPU device IDs (default: )--precision: Training precision mode (default: bf16-mixed)--logdir: Log directory (default: ./logs/backbone)--use-weighted-sampler: Balance classes by score intervals (flag)--seed: Random seed (default: 42).ckpt checkpoints in --logdir1python full_model/rnn_train.py \
2 -r /path/to/dataset_root \
3 --fold 0 \
4 -a right \
5 --variant lstm_mean \
6 --num-classes 2 \
7 -b 8 \
8 -f 32 \
9 -v 256 256 \
10 --max-epochs 15 \
11 --num-workers 16 \
12 --devices 0 \
13 --precision bf16-mixed \
14 --logdir ./logs/rnn \
15 --backbone-pt-dir backbone_weights \
16 --backbone-from-pt \
17 --rnn-folds-dir rnn_folds--variant: Head architecture:
mean_out: Mean of projection scores (no RNN)mean: MLP on mean pooled backbone featureslstm_mean: LSTM with mean pooling of hidden stateslstm_last: LSTM, use last hidden stategru_mean, gru_last: GRU variantsbert_mean, bert_cls, bert_cls2: Transformer encoder variantslstm_mean)--backbone-pt-dir: Path to .pt backbone weights (default: backbone_weights)--backbone-from-pt: Load backbone from .pt files (flag, default: True)--backbone-logdir: Alternative: load backbone from Lightning logs--rnn-folds-dir: Directory with rnn_fold{fold}_train.json, rnn_fold{fold}_eval.json (default: rnn_folds)--use-weighted-sampler: Balance by score (flag)--pt-weights-format: If loading pre-trained head, expect .pt instead of .ckpt (flag)--logdir/{artery}BinSyntax_R3D_fold{fold:02d}_{variant}_post/...1python inference/rnn_apply.py \
2 -d "test_philips_100.json" "test_siemens_20.json" \
3 -n "Philips Test" "Siemens Test" \
4 -p "philips_100" "siemens_20" \
5 -r /path/to/dataset_root \
6 --model-dir full_model_weights \
7 -v 256 256 \
8 --frames-per-clip 32 \
9 --num-workers 8 \
10 --variant lstm_mean \
11 --pt-weights-format \
12 --use-scaling \
13 --scaling-file scaling_coeffs.json \
14 -e "Ensemble_lstm_mean" \
15 -m metrics.json-d, --dataset-paths: Multiple JSON test dataset paths (relative to --dataset-root)-n, --dataset-names: Display names for each dataset (must match -d count)-p, --postfixes: Suffixes for result files (must match -d count)-r, --dataset-root: Dataset root (default: .)--model-dir: Directory with full model .pt weights (default: full_model_weights)-v, --video-size: Frame resolution H W (default: 256 256)--frames-per-clip: Frames per clip (default: 32)--num-workers: DataLoader workers (default: 8)--variant: Head model variant (default: lstm_mean)--pt-weights-format: Model weights are .pt raw state_dict (flag, default: True)--use-scaling: Apply post-calibration scaling a*x+b from JSON (flag)--scaling-file: Calibration coefficients file relative to --dataset-root-e, --ensemble-name: Experiment name for metrics (e.g., "Ensemble_lstm_mean")-m, --metrics-file: JSON output file path for all metrics--seed: Random seed (default: 42)results/{postfix}.json{metrics_file} with Pearson correlation, balanced accuracy, per-fold statisticsvisualizations/{postfix}.htmlbackbone/ scripts). Each record represents one angiographic video projection.folds/fold{fold_id}_{split}.json1[
2 {
3 "study_uid": "1.3.46.670589.28.26690171363123020190823114413193175",
4 "series_uid": "1.3.46.670589.28.26690171363123020190823115102913895.2.2",
5 "sop_uid": "1.3.46.670589.28.266901713631230201908231158145991852211512",
6 "path": "../anon_data/chunk1/.../IM-2254-0039.dcm",
7 "shape": [38, 512, 512],
8 "artery": 0,
9 "artery_prob": 1.9078343029832467e-05,
10 "syntax": 0.0,
11 "syntax_left": 0.0,
12 "syntax_right": 0.0,
13 "bin_syntax": 0,
14 "manufacturer": "Philips",
15 "device_model": "Allura Clarity"
16 },
17 {
18 "study_uid": "1.3.46.670589.28.26690171363123020190823114413193175",
19 "series_uid": "1.3.46.670589.28.26690171363123020190823115648342907.2.2",
20 "sop_uid": "1.3.46.670589.28.266901713631230201908231158148282212211512",
21 "path": "../anon_data/chunk1/.../IM-2260-0022.dcm",
22 "shape": [15, 512, 512],
23 "artery": 1,
24 "artery_prob": 0.9559882879257202,
25 "syntax": 0.0,
26 "syntax_left": 0.0,
27 "syntax_right": 0.0,
28 "bin_syntax": 0,
29 "manufacturer": "Philips",
30 "device_model": "Allura Clarity"
31 }
32]| Field | Type | Description |
|---|---|---|
study_uid | str | Unique study identifier (DICOM) |
series_uid | str | Unique series identifier (DICOM) |
sop_uid | str | Unique SOP instance UID (DICOM) |
path | str | Relative path to DICOM video file |
shape | list[int] | Video dimensions: [T, H, W] (frames, height, width) |
artery | int | Coronary artery: 0 = left (LCA), 1 = right (RCA) |
artery_prob | float | Model confidence for artery classification (0–1) |
syntax | float | Total SYNTAX score (LCA + RCA) |
syntax_left | float | SYNTAX score for left coronary artery |
syntax_right | float | SYNTAX score for right coronary artery |
bin_syntax | int | Binary classification: 0 = SYNTAX < threshold, 1 = SYNTAX ≥ threshold |
manufacturer | str | Imaging equipment manufacturer (e.g., "Philips", "Siemens") |
device_model | str | Equipment model (e.g., "Allura Clarity", "AXIOM-Artis") |
full_model/ scripts). Each record represents one patient study with all angiographic projections grouped by artery.rnn_folds/rnn_fold{fold_id}_{split}.json1[
2 {
3 "study_uid": "1.3.46.670589.28.26690171363123020190823114413193175",
4 "manufacturer": "Philips",
5 "device_model": "Allura Clarity",
6 "syntax": 0.0,
7 "syntax_left": 0.0,
8 "syntax_right": 0.0,
9 "bin_syntax": 0,
10 "videos": [
11 {
12 "series_uid": "1.3.46.670589.28.26690171363123020190823115102913895.2.2",
13 "sop_uid": "1.3.46.670589.28.266901713631230201908231158145991852211512",
14 "path": "../anon_data/chunk1/.../IM-2254-0039.dcm",
15 "shape": [38, 512, 512],
16 "artery": 0,
17 "artery_prob": 1.9078343029832467e-05
18 },
19 {
20 "series_uid": "1.3.46.670589.28.26690171363123020190823115648342907.2.2",
21 "sop_uid": "1.3.46.670589.28.266901713631230201908231158148282212211512",
22 "path": "../anon_data/chunk1/.../IM-2260-0022.dcm",
23 "shape": [15, 512, 512],
24 "artery": 1,
25 "artery_prob": 0.9559882879257202
26 }
27 ],
28 "videos_left": [
29 {
30 "series_uid": "1.3.46.670589.28.26690171363123020190823115102913895.2.2",
31 "sop_uid": "1.3.46.670589.28.266901713631230201908231158145991852211512",
32 "path": "../anon_data/chunk1/.../IM-2254-0039.dcm",
33 "shape": [38, 512, 512],
34 "artery": 0,
35 "artery_prob": 1.9078343029832467e-05
36 },
37 {
38 "series_uid": "1.3.46.670589.28.26690171363123020190823115533239902.2.2",
39 "sop_uid": "1.3.46.670589.28.266901713631230201908231158147562092211512",
40 "path": "../anon_data/chunk1/.../IM-2258-0055.dcm",
41 "shape": [50, 512, 512],
42 "artery": 0,
43 "artery_prob": 0.0003698925720527768
44 },
45 {
46 "series_uid": "1.3.46.670589.28.26690171363123020190823115052517893.2.2",
47 "sop_uid": "1.3.46.670589.28.266901713631230201908231158145581792211512",
48 "path": "../anon_data/chunk1/.../IM-2253-0047.dcm",
49 "shape": [47, 512, 512],
50 "artery": 0,
51 "artery_prob": 0.00018035581160802394
52 },
53 {
54 "series_uid": "1.3.46.670589.28.26690171363123020190823115550658904.2.2",
55 "sop_uid": "1.3.46.670589.28.266901713631230201908231158147942152211512",
56 "path": "../anon_data/chunk1/.../IM-2259-0047.dcm",
57 "shape": [41, 512, 512],
58 "artery": 0,
59 "artery_prob": 0.00022948876721784472
60 },
61 {
62 "series_uid": "1.3.46.670589.28.26690171363123020190823115523054900.2.2",
63 "sop_uid": "1.3.46.670589.28.266901713631230201908231158147162032211512",
64 "path": "../anon_data/chunk1/.../IM-2257-0047.dcm",
65 "shape": [43, 512, 512],
66 "artery": 0,
67 "artery_prob": 0.00034276593942195177
68 }
69 ],
70 "videos_right": [
71 {
72 "series_uid": "1.3.46.670589.28.26690171363123020190823115648342907.2.2",
73 "sop_uid": "1.3.46.670589.28.266901713631230201908231158148282212211512",
74 "path": "../anon_data/chunk1/.../IM-2260-0022.dcm",
75 "shape": [15, 512, 512],
76 "artery": 1,
77 "artery_prob": 0.9559882879257202
78 },
79 {
80 "series_uid": "1.3.46.670589.28.26690171363123020190823115657547909.2.2",
81 "sop_uid": "1.3.46.670589.28.266901713631230201908231158149262272211512",
82 "path": "../anon_data/chunk1/.../IM-2261-0070.dcm",
83 "shape": [62, 512, 512],
84 "artery": 1,
85 "artery_prob": 0.9999938011169434
86 },
87 {
88 "series_uid": "1.3.46.670589.28.26690171363123020190823115712699912.2.2",
89 "sop_uid": "1.3.46.670589.28.266901713631230201908231158149652332211512",
90 "path": "../anon_data/chunk1/.../IM-2262-0063.dcm",
91 "shape": [54, 512, 512],
92 "artery": 1,
93 "artery_prob": 0.999855637550354
94 }
95 ],
96 "videos_other": [
97 {
98 "series_uid": "1.3.46.670589.28.26690171363123020190823115128658897.2.2",
99 "sop_uid": "1.3.46.670589.28.266901713631230201908231158146391912211512",
100 "path": "../anon_data/chunk1/.../IM-2255-0035.dcm",
101 "shape": [33, 512, 512],
102 "artery": 0,
103 "artery_prob": 0.2866898477077484
104 },
105 {
106 "series_uid": "1.3.46.670589.28.26690171363123020190823115132067898.2.2",
107 "sop_uid": "1.3.46.670589.28.266901713631230201908231158146771972211512",
108 "path": "../anon_data/chunk1/.../IM-2256-0035.dcm",
109 "shape": [32, 512, 512],
110 "artery": 0,
111 "artery_prob": 0.3251875042915344
112 }
113 ]
114 }
115]| Field | Type | Description |
|---|---|---|
study_uid | str | Unique study identifier (same across all videos for this patient) |
manufacturer | str | Imaging equipment manufacturer |
device_model | str | Equipment model |
syntax | float | Total SYNTAX score (LCA + RCA) |
syntax_left | float | SYNTAX score for left coronary artery |
syntax_right | float | SYNTAX score for right coronary artery |
bin_syntax | int | Binary classification label |
videos | list[object] | All video projections (unfiltered) |
videos_left | list[object] | Projections classified as left coronary (artery=0, high confidence) |
videos_right | list[object] | Projections classified as right coronary (artery=1, high confidence) |
videos_other | list[object] | Low-confidence projections (excluded from training); typically ~5–7% of total |
| Field | Type | Description |
|---|---|---|
series_uid | str | Unique series identifier (DICOM) |
sop_uid | str | Unique SOP instance UID (DICOM) |
path | str | Relative path to DICOM video file |
shape | list[int] | Video dimensions: [T, H, W] |
artery | int | Artery classification: 0 = left, 1 = right |
artery_prob | float | Model confidence (0–1); threshold typically 0.5 for videos_left/videos_right inclusion |
test_philips_100.json, test_siemens_20.json) follows Format 2 with additional expert annotations:1[
2 {
3 "study_uid": "...",
4 "manufacturer": "Philips",
5 "device_model": "Allura Clarity",
6 "syntax": 18.5,
7 "syntax_left": 10.0,
8 "syntax_right": 8.5,
9 "mean_syntax": 18.5,
10 "expert0": 18,
11 "expert1": 19,
12 "expert2": 17,
13 "expert3": 19,
14 "expert4": 20,
15 "expert5": 18,
16 "videos_left": [...],
17 "videos_right": [...],
18 "videos_other": [...]
19 }
20]| Field | Type | Description |
|---|---|---|
mean_syntax | float | Consensus SYNTAX score (mean of 6 experts) |
expert0–expert5 | float | Individual expert SYNTAX score (6 independent cardiologists) |
expert0 typically represents aggregated/consensus annotations, while expert1–expert5 are individual readers.uint8 (8-bit grayscale)| Set | Files | Size | Equipment | Artery Classification |
|---|---|---|---|---|
| Backbone training | folds/fold{0-4}_train.json | ~8,000 videos | Philips | Single video |
| Backbone validation | folds/fold{0-4}_eval.json | ~1,000 videos | Philips | Single video |
| RNN training | rnn_folds/rnn_fold{0-4}_train.json | ~2,200 studies | Philips | Multi-view (patient) |
| RNN validation | rnn_folds/rnn_fold{0-4}_eval.json | ~290 studies | Philips | Multi-view (patient) |
| Test 1 (Philips) | test_philips_100.json | 100 studies | Philips Allura Clarity | 6 experts |
| Test 2 (Siemens) | test_siemens_20.json | 20 studies | Siemens AXIOM-Artis | 6 experts |
| Metric | Test Set (Philips) | Test Set (Siemens) | Description |
|---|---|---|---|
| Pearson r | 0.814 | 0.482 | Raw predictions |
| Balanced Accuracy | 0.687 | 0.566 | Pre-calibration, SYNTAX > 22 |
| Post-Calibration | |||
| Pearson r | 0.816 | 0.482 | After scaling |
| Balanced Accuracy | 0.825 | 0.643 | After scaling, SYNTAX > 22 |
backbone_weights/: .pt files for 5 folds × 2 arteries
RightBinSyntax_R3D_full_fold00.ptfull_model_weights/: .pt files for ensemble variants
RightBinSyntax_R3D_fold00_lstm_mean_post_best.ptscaling_coeffs/: Calibration parameters (a, b) per fold1@dataset{cardiosyntax_v2_2025,
2 title={CardioSyntax v2: Angiographic dataset for SYNTAX score estimation},
3 author={MesserMMP and collaborators},
4 year={2025},
5 url={https://huggingface.co/datasets/MesserMMP/coronary-angiography-syntax}
6}