def test_plot_trigger(samefreq_full_acq_file): chtrig = 3 test_path, test_filename = os.path.split(samefreq_full_acq_file) out = os.path.join(test_path, 'Test_belt_pulse_samefreq') phys_obj = io.load_acq(samefreq_full_acq_file, chtrig) viz.plot_trigger(phys_obj.timeseries[0], phys_obj.timeseries[chtrig], out, 1.5, 1.6, 60, test_filename) assert os.path.isfile(out + '_trigger_time.png')
def phys2bids(filename, info=False, indir='.', outdir='.', heur_file=None, sub=None, ses=None, chtrig=0, chsel=None, num_timepoints_expected=0, tr=1, thr=None, ch_name=[], chplot='', debug=False, quiet=False): """ Main workflow of phys2bids. Runs the parser, does some checks on input, then imports the right interface file to read the input. If only info is required, it returns a summary onscreen. Otherwise, it operates on the input to return a .tsv.gz file, possibily in BIDS format. Raises ------ NotImplementedError If the file extension is not supported yet. """ # Check options to make them internally coherent pt. I # #!# This can probably be done while parsing? outdir = utils.check_input_dir(outdir) utils.path_exists_or_make_it(outdir) # Create logfile name basename = 'phys2bids_' extension = 'tsv' isotime = datetime.datetime.now().strftime('%Y-%m-%dT%H%M%S') logname = os.path.join(outdir, (basename + isotime + '.' + extension)) # Set logging format log_formatter = logging.Formatter( '%(asctime)s\t%(name)-12s\t%(levelname)-8s\t%(message)s', datefmt='%Y-%m-%dT%H:%M:%S') # Set up logging file and open it for writing log_handler = logging.FileHandler(logname) log_handler.setFormatter(log_formatter) sh = logging.StreamHandler() if quiet: logging.basicConfig(level=logging.WARNING, handlers=[log_handler, sh]) elif debug: logging.basicConfig(level=logging.DEBUG, handlers=[log_handler, sh]) else: logging.basicConfig(level=logging.INFO, handlers=[log_handler, sh]) version_number = _version.get_versions()['version'] LGR.info(f'Currently running phys2bids version {version_number}') LGR.info(f'Input file is {filename}') # Check options to make them internally coherent pt. II # #!# This can probably be done while parsing? indir = utils.check_input_dir(indir) filename, ftype = utils.check_input_type(filename, indir) if heur_file: heur_file = utils.check_input_ext(heur_file, '.py') utils.check_file_exists(heur_file) infile = os.path.join(indir, filename) utils.check_file_exists(infile) # Read file! if ftype == 'acq': from phys2bids.interfaces.acq import populate_phys_input elif ftype == 'txt': from phys2bids.interfaces.txt import populate_phys_input else: # #!# We should add a logger here. raise NotImplementedError('Currently unsupported file type.') LGR.info(f'Reading the file {infile}') phys_in = populate_phys_input(infile, chtrig) LGR.info('Reading infos') phys_in.print_info(filename) # #!# Here the function viz.plot_channel should be called if chplot != '' or info: viz.plot_all(phys_in.ch_name, phys_in.timeseries, phys_in.units, phys_in.freq, infile, chplot) # If only info were asked, end here. if info: return # Create trigger plot. If possible, to have multiple outputs in the same # place, adds sub and ses label. if tr != 0 and num_timepoints_expected != 0: # Run analysis on trigger channel to get first timepoint and the time offset. # #!# Get option of no trigger! (which is wrong practice or Respiract) phys_in.check_trigger_amount(chtrig, thr, num_timepoints_expected, tr) LGR.info('Plot trigger') plot_path = os.path.join( outdir, os.path.splitext(os.path.basename(filename))[0]) if sub: plot_path += f'_sub-{sub}' if ses: plot_path += f'_ses-{ses}' viz.plot_trigger(phys_in.timeseries[0], phys_in.timeseries[chtrig], plot_path, tr, phys_in.thr, num_timepoints_expected, filename) else: LGR.warning('Skipping trigger pulse count. If you want to run it, ' 'call phys2bids using "-ntp" and "-tr" arguments') # The next few lines remove the undesired channels from phys_in. if chsel: LGR.info('Dropping unselected channels') for i in reversed(range(0, phys_in.ch_amount)): if i not in chsel: phys_in.delete_at_index(i) # If requested, change channel names. if ch_name: LGR.info('Renaming channels with given names') phys_in.rename_channels(ch_name) # The next few lines create a dictionary of different BlueprintInput # objects, one for each unique frequency in phys_in uniq_freq_list = set(phys_in.freq) output_amount = len(uniq_freq_list) if output_amount > 1: LGR.warning(f'Found {output_amount} different frequencies in input!') LGR.info(f'Preparing {output_amount} output files.') phys_out = {} # create phys_out dict that will have a # blueprint object per frequency # for each different frequency for uniq_freq in uniq_freq_list: # copy the phys_in object to the new dict entry phys_out[uniq_freq] = deepcopy(phys_in) # this counter will take into account how many channels are eliminated count = 0 # for each channel in the original phys_in object # take the frequency for idx, i in enumerate(phys_in.freq): # if that frequency is different than the frequency of the phys_obj entry if i != uniq_freq: # eliminate that channel from the dict since we only want channels # with the same frequency phys_out[uniq_freq].delete_at_index(idx - count) # take into acount the elimination so in the next eliminated channel we # eliminate correctly count += 1 # Also create a BlueprintOutput object for each unique frequency found. # Populate it with the corresponding blueprint input and replace it # in the dictionary. phys_out[uniq_freq] = BlueprintOutput.init_from_blueprint( phys_out[uniq_freq]) if heur_file and sub: LGR.info(f'Preparing BIDS output using {heur_file}') elif heur_file and not sub: LGR.warning(f'While "-heur" was specified, option "-sub" was not.\n' f'Skipping BIDS formatting.') # Preparing output parameters: name and folder. for uniq_freq in uniq_freq_list: # If possible, prepare bids renaming. if heur_file and sub: if output_amount > 1: # Add "recording-freq" to filename if more than one freq outfile = use_heuristic(heur_file, sub, ses, filename, outdir, uniq_freq) else: outfile = use_heuristic(heur_file, sub, ses, filename, outdir) else: outfile = os.path.join( outdir, os.path.splitext(os.path.basename(filename))[0]) if output_amount > 1: # Append "freq" to filename if more than one freq outfile = f'{outfile}_{uniq_freq}' LGR.info(f'Exporting files for freq {uniq_freq}') savetxt(outfile + '.tsv.gz', phys_out[uniq_freq].timeseries, fmt='%.8e', delimiter='\t') print_json(outfile, phys_out[uniq_freq].freq, phys_out[uniq_freq].start_time, phys_out[uniq_freq].ch_name) print_summary(filename, num_timepoints_expected, phys_in.num_timepoints_found, uniq_freq, phys_out[uniq_freq].start_time, outfile)
def _main(argv=None): """ Main workflow of phys2bids. Runs the parser, does some checks on input, then imports the right interface file to read the input. If only info is required, it returns a summary onscreen. Otherwise, it operates on the input to return a .tsv.gz file, possibily in BIDS format. """ options = _get_parser().parse_args(argv) # Check options to make them internally coherent # #!# This can probably be done while parsing? options.indir = utils.check_input_dir(options.indir) options.outdir = utils.check_input_dir(options.outdir) options.filename, ftype = utils.check_input_type(options.filename, options.indir) if options.heur_file: options.heur_file = utils.check_input_ext(options.heur_file, '.py') utils.check_file_exists(options.heur_file) infile = os.path.join(options.indir, options.filename) utils.check_file_exists(infile) outfile = os.path.join( options.outdir, os.path.splitext(os.path.basename(options.filename))[0]) # Read file! if ftype == 'acq': from phys2bids.interfaces.acq import populate_phys_input elif ftype == 'txt': from phys2bids.interfaces.txt import populate_phys_input else: # #!# We should add a logger here. raise NotImplementedError('Currently unsupported file type.') print('Reading the file') phys_in = populate_phys_input(infile, options.chtrig) print('Reading infos') phys_in.print_info(options.filename) # #!# Here the function viz.plot_channel should be called # for the desired channels. # If only info were asked, end here. if options.info: return # Run analysis on trigger channel to get first timepoint and the time offset. # #!# Get option of no trigger! (which is wrong practice or Respiract) phys_in.check_trigger_amount(options.thr, options.num_timepoints_expected, options.tr) # Create output folder if necessary print('Checking that the output folder exists') utils.path_exists_or_make_it(options.outdir) # Create trigger plot. If possible, to have multiple outputs in the same # place, adds sub and ses label. print('Plot trigger') plot_path = deepcopy(outfile) if options.sub: plot_path += f'_sub-{options.sub}' if options.ses: plot_path += f'_sub-{options.ses}' viz.plot_trigger(phys_in.timeseries[0], phys_in.timeseries[1], plot_path, options) # The next few lines remove the undesired channels from phys_in. if options.chsel: print('Dropping unselected channels') for i in reversed(range(0, phys_in.ch_amount)): if i not in options.chsel: phys_in.delete_at_index(i) # If requested, change channel names. if options.ch_name: print('Renaming channels with given names') phys_in.rename_channels(options.ch_name) # The next few lines create a dictionary of different BlueprintInput # objects, one for each unique frequency in phys_in uniq_freq_list = set(phys_in.freq) output_amount = len(uniq_freq_list) if output_amount > 1: print(f'Found {output_amount} different frequencies in input!') print(f'Preparing {output_amount} output files.') phys_out = {} for uniq_freq in uniq_freq_list: phys_out[uniq_freq] = deepcopy(phys_in) for i in reversed(phys_in.freq): if i != uniq_freq: phys_out[uniq_freq].delete_at_index(phys_in.ch_amount - i - 1) # Also create a BlueprintOutput object for each unique frequency found. # Populate it with the corresponding blueprint input and replace it # in the dictionary. phys_out[uniq_freq] = BlueprintOutput.init_from_blueprint( phys_out[uniq_freq]) if options.heur_file and options.sub: print(f'Preparing BIDS output using {options.heur_file}') elif options.heur_file and not options.sub: print(f'While "-heur" was specified, option "-sub" was not.\n' f'Skipping BIDS formatting.') for uniq_freq in uniq_freq_list: # If possible, prepare bids renaming. if options.heur_file and options.sub: if output_amount > 1: # Add "recording-freq" to filename if more than one freq outfile = use_heuristic(options.heur_file, options.sub, options.ses, options.filename, options.outdir, uniq_freq) else: outfile = use_heuristic(options.heur_file, options.sub, options.ses, options.filename, options.outdir) elif output_amount > 1: # Append "freq" to filename if more than one freq outfile = f'outfile_{uniq_freq}' print(f'Exporting files for freq {uniq_freq}') savetxt(outfile + '.tsv.gz', phys_out[uniq_freq].timeseries, fmt='%.8e', delimiter='\t') print_json(outfile, phys_out[uniq_freq].freq, phys_out[uniq_freq].start_time, phys_out[uniq_freq].ch_name) print_summary(options.filename, options.num_timepoints_expected, phys_in.num_timepoints_found, uniq_freq, phys_out[uniq_freq].start_time, outfile)