OSCR

Learning-based segmentation of diffusion-weighted MR images with arbitrary <i>q</i>-space samplings.

Code ↔ Paper

11 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 11 matches · 2 of them tie a paragraph to a whole file, not to given lines: weak matches, whose lines are not tinted
  1. [1] § Materials and Methods › Data › Preprocessing › Reference segmentations ↔ DISCUSseg/data_loader/dkt_labels.py, lines 1–23 · score 0.69 · Desikan Killiany Tourville, DKT, FreeSurfer, mapping
  2. [2] § Materials and Methods › Evaluation › Labels ↔ data_loader/load_neuroimaging_data.py, lines 405–520 · score 0.69 · corpus callosum, cortical region, hemisphere, hypointensities, lateral, ventricles
  3. [3] § Materials and Methods › Method › Architecture › Details ↔ DISCUSseg/run_prediction.py, lines 178–297 · score 0.63 · brain mask, DISCUS encoder, sagittal, axial, memory, inference
  4. [4] § Materials and Methods › Method › Architecture › Details ↔ DISCUSseg/data_loader/discus_preprocessing.py, the whole file · a weak match · score 0.63 · brain mask, DISCUS encoder, inside, width, inference, volume
  5. [5] § Materials and Methods › Method › Training › Phase 1: DISCUS pre-training ↔ DISCUSseg/train.py, lines 171–242 · score 0.62 · DISCUS reconstructs, train DISCUS, DISCUS encoder, decoder, loss, signals
  6. [6] § Materials and Methods › Method › Training › Phase 1: DISCUS pre-training ↔ DISCUSseg/data_loader/discus_preprocessing.py, the whole file · a weak match · score 0.59 · DISCUS reconstructs, DISCUS encoder, subsets, decoder, loss, signals
  7. [7] § Materials and Methods › Reference methods › SynthSeg ↔ process.sh, lines 1–59 · score 0.54 · fractional anisotropy, diffusion tensor, FA, fitted, brain, mask
  8. [8] § Materials and Methods › Data › Preprocessing › Masks ↔ DISCUSseg/run_prediction.py, lines 319–407 · score 0.54 · brain mask, FSL, binary, probability, inference, maps
  9. [9] § Materials and Methods › Reference methods › DDParcel ↔ process.sh, lines 1–59 · score 0.52 · fractional anisotropy, diffusion tensor, eigenvalues, brain, masking, .2
  10. [10] § Materials and Methods › Method › Training › Alternative training schemes › Multi-task training for segmentation and reconstruction ↔ DISCUSseg/train.py, lines 171–242 · score 0.52 · DISCUS encoder, rotation, embeddings, reconstruction, decoder, augmented
  11. [11] § Materials and Methods › Data › Preprocessing › Reference segmentations ↔ data_loader/load_neuroimaging_data.py, lines 405–520 · score 0.51 · corpus callosum, vessels, DKT, cortical, mapping, segmentations

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

