"""Expand OOD-healthy test cohort with IXI2D slices from HuggingFace. The iamkzntsv/IXI2D dataset (28,275 axial 2D slices from 600 healthy IXI subjects, 133 MB, skull-stripped + fsaverage-registered) is genuinely OOD relative to everything we've trained on: - v8 segmenter: trained on BraTS 2020 T1c + LGG kaggle_3m + Figshare + Kaggle 4-class. Never touched IXI. - v9b JEPA: trained on dataset_v8 augmented with OpenNeuro coronal-T1. Never touched IXI. Pulling a stratified sample of N healthy IXI slices, saving to samples/ood/healthy_ixi2d/. Caveat 3 addressed: gives us a larger OOD healthy cohort to estimate FPR more reliably than N=12. """ from __future__ import annotations import io import sys import time from pathlib import Path import numpy as np from PIL import Image ROOT = Path(__file__).resolve().parent.parent OUT = ROOT / 'samples' / 'ood' / 'healthy_ixi2d' OUT.mkdir(parents=True, exist_ok=True) # How many slices to pull. 100 gives us 1pp resolution on FPR estimates # (vs the previous 12 samples where each false positive = 8.3pp). N_SAMPLES = 100 def main(): try: from huggingface_hub import hf_hub_download except ImportError: sys.exit('pip install huggingface_hub') print(f'[init] target = {N_SAMPLES} IXI2D healthy slices -> {OUT}') # The dataset ships as parquet (imagefolder split). Easiest path: # use the `datasets` library to load + iterate, OR pull individual # files from the LFS-hosted train split. Try the datasets library # first; fall back to manual parquet download. try: from datasets import load_dataset print('[1/3] streaming IXI2D from HuggingFace ...') ds = load_dataset('iamkzntsv/IXI2D', split='train', streaming=True) # Take every N-th slice to spread across subjects (the train split # is ordered by subject). 25,400 train rows, take ~100 means stride # = 254. stride = max(1, 25400 // N_SAMPLES) print(f' stride={stride} -> ~{N_SAMPLES} samples spread across subjects') saved = 0 t0 = time.perf_counter() for i, ex in enumerate(ds): if i % stride != 0: continue img = ex.get('image') if img is None: continue # ex['image'] is a PIL Image (200x200 grayscale per the dataset card) fname = f'ixi2d_{i:05d}.png' img.save(OUT / fname) saved += 1 if saved >= N_SAMPLES: break if saved % 20 == 0: print(f' [{saved}/{N_SAMPLES}] elapsed={time.perf_counter()-t0:.0f}s') print(f'[done] saved {saved} slices in {time.perf_counter()-t0:.0f}s') except Exception as exc: print(f'[fail] {type(exc).__name__}: {exc}') print('Fallback path: manually browse https://huggingface.co/datasets/iamkzntsv/IXI2D/tree/main') sys.exit(1) if __name__ == '__main__': main()