Skip to content

Commit c97e757

Browse files
authored
Nifti visualization support (#7874)
* WIP: nifti vis working, now improve * seems to work fine, tests not there yet * remove uncommented lines
1 parent 17f40a3 commit c97e757

File tree

1 file changed

+61
-6
lines changed

1 file changed

+61
-6
lines changed

src/datasets/features/nifti.py

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import base64
12
import os
3+
import uuid
24
from dataclasses import dataclass, field
3-
from io import BytesIO
45
from pathlib import Path
56
from typing import TYPE_CHECKING, Any, ClassVar, Dict, Optional, Union
67

@@ -18,6 +19,62 @@
1819

1920
from .features import FeatureType
2021

22+
if config.NIBABEL_AVAILABLE:
23+
import nibabel as nib
24+
25+
class Nifti1ImageWrapper(nib.nifti1.Nifti1Image):
26+
"""
27+
A wrapper around nibabel's Nifti1Image to customize its representation.
28+
"""
29+
30+
def __init__(self, nifti_image: nib.nifti1.Nifti1Image):
31+
super().__init__(
32+
dataobj=nifti_image.get_fdata(),
33+
affine=nifti_image.affine,
34+
header=nifti_image.header,
35+
extra=nifti_image.extra,
36+
file_map=nifti_image.file_map,
37+
dtype=nifti_image.get_data_dtype(),
38+
)
39+
self.nifti_image = nifti_image
40+
41+
def _repr_html_(self):
42+
bytes_ = self.nifti_image.to_bytes()
43+
b64 = base64.b64encode(bytes_).decode("utf-8")
44+
45+
self.nifti_data_url = f"data:application/octet-stream;base64,{b64}"
46+
viewer_id = f"papaya-{uuid.uuid4().hex[:8]}"
47+
48+
html = f"""
49+
<div id="{viewer_id}" style="width: 100%; height: 800px;"></div>
50+
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/release/current/standard/papaya.css" />
51+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/release/current/standard/papaya.js"></script>
52+
<script type="text/javascript">
53+
(function() {{
54+
// Wait for Papaya to load
55+
function initPapaya() {{
56+
if (typeof papaya === 'undefined' || typeof papaya.Container === 'undefined') {{
57+
setTimeout(initPapaya, 100);
58+
return;
59+
}}
60+
61+
// Papaya loaded - manually initialize
62+
var params = {{}};
63+
params["images"] = ["{self.nifti_data_url}"];
64+
params["kioskMode"] = false;
65+
params["showControls"] = true;
66+
67+
// Manual initialization
68+
papaya.Container.startPapaya();
69+
papaya.Container.addViewer("{viewer_id}", params);
70+
}}
71+
72+
initPapaya();
73+
}})();
74+
</script>
75+
"""
76+
return html
77+
2178

2279
@dataclass
2380
class Nifti:
@@ -106,7 +163,7 @@ def encode_example(self, value: Union[str, bytes, bytearray, dict, "nib.Nifti1Im
106163
f"A nifti sample should be a string, bytes, Path, nibabel image, or dict, but got {type(value)}."
107164
)
108165

109-
def decode_example(self, value: dict, token_per_repo_id=None) -> "nib.nifti1.Nifti1Image":
166+
def decode_example(self, value: dict, token_per_repo_id=None) -> "Nifti1ImageWrapper":
110167
"""Decode example NIfTI file into nibabel image object.
111168
112169
Args:
@@ -165,11 +222,9 @@ def decode_example(self, value: dict, token_per_repo_id=None) -> "nib.nifti1.Nif
165222
): # gzip magic number, see https://stackoverflow.com/a/76055284/9534390 or "Magic number" on https://en.wikipedia.org/wiki/Gzip
166223
bytes_ = gzip.decompress(bytes_)
167224

168-
bio = BytesIO(bytes_)
169-
fh = nib.FileHolder(fileobj=bio)
170-
nifti = nib.Nifti1Image.from_file_map({"header": fh, "image": fh})
225+
nifti = nib.Nifti1Image.from_bytes(bytes_)
171226

172-
return nifti
227+
return Nifti1ImageWrapper(nifti)
173228

174229
def embed_storage(self, storage: pa.StructArray, token_per_repo_id=None) -> pa.StructArray:
175230
"""Embed NifTI files into the Arrow array.

0 commit comments

Comments
 (0)