Python · 987 lines · 36 KB · Unlicense · 2 matches

  1. # Copyright 2019 Image Analysis Lab, German Center for Neurodegenerative Diseases (DZNE), Bonn
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. # IMPORTS
  15. import nibabel as nib
  16. import numpy as np
  17. import h5py
  18. import scipy.ndimage.morphology as morphology
  19. import scipy.ndimage as ndimage
  20. import scipy.ndimage.filters as filters
  21. import sys
  22. import glob, os
  23. from skimage.measure import label
  24. from torch.utils.data.dataset import Dataset
  25. from .conform import is_conform, conform, check_affine_in_nifti
  26. ##
  27. # Helper Functions
  28. ##
  29. # Conform an MRI brain image to UCHAR, RAS orientation, and 1mm isotropic voxels
  30. def load_and_conform_image(img_filename, interpol=1, logger=None, imagetype='image'):
  31. """
  32. Function to load MRI image and conform it to UCHAR, RAS orientation and 1mm isotropic voxels size
  33. (if it does not already have this format)
  34. :param str img_filename: path and name of volume to read
  35. :param int interpol: interpolation order for image conformation (0=nearest,1=linear(default),2=quadratic,3=cubic)
  36. :return:
  37. """
  38. orig = nib.load(img_filename)
  39. if not is_conform(orig):
  40. if logger is not None:
  41. logger.info('Conforming image to UCHAR, RAS orientation, and 1mm isotropic voxels')
  42. else:
  43. print('Conforming image to RAS orientation, and 1mm isotropic voxels')
  44. if len(orig.shape) > 3 and orig.shape[3] != 1:
  45. sys.exit('ERROR: Multiple input frames (' + format(orig.shape[3]) + ') not supported!')
  46. # Check affine if image is nifti image
  47. if img_filename[-7:] == ".nii.gz" or img_filename[-4:] == ".nii":
  48. if not check_affine_in_nifti(orig, logger=logger):
  49. sys.exit("ERROR: inconsistency in nifti-header. Exiting now.\n")
  50. # conform
  51. orig = conform(orig, interpol, imagetype=imagetype)
  52. # Collect header and affine information
  53. header_info = orig.header
  54. affine_info = orig.affine
  55. orig = np.asarray(orig.get_fdata(), dtype=np.float32)
  56. return header_info, affine_info, orig
  57. def cropparameters(orig_, img_filename):
  58. data = orig_.get_fdata()
  59. # Transformation for mapping
  60. def transform_axial(vol, coronal2axial=True):
  61. """
  62. Function to transform volume into Axial axis and back
  63. :param np.ndarray vol: image volume to transform
  64. :param bool coronal2axial: transform from coronal to axial = True (default),
  65. transform from axial to coronal = False
  66. :return:
  67. """
  68. if coronal2axial:
  69. return np.moveaxis(vol, [0, 1, 2], [1, 2, 0])
  70. else:
  71. return np.moveaxis(vol, [0, 1, 2], [2, 0, 1])
  72. def transform_sagittal(vol, coronal2sagittal=True):
  73. """
  74. Function to transform volume into Sagittal axis and back
  75. :param np.ndarray vol: image volume to transform
  76. :param bool coronal2sagittal: transform from coronal to sagittal = True (default),
  77. transform from sagittal to coronal = False
  78. :return:
  79. """
  80. if coronal2sagittal:
  81. return np.moveaxis(vol, [0, 1, 2], [2, 1, 0])
  82. else:
  83. return np.moveaxis(vol, [0, 1, 2], [2, 1, 0])
  84. # Thick slice generator (for eval) and blank slices filter (for training)
  85. def get_thick_slices(img_data, slice_thickness=3):
  86. """
  87. Function to extract thick slices from the image
  88. (feed slice_thickness preceeding and suceeding slices to network,
  89. label only middle one)
  90. :param np.ndarray img_data: 3D MRI image read in with nibabel
  91. :param int slice_thickness: number of slices to stack on top and below slice of interest (default=3)
  92. :return:
  93. """
  94. h, w, d = img_data.shape
  95. img_data_pad = np.expand_dims(np.pad(img_data, ((0, 0), (0, 0), (slice_thickness, slice_thickness)), mode='edge'),
  96. axis=3)
  97. img_data_thick = np.ndarray((h, w, d, 0), dtype=np.uint8)
  98. for slice_idx in range(2 * slice_thickness + 1):
  99. img_data_thick = np.append(img_data_thick, img_data_pad[:, :, slice_idx:d + slice_idx, :], axis=3)
  100. return img_data_thick
  101. def filter_blank_slices_thick(img_vol, label_vol, weight_vol, threshold=50):
  102. """
  103. Function to filter blank slices from the volume using the label volume
  104. :param np.ndarray img_vol: orig image volume
  105. :param np.ndarray label_vol: label images (ground truth)
  106. :param np.ndarray weight_vol: weight corresponding to labels
  107. :param int threshold: threshold for number of pixels needed to keep slice (below = dropped)
  108. :return:
  109. """
  110. # Get indices of all slices with more than threshold labels/pixels
  111. select_slices = (np.sum(label_vol, axis=(0, 1)) > threshold)
  112. # Retain only slices with more than threshold labels/pixels
  113. img_vol = img_vol[:, :, select_slices, :]
  114. label_vol = label_vol[:, :, select_slices]
  115. weight_vol = weight_vol[:, :, select_slices]
  116. return img_vol, label_vol, weight_vol
  117. # weight map generator
  118. def create_weight_mask(mapped_aseg, max_weight=5, max_edge_weight=5):
  119. """
  120. Function to create weighted mask - with median frequency balancing and edge-weighting
  121. :param mapped_aseg:
  122. :param max_weight:
  123. :param max_edge_weight:
  124. :return:
  125. """
  126. unique, counts = np.unique(mapped_aseg, return_counts=True)
  127. # Median Frequency Balancing
  128. class_wise_weights = np.median(counts) / counts
  129. class_wise_weights[class_wise_weights > max_weight] = max_weight
  130. (h, w, d) = mapped_aseg.shape
  131. weights_mask = np.reshape(class_wise_weights[mapped_aseg.ravel()], (h, w, d))
  132. # Gradient Weighting
  133. (gx, gy, gz) = np.gradient(mapped_aseg)
  134. grad_weight = max_edge_weight * np.asarray(np.power(np.power(gx, 2) + np.power(gy, 2) + np.power(gz, 2), 0.5) > 0,
  135. dtype=np.float32)
  136. weights_mask += grad_weight
  137. return weights_mask
  138. # class unknown filler (cortex)
  139. def fill_unknown_labels_per_hemi(gt, unknown_label, cortex_stop):
  140. """
  141. Function to replace label 1000 (lh unknown) and 2000 (rh unknown) with closest class for each voxel.
  142. :param np.ndarray gt: ground truth segmentation with class unknown
  143. :param int unknown_label: class label for unknown (lh: 1000, rh: 2000)
  144. :param int cortex_stop: class label at which cortical labels of this hemi stop (lh: 2000, rh: 3000)
  145. :return:
  146. """
  147. # Define shape of image and dilation element
  148. h, w, d = gt.shape
  149. struct1 = ndimage.generate_binary_structure(3, 2)
  150. # Get indices of unknown labels, dilate them to get closest sorrounding parcels
  151. unknown = gt == unknown_label
  152. unknown = (morphology.binary_dilation(unknown, struct1) ^ unknown)
  153. list_parcels = np.unique(gt[unknown])
  154. # Mask all subcortical structures (fill unknown with closest cortical parcels only)
  155. mask = (list_parcels > unknown_label) & (list_parcels < cortex_stop)
  156. list_parcels = list_parcels[mask]
  157. # For each closest parcel, blur label with gaussian filter (spread), append resulting blurred images
  158. blur_vals = np.ndarray((h, w, d, 0), dtype=np.float32)
  159. for idx in range(len(list_parcels)):
  160. aseg_blur = filters.gaussian_filter(1000 * np.asarray(gt == list_parcels[idx], dtype=np.float32), sigma=5)
  161. blur_vals = np.append(blur_vals, np.expand_dims(aseg_blur, axis=3), axis=3)
  162. # Get for each position parcel with maximum value after blurring (= closest parcel)
  163. unknown = np.argmax(blur_vals, axis=3)
  164. unknown = np.reshape(list_parcels[unknown.ravel()], (h, w, d))
  165. # Assign the determined closest parcel to the unknown class (case-by-case basis)
  166. mask = gt == unknown_label
  167. gt[mask] = unknown[mask]
  168. return gt
  169. # class unknown filler (cortex)
  170. def fill_WMhyper_per_hemi(gt, WMhyper_label=77, replace_labels=[2, 41]):
  171. """
  172. Function to replace label 1000 (lh unknown) and 2000 (rh unknown) with closest class for each voxel.
  173. :param np.ndarray gt: ground truth segmentation with class unknown
  174. :param int WMhyper_label: class label for unknown: 77
  175. :param int cortex_stop: class label at which cortical labels of this hemi stop (lh: 2000, rh: 3000)
  176. :return:
  177. """
  178. # Define shape of image and dilation element
  179. h, w, d = gt.shape
  180. struct1 = ndimage.generate_binary_structure(3, 2)
  181. # Get indices of unknown labels, dilate them to get closest sorrounding parcels
  182. unknown = gt == WMhyper_label
  183. if np.sum(unknown) == 0:
  184. return gt
  185. unknown = (morphology.binary_dilation(unknown, struct1) ^ unknown)
  186. list_parcels = np.unique(gt[unknown])
  187. if np.intersect1d(list_parcels, replace_labels).shape[0] > 0:
  188. list_parcels = np.intersect1d(list_parcels, replace_labels)
  189. # For each closest parcel, blur label with gaussian filter (spread), append resulting blurred images
  190. blur_vals = np.ndarray((h, w, d, 0), dtype=np.float32)
  191. for idx in range(len(list_parcels)):
  192. aseg_blur = filters.gaussian_filter(1000 * np.asarray(gt == list_parcels[idx], dtype=np.float32), sigma=5)
  193. blur_vals = np.append(blur_vals, np.expand_dims(aseg_blur, axis=3), axis=3)
  194. # Get for each position parcel with maximum value after blurring (= closest parcel)
  195. unknown = np.argmax(blur_vals, axis=3)
  196. unknown = np.reshape(list_parcels[unknown.ravel()], (h, w, d))
  197. # Assign the determined closest parcel to the unknown class (case-by-case basis)
  198. mask = gt == WMhyper_label
  199. gt[mask] = unknown[mask]
  200. return gt
  201. # Label mapping functions (to aparc (eval) and to label (train))
  202. def map_label2aparc_aseg(mapped_aseg):
  203. """
  204. Function to perform look-up table mapping from label space to aparc.DKTatlas+aseg space
  205. :param np.ndarray mapped_aseg: label space segmentation (aparc.DKTatlas + aseg)
  206. :return:
  207. """
  208. aseg = np.zeros_like(mapped_aseg)
  209. labels = np.array([0, 2, 4, 5, 7, 8, 10, 11, 12, 13, 14,
  210. 15, 16, 17, 18, 24, 26, 28, 31, 41, 43, 44,
  211. 46, 47, 49, 50, 51, 52, 53, 54, 58, 60, 63,
  212. 192, 1001, 1002, 1003, 1005, 1006, 1007, 1008, 1009, 1010, 1011,
  213. 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022,
  214. 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035,
  215. 2002, 2005, 2010, 2012, 2013, 2014, 2016, 2017, 2021, 2022, 2023,
  216. 2024, 2025, 2028])
  217. h, w, d = aseg.shape
  218. aseg = labels[mapped_aseg.ravel()]
  219. aseg = aseg.reshape((h, w, d))
  220. return aseg
  221. def map_aparc_aseg2label(aseg, aseg_nocc=None):
  222. """
  223. Function to perform look-up table mapping of aparc.DKTatlas+aseg.mgz data to label space
  224. :param np.ndarray aseg: ground truth aparc+aseg
  225. :param None/np.ndarray aseg_nocc: ground truth aseg without corpus callosum segmentation
  226. :return:
  227. """
  228. aseg = aseg.astype(np.int16)
  229. aseg_temp = aseg.copy()
  230. aseg[aseg == 80] = 77 # Hypointensities Class
  231. aseg[aseg == 85] = 0 # Optic Chiasma to BKG
  232. aseg[aseg == 62] = 41 # Right Vessel to Right WM
  233. aseg[aseg == 30] = 2 # Left Vessel to Left MW
  234. aseg[aseg == 72] = 24 # 5th Ventricle to CSF
  235. # Fan: change wm-lh wm-rh to cerebral WM
  236. aseg[(aseg_temp >= 3000) & (aseg_temp < 3999)] = 2
  237. aseg[(aseg_temp >= 4000) & (aseg_temp < 4999)] = 41
  238. aseg[aseg_temp == 5001] = 2
  239. aseg[aseg_temp == 5002] = 41
  240. # Fan: add CC
  241. aseg[(aseg >= 251) & (aseg <= 255)] = 251
  242. # If corpus callosum is not removed yet, do it now
  243. if aseg_nocc is not None:
  244. cc_mask = (aseg >= 251) & (aseg <= 255)
  245. aseg[cc_mask] = aseg_nocc[cc_mask]
  246. aseg[aseg == 3] = 0 # Map Remaining Cortical labels to background
  247. aseg[aseg == 42] = 0
  248. # If ctx-unknowns are not filled yet, do it now
  249. if np.any(np.in1d([1000, 2000], aseg.ravel())):
  250. aseg = fill_unknown_labels_per_hemi(aseg, 1000, 2000)
  251. aseg = fill_unknown_labels_per_hemi(aseg, 2000, 3000)
  252. # Fan: Make all right to be left:
  253. cortical_label_mask = (aseg >= 2000) & (aseg <= 2999)
  254. aseg[cortical_label_mask] = aseg[cortical_label_mask] - 1000
  255. # Preserve Cortical Labels: The ones not touching:
  256. # "cortical regions touching each other across the hemispheres, are lateralized
  257. # while all others are com- bined thus reducing the total number of labels from 95
  258. # (DKT without corpus callosum segmentations which are added later) to 78 during network training."
  259. aseg[aseg_temp == 2014] = 2014
  260. aseg[aseg_temp == 2028] = 2028
  261. aseg[aseg_temp == 2012] = 2012
  262. aseg[aseg_temp == 2016] = 2016
  263. aseg[aseg_temp == 2002] = 2002
  264. aseg[aseg_temp == 2023] = 2023
  265. aseg[aseg_temp == 2017] = 2017
  266. aseg[aseg_temp == 2024] = 2024
  267. aseg[aseg_temp == 2010] = 2010
  268. aseg[aseg_temp == 2013] = 2013
  269. aseg[aseg_temp == 2025] = 2025
  270. aseg[aseg_temp == 2022] = 2022
  271. aseg[aseg_temp == 2021] = 2021
  272. aseg[aseg_temp == 2005] = 2005
  273. # Here 251, 1001, 1032, 1033 are added.
  274. labels = np.array([0, 2, 4, 5, 7, 8, 10, 11, 12, 13, 14,
  275. 15, 16, 17, 18, 24, 26, 28, 31, 41, 43, 44,
  276. 46, 47, 49, 50, 51, 52, 53, 54, 58, 60, 63,
  277. 77, 251, 1001, 1002, 1003, 1005, 1006, 1007, 1008, 1009, 1010, 1011,
  278. 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022,
  279. 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035,
  280. 2002, 2005, 2010, 2012, 2013, 2014, 2016, 2017, 2021, 2022, 2023,
  281. 2024, 2025, 2028])
  282. h, w, d = aseg.shape
  283. lut_aseg = np.zeros(max(labels) + 1, dtype='int')
  284. for idx, value in enumerate(labels):
  285. lut_aseg[value] = idx
  286. # Remap Label Classes - Perform LUT Mapping - Coronal, Axial
  287. mapped_aseg = lut_aseg.ravel()[aseg.ravel()]
  288. mapped_aseg = mapped_aseg.reshape((h, w, d))
  289. # Map Sagittal Labels
  290. aseg[aseg == 2] = 41
  291. aseg[aseg == 3] = 42
  292. aseg[aseg == 4] = 43
  293. aseg[aseg == 5] = 44
  294. aseg[aseg == 7] = 46
  295. aseg[aseg == 8] = 47
  296. aseg[aseg == 10] = 49
  297. aseg[aseg == 11] = 50
  298. aseg[aseg == 12] = 51
  299. aseg[aseg == 13] = 52
  300. aseg[aseg == 17] = 53
  301. aseg[aseg == 18] = 54
  302. aseg[aseg == 26] = 58
  303. aseg[aseg == 28] = 60
  304. aseg[aseg == 31] = 63
  305. cortical_label_mask = (aseg >= 2000) & (aseg <= 2999)
  306. aseg[cortical_label_mask] = aseg[cortical_label_mask] - 1000
  307. labels_sag = np.array([0, 14, 15, 16, 24, 41, 43, 44, 46, 47, 49,
  308. 50, 51, 52, 53, 54, 58, 60, 63, 77, 251, 1001, 1002,
  309. 1003, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014,
  310. 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025,
  311. 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035])
  312. h, w, d = aseg.shape
  313. lut_aseg = np.zeros(max(labels_sag) + 1, dtype='int')
  314. for idx, value in enumerate(labels_sag):
  315. lut_aseg[value] = idx
  316. # Remap Label Classes - Perform LUT Mapping - Coronal, Axial
  317. mapped_aseg_sag = lut_aseg.ravel()[aseg.ravel()]
  318. mapped_aseg_sag = mapped_aseg_sag.reshape((h, w, d))
  319. return mapped_aseg, mapped_aseg_sag
  320. def map_wmparc2label(aseg):
  321. """
  322. Function to perform look-up table mapping of aparc.DKTatlas+aseg.mgz data to label space
  323. :param np.ndarray aseg: ground truth aparc+aseg
  324. :param None/np.ndarray aseg_nocc: ground truth aseg without corpus callosum segmentation
  325. :return:
  326. """
  327. aseg = aseg.astype(np.int16)
  328. aseg_temp = aseg.copy()
  329. aseg[aseg == 80] = 77 # Hypointensities Class
  330. aseg[aseg == 85] = 0 # Optic Chiasma to BKG
  331. aseg[aseg == 62] = 41 # Right Vessel to Right WM
  332. aseg[aseg == 30] = 2 # Left Vessel to Left MW
  333. aseg[aseg == 72] = 24 # 5th Ventricle to CSF
  334. # Fan: change wm-lh wm-rh to cerebral WM
  335. aseg[(aseg_temp >= 3000) & (aseg_temp < 3999)] = 2
  336. aseg[(aseg_temp >= 4000) & (aseg_temp < 4999)] = 41
  337. aseg[aseg_temp == 5001] = 2
  338. aseg[aseg_temp == 5002] = 41
  339. # Fan: combine CC to Corpus_Callosum
  340. aseg[(aseg >= 251) & (aseg <= 255)] = 192
  341. aseg[aseg == 3] = 0 # Map Remaining Cortical labels to background
  342. aseg[aseg == 42] = 0
  343. # If ctx-unknowns are not filled yet, do it now
  344. if np.any(np.in1d([1000, 2000], aseg.ravel())):
  345. aseg = fill_unknown_labels_per_hemi(aseg, 1000, 2000)
  346. aseg = fill_unknown_labels_per_hemi(aseg, 2000, 3000)
  347. aseg = fill_WMhyper_per_hemi(aseg) # Remove hyperinstensity
  348. # Fan: Make all right to be left:
  349. cortical_label_mask = (aseg >= 2000) & (aseg <= 2999)
  350. aseg[cortical_label_mask] = aseg[cortical_label_mask] - 1000
  351. # Preserve Cortical Labels: The ones not touching:
  352. # "cortical regions touching each other across the hemispheres, are lateralized
  353. # while all others are com- bined thus reducing the total number of labels from 95
  354. # (DKT without corpus callosum segmentations which are added later) to 78 during network training."
  355. aseg[aseg_temp == 2014] = 2014
  356. aseg[aseg_temp == 2028] = 2028
  357. aseg[aseg_temp == 2012] = 2012
  358. aseg[aseg_temp == 2016] = 2016
  359. aseg[aseg_temp == 2002] = 2002
  360. aseg[aseg_temp == 2023] = 2023
  361. aseg[aseg_temp == 2017] = 2017
  362. aseg[aseg_temp == 2024] = 2024
  363. aseg[aseg_temp == 2010] = 2010
  364. aseg[aseg_temp == 2013] = 2013
  365. aseg[aseg_temp == 2025] = 2025
  366. aseg[aseg_temp == 2022] = 2022
  367. aseg[aseg_temp == 2021] = 2021
  368. aseg[aseg_temp == 2005] = 2005
  369. # Here 192, 1001, 1032, 1033 are added.
  370. labels = np.array([0, 2, 4, 5, 7, 8, 10, 11, 12, 13, 14, 15,
  371. 16, 17, 18, 24, 26, 28, 31, 41, 43, 44, 46, 47,
  372. 49, 50, 51, 52, 53, 54, 58, 60, 63, 192,
  373. 1001, 1002, 1003, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012,
  374. 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023,
  375. 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035,
  376. 2002, 2005, 2010, 2012, 2013, 2014, 2016, 2017, 2021, 2022, 2023,
  377. 2024, 2025, 2028])
  378. h, w, d = aseg.shape
  379. lut_aseg = np.zeros(max(labels) + 1, dtype='int')
  380. for idx, value in enumerate(labels):
  381. lut_aseg[value] = idx
  382. # Remap Label Classes - Perform LUT Mapping - Coronal, Axial
  383. mapped_aseg = lut_aseg.ravel()[aseg.ravel()]
  384. mapped_aseg = mapped_aseg.reshape((h, w, d))
  385. # Map Sagittal Labels
  386. aseg[aseg == 2] = 41
  387. aseg[aseg == 3] = 42
  388. aseg[aseg == 4] = 43
  389. aseg[aseg == 5] = 44
  390. aseg[aseg == 7] = 46
  391. aseg[aseg == 8] = 47
  392. aseg[aseg == 10] = 49
  393. aseg[aseg == 11] = 50
  394. aseg[aseg == 12] = 51
  395. aseg[aseg == 13] = 52
  396. aseg[aseg == 17] = 53
  397. aseg[aseg == 18] = 54
  398. aseg[aseg == 26] = 58
  399. aseg[aseg == 28] = 60
  400. aseg[aseg == 31] = 63
  401. cortical_label_mask = (aseg >= 2000) & (aseg <= 2999)
  402. aseg[cortical_label_mask] = aseg[cortical_label_mask] - 1000
  403. labels_sag = np.array([0, 14, 15, 16, 24, 41, 43, 44, 46, 47, 49,
  404. 50, 51, 52, 53, 54, 58, 60, 63, 192,
  405. 1001, 1002, 1003, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013,
  406. 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025,
  407. 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035])
  408. h, w, d = aseg.shape
  409. lut_aseg = np.zeros(max(labels_sag) + 1, dtype='int')
  410. for idx, value in enumerate(labels_sag):
  411. lut_aseg[value] = idx
  412. # Remap Label Classes - Perform LUT Mapping - Coronal, Axial
  413. mapped_aseg_sag = lut_aseg.ravel()[aseg.ravel()]
  414. mapped_aseg_sag = mapped_aseg_sag.reshape((h, w, d))
  415. return mapped_aseg, mapped_aseg_sag
  416. def map_wmparc2gtseg(aseg):
  417. """
  418. Function to perform look-up table mapping of aparc.DKTatlas+aseg.mgz data to label space
  419. :param np.ndarray aseg: ground truth aparc+aseg
  420. :param None/np.ndarray aseg_nocc: ground truth aseg without corpus callosum segmentation
  421. :return:
  422. """
  423. aseg = aseg.astype(np.int16)
  424. aseg_temp = aseg.copy()
  425. aseg[aseg == 80] = 77 # Hypointensities Class
  426. aseg[aseg == 85] = 0 # Optic Chiasma to BKG
  427. aseg[aseg == 62] = 41 # Right Vessel to Right WM
  428. aseg[aseg == 30] = 2 # Left Vessel to Left MW
  429. aseg[aseg == 72] = 24 # 5th Ventricle to CSF
  430. # Fan: change wm-lh wm-rh to cerebral WM
  431. aseg[(aseg_temp >= 3000) & (aseg_temp < 3999)] = 2
  432. aseg[(aseg_temp >= 4000) & (aseg_temp < 4999)] = 41
  433. aseg[aseg_temp == 5001] = 2
  434. aseg[aseg_temp == 5002] = 41
  435. # Fan: combine CC to Corpus_Callosum
  436. aseg[(aseg >= 251) & (aseg <= 255)] = 192
  437. aseg[aseg == 3] = 0 # Map Remaining Cortical labels to background
  438. aseg[aseg == 42] = 0
  439. # If ctx-unknowns are not filled yet, do it now
  440. if np.any(np.in1d([1000, 2000], aseg.ravel())):
  441. aseg = fill_unknown_labels_per_hemi(aseg, 1000, 2000)
  442. aseg = fill_unknown_labels_per_hemi(aseg, 2000, 3000)
  443. aseg = fill_WMhyper_per_hemi(aseg) # Remove hyperinstensity
  444. return aseg
  445. def sagittal_coronal_remap_lookup(x):
  446. """
  447. Dictionary mapping to convert left labels to corresponding right labels for aseg
  448. :param int x: label to look up
  449. :return:
  450. """
  451. return {
  452. 2: 41,
  453. 3: 42,
  454. 4: 43,
  455. 5: 44,
  456. 7: 46,
  457. 8: 47,
  458. 10: 49,
  459. 11: 50,
  460. 12: 51,
  461. 13: 52,
  462. 17: 53,
  463. 18: 54,
  464. 26: 58,
  465. 28: 60,
  466. 31: 63,
  467. }[x]
  468. def map_prediction_sagittal2full(prediction_sag, num_classes=85):
  469. """
  470. Function to remap the prediction on the sagittal network to full label space used by coronal and axial networks
  471. (full aparc.DKTatlas+aseg.mgz)
  472. :param prediction_sag: sagittal prediction (labels)
  473. :param int num_classes: number of classes (96 for full classes, 79 for hemi split)
  474. :return: Remapped prediction
  475. """
  476. if num_classes == 96:
  477. idx_list = np.asarray([0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 2, 3, 14, 15, 4, 16,
  478. 17, 18, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
  479. 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
  480. 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 20, 21, 22,
  481. 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
  482. 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50], dtype=np.int16)
  483. else:
  484. # idx_list = np.asarray([0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1, 2, 3, 14, 15, 4, 16,
  485. # 17, 18, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
  486. # 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
  487. # 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 20, 22, 27,
  488. # 29, 30, 31, 33, 34, 38, 39, 40, 41, 42, 45], dtype=np.int16)
  489. labels = np.array([0, 2, 4, 5, 7, 8, 10, 11, 12, 13, 14,
  490. 15, 16, 17, 18, 24, 26, 28, 31, 41, 43, 44,
  491. 46, 47, 49, 50, 51, 52, 53, 54, 58, 60, 63,
  492. 192, 1001, 1002, 1003, 1005, 1006, 1007, 1008, 1009, 1010, 1011,
  493. 1012, 1013, 1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022,
  494. 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035,
  495. 2002, 2005, 2010, 2012, 2013, 2014, 2016, 2017, 2021, 2022, 2023,
  496. 2024, 2025, 2028])
  497. labels_full_to_sag = np.array([0, 41, 43, 44, 46, 47, 49, 50, 51, 52, 14, 15, 16, 53, 54, 24, 58, 60, 63, 41, 43, 44, 46, 47, 49, 50, 51, 52, 53,
  498. 54, 58, 60, 63, 192, 1001, 1002, 1003, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016,
  499. 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035,
  500. 1002, 1005, 1010, 1012, 1013, 1014, 1016, 1017, 1021, 1022, 1023, 1024, 1025, 1028])
  501. labels_sag = np.array([0, 14, 15, 16, 24, 41, 43, 44, 46, 47, 49,
  502. 50, 51, 52, 53, 54, 58, 60, 63, 192, 1001, 1002,
  503. 1003, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014,
  504. 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025,
  505. 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035])
  506. idx = []
  507. for l in labels_full_to_sag:
  508. idx.append(np.where(labels_sag==l)[0][0])
  509. idx_list = np.array(idx)
  510. prediction_full = prediction_sag[:, idx_list, :, :]
  511. return prediction_full
  512. # Clean up and class separation
  513. def bbox_3d(img):
  514. """
  515. Function to extract the three-dimensional bounding box coordinates.
  516. :param np.ndarray img: mri image
  517. :return:
  518. """
  519. r = np.any(img, axis=(1, 2))
  520. c = np.any(img, axis=(0, 2))
  521. z = np.any(img, axis=(0, 1))
  522. rmin, rmax = np.where(r)[0][[0, -1]]
  523. cmin, cmax = np.where(c)[0][[0, -1]]
  524. zmin, zmax = np.where(z)[0][[0, -1]]
  525. return rmin, rmax, cmin, cmax, zmin, zmax
  526. def get_largest_cc(segmentation):
  527. """
  528. Function to find largest connected component of segmentation.
  529. :param np.ndarray segmentation: segmentation
  530. :return:
  531. """
  532. labels = label(segmentation, connectivity=3, background=0)
  533. bincount = np.bincount(labels.flat)
  534. background = np.argmax(bincount)
  535. bincount[background] = -1
  536. largest_cc = labels == np.argmax(bincount)
  537. return largest_cc
  538. # Class Operator for image loading (orig only)
  539. class OrigDataThickSlices(Dataset):
  540. """
  541. Class to load a given image and segmentation and prepare it
  542. for network training.
  543. """
  544. def __init__(self, img_filename, orig, plane='Axial', slice_thickness=3, transforms=None):
  545. try:
  546. self.img_filename = img_filename
  547. self.plane = plane
  548. self.slice_thickness = slice_thickness
  549. # Transform Data as needed
  550. if plane == 'Sagittal':
  551. orig = transform_sagittal(orig)
  552. print('Loading Sagittal')
  553. elif plane == 'Axial':
  554. orig = transform_axial(orig)
  555. print('Loading Axial')
  556. else:
  557. print('Loading Coronal.')
  558. # Create Thick Slices
  559. orig_thick = get_thick_slices(orig, self.slice_thickness)
  560. # Make 4D
  561. orig_thick = np.transpose(orig_thick, (2, 0, 1, 3))
  562. self.images = orig_thick
  563. self.count = self.images.shape[0]
  564. self.transforms = transforms
  565. print("Successfully loaded Image from {}".format(img_filename))
  566. except Exception as e:
  567. print("Loading failed. {}".format(e))
  568. def __getitem__(self, index):
  569. img = self.images[index]
  570. if self.transforms is not None:
  571. img = self.transforms(img)
  572. return {'image': img}
  573. def __len__(self):
  574. return self.count
  575. # Class Operator for image loading (orig only)
  576. class OrigDataThickSlices_Fused_Input(Dataset):
  577. """
  578. Class to load a given image and segmentation and prepare it
  579. for network training.
  580. """
  581. def __init__(self, img_filename, orig, plane='Axial', slice_thickness=3, transforms=None):
  582. try:
  583. self.img_filename = img_filename
  584. self.plane = plane
  585. self.slice_thickness = slice_thickness
  586. orig_thick_list = []
  587. for idx, orig_ in enumerate(orig):
  588. # Transform Data as needed
  589. if plane == 'Sagittal':
  590. orig_ = transform_sagittal(orig_)
  591. print('Loading Sagittal %d' % idx)
  592. elif plane == 'Axial':
  593. orig_ = transform_axial(orig_)
  594. print('Loading Axial %d' % idx)
  595. else:
  596. print('Loading Coronal %d' % idx)
  597. # Create Thick Slices
  598. orig_thick = get_thick_slices(orig_, self.slice_thickness)
  599. # Make 4D
  600. orig_thick = np.transpose(orig_thick, (2, 0, 1, 3))
  601. orig_thick_list.append(orig_thick)
  602. self.images = np.concatenate(orig_thick_list, axis=3)
  603. self.count = self.images.shape[0]
  604. self.transforms = transforms
  605. print("Successfully loaded Image from {}".format(img_filename))
  606. except Exception as e:
  607. print("Loading failed. {}".format(e))
  608. def __getitem__(self, index):
  609. img = self.images[index]
  610. if self.transforms is not None:
  611. img = self.transforms(img)
  612. return {'image': img}
  613. def __len__(self):
  614. return self.count
  615. ##
  616. # Dataset loading (for training)
  617. ##
  618. # Operator to load hdf5-file for training
  619. class AsegDatasetWithAugmentation(Dataset):
  620. """
  621. Class for loading aseg file with augmentations (transforms)
  622. """
  623. def __init__(self, params, transforms=None):
  624. # Load the h5 file and save it to the dataset
  625. try:
  626. self.params = params
  627. # Open file in reading mode
  628. with h5py.File(self.params['dataset_name'], "r") as hf:
  629. self.images = np.array(hf.get('orig_dataset'))
  630. self.labels = np.array(hf.get('aseg_dataset'))
  631. self.weights = np.array(hf.get('weight_dataset'))
  632. self.subjects = np.array(hf.get("subject"))
  633. self.count = self.images.shape[0]
  634. self.transforms = transforms
  635. print("Successfully loaded {} with plane: {}".format(params["dataset_name"], params["plane"]))
  636. except Exception as e:
  637. print("Loading failed: {}".format(e))
  638. def get_subject_names(self):
  639. return self.subjects
  640. def __getitem__(self, index):
  641. img = self.images[index]
  642. label = self.labels[index]
  643. weight = self.weights[index]
  644. if self.transforms is not None:
  645. tx_sample = self.transforms({'img': img, 'label': label, 'weight': weight})
  646. img = tx_sample['img']
  647. label = tx_sample['label']
  648. weight = tx_sample['weight']
  649. return {'image': img, 'label': label, 'weight': weight}
  650. def __len__(self):
  651. return self.count
  652. # Operator to load hdf5-file for training
  653. class AsegDatasetWithAugmentation_Fused_Input(Dataset):
  654. """
  655. Class for loading aseg file with augmentations (transforms)
  656. """
  657. def __init__(self, params, transforms=None):
  658. # Load the h5 file and save it to the dataset
  659. try:
  660. self.params = params
  661. self.images = []
  662. # Open file in reading mode
  663. for idx in range(len(self.params['dataset_name'])):
  664. with h5py.File(self.params['dataset_name'][idx], "r") as hf:
  665. self.images.append(np.array(hf.get('orig_dataset')))
  666. if idx == 0:
  667. self.labels = np.array(hf.get('aseg_dataset'))
  668. self.weights = np.array(hf.get('weight_dataset'))
  669. self.subjects = np.array(hf.get("subject"))
  670. self.images = np.concatenate(self.images, axis=3)
  671. self.count = self.images.shape[0]
  672. self.transforms = transforms
  673. print("Successfully loaded {} with plane: {}".format(params["dataset_name"], params["plane"]))
  674. except Exception as e:
  675. print("Loading failed: {}".format(e))
  676. def get_subject_names(self):
  677. return self.subjects
  678. def __getitem__(self, index):
  679. img = self.images[index]
  680. label = self.labels[index]
  681. weight = self.weights[index]
  682. if self.transforms is not None:
  683. tx_sample = self.transforms({'img': img, 'label': label, 'weight': weight})
  684. img = tx_sample['img']
  685. label = tx_sample['label']
  686. weight = tx_sample['weight']
  687. return {'image': img, 'label': label, 'weight': weight}
  688. def __len__(self):
  689. return self.count
  690. class AsegDatasetWithAugmentation_Slice(Dataset):
  691. """
  692. Class for loading aseg file with augmentations (transforms)
  693. """
  694. def __init__(self, params, transforms=None):
  695. # Load the h5 file and save it to the dataset
  696. try:
  697. self.params = params
  698. self.hdf5_files = sorted(glob.glob(os.path.join(params['dataset_name'], '*hdf5')))
  699. self.count = len(self.hdf5_files)
  700. self.transforms = transforms
  701. print("Successfully loaded {} with plane: {}".format(params["dataset_name"], params["plane"]))
  702. except Exception as e:
  703. print("Loading failed: {}".format(e))
  704. def get_subject_names(self):
  705. return self.subjects
  706. def __getitem__(self, index):
  707. hdf5 = self.hdf5_files[index]
  708. with h5py.File(hdf5, "r") as hf:
  709. img = np.array(hf.get('orig_dataset')).squeeze()
  710. label = np.array(hf.get('aseg_dataset')).squeeze()
  711. weight = np.array(hf.get('weight_dataset')).squeeze()
  712. if self.transforms is not None:
  713. tx_sample = self.transforms({'img': img, 'label': label, 'weight': weight})
  714. img = tx_sample['img']
  715. label = tx_sample['label']
  716. weight = tx_sample['weight']
  717. return {'image': img, 'label': label, 'weight': weight}
  718. def __len__(self):
  719. return self.count
  720. class AsegDatasetWithAugmentation_Slice_Fused_Input(Dataset):
  721. """
  722. Class for loading aseg file with augmentations (transforms)
  723. """
  724. def __init__(self, params, transforms=None):
  725. # Load the h5 file and save it to the dataset
  726. try:
  727. self.params = params
  728. self.params['dataset_name'] = sorted(self.params['dataset_name'])
  729. self.hdf5_files = []
  730. tmp_len = []
  731. for dataset in self.params['dataset_name']:
  732. filelist = sorted(glob.glob(os.path.join(dataset, '*hdf5')))
  733. self.hdf5_files.append(filelist)
  734. tmp_len.append(len(filelist))
  735. if np.unique(tmp_len).shape[0] != 1:
  736. print("Error: Input data should have the same number of hdf5 files!")
  737. exit()
  738. self.hdf5_files = np.array(self.hdf5_files)
  739. self.count = np.unique(tmp_len)[0]
  740. self.transforms = transforms
  741. print("Successfully loaded {} with plane: {}".format(params["dataset_name"], params["plane"]))
  742. except Exception as e:
  743. print("Loading failed: {}".format(e))
  744. def get_subject_names(self):
  745. return self.subjects
  746. def __getitem__(self, index):
  747. hdf5s = self.hdf5_files[:, index]
  748. if np.unique([os.path.split(n)[1] for n in hdf5s]).shape[0] != 1:
  749. print("Error: Must have the same file name!")
  750. print(hdf5s)
  751. exit()
  752. img = []
  753. for idx, hdf5 in enumerate(hdf5s):
  754. with h5py.File(hdf5, "r") as hf:
  755. img.append(np.array(hf.get('orig_dataset')).squeeze())
  756. if idx == 0:
  757. label = np.array(hf.get('aseg_dataset')).squeeze()
  758. weight = np.array(hf.get('weight_dataset')).squeeze()
  759. img = np.concatenate(img, axis=2)
  760. if self.transforms is not None:
  761. tx_sample = self.transforms({'img': img, 'label': label, 'weight': weight})
  762. img = tx_sample['img']
  763. label = tx_sample['label']
  764. weight = tx_sample['weight']
  765. return {'image': img, 'label': label, 'weight': weight}
  766. def __len__(self):
  767. return self.count

