mrid-python 0.1.4__py3-none-any.whl → 0.1.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -235,7 +235,7 @@ def skullstrip_D(
235
235
 
236
236
  # optionally add with skullstripped postfix
237
237
  if keep_original:
238
- skullstripped = {f"{k}_hd_bet": v for k,v in skullstripped}
238
+ skullstripped = {(f"{k}_hd_bet" if k != "seg_hd_bet" else k): v for k,v in skullstripped.items()}
239
239
  skullstripped.update(images.copy())
240
240
 
241
241
  return skullstripped
@@ -1,6 +1,5 @@
1
1
  import SimpleITK as sitk
2
2
  import numpy as np
3
- import SimpleITK as sitk
4
3
 
5
4
  from ..loading.convert import ImageLike, tositk, tonumpy
6
5
 
@@ -15,12 +14,12 @@ def expand_binary_mask(binary_mask: ImageLike, expand: int) -> sitk.Image:
15
14
  Negative values dilate the mask by this many pixels.
16
15
  """
17
16
  binary_mask = tositk(binary_mask)
18
- if expand > 0:
17
+ if expand < 0:
19
18
  inverted_mask = 1 - binary_mask
20
- return 1 - sitk.BinaryDilate(inverted_mask, (expand, expand, expand))
19
+ return 1 - sitk.BinaryDilate(inverted_mask, (-expand, -expand, -expand))
21
20
 
22
- if expand < 0:
23
- return sitk.BinaryDilate(binary_mask, (-expand, -expand, -expand))
21
+ if expand > 0:
22
+ return sitk.BinaryDilate(binary_mask, (expand, expand, expand))
24
23
 
25
24
  return binary_mask
26
25
 
@@ -73,8 +73,9 @@ def resize(img: ImageLike, new_size: Sequence[int], interpolator=sitk.sitkLinear
73
73
 
74
74
  return sitk.Resample(img, reference_image, centered_transform, interpolator, 0.0)
75
75
 
76
- def downsample(image:ImageLike, factor:float, dims: Sequence[int] | None, interpolator=sitk.sitkLinear) -> sitk.Image:
76
+ def downsample(image:ImageLike, factor:float, dims: int | Sequence[int] | None, interpolator=sitk.sitkLinear) -> sitk.Image:
77
77
  """factor = 2 for 2x downsampling"""
78
+ if isinstance(dims, int): dims = (dims, )
78
79
  image = tositk(image)
79
80
  size = sitk.GetArrayFromImage(image).shape
80
81
  size = [round(s/factor) if (dims is None or i in dims) else s for i,s in enumerate(size)]
@@ -70,6 +70,7 @@ def run_synthstrip(
70
70
  fill: int | None = None,
71
71
  no_csf: bool | None = None,
72
72
  model: str | os.PathLike | None = None,
73
+ verbose: bool = True,
73
74
  ):
74
75
  """Runs ``synthstrip`` command-line routine via ``subprocess.run``.
75
76
 
@@ -108,7 +109,10 @@ def run_synthstrip(
108
109
  if model is not None: command.extend(["--model", os.path.normpath(model)])
109
110
 
110
111
  # run
111
- subprocess.run(command, check=True)
112
+ if verbose:
113
+ subprocess.run(command, check=True)
114
+ else:
115
+ subprocess.run(command, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.STDOUT)
112
116
 
113
117
  def predict_brain_mask(
114
118
  synthstrip_script_path: str | os.PathLike,
@@ -117,6 +121,7 @@ def predict_brain_mask(
117
121
  border: int | None = None,
118
122
  threads: int | None = None,
119
123
  model: str | os.PathLike | None = None,
124
+ verbose: bool = True,
120
125
  ):
121
126
  """Returns brain mask of ``input`` predicted by ``synthstrip``.
122
127
 
@@ -141,6 +146,7 @@ def predict_brain_mask(
141
146
  border=border,
142
147
  threads=threads,
143
148
  model=model,
149
+ verbose=verbose,
144
150
  )
145
151
 
146
152
  brain_mask = tositk(os.path.join(tmpdir, "synthstrip_mask.nii.gz"))
@@ -155,6 +161,7 @@ def skullstrip(
155
161
  threads: int | None = None,
156
162
  model: str | os.PathLike | None = None,
157
163
  expand: int = 0,
164
+ verbose: bool = True,
158
165
  ):
159
166
  """Skullstrips ``input`` using synthstrip.
160
167
 
@@ -181,6 +188,7 @@ def skullstrip(
181
188
  border=border,
182
189
  threads=threads,
183
190
  model=model,
191
+ verbose=verbose
184
192
  )
185
193
  if expand != 0:
186
194
  mask = expand_binary_mask(mask, expand=expand)
@@ -202,6 +210,8 @@ def skullstrip_D(
202
210
  include_mask: bool = False,
203
211
  keep_original: bool = False,
204
212
 
213
+ verbose: bool = True,
214
+
205
215
  ) -> dict[str, sitk.Image]:
206
216
  """Predicts brain mask of ``images[key]`` using synthstrip, then uses this mask to skull strip all values in ``images``.
207
217
 
@@ -233,6 +243,7 @@ def skullstrip_D(
233
243
  border=border,
234
244
  threads=threads,
235
245
  model=model,
246
+ verbose=verbose,
236
247
  )
237
248
  skullstripped = {}
238
249
 
@@ -252,7 +263,7 @@ def skullstrip_D(
252
263
 
253
264
  # optionally add with skullstripped postfix
254
265
  if keep_original:
255
- skullstripped = {f"{k}_synthstrip": v for k,v in skullstripped}
266
+ skullstripped = {(f"{k}_synthstrip" if k != "seg_synthstrip" else k): v for k,v in skullstripped.items()}
256
267
  skullstripped.update(images.copy())
257
268
 
258
269
  return skullstripped
mrid/study.py CHANGED
@@ -79,6 +79,19 @@ class Study(UserDict[str, sitk.Image | Any]):
79
79
  study[key] = item
80
80
  return study
81
81
 
82
+ def remove(self, *keys: str | Sequence[str]):
83
+ """Returns a new study without specified keys"""
84
+ keys_proc = []
85
+ for k in keys:
86
+ if isinstance(k, str): keys_proc.append(k)
87
+ else: keys_proc.extend(k)
88
+
89
+ study = self.copy()
90
+ for k in keys_proc:
91
+ del study[k]
92
+
93
+ return study
94
+
82
95
  def get_scans(self):
83
96
  """Returns a new ``Study`` with segmentations and info removed."""
84
97
  return self.__class__({k:v for k,v in self.items() if not k.startswith(("seg", "info"))})
@@ -158,7 +171,7 @@ class Study(UserDict[str, sitk.Image | Any]):
158
171
  and uses that bounding box to crop all other images, including segmentations.
159
172
 
160
173
  Args:
161
- key: The key of the image to use for finding the foreground bounding box.
174
+ key: The key of the image (scan or segmentation) to use for finding the foreground bounding box.
162
175
  """
163
176
  d = preprocessing.cropping.crop_bg_D(self.get_images(), key)
164
177
  return Study(**d, **self.get_info())
@@ -231,6 +244,8 @@ class Study(UserDict[str, sitk.Image | Any]):
231
244
 
232
245
  include_mask: bool = False,
233
246
  keep_original: bool = False,
247
+
248
+ verbose: bool = True,
234
249
  ):
235
250
  """Returns a new study with all scans skullstripped.
236
251
 
@@ -260,6 +275,7 @@ class Study(UserDict[str, sitk.Image | Any]):
260
275
  key=key,
261
276
  gpu=gpu, border=border, threads=threads, model=model,
262
277
  expand=expand, include_mask=include_mask, keep_original=keep_original,
278
+ verbose=verbose,
263
279
  )
264
280
  return Study(**d, **self.get_segmentations(), **self.get_info())
265
281
 
@@ -460,7 +476,6 @@ class Study(UserDict[str, sitk.Image | Any]):
460
476
  arr = tfm(self.numpy(key))
461
477
  return self.add(f'{key}{postfix}', arr, reference_key=key)
462
478
 
463
-
464
479
  def numpy(self, key: str):
465
480
  """returns ``study[key]`` converted to a numpy array."""
466
481
  return tonumpy(self[key])
@@ -527,7 +542,7 @@ class Study(UserDict[str, sitk.Image | Any]):
527
542
 
528
543
  def plot(self):
529
544
  from .utils.plotting import plot_study
530
- plot_study(self.get_images().numpy_dict())
545
+ return plot_study(self.get_images().numpy_dict())
531
546
 
532
547
  def save(
533
548
  self,
mrid/training/slicer.py CHANGED
@@ -1,10 +1,11 @@
1
+ import os
1
2
  import random
2
3
  from functools import partial
3
4
  from typing import Any, Literal, cast
4
5
  from collections.abc import Callable, Sequence
5
6
  import torch
6
7
 
7
- from ..loading import ImageLike, totensor
8
+ from ..loading import ImageLike, totensor, tonumpy
8
9
 
9
10
 
10
11
  class SliceSampler:
@@ -216,6 +217,7 @@ class SliceDataset(torch.utils.data.Dataset):
216
217
  randflip: bool = True,
217
218
  flatten: bool = True,
218
219
  repeat: int = 1,
220
+ tfm: Callable[[torch.Tensor, torch.Tensor], Any] | None = None,
219
221
  ):
220
222
  super().__init__()
221
223
  self._callables = [s.random_weighted_callable(
@@ -223,6 +225,8 @@ class SliceDataset(torch.utils.data.Dataset):
223
225
 
224
226
  self._repeat = repeat
225
227
 
228
+ self.tfm = tfm
229
+
226
230
  def __len__(self):
227
231
  return len(self._callables) * self._repeat
228
232
 
@@ -232,4 +236,9 @@ class SliceDataset(torch.utils.data.Dataset):
232
236
  if i >= length:
233
237
  raise IndexError(f"Index {i} is larger than length of SliceDataset {length}")
234
238
 
235
- return self._callables[i % len(self._callables)]()
239
+ img, seg = self._callables[i % len(self._callables)]()
240
+
241
+ if self.tfm is not None:
242
+ return self.tfm(img, seg)
243
+
244
+ return img, seg
@@ -9,29 +9,41 @@ ARR = TypeVar("ARR", bound=Any)
9
9
  def crop(
10
10
  arr: ARR,
11
11
  reduction: Sequence[int],
12
- where: Literal["start", "end", "center"] = "center",
12
+ where: Literal["start", "end", "center", "random"] = "center",
13
13
  ) -> ARR:
14
- """Crop ``input`` such that ``output.shape[i] = input.shape[i] - reduction[i]``"""
14
+ """Crop ``arr`` such that ``output.shape[i] = input.shape[i] - reduction[i]``"""
15
15
 
16
- # create slices
17
- if where == 'center':
18
- slices = [(int(i / 2), -int(i / 2)) if i % 2 == 0 else (int(i / 2), -int(i / 2) - 1) for i in reduction]
16
+ shape = arr.shape[-len(reduction):]
17
+ slices = []
19
18
 
20
- elif where == 'start':
21
- slices = [(None, -i) for i in reduction]
19
+ for r, sh in zip(reduction, shape):
20
+ if r == 0:
21
+ slices.append(slice(None))
22
+ continue
22
23
 
23
- elif where == 'end':
24
- slices = [(i, None) for i in reduction]
24
+ if r < 0: raise ValueError("Reduction cannot be negative")
25
+ if r > sh: raise ValueError(f"Reduction {r} exceeds dimension size {sh}")
25
26
 
26
- slices = [slice(i if i!=0 else None, j if j != 0 else None) for i, j in slices]
27
+ if where == 'start': start, end = 0, sh - r
28
+ elif where == 'end': start, end = r, sh
29
+ elif where == 'center':
30
+ start = r // 2
31
+ end = start + (sh - r)
32
+ elif where == 'random':
33
+ start = random.randint(0, r)
34
+ end = start + (sh - r)
35
+ else:
36
+ raise ValueError(f"Invalid where: {where}")
27
37
 
28
- # crop with broadcasting
38
+ slices.append(slice(start, end))
39
+
40
+ # apply with broadcasting
29
41
  return arr[(..., *slices)]
30
42
 
31
43
  def crop_to_shape(
32
44
  input: ARR,
33
45
  shape: Sequence[int],
34
- where: Literal["start", "end", "center"] = "center",
46
+ where: Literal["start", "end", "center", "random"] = "center",
35
47
  ) -> ARR:
36
48
  """Crop ``input`` to ``shape``."""
37
49
 
@@ -42,7 +54,6 @@ def crop_to_shape(
42
54
  return crop(input, [i - j for i, j in zip(input.shape, shape)], where=where)
43
55
 
44
56
 
45
-
46
57
  def shuffle_channels(x:torch.Tensor):
47
58
  """Shuffle first axis in a ``(C, *)`` tensor"""
48
59
  return x[torch.randperm(x.shape[0])]
mrid/utils/plotting.py CHANGED
@@ -1,11 +1,12 @@
1
1
  from collections.abc import Mapping
2
2
 
3
- import matplotlib.gridspec as gridspec
4
- import matplotlib.pyplot as plt
5
3
  import numpy as np
6
4
  from ..loading import ImageLike, tonumpy
7
5
 
8
6
  def plot_study(data: Mapping[str, ImageLike]):
7
+ import matplotlib.gridspec as gridspec
8
+ import matplotlib.pyplot as plt
9
+
9
10
  data = {k: tonumpy(v) for k,v in data.items()}
10
11
  n_vals = len(data)
11
12
 
@@ -77,7 +78,8 @@ def plot_study(data: Mapping[str, ImageLike]):
77
78
  fig.text(box.x0 + box.width/2, box.y1 + 0.04, modality_name,
78
79
  ha='center', va='bottom', fontsize=14, fontweight='bold')
79
80
 
80
- plt.show()
81
+ # plt.show()
82
+ return fig
81
83
 
82
84
  if __name__ == "__main__":
83
85
 
@@ -97,8 +99,8 @@ if __name__ == "__main__":
97
99
  return mask
98
100
 
99
101
  plot_study({
100
- "T1 Weighted (Sphere)": dummy_data((60, 60, 60), 'sphere'),
101
- "T2 Weighted (Cube)": dummy_data((60, 60, 60), 'cube'),
102
- "Proton Density (Noise)": dummy_data((60, 60, 60), 'noise'),
103
- "FLAIR (Sphere)": dummy_data((60, 60, 60), 'sphere'),
102
+ "Sphere": dummy_data((60, 60, 60), 'sphere'),
103
+ "Cube": dummy_data((60, 60, 60), 'cube'),
104
+ "Noise": dummy_data((60, 60, 60), 'noise'),
105
+ "Another sphere": dummy_data((60, 60, 60), 'sphere'),
104
106
  })
mrid/utils/stl_utils.py CHANGED
@@ -6,6 +6,69 @@ import SimpleITK as sitk
6
6
 
7
7
  from ..loading.convert import tositk
8
8
 
9
+ # def stl2sitk(
10
+ # stl_path: str | os.PathLike,
11
+ # reference: str | os.PathLike | sitk.Image,
12
+ # fix_holes: bool = False,
13
+ # ):
14
+ # """
15
+ # Loads an STL file under ``stl_path`` and converts it to ``sitk.Image`` aligned with ``reference``.
16
+ # Note that this might take a few minutes.
17
+
18
+ # Args:
19
+ # stl_path (str): Path to the STL segmentation file. The STL coordinates
20
+ # MUST be in the same coordinate system as the ``reference``.
21
+ # reference (str | os.PathLike | sitk.Image): path to a directory of DICOM files or a NIfTI file, or a ``sitk.Image``.
22
+ # fix_holes (bool, optional): whether to try to fix holes in STL if they are detected (this can be very slow).
23
+ # """
24
+ # import trimesh
25
+
26
+ # # ------------------------------ load reference ------------------------------ #
27
+ # reference = tositk(reference)
28
+
29
+ # origin = np.array(reference.GetOrigin())
30
+ # spacing = np.array(reference.GetSpacing())
31
+ # # ct_direction = np.array(ct_image.GetDirection()).reshape(3, 3)
32
+ # size = np.array(reference.GetSize()) # Order: x, y, z
33
+ # shape_xyz = size
34
+ # shape_zyx = size[::-1]
35
+
36
+ # # --------------------------------- load STL --------------------------------- #
37
+ # mesh = trimesh.load_mesh(stl_path)
38
+
39
+ # if not mesh.is_watertight:
40
+ # warnings.warn(f"Warning: STL mesh '{stl_path}' is not watertight. Voxelization using 'contains' might be inaccurate.")
41
+ # if fix_holes:
42
+ # mesh.fill_holes()
43
+ # if not mesh.is_watertight:
44
+ # warnings.warn("Warning: Failed to make mesh watertight after filling holes.")
45
+
46
+ # # ------------------------------- voxelize STL ------------------------------- #
47
+ # x_coords = origin[0] + np.arange(shape_xyz[0]) * spacing[0]
48
+ # y_coords = origin[1] + np.arange(shape_xyz[1]) * spacing[1]
49
+ # z_coords = origin[2] + np.arange(shape_xyz[2]) * spacing[2]
50
+
51
+ # # Use meshgrid to create a grid of coordinates
52
+ # # Note the 'ij' indexing to match the z, y, x array structure
53
+ # zz, yy, xx = np.meshgrid(z_coords, y_coords, x_coords, indexing='ij')
54
+
55
+ # # Stack coordinates into a (N, 3) array where N = Z*Y*X
56
+ # voxel_centers_xyz = np.stack([xx.ravel(), yy.ravel(), zz.ravel()], axis=-1)
57
+
58
+ # # This checks which voxel center points fall inside the mesh volume
59
+ # voxel_mask_flat = mesh.contains(voxel_centers_xyz)
60
+
61
+ # # Reshape the flat boolean mask back into the 3D CT shape (z, y, x)
62
+ # stl_array = voxel_mask_flat.reshape(shape_zyx).astype(np.uint8) # Use uint8 for masks
63
+
64
+ # # ------------------------------ make sitk.Image ----------------------------- #
65
+ # stl_sitk = sitk.GetImageFromArray(stl_array)
66
+
67
+ # stl_sitk.SetOrigin(reference.GetOrigin())
68
+ # stl_sitk.SetSpacing(reference.GetSpacing())
69
+ # stl_sitk.SetDirection(reference.GetDirection())
70
+ # return stl_sitk
71
+
9
72
  def stl2sitk(
10
73
  stl_path: str | os.PathLike,
11
74
  reference: str | os.PathLike | sitk.Image,
@@ -25,11 +88,10 @@ def stl2sitk(
25
88
 
26
89
  # ------------------------------ load reference ------------------------------ #
27
90
  reference = tositk(reference)
28
-
29
91
  origin = np.array(reference.GetOrigin())
30
92
  spacing = np.array(reference.GetSpacing())
31
- # ct_direction = np.array(ct_image.GetDirection()).reshape(3, 3)
32
- size = np.array(reference.GetSize()) # Order: x, y, z
93
+ size = np.array(reference.GetSize()) # x, y, z
94
+
33
95
  shape_xyz = size
34
96
  shape_zyx = size[::-1]
35
97
 
@@ -37,34 +99,44 @@ def stl2sitk(
37
99
  mesh = trimesh.load_mesh(stl_path)
38
100
 
39
101
  if not mesh.is_watertight:
40
- warnings.warn(f"Warning: STL mesh '{stl_path}' is not watertight. Voxelization using 'contains' might be inaccurate.")
102
+ warnings.warn(f"STL mesh '{stl_path}' is not watertight.")
41
103
  if fix_holes:
42
104
  mesh.fill_holes()
43
- if not mesh.is_watertight:
44
- print("Warning: Failed to make mesh watertight after filling holes.")
45
105
 
46
- # ------------------------------- voxelize STL ------------------------------- #
106
+ # ----------------------- find bounding box of the mask ---------------------- #
107
+ bounds = mesh.bounds # [[min_x, min_y, min_z], [max_x, max_y, max_z]]
108
+ voxel_min = np.floor((bounds[0] - origin) / spacing).astype(int)
109
+ voxel_max = np.ceil((bounds[1] - origin) / spacing).astype(int)
110
+
111
+ z_min, z_max = np.clip([voxel_min[2], voxel_max[2]], 0, shape_xyz[2] - 1)
112
+
113
+ # --------------------------------- voxelize --------------------------------- #
114
+ stl_array = np.zeros(shape_zyx, dtype=np.uint8)
47
115
  x_coords = origin[0] + np.arange(shape_xyz[0]) * spacing[0]
48
116
  y_coords = origin[1] + np.arange(shape_xyz[1]) * spacing[1]
49
- z_coords = origin[2] + np.arange(shape_xyz[2]) * spacing[2]
50
117
 
51
- # Use meshgrid to create a grid of coordinates
52
- # Note the 'ij' indexing to match the z, y, x array structure
53
- zz, yy, xx = np.meshgrid(z_coords, y_coords, x_coords, indexing='ij')
118
+ yy, xx = np.meshgrid(y_coords, x_coords, indexing='ij')
119
+ points_2d = np.stack([xx.ravel(), yy.ravel()], axis=-1)
120
+
121
+ for z_idx in range(z_min, z_max + 1):
122
+ z_val = origin[2] + z_idx * spacing[2]
54
123
 
55
- # Stack coordinates into a (N, 3) array where N = Z*Y*X
56
- voxel_centers_xyz = np.stack([xx.ravel(), yy.ravel(), zz.ravel()], axis=-1)
124
+ # Create 3D points for this slice: [X, Y, current_Z]
125
+ points_3d = np.column_stack([
126
+ points_2d,
127
+ np.full(points_2d.shape[0], z_val)
128
+ ])
57
129
 
58
- # This checks which voxel center points fall inside the mesh volume
59
- voxel_mask_flat = mesh.contains(voxel_centers_xyz)
130
+ # Check containment for this slice only
131
+ mask_flat = mesh.contains(points_3d)
60
132
 
61
- # Reshape the flat boolean mask back into the 3D CT shape (z, y, x)
62
- stl_array = voxel_mask_flat.reshape(shape_zyx).astype(np.uint8) # Use uint8 for masks
133
+ # Reshape and insert into the 3D array
134
+ stl_array[z_idx, :, :] = mask_flat.reshape(shape_xyz[1], shape_xyz[0])
63
135
 
64
136
  # ------------------------------ make sitk.Image ----------------------------- #
65
137
  stl_sitk = sitk.GetImageFromArray(stl_array)
66
-
67
138
  stl_sitk.SetOrigin(reference.GetOrigin())
68
139
  stl_sitk.SetSpacing(reference.GetSpacing())
69
140
  stl_sitk.SetDirection(reference.GetDirection())
141
+
70
142
  return stl_sitk
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mrid-python
3
- Version: 0.1.4
3
+ Version: 0.1.5
4
4
  Summary: Tools for working with 3D medical images and segmentations - registration, brain skull-stripping, etc.
5
5
  Author-email: Ivan Nikishev <nkshv2@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/inikishev/mrid
@@ -16,7 +16,7 @@ Requires-Dist: SimpleITK
16
16
 
17
17
  mrid is a library for preprocessing of 3D images, particularly medical images.
18
18
 
19
- It provide interfaces for many medical image processing tools such as [SimpleElastix](https://simpleelastix.github.io/), [HD-BET](https://github.com/MIC-DKFZ/HD-BET#Installation), [SynthStrip](https://surfer.nmr.mgh.harvard.edu/docs/synthstrip/), [CTSeg](https://github.com/WCHN/CTseg). Note that those libraries are not bundled with mrid, I've included installation instructions in all notebooks.
19
+ It provide interfaces for many medical image processing tools such as [SimpleElastix](https://simpleelastix.github.io/), [HD-BET](https://github.com/MIC-DKFZ/HD-BET#Installation), [SynthStrip](https://surfer.nmr.mgh.harvard.edu/docs/synthstrip/), [CTSeg](https://github.com/WCHN/CTseg). Note that those libraries are not bundled, installation instructions are included in all examples below.
20
20
 
21
21
  ### Installation
22
22
 
@@ -32,10 +32,6 @@ or
32
32
  pip install git+https://github.com/inikishev/mrid
33
33
  ```
34
34
 
35
- ### Basics
36
-
37
- The images you pass to all functions in mrid can be path to a .nii.gz file, DICOM directory, sitk.Image, numpy array or torch tensor. All functions return results as `sitk.Image`. If you need a numpy array, you can use `mrid.tonumpy(sitk_image)`.
38
-
39
35
  ### Registering images with SimpleITK-SimpleElastix
40
36
 
41
37
  [SimpleElastix](https://simpleelastix.github.io/) is a robust tool for image registration which works really well out-of-the-box. It works on both Windows and Linux.
@@ -69,7 +65,7 @@ Many [BraTS](https://www.synapse.org/brats) datasets are provided as skullstripp
69
65
 
70
66
  <img width="828" height="849" alt="image" src="https://github.com/user-attachments/assets/f1b38db3-6648-4660-a381-d68a2eb8508d" />
71
67
 
72
- (T1n image looks weird because that's just how it is in the dataset)
68
+ (T1n image looks weird because that's just how it is in the zenodo dataset)
73
69
 
74
70
  ### References
75
71
  The MRIs for all images above are from https://zenodo.org/records/7213153.
@@ -1,5 +1,5 @@
1
1
  mrid/__init__.py,sha256=d-h6l6e-sfPUKDo27_dXcx8IRdZ5ByDC1YtdkM6y39o,138
2
- mrid/study.py,sha256=oLvpsPCAbm0bhofl5wt4ekRedZo_59v5sTIEPhJZlKw,29084
2
+ mrid/study.py,sha256=32dZe3x40lbKvIFsuQ8xaPJLEKElM-wGUv5Sm56J13g,29524
3
3
  mrid/atlas/__init__.py,sha256=ecOAVXUOt0TUI_wK19Ed05arF6b_G61wucVq2c8U4ZY,110
4
4
  mrid/atlas/MNI152/__init__.py,sha256=XTakiwb_rFxpO08O45tVKyHDzcT3xvJoLLMLu5EJhGw,3038
5
5
  mrid/atlas/SRI24/__init__.py,sha256=jFFY6LeJcWwCtxVJGbdWPEUGFKZjqzxu01ZTbbSAwEo,2939
@@ -9,26 +9,26 @@ mrid/preprocessing/CTseg.py,sha256=Zum1bvvU28JptrXLIOAaWVQA6LWPfaheCo-6liN1Gq4,3
9
9
  mrid/preprocessing/__init__.py,sha256=UNkuK_OKqBSdAt7n8dc7t5p7206W2wwksJwGy7rXdr0,412
10
10
  mrid/preprocessing/bias_field_correction.py,sha256=K2w75JkVIV_Gj7W-9gpHUwz2teEvgSRt5wX-CqQeRik,1143
11
11
  mrid/preprocessing/cropping.py,sha256=I26hxo7RuWqdjx8ApJ2t9FBPdi3PtRsBdMlLC2KgKTk,1336
12
- mrid/preprocessing/hd_bet.py,sha256=W7jsWVs7ZdoDBZe_OvSJa6zMOB9hpNK1_7SM_9s9VOY,11231
13
- mrid/preprocessing/mask.py,sha256=oD0jONMHlSpOBLuvzF35nc_cZ30L9q8cCIELBmAlffE,1375
12
+ mrid/preprocessing/hd_bet.py,sha256=ybjtKfrdI5sFkjPZHh1rTPaMPy8JA7cIR71UlElKZ2A,11269
13
+ mrid/preprocessing/mask.py,sha256=-x-9-CMbUR7Jx8p7YVtBb3r-OnGut9YefmEVI04jyDc,1350
14
14
  mrid/preprocessing/simple_elastix.py,sha256=gddw-B-IpGIyhfqj05kex8Zqb3_gMD8Z8w0BiLlx8dA,7975
15
- mrid/preprocessing/spatial.py,sha256=lV9XVf3jrHGJfRcEvgImlg6Rxb42WAZAYb0QLKgBpbA,4264
16
- mrid/preprocessing/synthstrip.py,sha256=fS3BESkOS9Du--zxBtKbqrt5e12WSyWEVnP7KBlJuk0,10149
15
+ mrid/preprocessing/spatial.py,sha256=hsbGprUEcbeH0FY0_cgHZHjDOXGF55-ojtznuHttLkU,4316
16
+ mrid/preprocessing/synthstrip.py,sha256=TaRFJn4vPmZ7V-SmKdaOTR3yZakoA3OfhwdjiBQg2Xs,10501
17
17
  mrid/training/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- mrid/training/slicer.py,sha256=mbis5a9D4wBCCxN1Gp9WAFnusxfMmiCq0Y_f04yi6y4,10239
19
- mrid/training/transforms.py,sha256=ZVpvk-19DfE6-PjgWqwaDcyI3_zPrN5DgiHI1644oMM,3641
18
+ mrid/training/slicer.py,sha256=7eRQMDQ40FvpHs2J_eg_VoMnI42cOXG_tmgTDtYvVx8,10454
19
+ mrid/training/transforms.py,sha256=fWoXH3KbkN3s-_A3h0bqt2yz9Ek5K8PzSIBaz3s5goM,4005
20
20
  mrid/utils/__init__.py,sha256=vDuaVk8uKs84R_Fg4RmGhQ-qHmpzS4--wPM86opvgig,153
21
21
  mrid/utils/dcm2niix.py,sha256=KHU7dgUVsU7WOEa82HFO-G0SsGIxYjeUFEBcX27mkWQ,4124
22
22
  mrid/utils/dicom_uid_fixer.py,sha256=xjjfvAF13Ni5CEgwPcr7oDT7excdEoeRGQetvjGkSRM,4046
23
- mrid/utils/plotting.py,sha256=UeEtMRZapJXKT27_567sELGgYZjJDsr-gPl-A6Xb33A,3904
23
+ mrid/utils/plotting.py,sha256=bALTvNQxGXWl0fxGdKbSON47dz4OXfxVYEn6WeqdNK4,3885
24
24
  mrid/utils/python_utils.py,sha256=IYB8-KN4d6l-5ILgUNYux0vNrK0D0vh0AbSRqWhs-fo,1793
25
- mrid/utils/stl_utils.py,sha256=T4qT11NgvFCERfaB3duA_O1Qi9FBW7-b3B5aZKS5UU4,2888
25
+ mrid/utils/stl_utils.py,sha256=vR6CqG1D4cpDmjkcZU0H5jCMVROeLwOTAzvoQzipgxs,5869
26
26
  mrid/utils/torch_utils.py,sha256=nKby_tPWzCGdEASkDUq7xNo-iUe9zjwmhq_chV0RCGU,407
27
27
  tests/test_loading.py,sha256=CciTvnqQ7hY-Vk2GgH2SWSu8nyS8lLeBqHu3XzClBLY,2141
28
28
  tests/test_preprocessing.py,sha256=HcoubITfmlJkgPaBbkxZiY04fJv9URBGMWDJWivDqLU,1300
29
29
  tests/test_study.py,sha256=pK__azqnMz8DSyOYaWQmBkpAqLntCIIjEz0m8oBS4RQ,3984
30
30
  tests/test_utils.py,sha256=Q4uf5dog673RDofOC6RPLYnxVN5NAIVRTiyTbf9Az74,359
31
- mrid_python-0.1.4.dist-info/METADATA,sha256=hxI4NAcTSuESvPcm__RfsqQKi5pi4nQS38tO0Ko94fI,4054
32
- mrid_python-0.1.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
33
- mrid_python-0.1.4.dist-info/top_level.txt,sha256=lBv75ms7UoIM4elDVX3CbKiSlh-X0vABtaUwj4TGx4o,11
34
- mrid_python-0.1.4.dist-info/RECORD,,
31
+ mrid_python-0.1.5.dist-info/METADATA,sha256=F5EwkIBp-KDPbLvwuMFWbsE-S-AUMk4OkuoK9u0jkm0,3795
32
+ mrid_python-0.1.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
33
+ mrid_python-0.1.5.dist-info/top_level.txt,sha256=lBv75ms7UoIM4elDVX3CbKiSlh-X0vABtaUwj4TGx4o,11
34
+ mrid_python-0.1.5.dist-info/RECORD,,