def test_to_data_frame(fname): """Test EDF/BDF Raw Pandas exporter.""" ext = op.splitext(fname)[1][1:].lower() if ext == 'edf': raw = read_raw_edf(fname, preload=True, verbose='error') elif ext == 'bdf': raw = read_raw_bdf(fname, preload=True, verbose='error') _, times = raw[0, :10] df = raw.to_data_frame() assert (df.columns == raw.ch_names).all() assert_array_equal(np.round(times * 1e3), df.index.values[:10]) df = raw.to_data_frame(index=None, scalings={'eeg': 1e13}) assert 'time' in df.index.names assert_array_equal(df.values[:, 0], raw._data[0] * 1e13)
def test_load_generator(fname, recwarn): """Test IO of annotations from edf and bdf files with raw info.""" ext = op.splitext(fname)[1][1:].lower() if ext == 'edf': raw = read_raw_edf(fname) elif ext == 'bdf': raw = read_raw_bdf(fname) assert len(raw.annotations.onset) == 2 found_types = [k for k, v in channel_indices_by_type(raw.info, picks=None).items() if v] assert len(found_types) == 1 events, event_id = events_from_annotations(raw) ch_names = ['squarewave', 'ramp', 'pulse', 'ECG', 'noise', 'sine 1 Hz', 'sine 8 Hz', 'sine 8.5 Hz', 'sine 15 Hz', 'sine 17 Hz', 'sine 50 Hz'] assert raw.get_data().shape == (11, 120000) assert raw.ch_names == ch_names assert event_id == {'RECORD START': 2, 'REC STOP': 1} assert_array_equal(events, [[0, 0, 2], [120000, 0, 1]])
def test_bdf_crop_save_stim_channel(tmpdir): """Test EDF with various sampling rates.""" raw = read_raw_bdf(bdf_stim_channel_path) raw.save(tmpdir.join('test-raw.fif'), tmin=1.2, tmax=4.0, overwrite=True)
# channels to be exclude from import exclude = ['EXG5', 'EXG6', 'EXG7', 'EXG8'] # ======================================================================== # --------- loop through files and make bids-files blocks --------------- for ind, file in enumerate(files): # --- 1) set up paths and file names ----------------------- filepath, filename = os.path.split(file) # subject in question subj = re.findall(r'\d+', filename)[0].rjust(3, '0') # --- 2) import the data ----------------------------------- raw = read_raw_bdf(file, preload=False, exclude=exclude) # --- 3) dataset info -------------------------------------- # keep the sampling rate sfreq = raw.info['sfreq'] # all channels in raw chans = raw.info['ch_names'] # channels in montage montage_chans = montage.ch_names # nr of eeg channels n_eeg = len([chan for chan in chans if chan in montage_chans]) # channel types types = [] for chan in chans: if chan in montage_chans:
def copyfile_edf(src, dest, anonymize=None): """Copy an EDF, EDF+, or BDF file to a new location, optionally anonymize. .. warning:: EDF/EDF+/BDF files contain two fields for recording dates: A generic "startdate" field that supports only 2-digit years, and a "Startdate" field as part of the "local recording identification", which supports 4-digit years. If you want to anonymize your file, MNE-BIDS will set the "startdate" field to 85 (i.e., 1985), the earliest possible date for that field. However, the "Startdate" field in the file's "local recording identification" and the date in the session's corresponding ``scans.tsv`` will be set correctly according to the argument provided to the ``anonymize`` parameter. Note that it is possible that not all EDF/EDF+/BDF reading software parses the accurate recording date, and that for some reading software, the wrong year (1985) may be parsed. Parameters ---------- src : str | pathlib.Path The source path of the .edf or .bdf file to be copied. dest : str | pathlib.Path The destination path of the .edf or .bdf file. anonymize : dict | None If None (default), no anonymization is performed. If dict, data will be anonymized depending on the keys provided with the dict: `daysback` is a required key, `keep_his` is an optional key. `daysback` : int Number of days by which to move back the recording date in time. In studies with multiple subjects the relative recording date differences between subjects can be kept by using the same number of `daysback` for all subject anonymizations. `daysback` should be great enough to shift the date prior to 1925 to conform with BIDS anonymization rules. Due to limitations of the EDF/BDF format, the year of the anonymized date will always be set to 1985 in the 'startdate' field of the file. The correctly-shifted year will be written to the 'local recording identification' region of the file header, which may not be parsed by all EDF/EDF+/BDF reader softwares. `keep_his` : bool By default (False), all subject information next to the recording date will be overwritten as well. If True, keep subject information apart from the recording date. Participant names and birthdates will always be anonymized if present, regardless of this setting. See Also -------- mne.io.anonymize_info copyfile_brainvision copyfile_bti copyfile_ctf copyfile_eeglab copyfile_kit """ # Ensure source & destination extensions are the same fname_src, ext_src = _parse_ext(src) fname_dest, ext_dest = _parse_ext(dest) if ext_src != ext_dest: raise ValueError(f'Need to move data with same extension, ' f' but got "{ext_src}" and "{ext_dest}"') # Copy data prior to any anonymization sh.copyfile(src, dest) # Anonymize EDF/BDF data, if requested if anonymize is not None: if ext_src == '.bdf': raw = read_raw_bdf(dest, preload=False, verbose=0) elif ext_src == '.edf': raw = read_raw_edf(dest, preload=False, verbose=0) else: raise ValueError('Unsupported file type ({0})'.format(ext_src)) # Get subject info, recording info, and recording date with open(dest, 'rb') as f: f.seek(8) # id_info field starts 8 bytes in id_info = f.read(80).decode('ascii').rstrip() rec_info = f.read(80).decode('ascii').rstrip() # Parse metadata from file if len(id_info) == 0 or len(id_info.split(' ')) != 4: id_info = "X X X X" if len(rec_info) == 0 or len(rec_info.split(' ')) != 5: rec_info = "Startdate X X X X" pid, sex, birthdate, name = id_info.split(' ') start_date, admin_code, tech, equip = rec_info.split(' ')[1:5] # Try to anonymize the recording date daysback, keep_his = _check_anonymize(anonymize, raw, '.edf') anonymize_info(raw.info, daysback=daysback, keep_his=keep_his) start_date = '01-JAN-1985' meas_date = '01.01.85' # Anonymize ID info and write to file if keep_his: # Always remove participant birthdate and name to be safe id_info = [pid, sex, "X", "X"] rec_info = ["Startdate", start_date, admin_code, tech, equip] else: id_info = ["0", "X", "X", "X"] rec_info = ["Startdate", start_date, "X", "mne-bids_anonymize", "X"] with open(dest, 'r+b') as f: f.seek(8) # id_info field starts 8 bytes in f.write(bytes(" ".join(id_info).ljust(80), 'ascii')) f.write(bytes(" ".join(rec_info).ljust(80), 'ascii')) f.write(bytes(meas_date, 'ascii'))
from config import fname, task_name, montage, parser, LoggingFormat ############################################################################### # Start processing step # Handle command line arguments args = parser.parse_args() subject = args.subject print(LoggingFormat.PURPLE + LoggingFormat.BOLD + 'Converting to BIDS: Subject %s' % subject + LoggingFormat.END) ############################################################################### # 1) Import the data input_file = fname.source(source_type='eeg', subject=subject) raw = read_raw_bdf(input_file, preload=False) # sampling rate sfreq = raw.info['sfreq'] # channels in dataset channels = raw.info['ch_names'] ############################################################################### # 2) Modify dataset info # identify channel types based on matching names in montage types = [] for channel in channels: if channel in montage.ch_names: types.append('eeg') elif channel.startswith('EOG') | channel.startswith('EXG'): types.append('eog')
def importbdf(bdfname, nchans=34, refchans=['EXG1', 'EXG2'], hptsname=None, mask=255, extrachans=[], exclude=[], verbose=None): """Wrapper around mne-python to import BDF files Parameters ---------- bdfname - Name of the biosemi .bdf filename with full path nchans - Number of EEG channels (including references) (Optional) By default, 34 (32 + 2 references) refchans - list of strings with reference channel names (Optional) By default ['EXG1','EXG2']. Use None for average reference. hptsname - Name of the electrode position file in .hpts format with path (Optional) By default a 32 channel Biosemi layout is used. If the nchans is >= 64 and < 96, a 64 channel Biosemi layout is used. If nchans >= 96, a 96 channel biosemi layout is used. Formats other than .hpts will also likely work, but behavior may vary. mask - Integer mask to use for trigger channel (Default is 255). extrachans - Additional channels other than EEG and EXG that may be in the bdf file. These will be marked as MISC in mne-python. Specify as list of names. exclude - List of channel names to exclude from importing verbose - bool, str, int, or None (Optional) The verbosity of messages to print. If a str, it can be either DEBUG, INFO, WARNING, ERROR, or CRITICAL. Returns ------- raw - MNE raw data object of rereferences and preloaded data eves - Event list (3 column array as required by mne.Epochs) Requires -------- mne-python module > release 0.7 """ # Default HPTS file if (hptsname is None): anlffr_root = os.path.dirname(sys.modules['anlffr'].__file__) if nchans >= 64 and nchans < 96: logger.info('Number of channels is greater than 64.' ' Hence loading a 64 channel montage.') hptspath = os.path.join(anlffr_root, 'helper/sysfiles/') hptsname = 'biosemi64.hpts' montage = read_dig_hpts(hptspath + hptsname) misc = ['EXG3', 'EXG4', 'EXG5', 'EXG6', 'EXG7', 'EXG8'] else: if nchans >= 96: logger.info('Number of channels is greater than 96.' ' Hence loading a 96 channel montage.') hptspath = os.path.join(anlffr_root, 'helper/sysfiles/') hptsname = 'biosemi96.hpts' montage = read_dig_hpts(hptspath + hptsname) misc = ['EXG3', 'EXG4', 'EXG5', 'EXG6', 'EXG7', 'EXG8'] else: if nchans == 2: logger.info('Number of channels is 2.' 'Guessing ABR montage or saccades.') montage = None misc = [] else: logger.info('Loading a default 32 channel montage.') hptspath = os.path.join(anlffr_root, 'helper/sysfiles/') hptsname = 'biosemi32.hpts' montage = read_dig_hpts(hptspath + hptsname) misc = ['EXG3', 'EXG4', 'EXG5', 'EXG6', 'EXG7', 'EXG8'] else: montage = read_dig_hpts(hptsname) # User-supplied misc = ['EXG3', 'EXG4', 'EXG5', 'EXG6', 'EXG7', 'EXG8'] misc += extrachans raw = read_raw_bdf(bdfname, preload=True, misc=misc, exclude=exclude, stim_channel='auto') raw.set_montage(montage, on_missing='warn') # Rereference if refchans is not None: sys.stdout.write('Re-referencing data to: ' + ' '.join(refchans)) (raw, ref_data) = set_eeg_reference(raw, refchans, copy=False) raw.info['bads'] += refchans else: # Add average reference operator for possible use later ave_ref_operator = make_eeg_average_ref_proj(raw.info, activate=False) raw = raw.add_proj(ave_ref_operator) eves = find_events(raw, shortest_event=1, mask=mask) return (raw, eves)
montage = make_standard_montage(kind='standard_1020') # channels to be exclude from import exclude = ['EXG5', 'EXG6', 'EXG7', 'EXG8'] # ======================================================================== # ------------ loop through files and extract blocks -------------------- for file in files: # --- 1) set up paths and file names ----------------------- filepath, filename = op.split(file) # subject in question subj = re.findall(r'\d+', filename)[0].rjust(3, '0') print(subj) # --- 2) import the data ----------------------------------- raw = read_raw_bdf(file, preload=True, exclude=exclude) # check data and remove channel with low (i.e., near zero variance) flats = np.where(np.std(raw.get_data(), axis=1) * 1e6 < 10.)[0] # names flats = [raw.ch_names[ch] for ch in flats] # print summary print('Following channels were dropped as variance ~ 0:', flats) # remove them from data set raw.drop_channels(flats) # --- 3) modify data set information ---------------------- # keep the sampling rate sfreq = raw.info['sfreq'] # and date of measurement date_of_record = raw.annotations.orig_time
clip = None # === LOOP THROUGH FILES AND RUN PRE-PROCESSING ========================== for file in files: # --- 1) Set up paths and file names ----------------------- filepath, filename = os.path.split(file) filename, ext = os.path.splitext(filename) subj = re.findall(r'\d+', filename)[0] # --- 2) READ IN THE DATA ---------------------------------- # Set EEG arrangement montage = read_montage(kind='biosemi64') # Import raw data raw = read_raw_bdf(file, montage=montage, preload=True, exclude=exclude) # Note the sampling rate of recording sfreq = raw.info['sfreq'] # all channels in raw chans = raw.info['ch_names'] # channels in montage montage_chans = montage.ch_names # nr of eeg channels n_eeg = len([chan for chan in chans if chan in montage_chans]) # channel types types = [] for chan in chans: if chan in montage_chans: types.append('eeg') elif re.match('EOG|EXG', chan):