load_neuroimaging_data.py at commit 257ec7c, under Unlicense · at the source

Overview

  1. German Center for Neurodegenerative Diseases (DZNE), Bonn, Germany
  2. A. A. Martinos Center for Biomedical Imaging, Massachusetts General Hospital, Boston, MA, United States
  3. Department of Radiology, Harvard Medical School, Boston, MA, United States
Journal: Imaging neuroscience (Cambridge, Mass.), volume 4, article IMAG.a.1183
Dates: received 1 July 2025; accepted 13 February 2026; published online 2 June 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1162/imag.a.1183 · PMID 42245204 · PMCID PMC13231284 · OpenAlex W7134815812
Open access: diamond, a free copy (OpenAlex)
Status: code verified
Categories: structural MRI / diffusion (modality), methods / tools (subfield)
Methods: Connectivity, Statistics, Machine learning, fMRI & imaging
Keywords: deep learning, geometric deep learning, segmentation, diffusion MRI, q-space
Topic: Advanced Neuroimaging Techniques and Applications (Radiology, Nuclear Medicine and Imaging, Medicine), according to OpenAlex
Funding: Federal Ministry of Research, Technology and Space of Germany (BMFTR 031L0206); Chan Zuckerberg Initiative (EOSS5 2022-252594); Helmholtz Foundation Model Initiative (The Human Radiome Project); U.S. Department of Health & Human Services | NIH | National Institute on Aging (U.S. National Institute on Aging) (R01-AG064027); HHS | NIH | National Institute of Mental Health (NIMH) (R01-MH131586, R01-MH130899); Deutsches Zentrum für Neurodegenerative Erkrankungen (German Center for Neurodegenerative Diseases)
Citations: not cited yet (Europe PMC); 61 references in the paper

