isaac commited on
Commit
d8a2ed8
·
1 Parent(s): 978cbc0

Accept NRRD/MetaImage volumes via SimpleITK normalize-to-NIfTI

Browse files
Files changed (2) hide show
  1. app.py +11 -15
  2. intake.py +38 -0
app.py CHANGED
@@ -32,6 +32,7 @@ os.environ.setdefault("CONFIGS_ROOT", CKPT_DIR)
32
 
33
  import sys
34
  sys.path.insert(0, os.path.join(ROOT, "RADAR_inference")) # vendored absolute imports (dynamic_network_architectures) resolve from here
 
35
  import pandas as pd # noqa: E402
36
  import spaces # noqa: E402
37
  import gradio as gr # noqa: E402
@@ -77,22 +78,17 @@ def _english_name(column: str) -> str:
77
  def diagnose(ct_file):
78
  """Run RADAR on one uploaded contrast-enhanced abdominal CT volume."""
79
  path = ct_file if isinstance(ct_file, str) else ct_file.name
80
- if not path.endswith((".nii", ".nii.gz")):
81
- raise gr.Error("upload a .nii or .nii.gz CT volume")
82
- try:
83
- import nibabel as nib
84
-
85
- nib.load(path)
86
- except Exception:
87
- raise gr.Error("upload a .nii or .nii.gz CT volume")
88
-
89
  tmpdir = tempfile.mkdtemp(prefix="radar_case_")
90
  outdir = tempfile.mkdtemp(prefix="radar_out_")
91
  try:
92
- fname = os.path.basename(path)
93
- if not fname.endswith((".nii", ".nii.gz")):
94
- fname += ".nii.gz"
95
- shutil.copy(path, os.path.join(tmpdir, fname))
 
 
96
  try:
97
  evaluate(pad_func, model, tmpdir, outdir, "space")
98
  except (OSError, ValueError, RuntimeError) as exc:
@@ -119,8 +115,8 @@ demo = gr.Interface(
119
  fn=diagnose,
120
  inputs=gr.File(
121
  # ".gz" not ".nii.gz": Gradio matches Path.suffix ("gz"), so the compound form never validates.
122
- # diagnose() still enforces .nii/.nii.gz + NIfTI-parseable content server-side.
123
- label="Abdominal CT (.nii.gz)", file_types=[".nii", ".gz"]
124
  ),
125
  outputs=[gr.Label(label="Top findings", num_top_classes=10), gr.Dataframe(label="All finding scores")],
126
  title="RADAR Abdominal CT",
 
32
 
33
  import sys
34
  sys.path.insert(0, os.path.join(ROOT, "RADAR_inference")) # vendored absolute imports (dynamic_network_architectures) resolve from here
35
+ from intake import _to_nifti # noqa: E402
36
  import pandas as pd # noqa: E402
37
  import spaces # noqa: E402
38
  import gradio as gr # noqa: E402
 
78
  def diagnose(ct_file):
79
  """Run RADAR on one uploaded contrast-enhanced abdominal CT volume."""
80
  path = ct_file if isinstance(ct_file, str) else ct_file.name
81
+ if path.lower().endswith(".dcm"):
82
+ raise gr.Error("a single .dcm is one slice, not a volume: upload the full series")
 
 
 
 
 
 
 
83
  tmpdir = tempfile.mkdtemp(prefix="radar_case_")
84
  outdir = tempfile.mkdtemp(prefix="radar_out_")
85
  try:
86
+ src = _to_nifti(path, tmpdir)
87
+ fname = os.path.basename(src)
88
+ if src != os.path.join(tmpdir, fname):
89
+ if not fname.endswith((".nii", ".nii.gz")):
90
+ fname += ".nii.gz"
91
+ shutil.copy(src, os.path.join(tmpdir, fname))
92
  try:
93
  evaluate(pad_func, model, tmpdir, outdir, "space")
94
  except (OSError, ValueError, RuntimeError) as exc:
 
115
  fn=diagnose,
116
  inputs=gr.File(
117
  # ".gz" not ".nii.gz": Gradio matches Path.suffix ("gz"), so the compound form never validates.
118
+ # diagnose() still enforces volume suffix + parseable content server-side.
119
+ label="Abdominal CT (NIfTI / NRRD / MetaImage)", file_types=[".nii", ".gz", ".nrrd", ".nhdr", ".mha", ".mhd"]
120
  ),
121
  outputs=[gr.Label(label="Top findings", num_top_classes=10), gr.Dataframe(label="All finding scores")],
122
  title="RADAR Abdominal CT",
intake.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Upload intake: normalize NIfTI / NRRD / MetaImage / DICOM series to .nii.gz.
2
+
3
+ Downstream (RADAR_inference DataFolder/evaluate) only ever sees one .nii.gz,
4
+ so the vendored upstream chain stays untouched. No new dependencies:
5
+ SimpleITK and nibabel are already required.
6
+ """
7
+
8
+ import os
9
+
10
+ import gradio as gr
11
+
12
+ VOLUME_SUFFIXES = (".nii", ".nii.gz", ".nrrd", ".nhdr", ".mha", ".mhd")
13
+
14
+
15
+ def _to_nifti(path: str, workdir: str) -> str:
16
+ """Normalize one single-file volume to .nii.gz via SimpleITK."""
17
+ import SimpleITK as sitk
18
+
19
+ low = path.lower()
20
+ if low.endswith((".nii", ".nii.gz")):
21
+ import nibabel as nib
22
+
23
+ try:
24
+ nib.load(path)
25
+ except Exception:
26
+ raise gr.Error("upload a .nii or .nii.gz CT volume")
27
+ return path
28
+ if not low.endswith((".nrrd", ".nhdr", ".mha", ".mhd")):
29
+ raise gr.Error("upload a .nii.gz, .nrrd/.nhdr, .mha/.mhd volume, or a DICOM series")
30
+ try:
31
+ img = sitk.ReadImage(path)
32
+ except Exception:
33
+ raise gr.Error(f"could not read {os.path.basename(path)} as an image volume")
34
+ if img.GetDimension() != 3 or min(img.GetSize()) < 1:
35
+ raise gr.Error(f"{os.path.basename(path)} is not a 3D volume")
36
+ out = os.path.join(workdir, "case.nii.gz")
37
+ sitk.WriteImage(img, out)
38
+ return out