def main(): # Parse command line arguments parser = argparse.ArgumentParser(description='Convert DICOM files to BIDS-compliant Nifty structure') parser.add_argument('-d', '--dataset', default='.', help='BIDS dataset directory containing sourcedata subdirectory') parser.add_argument('--no-sessions', action='store_true', default=False, help='Do not use session sub-directories') parser.add_argument('--overwrite', action='store_true', default=False, help='Overwrite existing files') parser.add_argument('--skip_if_pruning', action='store_true', default=False, help='Skip pruning of nonexistent IntendedFor items in json files') parser.add_argument('--clean_conv_dir', action='store_true', default=False, help='Clean up conversion directory') # Parse command line arguments args = parser.parse_args() dataset_dir = os.path.realpath(args.dataset) no_sessions = args.no_sessions overwrite = args.overwrite print('\n------------------------------------------------------------') print('BIDSKIT %s' % __version__) print('------------------------------------------------------------') # Check for minimum dcm2niix version (mostly for multirecon suffix handling) btr.check_dcm2niix_version('v1.0.20181125') # Create a BIDS directory tree object to handle file locations # Creates directory btree = BIDSTree(dataset_dir, overwrite) print('\nSource data directory : %s' % btree.sourcedata_dir) print('Working Directory : %s' % btree.work_dir) print('Use Session Directories : %s' % ('No' if no_sessions else 'Yes')) print('Overwrite Existing Files : %s' % ('Yes' if overwrite else 'No')) # Load protocol translation and exclusion info from derivatives/conversion directory # If no translator is present, prot_dict is an empty dictionary # and a template will be created in the derivatives/conversion directory. # This template should be completed by the user and the conversion rerun. translator = btree.read_translator() if translator and os.path.isdir(btree.work_dir): print('\n------------------------------------------------------------') print('Pass 2 : Populating BIDS directory') print('------------------------------------------------------------') first_pass = False else: print('\n------------------------------------------------------------') print('Pass 1 : DICOM to Nifti conversion and translator creation') print('------------------------------------------------------------') first_pass = True subject_dir_list = [] # Loop over subject directories in DICOM root for dcm_sub_dir in glob(btree.sourcedata_dir + '/*/'): sid = os.path.basename(dcm_sub_dir.strip('/')) # Check that subject ID is legal btr.check_subject_session(sid) subject_dir_list.append(dataset_dir + "/sub-" + sid) print('\n------------------------------------------------------------') print('Processing subject ' + sid) print('------------------------------------------------------------') # Handle subject vs subject/session directory lists if no_sessions: dcm_dir_list = [dcm_sub_dir] else: dcm_dir_list = glob(dcm_sub_dir + '/*/') # Loop over source data session directories in subject directory for dcm_dir in dcm_dir_list: # BIDS subject, session and conversion directories sub_prefix = 'sub-' + sid if no_sessions: # If session subdirs aren't being used, *_ses_dir = *sub_dir # Use an empty ses_prefix with os.path.join to achieve this ses = '' ses_prefix = '' else: ses = os.path.basename(dcm_dir.strip('/')) # Check that session ID is legal btr.check_subject_session(ses) ses_prefix = 'ses-' + ses print(' Processing session ' + ses) # Working conversion directories work_subj_dir = os.path.join(btree.work_dir, sub_prefix) work_conv_dir = os.path.join(work_subj_dir, ses_prefix) # BIDS source directory directories bids_subj_dir = os.path.join(dataset_dir, sub_prefix) bids_ses_dir = os.path.join(bids_subj_dir, ses_prefix) print(' Working subject directory : %s' % work_subj_dir) if not no_sessions: print(' Working session directory : %s' % work_conv_dir) print(' BIDS subject directory : %s' % bids_subj_dir) if not no_sessions: print(' BIDS session directory : %s' % bids_ses_dir) # Safely create working directory for current subject # Flag for conversion if no working directory existed if not os.path.isdir(work_conv_dir): os.makedirs(work_conv_dir) needs_converting = True else: needs_converting = False if first_pass or needs_converting: # Run dcm2niix conversion into working conversion directory print(' Converting all DICOM images in %s' % dcm_dir) devnull = open(os.devnull, 'w') subprocess.call(['dcm2niix', '-b', 'y', '-z', 'y', '-f', '%n--%d--%q--%s', '-o', work_conv_dir, dcm_dir], stdout=devnull, stderr=subprocess.STDOUT) if not first_pass: # Get subject age and sex from representative DICOM header dcm_info = bio.dcm_info(dcm_dir) # Add line to participants TSV file btr.add_participant_record(dataset_dir, sid, dcm_info['Age'], dcm_info['Sex']) # Organize dcm2niix output into BIDS subject/session directories organize_series(work_conv_dir, first_pass, translator, bids_ses_dir, sid, ses, args.clean_conv_dir, overwrite) if first_pass: # Create a template protocol dictionary btree.write_translator(translator) if not args.skip_if_pruning: print("\nSubject directories to prune: " + ", ".join(subject_dir_list)) for bids_subj_dir in subject_dir_list: btr.prune_intendedfors(bids_subj_dir, True) # Finally validate that all is well with the BIDS dataset if not first_pass: btree.validate() # Clean exit sys.exit(0)
def main(): # Parse command line arguments parser = argparse.ArgumentParser( description='Convert DICOM files to BIDS-compliant Nifty structure') parser.add_argument( '-d', '--dataset', default='.', help='BIDS dataset directory containing sourcedata subdirectory') parser.add_argument( '-s', '--subjects', nargs='+', default=[], help= 'List of subject IDs to convert (eg --subjects alpha bravo charlie)') parser.add_argument('--no-sessions', action='store_true', default=False, help='Do not use session sub-directories') parser.add_argument( '--no-anon', action='store_true', default=False, help='Do not anonymize BIDS output (eg for phantom data)') parser.add_argument('--overwrite', action='store_true', default=False, help='Overwrite existing files') parser.add_argument( '--skip-if-pruning', action='store_true', default=False, help='Skip pruning of nonexistent IntendedFor items in json files') parser.add_argument('--clean-conv-dir', action='store_true', default=False, help='Clean up conversion directory') parser.add_argument( '--bind-fmaps', action='store_true', default=False, help='Bind fieldmaps to fMRI series using IntendedFor field') parser.add_argument('-V', '--version', action='store_true', default=False, help='Display bidskit version number and exit') # Parse command line arguments args = parser.parse_args() dataset_dir = os.path.realpath(args.dataset) subject_list = args.subjects no_sessions = args.no_sessions no_anon = args.no_anon overwrite = args.overwrite bind_fmaps = args.bind_fmaps # Read version from setup.py ver = pkg_resources.get_distribution('bidskit').version if args.version: print('BIDSKIT {}'.format(ver)) sys.exit(1) print('') print('------------------------------------------------------------') print('BIDSKIT {}'.format(ver)) print('------------------------------------------------------------') # Check for minimum dcm2niix version (mostly for multirecon suffix handling) btr.check_dcm2niix_version('v1.0.20181125') # Create a BIDS directory tree object to handle file locations # Creates directory btree = BIDSTree(dataset_dir, overwrite) if len(subject_list) > 1: subj_to_convert = ' '.join(subject_list) else: subj_to_convert = 'All' print('') print('Subjects to convert : {}'.format(subj_to_convert)) print('Source data directory : {}'.format(btree.sourcedata_dir)) print('Working Directory : {}'.format(btree.work_dir)) print('Use Session Directories : {}'.format( 'No' if no_sessions else 'Yes')) print( 'Overwrite Existing Files : {}'.format('Yes' if overwrite else 'No')) print('Anonymize BIDS Output : {}'.format('No' if no_anon else 'Yes')) print('Bind fieldmaps : {}'.format( 'Yes' if bind_fmaps else 'No')) # Load protocol translation and exclusion info from derivatives/conversion directory # If no translator is present, prot_dict is an empty dictionary # and a template will be created in the derivatives/conversion directory. # This template should be completed by the user and the conversion rerun. translator = btree.read_translator() if translator and os.path.isdir(btree.work_dir): print('') print('------------------------------------------------------------') print('Pass 2 : Populating BIDS directory') print('------------------------------------------------------------') first_pass = False else: print('') print('------------------------------------------------------------') print('Pass 1 : DICOM to Nifti conversion and translator creation') print('------------------------------------------------------------') first_pass = True # Init list of output subject directories out_subj_dir_list = [] # Init list of source subject directories from sourcedata contents if no subjects provided in command line if len(subject_list) < 1: print(' Creating subject list from sourcedata contents') subject_list = [] for it in os.scandir(btree.sourcedata_dir): if it.is_dir(): subject_list.append(it.name) print(' Found {:d} subjects in sourcedata folder'.format( len(subject_list))) # Loop over subject list (either from sourcedata contents or command line) for sid in subject_list: print('') print('------------------------------------------------------------') print('Processing subject {}'.format(sid)) print('------------------------------------------------------------') # Full path to subject directory in sourcedata/ src_subj_dir = os.path.realpath(os.path.join(btree.sourcedata_dir, sid)) # BIDS subject ID with prefix subj_prefix = 'sub-{:s}'.format(sid) # Add full path to subject output directory to running list out_subj_dir_list.append(os.path.join(dataset_dir, subj_prefix)) # Create list of DICOM directories to convert # This will be either a session or series folder list depending on no-sessions command line flag if no_sessions: dcm_dir_list = [src_subj_dir] else: dcm_dir_list = glob(os.path.join(src_subj_dir, '*')) # Loop over DICOM directories in subject directory for dcm_dir in dcm_dir_list: if no_sessions: # If session subdirs aren't being used, *_ses_dir = *sub_dir # Use an empty ses_prefix with os.path.join to achieve this ses = '' ses_prefix = '' else: ses = os.path.basename(os.path.normpath(dcm_dir)) ses_prefix = 'ses-{:s}'.format(ses) print(' Processing session ' + ses) # Working conversion directories work_subj_dir = os.path.join(btree.work_dir, subj_prefix) work_conv_dir = os.path.join(work_subj_dir, ses_prefix) # BIDS source directory directories bids_subj_dir = os.path.join(dataset_dir, subj_prefix) bids_ses_dir = os.path.join(bids_subj_dir, ses_prefix) print(' Working subject directory : %s' % work_subj_dir) if not no_sessions: print(' Working session directory : %s' % work_conv_dir) print(' BIDS subject directory : %s' % bids_subj_dir) if not no_sessions: print(' BIDS session directory : %s' % bids_ses_dir) # Safely create working directory for current subject # Flag for conversion if no working directory exists if not os.path.isdir(work_conv_dir): os.makedirs(work_conv_dir) needs_converting = True else: needs_converting = False if first_pass or needs_converting: # Run dcm2niix conversion into working conversion directory print(' Converting all DICOM images in %s' % dcm_dir) devnull = open(os.devnull, 'w') # BIDS anonymization flag - default 'y' anon = 'n' if no_anon else 'y' # Compose command cmd = [ 'dcm2niix', '-b', 'y', '-ba', anon, '-z', 'y', '-f', '%n--%d--%q--%s', '-o', work_conv_dir, dcm_dir ] with open(os.devnull, 'w') as devnull: subprocess.run(cmd, stdout=devnull, stderr=devnull) if not first_pass: # Get subject age and sex from representative DICOM header dcm_info = bio.dcm_info(dcm_dir) # Add line to participants TSV file btr.add_participant_record(dataset_dir, sid, dcm_info['Age'], dcm_info['Sex']) # Organize dcm2niix output into BIDS subject/session directories organize_series(work_conv_dir, first_pass, translator, bids_ses_dir, sid, ses, args.clean_conv_dir, overwrite) if first_pass: # Create a template protocol dictionary btree.write_translator(translator) if not args.skip_if_pruning: print('') print('Subject directories to prune: ' + ', '.join(out_subj_dir_list)) for bids_subj_dir in out_subj_dir_list: btr.prune_intendedfors(bids_subj_dir, True) if not first_pass: if args.bind_fmaps: print('') print('Binding nearest fieldmap to each functional series') for bids_subj_dir in out_subj_dir_list: btr.bind_fmaps(bids_subj_dir) # Finally validate that all is well with the BIDS dataset if not first_pass: btree.validate() # Clean exit sys.exit(0)
def main(): # Parse command line arguments parser = argparse.ArgumentParser( description='Convert DICOM files to BIDS-compliant Nifty structure') parser.add_argument( '-d', '--dataset', default='.', help='BIDS dataset directory containing sourcedata subdirectory') parser.add_argument('--no-sessions', action='store_true', default=False, help='Do not use session sub-directories') parser.add_argument( '--no-anon', action='store_true', default=False, help='Do not anonymize BIDS output (eg for phantom data)') parser.add_argument('--overwrite', action='store_true', default=False, help='Overwrite existing files') parser.add_argument( '--skip_if_pruning', action='store_true', default=False, help='Skip pruning of nonexistent IntendedFor items in json files') parser.add_argument('--clean_conv_dir', action='store_true', default=False, help='Clean up conversion directory') # TODO: Implement option for linking nearest fieldmap in time to BOLD series via IntendedFor field # parser.add_argument('--nearest_fmap', action='store_true', default=False, # help='Associate BOLD series with nearest fieldmap in time to series start') # Parse command line arguments args = parser.parse_args() dataset_dir = os.path.realpath(args.dataset) no_sessions = args.no_sessions no_anon = args.no_anon overwrite = args.overwrite # nearest_fmap = args.nearest_fmap # Read version from setup.py ver = pkg_resources.get_distribution('bidskit').version print('') print('------------------------------------------------------------') print('BIDSKIT {}'.format(ver)) print('------------------------------------------------------------') # Check for minimum dcm2niix version (mostly for multirecon suffix handling) btr.check_dcm2niix_version('v1.0.20181125') # Create a BIDS directory tree object to handle file locations # Creates directory btree = BIDSTree(dataset_dir, overwrite) print('') print('Source data directory : {}'.format(btree.sourcedata_dir)) print('Working Directory : {}'.format(btree.work_dir)) print('Use Session Directories : {}'.format( 'No' if no_sessions else 'Yes')) print( 'Overwrite Existing Files : {}'.format('Yes' if overwrite else 'No')) print('Anonymize BIDS Output : {}'.format('No' if no_anon else 'Yes')) # print('Use Nearest Fieldmap : {}'.format('Yes' if nearest_fmap else 'No')) # Load protocol translation and exclusion info from derivatives/conversion directory # If no translator is present, prot_dict is an empty dictionary # and a template will be created in the derivatives/conversion directory. # This template should be completed by the user and the conversion rerun. translator = btree.read_translator() if translator and os.path.isdir(btree.work_dir): print('') print('------------------------------------------------------------') print('Pass 2 : Populating BIDS directory') print('------------------------------------------------------------') first_pass = False else: print('') print('------------------------------------------------------------') print('Pass 1 : DICOM to Nifti conversion and translator creation') print('------------------------------------------------------------') first_pass = True # Init list of output subject directories subject_dir_list = [] # Loop over list of subject directories in sourcedata directory for dcm_sub_dir in glob(btree.sourcedata_dir + os.sep + '*' + os.sep): sid = os.path.basename(os.path.normpath(dcm_sub_dir)) subject_dir_list.append(os.path.join(dataset_dir, 'sub-' + sid)) print('') print('------------------------------------------------------------') print('Processing subject ' + sid) print('------------------------------------------------------------') # Handle subject vs subject/session directory lists if no_sessions: dcm_dir_list = [dcm_sub_dir] else: dcm_dir_list = glob(dcm_sub_dir + os.sep + '*' + os.sep) # Loop over source data session directories in subject directory for dcm_dir in dcm_dir_list: # BIDS subject, session and conversion directories sub_prefix = 'sub-' + sid if no_sessions: # If session subdirs aren't being used, *_ses_dir = *sub_dir # Use an empty ses_prefix with os.path.join to achieve this ses = '' ses_prefix = '' else: ses = os.path.basename(os.path.normpath(dcm_dir)) ses_prefix = 'ses-' + ses print(' Processing session ' + ses) # Working conversion directories work_subj_dir = os.path.join(btree.work_dir, sub_prefix) work_conv_dir = os.path.join(work_subj_dir, ses_prefix) # BIDS source directory directories bids_subj_dir = os.path.join(dataset_dir, sub_prefix) bids_ses_dir = os.path.join(bids_subj_dir, ses_prefix) print(' Working subject directory : %s' % work_subj_dir) if not no_sessions: print(' Working session directory : %s' % work_conv_dir) print(' BIDS subject directory : %s' % bids_subj_dir) if not no_sessions: print(' BIDS session directory : %s' % bids_ses_dir) # Safely create working directory for current subject # Flag for conversion if no working directory existed if not os.path.isdir(work_conv_dir): os.makedirs(work_conv_dir) needs_converting = True else: needs_converting = False if first_pass or needs_converting: # Run dcm2niix conversion into working conversion directory print(' Converting all DICOM images in %s' % dcm_dir) devnull = open(os.devnull, 'w') # BIDS anonymization flag - default 'y' anon = 'n' if no_anon else 'y' # Compose command cmd = [ 'dcm2niix', '-b', 'y', '-ba', anon, '-z', 'y', '-f', '%n--%d--%q--%s', '-o', work_conv_dir, dcm_dir ] with open(os.devnull, 'w') as devnull: subprocess.run(cmd, stdout=devnull, stderr=devnull) if not first_pass: # Get subject age and sex from representative DICOM header dcm_info = bio.dcm_info(dcm_dir) # Add line to participants TSV file btr.add_participant_record(dataset_dir, sid, dcm_info['Age'], dcm_info['Sex']) # Organize dcm2niix output into BIDS subject/session directories organize_series(work_conv_dir, first_pass, translator, bids_ses_dir, sid, ses, args.clean_conv_dir, overwrite) if first_pass: # Create a template protocol dictionary btree.write_translator(translator) if not args.skip_if_pruning: print('') print('Subject directories to prune: ' + ', '.join(subject_dir_list)) for bids_subj_dir in subject_dir_list: btr.prune_intendedfors(bids_subj_dir, True) # if not first_pass: # # if args.nearest_fmap: # # print('') # print('Assigning nearest fieldmap to each BOLD series') # btr.intendedfor_nearest_fieldmap(bids_subj_dir) # Finally validate that all is well with the BIDS dataset if not first_pass: btree.validate() # Clean exit sys.exit(0)