def _processing_wrapper(args): hdf5_filename = args[0] labels_img = args[1] in_label, out_label = args[2] measures_to_compute = copy.copy(args[3]) if args[4] is not None: similarity_directory = args[4][0] weighted = args[5] include_dps = args[6] min_lesion_vol = args[7] hdf5_file = h5py.File(hdf5_filename, 'r') key = '{}_{}'.format(in_label, out_label) if key not in hdf5_file: return streamlines = reconstruct_streamlines_from_hdf5(hdf5_file, key) if len(streamlines) == 0: return affine, dimensions, voxel_sizes, _ = get_reference_info(labels_img) measures_to_return = {} if not (np.allclose(hdf5_file.attrs['affine'], affine, atol=1e-03) and np.array_equal(hdf5_file.attrs['dimensions'], dimensions)): raise ValueError('Provided hdf5 have incompatible headers.') # Precompute to save one transformation, insert later if 'length' in measures_to_compute: streamlines_copy = list(streamlines) # scil_decompose_connectivity.py requires isotropic voxels mean_length = np.average(length(streamlines_copy))*voxel_sizes[0] # If density is not required, do not compute it # Only required for volume, similarity and any metrics if not ((len(measures_to_compute) == 1 and ('length' in measures_to_compute or 'streamline_count' in measures_to_compute)) or (len(measures_to_compute) == 2 and ('length' in measures_to_compute and 'streamline_count' in measures_to_compute))): density = compute_tract_counts_map(streamlines, dimensions) if 'volume' in measures_to_compute: measures_to_return['volume'] = np.count_nonzero(density) * \ np.prod(voxel_sizes) measures_to_compute.remove('volume') if 'streamline_count' in measures_to_compute: measures_to_return['streamline_count'] = len(streamlines) measures_to_compute.remove('streamline_count') if 'length' in measures_to_compute: measures_to_return['length'] = mean_length measures_to_compute.remove('length') if 'similarity' in measures_to_compute and similarity_directory: density_sim = load_node_nifti(similarity_directory, in_label, out_label, labels_img) if density_sim is None: ba_vox = 0 else: ba_vox = compute_bundle_adjacency_voxel(density, density_sim) measures_to_return['similarity'] = ba_vox measures_to_compute.remove('similarity') for measure in measures_to_compute: # Maps if isinstance(measure, str) and os.path.isdir(measure): map_dirname = measure map_data = load_node_nifti(map_dirname, in_label, out_label, labels_img) measures_to_return[map_dirname] = np.average( map_data[map_data > 0]) elif isinstance(measure, tuple): if not isinstance(measure[0], tuple) \ and os.path.isfile(measure[0]): metric_filename = measure[0] metric_img = measure[1] if not is_header_compatible(metric_img, labels_img): logging.error('{} do not have a compatible header'.format( metric_filename)) raise IOError metric_data = metric_img.get_fdata(dtype=np.float64) if weighted: avg_value = np.average(metric_data, weights=density) else: avg_value = np.average(metric_data[density > 0]) measures_to_return[metric_filename] = avg_value # lesion else: lesion_filename = measure[0][0] computed_lesion_labels = measure[0][1] lesion_img = measure[1] if not is_header_compatible(lesion_img, labels_img): logging.error('{} do not have a compatible header'.format( lesion_filename)) raise IOError voxel_sizes = lesion_img.header.get_zooms()[0:3] lesion_img.set_filename('tmp.nii.gz') lesion_atlas = get_data_as_label(lesion_img) tmp_dict = compute_lesion_stats( density.astype(bool), lesion_atlas, voxel_sizes=voxel_sizes, single_label=True, min_lesion_vol=min_lesion_vol, precomputed_lesion_labels=computed_lesion_labels) tmp_ind = _streamlines_in_mask(list(streamlines), lesion_atlas.astype(np.uint8), np.eye(3), [0, 0, 0]) streamlines_count = len( np.where(tmp_ind == [0, 1][True])[0].tolist()) if tmp_dict: measures_to_return[lesion_filename+'vol'] = \ tmp_dict['lesion_total_volume'] measures_to_return[lesion_filename+'count'] = \ tmp_dict['lesion_count'] measures_to_return[lesion_filename+'sc'] = \ streamlines_count else: measures_to_return[lesion_filename+'vol'] = 0 measures_to_return[lesion_filename+'count'] = 0 measures_to_return[lesion_filename+'sc'] = 0 if include_dps: for dps_key in hdf5_file[key].keys(): if dps_key not in ['data', 'offsets', 'lengths']: out_file = os.path.join(include_dps, dps_key) if 'commit' in dps_key: measures_to_return[out_file] = np.sum( hdf5_file[key][dps_key]) else: measures_to_return[out_file] = np.average( hdf5_file[key][dps_key]) return {(in_label, out_label): measures_to_return}
def _processing_wrapper(args): bundles_dir = args[0] in_label, out_label = args[1] measures_to_compute = copy.copy(args[2]) weighted = args[3] if args[4] is not None: similarity_directory = args[4][0] in_filename_1 = os.path.join(bundles_dir, '{}_{}.trk'.format(in_label, out_label)) in_filename_2 = os.path.join(bundles_dir, '{}_{}.trk'.format(out_label, in_label)) if os.path.isfile(in_filename_1): in_filename = in_filename_1 elif os.path.isfile(in_filename_2): in_filename = in_filename_2 else: return sft = load_tractogram(in_filename, 'same') affine, dimensions, voxel_sizes, _ = sft.space_attributes measures_to_return = {} # Precompute to save one transformation, insert later if 'length' in measures_to_compute: streamlines_copy = list(sft.get_streamlines_copy()) mean_length = np.average(length(streamlines_copy)) # If density is not required, do not compute it # Only required for volume, similarity and any metrics if not ((len(measures_to_compute) == 1 and ('length' in measures_to_compute or 'streamline_count' in measures_to_compute)) or (len(measures_to_compute) == 2 and ('length' in measures_to_compute and 'streamline_count' in measures_to_compute))): sft.to_vox() sft.to_corner() density = compute_tract_counts_map(sft.streamlines, dimensions) if 'volume' in measures_to_compute: measures_to_return['volume'] = np.count_nonzero(density) * \ np.prod(voxel_sizes) measures_to_compute.remove('volume') if 'streamline_count' in measures_to_compute: measures_to_return['streamline_count'] = len(sft) measures_to_compute.remove('streamline_count') if 'length' in measures_to_compute: measures_to_return['length'] = mean_length measures_to_compute.remove('length') if 'similarity' in measures_to_compute and similarity_directory: density_sim = load_node_nifti(similarity_directory, in_label, out_label, in_filename) ba_vox = compute_bundle_adjacency_voxel(density, density_sim) measures_to_return['similarity'] = ba_vox measures_to_compute.remove('similarity') for measure in measures_to_compute: if os.path.isdir(measure): map_dirname = measure map_data = load_node_nifti(map_dirname, in_label, out_label, in_filename) measures_to_return[map_dirname] = np.average( map_data[map_data > 0]) elif os.path.isfile(measure): metric_filename = measure if not is_header_compatible(metric_filename, sft): logging.error( '{} and {} do not have a compatible header'.format( in_filename, metric_filename)) raise IOError metric_data = nib.load(metric_filename).get_data() if weighted: density = density / np.max(density) voxels_value = metric_data * density voxels_value = voxels_value[voxels_value > 0] else: voxels_value = metric_data[density > 0] measures_to_return[metric_filename] = np.average(voxels_value) return {(in_label, out_label): measures_to_return}
def _processing_wrapper(args): hdf5_filename = args[0] labels_img = args[1] in_label, out_label = args[2] measures_to_compute = copy.copy(args[3]) if args[4] is not None: similarity_directory = args[4][0] weighted = args[5] include_dps = args[6] hdf5_file = h5py.File(hdf5_filename, 'r') key = '{}_{}'.format(in_label, out_label) if key not in hdf5_file: return streamlines = reconstruct_streamlines_from_hdf5(hdf5_file, key) affine, dimensions, voxel_sizes, _ = get_reference_info(labels_img) measures_to_return = {} if not (np.allclose(hdf5_file.attrs['affine'], affine, atol=1e-03) and np.array_equal(hdf5_file.attrs['dimensions'], dimensions)): raise ValueError('Provided hdf5 have incompatible headers.') # Precompute to save one transformation, insert later if 'length' in measures_to_compute: streamlines_copy = list(streamlines) # scil_decompose_connectivity.py requires isotropic voxels mean_length = np.average(length(streamlines_copy)) * voxel_sizes[0] # If density is not required, do not compute it # Only required for volume, similarity and any metrics if not ((len(measures_to_compute) == 1 and ('length' in measures_to_compute or 'streamline_count' in measures_to_compute)) or (len(measures_to_compute) == 2 and ('length' in measures_to_compute and 'streamline_count' in measures_to_compute))): density = compute_tract_counts_map(streamlines, dimensions) if 'volume' in measures_to_compute: measures_to_return['volume'] = np.count_nonzero(density) * \ np.prod(voxel_sizes) measures_to_compute.remove('volume') if 'streamline_count' in measures_to_compute: measures_to_return['streamline_count'] = len(streamlines) measures_to_compute.remove('streamline_count') if 'length' in measures_to_compute: measures_to_return['length'] = mean_length measures_to_compute.remove('length') if 'similarity' in measures_to_compute and similarity_directory: density_sim = load_node_nifti(similarity_directory, in_label, out_label, labels_img) if density_sim is None: ba_vox = 0 else: ba_vox = compute_bundle_adjacency_voxel(density, density_sim) measures_to_return['similarity'] = ba_vox measures_to_compute.remove('similarity') for measure in measures_to_compute: if isinstance(measure, str) and os.path.isdir(measure): map_dirname = measure map_data = load_node_nifti(map_dirname, in_label, out_label, labels_img) measures_to_return[map_dirname] = np.average( map_data[map_data > 0]) elif isinstance(measure, tuple) and os.path.isfile(measure[0]): metric_filename = measure[0] metric_img = measure[1] if not is_header_compatible(metric_img, labels_img): logging.error('{} do not have a compatible header'.format( metric_filename)) raise IOError metric_data = metric_img.get_fdata(dtype=np.float64) if weighted: density = density / np.max(density) voxels_value = metric_data * density voxels_value = voxels_value[voxels_value > 0] else: voxels_value = metric_data[density > 0] measures_to_return[metric_filename] = np.average(voxels_value) if include_dps: for dps_key in hdf5_file[key].keys(): if dps_key not in ['data', 'offsets', 'lengths']: out_file = os.path.join(include_dps, dps_key) measures_to_return[out_file] = np.average( hdf5_file[key][dps_key]) return {(in_label, out_label): measures_to_return}
def compute_all_measures(args): tuple_1, tuple_2 = args[0] filename_1, reference_1 = tuple_1 filename_2, reference_2 = tuple_2 streamline_dice = args[1] disable_streamline_distance = args[2] if not is_header_compatible(reference_1, reference_2): raise ValueError('{} and {} have incompatible headers'.format( filename_1, filename_2)) data_tuple_1 = load_data_tmp_saving([filename_1, reference_1, False, disable_streamline_distance]) if data_tuple_1 is None: return None density_1, endpoints_density_1, bundle_1, \ centroids_1 = data_tuple_1 data_tuple_2 = load_data_tmp_saving([filename_2, reference_2, False, disable_streamline_distance]) if data_tuple_2 is None: return None density_2, endpoints_density_2, bundle_2, \ centroids_2 = data_tuple_2 _, _, voxel_size, _ = get_reference_info(reference_1) voxel_size = np.product(voxel_size) # These measures are in mm^3 binary_1 = copy.copy(density_1) binary_1[binary_1 > 0] = 1 binary_2 = copy.copy(density_2) binary_2[binary_2 > 0] = 1 volume_overlap = np.count_nonzero(binary_1 * binary_2) volume_overlap_endpoints = np.count_nonzero( endpoints_density_1 * endpoints_density_2) volume_overreach = np.abs(np.count_nonzero( binary_1 + binary_2) - volume_overlap) volume_overreach_endpoints = np.abs(np.count_nonzero( endpoints_density_1 + endpoints_density_2) - volume_overlap_endpoints) # These measures are in mm bundle_adjacency_voxel = compute_bundle_adjacency_voxel(density_1, density_2, non_overlap=True) if streamline_dice and not disable_streamline_distance: bundle_adjacency_streamlines = \ compute_bundle_adjacency_streamlines(bundle_1, bundle_2, non_overlap=True) elif not disable_streamline_distance: bundle_adjacency_streamlines = \ compute_bundle_adjacency_streamlines(bundle_1, bundle_2, centroids_1=centroids_1, centroids_2=centroids_2, non_overlap=True) # These measures are between 0 and 1 dice_vox, w_dice_vox = compute_dice_voxel(density_1, density_2) dice_vox_endpoints, w_dice_vox_endpoints = compute_dice_voxel( endpoints_density_1, endpoints_density_2) density_correlation = compute_correlation(density_1, density_2) density_correlation_endpoints = compute_correlation(endpoints_density_1, endpoints_density_2) measures_name = ['bundle_adjacency_voxels', 'dice_voxels', 'w_dice_voxels', 'volume_overlap', 'volume_overreach', 'dice_voxels_endpoints', 'w_dice_voxels_endpoints', 'volume_overlap_endpoints', 'volume_overreach_endpoints', 'density_correlation', 'density_correlation_endpoints'] measures = [bundle_adjacency_voxel, dice_vox, w_dice_vox, volume_overlap * voxel_size, volume_overreach * voxel_size, dice_vox_endpoints, w_dice_vox_endpoints, volume_overlap_endpoints * voxel_size, volume_overreach_endpoints * voxel_size, density_correlation, density_correlation_endpoints] if not disable_streamline_distance: measures_name += ['bundle_adjacency_streamlines'] measures += [bundle_adjacency_streamlines] # Only when the tractograms are exactly the same if streamline_dice: dice_streamlines, streamlines_intersect, streamlines_union = \ compute_dice_streamlines(bundle_1, bundle_2) streamlines_count_overlap = len(streamlines_intersect) streamlines_count_overreach = len( streamlines_union) - len(streamlines_intersect) measures_name += ['dice_streamlines', 'streamlines_count_overlap', 'streamlines_count_overreach'] measures += [dice_streamlines, streamlines_count_overlap, streamlines_count_overreach] return dict(zip(measures_name, measures))