def get_tractogram_in_voxel_space(tract_fname, ref_anat_fname, tracts_attribs={'orientation': 'unknown'}, origin=Origin.NIFTI): if tracts_attribs['orientation'] != 'unknown': to_lps = tracts_attribs['orientation'] == 'LPS' streamlines = load_vtk_streamlines(tract_fname, to_lps) sft = StatefulTractogram(streamlines, ref_anat_fname, Space.RASMM) else: sft = load_tractogram(tract_fname, ref_anat_fname, bbox_valid_check=False, trk_header_check=False) sft.to_vox() sft.to_origin(origin) return sft
def load_tractogram(filename, reference, to_space=Space.RASMM, to_origin=Origin.NIFTI, bbox_valid_check=True, trk_header_check=True): """ Load the stateful tractogram from any format (trk, tck, vtk, fib, dpy) Parameters ---------- filename : string Filename with valid extension reference : Nifti or Trk filename, Nifti1Image or TrkFile, Nifti1Header or trk.header (dict), or 'same' if the input is a trk file. Reference that provides the spatial attribute. Typically a nifti-related object from the native diffusion used for streamlines generation to_space : Enum (dipy.io.stateful_tractogram.Space) Space to which the streamlines will be transformed after loading to_origin : Enum (dipy.io.stateful_tractogram.Origin) Origin to which the streamlines will be transformed after loading NIFTI standard, default (center of the voxel) TRACKVIS standard (corner of the voxel) bbox_valid_check : bool Verification for negative voxel coordinates or values above the volume dimensions. Default is True, to enforce valid file. trk_header_check : bool Verification that the reference has the same header as the spatial attributes as the input tractogram when a Trk is loaded Returns ------- output : StatefulTractogram The tractogram to load (must have been saved properly) """ _, extension = os.path.splitext(filename) if extension not in ['.trk', '.tck', '.vtk', '.fib', '.dpy']: logging.error('Output filename is not one of the supported format.') return False if to_space not in Space: logging.error('Space MUST be one of the 3 choices (Enum).') return False if reference == 'same': if extension == '.trk': reference = filename else: logging.error('Reference must be provided, "same" is only ' 'available for Trk file.') return False if trk_header_check and extension == '.trk': if not is_header_compatible(filename, reference): logging.error('Trk file header does not match the provided ' 'reference.') return False timer = time.time() data_per_point = None data_per_streamline = None if extension in ['.trk', '.tck']: tractogram_obj = nib.streamlines.load(filename).tractogram streamlines = tractogram_obj.streamlines if extension == '.trk': data_per_point = tractogram_obj.data_per_point data_per_streamline = tractogram_obj.data_per_streamline elif extension in ['.vtk', '.fib']: streamlines = load_vtk_streamlines(filename) elif extension in ['.dpy']: dpy_obj = Dpy(filename, mode='r') streamlines = list(dpy_obj.read_tracks()) dpy_obj.close() logging.debug('Load %s with %s streamlines in %s seconds.', filename, len(streamlines), round(time.time() - timer, 3)) sft = StatefulTractogram(streamlines, reference, Space.RASMM, origin=Origin.NIFTI, data_per_point=data_per_point, data_per_streamline=data_per_streamline) sft.to_space(to_space) sft.to_origin(to_origin) if bbox_valid_check and not sft.is_bbox_in_vox_valid(): raise ValueError('Bounding box is not valid in voxel space, cannot ' 'load a valid file if some coordinates are invalid.\n' 'Please set bbox_valid_check to False and then use ' 'the function remove_invalid_streamlines to discard ' 'invalid streamlines.') return sft
def transform_warp_sft(sft, linear_transfo, target, inverse=False, reverse_op=False, deformation_data=None, remove_invalid=True, cut_invalid=False): """ Transform tractogram using a affine Subsequently apply a warp from antsRegistration (optional). Remove/Cut invalid streamlines to preserve sft validity. Parameters ---------- sft: StatefulTractogram Stateful tractogram object containing the streamlines to transform. linear_transfo: numpy.ndarray Linear transformation matrix to apply to the tractogram. target: Nifti filepath, image object, header Final reference for the tractogram after registration. inverse: boolean Apply the inverse linear transformation. reverse_op: boolean Apply both transformation in the reverse order deformation_data: np.ndarray 4D array containing a 3D displacement vector in each voxel. remove_invalid: boolean Remove the streamlines landing out of the bounding box. cut_invalid: boolean Cut invalid streamlines rather than removing them. Keep the longest segment only. Return ---------- new_sft : StatefulTractogram """ # Keep track of the streamlines' original space/origin space = sft.space origin = sft.origin dtype = sft.streamlines._data.dtype sft.to_rasmm() sft.to_center() if len(sft.streamlines) == 0: return StatefulTractogram(sft.streamlines, target, Space.RASMM) if inverse: linear_transfo = np.linalg.inv(linear_transfo) if not reverse_op: streamlines = transform_streamlines(sft.streamlines, linear_transfo) else: streamlines = sft.streamlines if deformation_data is not None: if not reverse_op: affine, _, _, _ = get_reference_info(target) else: affine = sft.affine # Because of duplication, an iteration over chunks of points is # necessary for a big dataset (especially if not compressed) streamlines = ArraySequence(streamlines) nb_points = len(streamlines._data) cur_position = 0 chunk_size = 1000000 nb_iteration = int(np.ceil(nb_points / chunk_size)) inv_affine = np.linalg.inv(affine) while nb_iteration > 0: max_position = min(cur_position + chunk_size, nb_points) points = streamlines._data[cur_position:max_position] # To access the deformation information, we need to go in VOX space # No need for corner shift since we are doing interpolation cur_points_vox = np.array(transform_streamlines( points, inv_affine)).T x_def = map_coordinates(deformation_data[..., 0], cur_points_vox.tolist(), order=1) y_def = map_coordinates(deformation_data[..., 1], cur_points_vox.tolist(), order=1) z_def = map_coordinates(deformation_data[..., 2], cur_points_vox.tolist(), order=1) # ITK is in LPS and nibabel is in RAS, a flip is necessary for ANTs final_points = np.array([-1 * x_def, -1 * y_def, z_def]) final_points += np.array(points).T streamlines._data[cur_position:max_position] = final_points.T cur_position = max_position nb_iteration -= 1 if reverse_op: streamlines = transform_streamlines(streamlines, linear_transfo) streamlines._data = streamlines._data.astype(dtype) new_sft = StatefulTractogram(streamlines, target, Space.RASMM, data_per_point=sft.data_per_point, data_per_streamline=sft.data_per_streamline) if cut_invalid: new_sft, _ = cut_invalid_streamlines(new_sft) elif remove_invalid: new_sft.remove_invalid_streamlines() # Move the streamlines back to the original space/origin sft.to_space(space) sft.to_origin(origin) new_sft.to_space(space) new_sft.to_origin(origin) return new_sft