def subj_data_from_dicoms(ctx, crumb_path, arg_name, verbose=False): """ Print a list of folder_name -> NUK id. The NUK ID is calculated from the first DICOM file found in the end of the `dicom_path`. Parameters ---------- crumb_path: str Path with Crumbs to the DICOM files, e.g., /home/hansel/data/{subj_id}/{session}/{acq}/{dcm_file} arg_name: str Name of the argument in `dicom_path` of the subj_id Returns ------- subj_data: dict of subj records A dict with records of the information extracted from the DICOM files as well as the calculated NUK Pseudonym. """ if verbose: verbose_switch(verbose) crumb = Crumb(os.path.expanduser(os.path.abspath(crumb_path)), ignore_list=['.*']) if not crumb.has_crumbs(): raise ValueError('Expected a path with crumb arguments, e.g., ' '"/home/hansel/data/{group}/{sid}/{session}"') subj_nuks = [] for path in crumb.ls(arg_name): log.info('Reading DICOMs in {}.'.format(path)) subj_path = path.split()[0] subj = _read_dcm_until_valid(subj_path) if subj is None: log.info('Could not find a valid DICOM in {}.'.format(subj_path)) else: subj_nuks.append(subj) return subj_nuks
def _get_plot_file_pairs( background: hansel.Crumb, foreground: hansel.Crumb) -> Iterable[Tuple[str, Union[str, None]]]: if background is None and foreground is not None: background = foreground if not foreground: return ((bg, None) for bg in background.ls(make_crumbs=False)) if not background.has_crumbs() and not foreground.has_crumbs(): return [(background.ls(make_crumbs=False)[0], foreground.ls(make_crumbs=False)[0])] if foreground.has_crumbs() and background.has_crumbs(): background_args = tuple(background.open_args()) foreground_args = tuple(foreground.open_args()) if background_args != foreground_args: raise click.BadParameter( 'Make sure that background and foreground have the same crumb arguments. ' 'Got {}, and {}.'.format(background_args, foreground_args)) try: intersect_values = hansel.intersection(background, foreground) except KeyError as ke: raise click.BadParameter( 'Make sure that background and foreground have the same crumb arguments. ' 'Got {}, and {}.'.format(background_args, foreground_args)) from ke else: return ((background.build_paths(crumb_args, make_crumbs=False)[0], foreground.build_paths(crumb_args, make_crumbs=False)[0]) for crumb_args in intersect_values) bg_files = [] fg_files = [] if background.has_crumbs(): bg_files = list(background.ls(make_crumbs=False)) fg_files = [foreground.ls(make_crumbs=False)[0]] * len(bg_files) if foreground.has_crumbs(): fg_files = list(foreground.ls(make_crumbs=False)) bg_files = [background.ls(make_crumbs=False)[0]] * len(fg_files) return zip(bg_files, fg_files)
def dcm2nii(ctx, input_crumb_path, output_dir, regex='fnmatch', ncpus=3): """ Convert all DICOM files within `input_crumb_path` into NifTI in `output_folder`. Will copy only the NifTI files reoriented by MRICron's dcm2nii command. Will rename the NifTI files that are matched with recognized modalities to the short modality name from config.ACQ_PATTERNS. Parameters ---------- input_dir: str A crumb path str indicating the whole path until the DICOM files. Example: '/home/hansel/data/{group}/{subj_id}/{session_id}/{acquisition}/{dcm_file} The crumb argument just before the last one will be used as folder container reference for the DICOM series. output_dir: str The root folder path where to save the tree of nifti files. Example: '/home/hansel/nifti' This function will create the same tree as the crumbs in input_crumb_path, hence for the example above the output would have the following structure: '/home/hansel/nifti/{group}/{subj_id}/{session_id}/{nifti_file}' Where {nifti_file} will take the name from the {acquisition} or from the patterns in ACQ_PATTERNS in `config.py` file. regex: str The regular expression syntax you may want to set in the Crumbs. See hansel.Crumb documentation for this. ncpus: int this says the number of processes that will be launched for dcm2nii in parallel. """ from boyle.dicom.convert import convert_dcm2nii input_dir = os.path.expanduser(input_crumb_path) output_dir = os.path.expanduser(output_dir) if not os.path.exists(output_dir): log.info('Creating output folder {}.'.format(output_dir)) os.makedirs(output_dir) else: log.info('Output folder {} already exists, this will overwrite/merge ' 'whatever is inside.'.format(output_dir)) input_dir = Crumb(input_dir, regex=regex, ignore_list=['.*']) if not input_dir.has_crumbs(): raise ValueError( 'I am almost sure that this cannot work if you do not ' 'use crumb arguments in the input path, got {}.'.format(input_dir)) acq_folder_arg, last_in_arg = tuple(input_dir.all_args())[-2:] out_arg_names = [ '{' + arg + '}' for arg in tuple(input_dir.all_args())[:-1] ] output_dir = Crumb(os.path.join(output_dir, *out_arg_names), regex=regex, ignore_list=['.*']) src_dst = [] acquisitions = input_dir.ls(acq_folder_arg, make_crumbs=True) for acq in acquisitions: out_args = acq.arg_values.copy() acq_out = output_dir.replace(**out_args) out_dir = os.path.dirname(acq_out.path) out_file = os.path.basename(acq_out.path) + '.nii.gz' os.makedirs(out_dir, exist_ok=True) src_dst.append((acq.split()[0], out_dir, out_file)) if ncpus > 1: import multiprocessing as mp pool = mp.Pool(processes=ncpus) results = [ pool.apply_async(convert_dcm2nii, args=(dr, ss, dst)) for dr, ss, dst in src_dst ] _ = [p.get() for p in results] else: _ = [convert_dcm2nii(path, sess, dst) for path, sess, dst in src_dst]
def rename_to_nukid(crumb_path, arg_name, subj_data, verbose_only=False): """ Rename the folders at the `arg_name` level using `subj_data` records. Will rename from subj_data['DCM Folder'] to subj_data['NUK Pseudonym']. Parameters ---------- crumb_path: str Path with Crumbs to the DICOM files, e.g., /home/hansel/data/{subj_id} arg_name: str Name of the argument in `dicom_path` of the subject identification. These names should be the same as the ones in 'DCM Folder' value. outfile: str verbose_only: bool Returns ------- src_dsts: list of 2-tuples of str """ def rename_many(src_dsts, verbose_only=False): """ For each 2-tuple in src_dsts of file/folder paths will rename the first element to the second. Parameters ---------- src_dsts : list of 2-tuple verbose_only: bool Will not perform the operation will only print them. """ for (src, dst) in src_dsts: if not os.path.exists(src): raise IOError('Could not find source file {}.'.format(src)) if os.path.exists(dst): if src == dst: continue else: raise IOError('Destination path {} already exists.'.format(dst)) log.info('mv {} -> {}'.format(src, dst)) if not verbose_only: os.rename(src, dst) if isinstance(subj_data, pd.DataFrame): subjs = subj_data.to_records() else: subjs = subj_data if not Crumb.has_crumbs(Crumb(crumb_path)): raise ValueError('Expected a path with crumb arguments, e.g., ' '"/home/hansel/data/{group}/{sid}/{session}"') crumb = Crumb(os.path.expanduser(os.path.abspath(crumb_path)), ignore_list=['.*']) src_dsts = [] for subj in subjs: src_crs = crumb.replace(**{arg_name: subj['DCM Folder']}).unfold() for src_cr in src_crs: dst_args = src_cr.arg_values.copy() dst_args[arg_name] = subj['nukid'] dst_cr = crumb.replace(**dst_args) if not os.path.exists(src_cr.path): raise IOError('Could not find folder {} for subject {}.'.format(src_cr, subj)) if Crumb.has_crumbs(dst_cr.path): raise KeyError('The destination path should be fully specified, got {}.'.format(dst_cr)) src_dsts.append((src_cr.path, dst_cr.path)) rename_many(src_dsts=src_dsts, verbose_only=verbose_only) return src_dsts