Example #1
0
def _get_bids_fname_from_filesystem(*, bids_basename, bids_root, sub, ses,
                                    kind):
    if kind is None:
        kind = _infer_kind(bids_basename=bids_basename,
                           bids_root=bids_root,
                           sub=sub,
                           ses=ses)

    data_dir = make_bids_folders(subject=sub,
                                 session=ses,
                                 kind=kind,
                                 make_dir=False)

    bti_dir = op.join(bids_root, data_dir, f'{bids_basename}_{kind}')
    if op.isdir(bti_dir):
        logger.info(f'Assuming BTi data in {bti_dir}')
        bids_fname = f'{bti_dir}.pdf'
    else:
        # Find all matching files in all supported formats.
        valid_exts = list(reader.keys())
        matching_paths = glob.glob(
            op.join(bids_root, data_dir, f'{bids_basename}_{kind}.*'))
        matching_paths = [
            p for p in matching_paths if _parse_ext(p)[1] in valid_exts
        ]

        if not matching_paths:
            msg = ('Could not locate a data file of a supported format. This '
                   'is likely a problem with your BIDS dataset. Please run '
                   'the BIDS validator on your data.')
            raise RuntimeError(msg)

        # FIXME This will break e.g. with FIFF data split across multiple
        # FIXME files.
        if len(matching_paths) > 1:
            msg = ('Found more than one matching data file for the requested '
                   'recording. Cannot proceed due to the ambiguity. This is '
                   'likely a problem with your BIDS dataset. Please run the '
                   'BIDS validator on your data.')
            raise RuntimeError(msg)

        matching_path = matching_paths[0]
        bids_fname = op.basename(matching_path)

    return bids_fname
Example #2
0
def get_matched_empty_room(bids_basename, bids_root):
    """Get matching empty-room file for an MEG recording.

    Parameters
    ----------
    bids_basename : str | BIDSPath
        The base filename of the BIDS-compatible file. Typically, this can be
        generated using :func:`mne_bids.make_bids_basename`.
    bids_root : str | pathlib.Path
        Path to the BIDS root folder.

    Returns
    -------
    er_basename : str | None.
        The basename corresponding to the best-matching empty-room measurement.
        Returns None if none was found.
    """
    # convert to BIDS Path
    if isinstance(bids_basename, str):
        params = _parse_bids_filename(bids_basename, False)
        bids_basename = BIDSPath(subject=params.get('sub'),
                                 session=params.get('ses'),
                                 recording=params.get('rec'),
                                 acquisition=params.get('acq'),
                                 processing=params.get('proc'),
                                 space=params.get('space'),
                                 run=params.get('run'),
                                 task=params.get('task'))

    kind = 'meg'  # We're only concerned about MEG data here
    bids_fname = bids_basename.get_bids_fname(kind=kind, bids_root=bids_root)
    _, ext = _parse_ext(bids_fname)
    if ext == '.fif':
        extra_params = dict(allow_maxshield=True)
    else:
        extra_params = None

    raw = read_raw_bids(bids_basename=bids_basename,
                        bids_root=bids_root,
                        kind=kind,
                        extra_params=extra_params)
    if raw.info['meas_date'] is None:
        raise ValueError('The provided recording does not have a measurement '
                         'date set. Cannot get matching empty-room file.')

    ref_date = raw.info['meas_date']
    if not isinstance(ref_date, datetime):
        # for MNE < v0.20
        ref_date = datetime.fromtimestamp(raw.info['meas_date'][0])

    emptyroom_dir = pathlib.Path(
        make_bids_folders(bids_root=bids_root,
                          subject='emptyroom',
                          make_dir=False))

    if not emptyroom_dir.exists():
        return None

    # Find the empty-room recording sessions.
    emptyroom_session_dirs = [
        x for x in emptyroom_dir.iterdir()
        if x.is_dir() and str(x.name).startswith('ses-')
    ]
    if not emptyroom_session_dirs:  # No session sub-directories found
        emptyroom_session_dirs = [emptyroom_dir]

    # Now try to discover all recordings inside the session directories.

    allowed_extensions = list(reader.keys())
    # `.pdf` is just a "virtual" extension for BTi data (which is stored inside
    # a dedicated directory that doesn't have an extension)
    del allowed_extensions[allowed_extensions.index('.pdf')]

    candidate_er_fnames = []
    for session_dir in emptyroom_session_dirs:
        dir_contents = glob.glob(
            op.join(session_dir, kind, f'sub-emptyroom_*_{kind}*'))
        for item in dir_contents:
            item = pathlib.Path(item)
            if ((item.suffix in allowed_extensions)
                    or (not item.suffix and item.is_dir())):  # Hopefully BTi?
                candidate_er_fnames.append(item.name)

    # Walk through recordings, trying to extract the recording date:
    # First, from the filename; and if that fails, from `info['meas_date']`.
    best_er_basename = None
    min_delta_t = np.inf
    date_tie = False

    failed_to_get_er_date_count = 0
    for er_fname in candidate_er_fnames:
        params = _parse_bids_filename(er_fname, verbose=False)
        er_meas_date = None

        er_bids_path = BIDSPath(subject='emptyroom',
                                session=params.get('ses', None),
                                task=params.get('task', None),
                                acquisition=params.get('acq', None),
                                run=params.get('run', None),
                                processing=params.get('proc', None),
                                recording=params.get('rec', None),
                                space=params.get('space', None))
        er_basename = str(er_bids_path)

        # Try to extract date from filename.
        if params['ses'] is not None:
            try:
                er_meas_date = datetime.strptime(params['ses'], '%Y%m%d')
            except (ValueError, TypeError):
                # There is a session in the filename, but it doesn't encode a
                # valid date.
                pass

        if er_meas_date is None:  # No luck so far! Check info['meas_date']
            _, ext = _parse_ext(er_fname)
            if ext == '.fif':
                extra_params = dict(allow_maxshield=True)
            else:
                extra_params = None

            er_raw = read_raw_bids(bids_basename=er_basename,
                                   bids_root=bids_root,
                                   kind=kind,
                                   extra_params=extra_params)

            er_meas_date = er_raw.info['meas_date']
            if er_meas_date is None:  # There's nothing we can do.
                failed_to_get_er_date_count += 1
                continue

        er_meas_date = er_meas_date.replace(tzinfo=ref_date.tzinfo)
        delta_t = er_meas_date - ref_date

        if abs(delta_t.total_seconds()) == min_delta_t:
            date_tie = True
        elif abs(delta_t.total_seconds()) < min_delta_t:
            min_delta_t = abs(delta_t.total_seconds())
            best_er_basename = er_basename
            date_tie = False

    if failed_to_get_er_date_count > 0:
        msg = (f'Could not retrieve the empty-room measurement date from '
               f'a total of {failed_to_get_er_date_count} recording(s).')
        warn(msg)

    if date_tie:
        msg = ('Found more than one matching empty-room measurement with the '
               'same recording date. Selecting the first match.')
        warn(msg)

    return best_er_basename