Abstract

Segmenting anatomical regions is a crucial step in many diffusion-weighted MRI (dMRI) workflows, such as region-of-interest analysis or anatomically-constrained tractography, which enable in vivo studies of brain microstructure and connectivity. However, convolutional neural networks (CNNs)—the foundation of most state-of-the-art segmentation models—require structured inputs with a fixed number of channels. This makes them ill-suited for dMRI, where acquisition protocols vary widely in q-space sampling—the number of measurements as well as their directions (b-vectors) and weightings (b-values)—resulting in unstructured data with inconsistent dimensionality across studies. As a consequence, the applicability of CNN-based methods is generally limited to the dataset on which they were trained. To address this, existing methods like DeepAnat and DDParcel rely on diffusion model fits, such as the diffusion tensor, to convert raw data into structured representations compatible with CNNs. While this enables broader applicability, it introduces lossy compression that can degrade performance. In this work, we propose a novel method that combines the geometric deep learning-based reconstruction framework DISCUS with the segmentation network VINN to directly map unstructured dMRI data to anatomical segmentations. Our segmentation approach is the first to achieve robust generalization across heterogeneous acquisition schemes using a single neural network without requiring diffusion model fits. Our approach generates the segmentation in minutes, whereas DeepAnat relies on the external FreeSurfer software, which runs for several hours. Additionally, we demonstrate generally superior segmentation performance of our approach across multiple datasets and acquisition settings with respect to DeepAnat, DDParcel, and SynthSeg.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repositories

