|
1 | 1 | """Read/write linear transforms.""" |
2 | 2 | from pathlib import Path |
3 | 3 | import numpy as np |
4 | | -import nibabel as nb |
5 | 4 | from nibabel import load as loadimg |
6 | 5 |
|
7 | | -import h5py |
8 | | - |
9 | 6 | from ..patched import LabeledWrapStruct |
10 | 7 |
|
11 | 8 |
|
12 | | -def get_xfm_filetype(xfm_file): |
13 | | - path = Path(xfm_file) |
14 | | - ext = path.suffix |
15 | | - if ext == '.gz' and path.name.endswith('.nii.gz'): |
16 | | - return 'nifti' |
17 | | - |
18 | | - file_types = { |
19 | | - '.nii': 'nifti', |
20 | | - '.h5': 'hdf5', |
21 | | - '.x5': 'x5', |
22 | | - '.txt': 'txt', |
23 | | - '.mat': 'txt' |
24 | | - } |
25 | | - return file_types.get(ext, 'unknown') |
26 | | - |
27 | | -def gather_fields(x5=None, hdf5=None, nifti=None, shape=None, affine=None, header=None): |
28 | | - xfm_fields = { |
29 | | - "x5": x5, |
30 | | - "hdf5": hdf5, |
31 | | - "nifti": nifti, |
32 | | - "header": header, |
33 | | - "shape": shape, |
34 | | - "affine": affine |
35 | | - } |
36 | | - return xfm_fields |
37 | | - |
38 | | -def load_nifti(nifti_file): |
39 | | - nifti_xfm = nb.load(nifti_file) |
40 | | - xfm_data = nifti_xfm.get_fdata() |
41 | | - shape = nifti_xfm.shape |
42 | | - affine = nifti_xfm.affine |
43 | | - header = getattr(nifti_xfm, "header", None) |
44 | | - return gather_fields(nifti=xfm_data, shape=shape, affine=affine, header=header) |
45 | | - |
46 | | -def load_hdf5(hdf5_file): |
47 | | - storage = {} |
48 | | - |
49 | | - def get_hdf5_items(name, x5_root): |
50 | | - if isinstance(x5_root, h5py.Dataset): |
51 | | - storage[name] = { |
52 | | - 'type': 'dataset', |
53 | | - 'attrs': dict(x5_root.attrs), |
54 | | - 'shape': x5_root.shape, |
55 | | - 'data': x5_root[()] |
56 | | - } |
57 | | - elif isinstance(x5_root, h5py.Group): |
58 | | - storage[name] = { |
59 | | - 'type': 'group', |
60 | | - 'attrs': dict(x5_root.attrs), |
61 | | - 'members': {} |
62 | | - } |
63 | | - |
64 | | - with h5py.File(hdf5_file, 'r') as f: |
65 | | - f.visititems(get_hdf5_items) |
66 | | - if storage: |
67 | | - hdf5_storage = {'hdf5': storage} |
68 | | - return hdf5_storage |
69 | | - |
70 | | -def load_x5(x5_file): |
71 | | - load_hdf5(x5_file) |
72 | | - |
73 | | -def load_mat(mat_file): |
74 | | - affine_matrix = np.loadtxt(mat_file) |
75 | | - affine = nb.affines.from_matvec(affine_matrix[:,:3], affine_matrix[:,3]) |
76 | | - return gather_fields(affine=affine) |
77 | | - |
78 | | -def xfm_loader(xfm_file): |
79 | | - loaders = { |
80 | | - 'nifti': load_nifti, |
81 | | - 'hdf5': load_hdf5, |
82 | | - 'x5': load_x5, |
83 | | - 'txt': load_mat, |
84 | | - 'mat': load_mat |
85 | | - } |
86 | | - xfm_filetype = get_xfm_filetype(xfm_file) |
87 | | - loader = loaders.get(xfm_filetype) |
88 | | - if loader is None: |
89 | | - raise ValueError(f"Unsupported file type: {xfm_filetype}") |
90 | | - return loader(xfm_file) |
91 | | - |
92 | | -def to_filename(self, filename, fmt="X5"): |
93 | | - """Store the transform in BIDS-Transforms HDF5 file format (.x5).""" |
94 | | - with h5py.File(filename, "w") as out_file: |
95 | | - out_file.attrs["Format"] = "X5" |
96 | | - out_file.attrs["Version"] = np.uint16(1) |
97 | | - root = out_file.create_group("/0") |
98 | | - self._to_hdf5(root) |
99 | | - |
100 | | - return filename |
101 | | - |
102 | | -def _to_hdf5(self, x5_root): |
103 | | - """Serialize this object into the x5 file format.""" |
104 | | - transform_group = x5_root.create_group("TransformGroup") |
105 | | - |
106 | | - """Group '0' containing Affine transform""" |
107 | | - transform_0 = transform_group.create_group("0") |
108 | | - |
109 | | - transform_0.attrs["Type"] = "Affine" |
110 | | - transform_0.create_dataset("Transform", data=self._affine) |
111 | | - transform_0.create_dataset("Inverse", data=np.linalg.inv(self._affine)) |
112 | | - |
113 | | - metadata = {"key": "value"} |
114 | | - transform_0.attrs["Metadata"] = str(metadata) |
115 | | - |
116 | | - """sub-group 'Domain' contained within group '0' """ |
117 | | - domain_group = transform_0.create_group("Domain") |
118 | | - #domain_group.attrs["Grid"] = self._grid |
119 | | - #domain_group.create_dataset("Size", data=_as_homogeneous(self._reference.shape)) |
120 | | - #domain_group.create_dataset("Mapping", data=self.mapping) |
121 | | - |
122 | | -def _from_x5(self, x5_root): |
123 | | - variables = {} |
124 | | - |
125 | | - x5_root.visititems(lambda name, x5_root: loader(name, x5_root, variables)) |
126 | | - |
127 | | - _transform = variables["TransformGroup/0/Transform"] |
128 | | - _inverse = variables["TransformGroup/0/Inverse"] |
129 | | - _size = variables["TransformGroup/0/Domain/Size"] |
130 | | - _mapping = variables["TransformGroup/0/Domain/Mapping"] |
131 | | - |
132 | | - return _transform, _inverse, _size, _map |
133 | | - |
134 | | - |
135 | 9 | class TransformIOError(IOError): |
136 | 10 | """General I/O exception while reading/writing transforms.""" |
137 | 11 |
|
|
0 commit comments