def test_patch_up_roi(): roi_bad = np.zeros((10, 10, 10)) roi_good = np.ones((10, 10, 10)) afv.patch_up_roi(roi_good) with pytest.raises(ValueError): afv.patch_up_roi(roi_bad)
def get_mask(self, afq_object, row): if afq_object.use_prealign: reg_prealign = np.load(afq_object._reg_prealign(row)) reg_prealign_inv = np.linalg.inv(reg_prealign) else: reg_prealign_inv = None mapping = reg.read_mapping(afq_object._mapping(row), row['dwi_file'], afq_object.reg_template_img, prealign=reg_prealign_inv) mask_data = None for bundle_name, bundle_info in afq_object.bundle_dict.items(): for idx, roi in enumerate(bundle_info['ROIs']): if afq_object.bundle_dict[bundle_name]['rules'][idx]: warped_roi = auv.patch_up_roi(mapping.transform_inverse( roi.get_fdata().astype(np.float32), interpolation='linear'), bundle_name=bundle_name) if mask_data is None: mask_data = np.zeros(warped_roi.shape) mask_data = np.logical_or(mask_data, warped_roi.astype(bool)) return mask_data, afq_object["dwi_affine"], dict(source="ROIs")
def _get_bundle_info(self, bundle_idx, bundle): """ Get fiber probabilites and ROIs for a given bundle. """ rules = self.bundle_dict[bundle]['rules'] include_rois = [] exclude_rois = [] for rule_idx, rule in enumerate(rules): roi = self.bundle_dict[bundle]['ROIs'][rule_idx] if not isinstance(roi, np.ndarray): roi = roi.get_fdata() warped_roi = auv.patch_up_roi( self.mapping.transform_inverse(roi.astype(np.float32), interpolation='linear')) if rule: # include ROI: include_rois.append(np.array(np.where(warped_roi)).T) else: # Exclude ROI: exclude_rois.append(np.array(np.where(warped_roi)).T) # The probability map if doesn't exist is all ones with the same # shape as the ROIs: prob_map = self.bundle_dict[bundle].get('prob_map', np.ones(roi.shape)) if not isinstance(prob_map, np.ndarray): prob_map = prob_map.get_fdata() warped_prob_map = \ self.mapping.transform_inverse(prob_map, interpolation='nearest') return warped_prob_map, include_rois, exclude_rois
def _export_rois(row, bundle_dict, reg_template, force_recompute=False): reg_prealign = np.load(_reg_prealign(row, force_recompute=force_recompute)) mapping = reg.read_mapping(_mapping(row, reg_template), row['dwi_file'], reg_template, prealign=np.linalg.inv(reg_prealign)) rois_dir = op.join(row['results_dir'], 'ROIs') os.makedirs(rois_dir, exist_ok=True) for bundle in bundle_dict: for ii, roi in enumerate(bundle_dict[bundle]['ROIs']): if bundle_dict[bundle]['rules'][ii]: inclusion = 'include' else: inclusion = 'exclude' fname = op.join(rois_dir, '%s_roi%s_%s.nii.gz' % (bundle, ii + 1, inclusion)) warped_roi = auv.patch_up_roi((mapping.transform_inverse( roi.get_data(), interpolation='linear')) > 0).astype(int) # Cast to float32, so that it can be read in by MI-Brain: nib.save( nib.Nifti1Image(warped_roi.astype(np.float32), row['dwi_affine']), fname)
def visualize_roi(roi, affine_or_mapping=None, static_img=None, roi_affine=None, static_affine=None, reg_template=None, scene=None, color=np.array([1, 0, 0]), opacity=1.0, inline=False, interact=False): """ Render a region of interest into a VTK viz as a volume """ if not isinstance(roi, np.ndarray): if isinstance(roi, str): roi = nib.load(roi).get_fdata() else: roi = roi.get_fdata() if affine_or_mapping is not None: if isinstance(affine_or_mapping, np.ndarray): # This is an affine: if (static_img is None or roi_affine is None or static_affine is None): raise ValueError( "If using an affine to transform an ROI, " "need to also specify all of the following", "inputs: `static_img`, `roi_affine`, ", "`static_affine`") roi = reg.resample(roi, static_img, roi_affine, static_affine) else: # Assume it is a mapping: if (isinstance(affine_or_mapping, str) or isinstance(affine_or_mapping, nib.Nifti1Image)): if reg_template is None or static_img is None: raise ValueError( "If using a mapping to transform an ROI, need to ", "also specify all of the following inputs: ", "`reg_template`, `static_img`") affine_or_mapping = reg.read_mapping(affine_or_mapping, static_img, reg_template) roi = auv.patch_up_roi( affine_or_mapping.transform_inverse( roi, interpolation='nearest')).astype(bool) if scene is None: scene = window.Scene() roi_actor = actor.contour_from_roi(roi, color=color, opacity=opacity) scene.add(roi_actor) if inline: tdir = tempfile.gettempdir() fname = op.join(tdir, "fig.png") window.snapshot(scene, fname=fname) display.display_png(display.Image(fname)) return _inline_interact(scene, inline, interact)
def _export_rois(self, row): if self.use_prealign: reg_prealign = np.load(self._reg_prealign(row)) reg_prealign_inv = np.linalg.inv(reg_prealign) else: reg_prealign_inv = None mapping = reg.read_mapping(self._mapping(row), row['dwi_file'], self.reg_template, prealign=reg_prealign_inv) rois_dir = op.join(row['results_dir'], 'ROIs') os.makedirs(rois_dir, exist_ok=True) for bundle in self.bundle_dict: for ii, roi in enumerate(self.bundle_dict[bundle]['ROIs']): if self.bundle_dict[bundle]['rules'][ii]: inclusion = 'include' else: inclusion = 'exclude' warped_roi = auv.patch_up_roi((mapping.transform_inverse( roi.get_fdata(), interpolation='linear')) > 0).astype(int) fname = op.split( self._get_fname( row, f'_desc-ROI-{bundle}-{ii + 1}-{inclusion}.nii.gz')) fname = op.join(fname[0], rois_dir, fname[1]) # Cast to float32, so that it can be read in by MI-Brain: nib.save( nib.Nifti1Image(warped_roi.astype(np.float32), row['dwi_affine']), fname) meta = dict() meta_fname = fname.split('.')[0] + '.json' afd.write_json(meta_fname, meta)
def _get_bundle_info(self, bundle_idx, bundle): """ Get fiber probabilites and ROIs for a given bundle. """ rules = self.bundle_dict[bundle]['rules'] include_rois = [] exclude_rois = [] for rule_idx, rule in enumerate(rules): roi = self.bundle_dict[bundle]['ROIs'][rule_idx] if not isinstance(roi, np.ndarray): roi = roi.get_fdata() warped_roi = auv.patch_up_roi( self.mapping.transform_inverse(roi.astype(np.float32), interpolation='linear')) if rule: # include ROI: include_rois.append(np.array(np.where(warped_roi)).T) else: # Exclude ROI: exclude_rois.append(np.array(np.where(warped_roi)).T) ########for debugging: save the warped ROI that is actually used in segment_afq() ########## # path_for_debugging = '/debugpath/' # nib.save(nib.Nifti1Image(warped_roi.astype(np.float32), # self.img_affine), # debugpath+'warpedROI_'+bundle+'as_used.nii.gz') ############################################################################################ # The probability map if doesn't exist is all ones with the same # shape as the ROIs: prob_map = self.bundle_dict[bundle].get('prob_map', np.ones(roi.shape)) if not isinstance(prob_map, np.ndarray): prob_map = prob_map.get_fdata() warped_prob_map = \ self.mapping.transform_inverse(prob_map, interpolation='nearest') return warped_prob_map, include_rois, exclude_rois
def _get_bundle_info(self, bundle_idx, bundle): """ Get fiber probabilites and ROIs for a given bundle. """ rules = self.bundle_dict[bundle]['rules'] include_rois = [] exclude_rois = [] for rule_idx, rule in enumerate(rules): roi = self.bundle_dict[bundle]['ROIs'][rule_idx] if not isinstance(roi, np.ndarray): roi = roi.get_fdata() warped_roi = auv.patch_up_roi( self.mapping.transform_inverse(roi.astype(np.float32), interpolation='linear')) if rule: # include ROI: include_rois.append(np.array(np.where(warped_roi)).T) else: # Exclude ROI: exclude_rois.append(np.array(np.where(warped_roi)).T) # For debugging purposes, we can save the variable as it is: if self.save_intermediates is not None: nib.save( nib.Nifti1Image(warped_roi.astype(np.float32), self.img_affine), op.join(self.save_intermediates, 'warpedROI_', bundle, 'as_used.nii.gz')) # The probability map if doesn't exist is all ones with the same # shape as the ROIs: prob_map = self.bundle_dict[bundle].get('prob_map', np.ones(roi.shape)) if not isinstance(prob_map, np.ndarray): prob_map = prob_map.get_fdata() warped_prob_map = \ self.mapping.transform_inverse(prob_map, interpolation='nearest') return warped_prob_map, include_rois, exclude_rois
# - Each step is 0.5 mm # # .. note:: # In this example tractography results in a large number of candidate # streamlines for the anterior forceps, but not many streamlines anywhere # else. print("Tracking...") if not op.exists(op.join(working_dir, 'dti_streamlines.trk')): seed_roi = np.zeros(img.shape[:-1]) for bundle in bundles: for idx, roi in enumerate(bundles[bundle]['ROIs']): if bundles[bundle]['rules'][idx]: warped_roi = patch_up_roi(mapping.transform_inverse( roi.get_fdata().astype(np.float32), interpolation='linear'), bundle_name=bundle) nib.save(nib.Nifti1Image(warped_roi.astype(float), img.affine), op.join(working_dir, f"{bundle}_{idx+1}.nii.gz")) warped_roi_img = nib.load( op.join(working_dir, f"{bundle}_{idx+1}.nii.gz")) show_anatomical_slices(warped_roi_img.get_fdata(), f'warped {bundle}_{idx+1} ROI') # Add voxels that aren't there yet: seed_roi = np.logical_or(seed_roi, warped_roi) seed_roi_img = nib.Nifti1Image(seed_roi.astype(float), img.affine) nib.save(seed_roi_img, op.join(working_dir, 'seed_roi.nii.gz'))
def segment(fdata, fbval, fbvec, streamlines, bundle_dict, mapping, reg_prealign=None, b0_threshold=0, reg_template=None, prob_threshold=0): """ Segment streamlines into bundles based on inclusion ROIs. Parameters ---------- fdata, fbval, fbvec : str Full path to data, bvals, bvecs streamlines : list of 2D arrays Each array is a streamline, shape (3, N). bundle_dict: dict The format is something like:: {'name': {'ROIs':[img1, img2], 'rules':[True, True]}, 'prob_map': img3, 'cross_midline': False} mapping : a DiffeomorphicMapping object Used to align the ROIs to the data. reg_template : str or nib.Nifti1Image, optional. Template to use for registration (defaults to the MNI T2) mapping : DiffeomorphicMap object, str or nib.Nifti1Image, optional A mapping between DWI space and a template. Defaults to generate this. prob_threshold : float. Initial cleaning of fiber groups is done using probability maps from [Hua2008]_. Here, we choose an average probability that needs to be exceeded for an individual streamline to be retained. Default: 0. References ---------- .. [Hua2008] Hua K, Zhang J, Wakana S, Jiang H, Li X, et al. (2008) Tract probability maps in stereotaxic spaces: analyses of white matter anatomy and tract-specific quantification. Neuroimage 39: 336-347 """ img, _, gtab, _ = ut.prepare_data(fdata, fbval, fbvec, b0_threshold=b0_threshold) tol = dts.dist_to_corner(img.affine) if reg_template is None: reg_template = dpd.read_mni_template() # Classify the streamlines and split those that: 1) cross the # midline, and 2) pass under 10 mm below the mid-point of their # representation in the template space: xform_sl, crosses = split_streamlines(streamlines, img) if isinstance(mapping, str) or isinstance(mapping, nib.Nifti1Image): if reg_prealign is None: reg_prealign = np.eye(4) mapping = reg.read_mapping(mapping, img, reg_template, prealign=reg_prealign) fiber_probabilities = np.zeros((len(xform_sl), len(bundle_dict))) # For expedience, we approximate each streamline as a 100 point curve: fgarray = _resample_bundle(xform_sl, 100) streamlines_in_bundles = np.zeros((len(xform_sl), len(bundle_dict))) min_dist_coords = np.zeros((len(xform_sl), len(bundle_dict), 2)) fiber_groups = {} for bundle_idx, bundle in enumerate(bundle_dict): rules = bundle_dict[bundle]['rules'] include_rois = [] exclude_rois = [] for rule_idx, rule in enumerate(rules): roi = bundle_dict[bundle]['ROIs'][rule_idx] if not isinstance(roi, np.ndarray): roi = roi.get_data() warped_roi = auv.patch_up_roi( (mapping.transform_inverse( roi, interpolation='linear')) > 0) if rule: # include ROI: include_rois.append(np.array(np.where(warped_roi)).T) else: # Exclude ROI: exclude_rois.append(np.array(np.where(warped_roi)).T) crosses_midline = bundle_dict[bundle]['cross_midline'] # The probability map if doesn't exist is all ones with the same # shape as the ROIs: prob_map = bundle_dict[bundle].get('prob_map', np.ones(roi.shape)) if not isinstance(prob_map, np.ndarray): prob_map = prob_map.get_data() warped_prob_map = mapping.transform_inverse(prob_map, interpolation='nearest') fiber_probabilities = dts.values_from_volume(warped_prob_map, fgarray) fiber_probabilities = np.mean(fiber_probabilities, -1) for sl_idx, sl in enumerate(xform_sl): if fiber_probabilities[sl_idx] > prob_threshold: if crosses_midline is not None: if crosses[sl_idx]: # This means that the streamline does # cross the midline: if crosses_midline: # This is what we want, keep going pass else: # This is not what we want, skip to next streamline continue is_close, dist = _check_sl_with_inclusion(sl, include_rois, tol) if is_close: is_far = _check_sl_with_exclusion(sl, exclude_rois, tol) if is_far: min_dist_coords[sl_idx, bundle_idx, 0] =\ np.argmin(dist[0], 0)[0] min_dist_coords[sl_idx, bundle_idx, 1] =\ np.argmin(dist[1], 0)[0] streamlines_in_bundles[sl_idx, bundle_idx] =\ fiber_probabilities[sl_idx] # Eliminate any fibers not selected using the plane ROIs: possible_fibers = np.sum(streamlines_in_bundles, -1) > 0 xform_sl = xform_sl[possible_fibers] streamlines_in_bundles = streamlines_in_bundles[possible_fibers] min_dist_coords = min_dist_coords[possible_fibers] bundle_choice = np.argmax(streamlines_in_bundles, -1) # We do another round through, so that we can orient all the # streamlines within a bundle in the same orientation with respect to # the ROIs. This order is ARBITRARY but CONSISTENT (going from ROI0 # to ROI1). for bundle_idx, bundle in enumerate(bundle_dict): select_idx = np.where(bundle_choice == bundle_idx) # Use a list here, because Streamlines don't support item assignment: select_sl = list(xform_sl[select_idx]) if len(select_sl) == 0: fiber_groups[bundle] = dts.Streamlines([]) # There's nothing here, move to the next bundle: continue # Sub-sample min_dist_coords: min_dist_coords_bundle = min_dist_coords[select_idx] for idx in range(len(select_sl)): min0 = min_dist_coords_bundle[idx, bundle_idx, 0] min1 = min_dist_coords_bundle[idx, bundle_idx, 1] if min0 > min1: select_sl[idx] = select_sl[idx][::-1] # Set this to nibabel.Streamlines object for output: select_sl = dts.Streamlines(select_sl) fiber_groups[bundle] = select_sl return fiber_groups