Example #3
0
def run():
    """Run the mark_channels command."""
    from mne.commands.utils import get_optparser

    parser = get_optparser(__file__,
                           usage="usage: %prog options args",
                           prog_prefix='mne_bids',
                           version=mne_bids.__version__)

    parser.add_option('--ch_name',
                      dest='ch_names',
                      action='append',
                      default=[],
                      help='The names of the bad channels. If multiple '
                      'channels are bad, pass the --ch_name parameter '
                      'multiple times.')
    parser.add_option('--status',
                      default='bad',
                      help='Status of the channels (Either "good", or "bad").')
    parser.add_option('--description',
                      dest='descriptions',
                      action='append',
                      default=[],
                      help='Descriptions as to why the channels are bad. '
                      'Must match the number of bad channels provided. '
                      'Pass multiple times to supply more than one '
                      'value in that case.')
    parser.add_option('--bids_root',
                      dest='bids_root',
                      help='The path of the folder containing the BIDS '
                      'dataset')
    parser.add_option('--subject_id', dest='subject', help=('Subject name'))
    parser.add_option('--session_id', dest='session', help='Session name')
    parser.add_option('--task', dest='task', help='Task name')
    parser.add_option('--acq',
                      dest='acquisition',
                      help='Acquisition parameter')
    parser.add_option('--run', dest='run', help='Run number')
    parser.add_option('--proc', dest='processing', help='Processing label.')
    parser.add_option('--rec', dest='recording', help='Recording name')
    parser.add_option('--type',
                      dest='datatype',
                      help='Recording data type, e.g. meg, ieeg or eeg')
    parser.add_option('--suffix',
                      dest='suffix',
                      help='The filename suffix, i.e. the last part before '
                      'the extension')
    parser.add_option('--ext',
                      dest='extension',
                      help='The filename extension, including the leading '
                      'period, e.g. .fif')
    parser.add_option('--verbose',
                      dest='verbose',
                      action='store_true',
                      help='Whether do generate additional diagnostic output')

    opt, args = parser.parse_args()
    if args:
        parser.print_help()
        parser.error(f'Please do not specify arguments without flags. '
                     f'Got: {args}.\n')

    if opt.bids_root is None:
        parser.print_help()
        parser.error('You must specify bids_root')
    if opt.ch_names is None:
        parser.print_help()
        parser.error('You must specify some --ch_name parameters.')

    status = opt.status
    ch_names = [] if opt.ch_names == [''] else opt.ch_names
    bids_path = BIDSPath(subject=opt.subject,
                         session=opt.session,
                         task=opt.task,
                         acquisition=opt.acquisition,
                         run=opt.run,
                         processing=opt.processing,
                         recording=opt.recording,
                         datatype=opt.datatype,
                         suffix=opt.suffix,
                         extension=opt.extension,
                         root=opt.bids_root)

    bids_paths = bids_path.match()
    # Only keep data we can actually read & write.
    allowed_extensions = list(reader.keys())
    bids_paths = [p for p in bids_paths if p.extension in allowed_extensions]

    if not bids_paths:
        logger.info('No matching files found. Please consider using a less '
                    'restrictive set of entities to broaden the search.')
        return  # XXX should be return with an error code?

    logger.info(f'Marking channels {", ".join(ch_names)} as bad in '
                f'{len(bids_paths)} recording(s) …')
    for bids_path in bids_paths:
        logger.info(f'Processing: {bids_path.basename}')
        mark_channels(bids_path=bids_path,
                      ch_names=ch_names,
                      status=status,
                      descriptions=opt.descriptions,
                      verbose=opt.verbose)