Ejemplo n.º 1
0
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')
Ejemplo n.º 2
0
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)
Ejemplo n.º 3
0
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)