Its files are read in the Code ↔ Paper reader above, with 11 matches between paragraphs and lines of code.

liziyu0929/DeepAnat

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 365f8e4529faa4cce3470450e6aaef4f395b51e4, 9 February 2023
Languages: Python (6), Jupyter (1)
Size: 13 files, 7 scripts
Software Heritage: not archived
Found in: the end of the paper
Holds: README, license file, 1 notebook
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: Keras (7 files), TensorFlow (7 files), NiBabel (5 files), NumPy (5 files), Matplotlib (4 files), SciPy (4 files)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
9 files

zhangfanmark/DDParcel

License: Unlicense
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 257ec7c2a02209f86365dc5c0b39755458a3f776, 9 December 2024
Languages: Python (11), Shell (2)
Size: 16 files, 13 scripts
Software Heritage: not archived
Found in: the end of the paper
Holds: README, license file
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: PyTorch (7 files), NumPy (6 files), NiBabel (4 files), scikit-image (4 files), SciPy (4 files), h5py (1 file), Matplotlib (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
15 files

Deep-MI/DISCUSseg

License: Apache-2.0
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: c45ed3b5ba88c09c163074d5e08b3d6c521bdb9c, 1 June 2026
Languages: Python (37)
Size: 44 files, 37 scripts
Software Heritage: not archived
Found in: “Data and Code Availability”
Holds: README, license file
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: PyTorch (22 files), NumPy (16 files), NiBabel (5 files), Matplotlib (3 files), scikit-image (3 files), SciPy (2 files), pandas (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
39 files

The paper's code and data availability statement is in the Data section.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 3 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 57 scripts, each with its path and the digest of its content;
  • 11 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

No dataset and no data link were found in the paper.

Data and Code Availability

In this paper, we use MRI data of two public datasets, the Young Adult dataset of the Human Connectome Project (HCP), available at https://balsa.wustl.edu, and the dataset of the Alzheimer’s Disease Neuroimaging Initiative (ADNI), available at https://ida.loni.usc.edu. In addition, we use MRI data of the Rhineland Study which is not publicly available due to data protection regulations. Access can be provided to scientists in accordance with the Rhineland Study’s Data Use and Access Policy. Requests to access the data should be directed to Monique M.B. Breteler at . The source code for our method can be found on the website https://github.com/Deep-MI/DISCUSseg.

Reproduced under the paper's license (CC BY), from the paper cited above.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, pages, dates, 3 authors, 5 keywords, 6 funders, 59 references.

Cite

This paper

Ewert, C., Kügler, D., & Reuter, M. (2026). Learning-based segmentation of diffusion-weighted MR images with arbitrary &lt;i&gt;q&lt;/i&gt;-space samplings. Imaging neuroscience (Cambridge, Mass.), 4, IMAG.a.1183. https://doi.org/10.1162/imag.a.1183

BibTeX

@article{ewert2026learning,
author = {Ewert, Christian and Kügler, David and Reuter, Martin},
title = {{Learning-based segmentation of diffusion-weighted MR images with arbitrary \&lt;i\&gt;q\&lt;/i\&gt;-space samplings}},
journal = {Imaging neuroscience (Cambridge, Mass.)},
year = {2026},
month = jun,
volume = {4},
pages = {IMAG.a.1183},
publisher = {MIT Press},
issn = {2837-6056},
doi = {10.1162/imag.a.1183},
url = {https://doi.org/10.1162/imag.a.1183},
pmid = {42245204},
pmcid = {PMC13231284}
}

RIS

TY - JOUR
AU - Ewert, Christian
AU - Kügler, David
AU - Reuter, Martin
TI - Learning-based segmentation of diffusion-weighted MR images with arbitrary &lt;i&gt;q&lt;/i&gt;-space samplings
T2 - Imaging neuroscience (Cambridge, Mass.)
J2 - Imaging Neurosci (Camb)
PY - 2026
DA - 2026/06/02
VL - 4
SP - IMAG.a.1183
SN - 2837-6056
PB - MIT Press
DO - 10.1162/imag.a.1183
UR - https://doi.org/10.1162/imag.a.1183
LA - en
ER -

CSL-JSON

{
"id": "10.1162/imag.a.1183",
"type": "article-journal",
"title": "Learning-based segmentation of diffusion-weighted MR images with arbitrary &lt;i&gt;q&lt;/i&gt;-space samplings",
"container-title": "Imaging neuroscience (Cambridge, Mass.)",
"author": [
{
"family": "Ewert",
"given": "Christian"
},
{
"family": "Kügler",
"given": "David"
},
{
"family": "Reuter",
"given": "Martin"
}
],
"container-title-short": "Imaging Neurosci (Camb)",
"volume": "4",
"page": "IMAG.a.1183",
"DOI": "10.1162/imag.a.1183",
"PMID": "42245204",
"PMCID": "PMC13231284",
"ISSN": "2837-6056",
"publisher": "MIT Press",
"URL": "https://doi.org/10.1162/imag.a.1183",
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
2
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1038/s41598-026-54446-8 [code]
Deep learning-based Desikan-Killiany parcellation of the brain using diffusion MRI.
Journal: Scientific reports
In common: scikit-image, h5py, NiBabel, 3 other tools, methods / tools, structural MRI / diffusion, 12 references
[2] doi:10.1002/advs.76596 [code]
DDSurfer: A Weakly-Supervised Dual-Stream Deep Learning Framework for Cortical Surface Reconstruction From Diffusion MRI.
Journal: Advanced science (Weinheim, Baden-Wurttemberg, Germany)
In common: NiBabel, PyTorch, pandas, 2 other tools, methods / tools, structural MRI / diffusion, 10 references
[3] doi:10.1002/nbm.70353 [code]
Automated Surface-Based Segmentation of Deep Gray Matter Regions Based on Diffusion Tensor Images Reveals Unique Age Trajectories Over the Healthy Lifespan.
Journal: NMR in biomedicine
In common: scikit-image, NiBabel, SciPy, 1 other tool, structural MRI / diffusion, 9 references
[4] doi:10.1038/s41597-026-07184-5 [code]
A Multiple Sclerosis MRI Dataset with Tri-Mask Annotations for Lesion Segmentation.
Journal: Scientific data
In common: Keras, TensorFlow, scikit-image, 5 other tools, methods / tools, structural MRI / diffusion, 3 references
[5] doi:10.1162/imag.a.1198 [code]
MEPrep: A robust pipeline for multi-echo fMRI denoising and preprocessing.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: scikit-image, h5py, NiBabel, 4 other tools, methods / tools, 5 references
[6] doi:10.1038/s41598-026-55397-w [code]
Fast surface reconstruction of human brain MRI: benchmarking deep-learning based morphometry tools.
Journal: Scientific reports
In common: scikit-image, NiBabel, PyTorch, 3 other tools, methods / tools, structural MRI / diffusion, 6 references
[7] doi:10.1162/imag.a.1366 [code]
MICAFlow: Fast and robust MRI preprocessing bridging research neuroimaging and clinical practice.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: Keras, TensorFlow, NiBabel, 4 other tools, methods / tools, structural MRI / diffusion, 4 references
[8] doi:10.1038/s41586-026-10631-3 [code]
A prognostic human brain network for diffuse midline glioma.
Journal: Nature
In common: Keras, TensorFlow, h5py, 5 other tools, 4 references
[9] doi:10.1002/alz.71530 [code]
Differential associations of plasma biomarkers with Alzheimer's disease and small vessel disease: A multimodal imaging study.
Journal: Alzheimer's & dementia : the journal of the Alzheimer's Association
In common: NiBabel, PyTorch, pandas, 3 other tools, structural MRI / diffusion, 6 references
[10] doi:10.1080/07853890.2026.2685416 [code]
Pulmonary and cerebral damage in COVID-19 survivors: is there any association?
Journal: Annals of medicine
In common: Keras, TensorFlow, scikit-image, 7 other tools, structural MRI / diffusion

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.