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
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
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)