示例#1
0
def event_timestamps_to_indices(sigfile, eventfile):
	"""
	Convert LSL timestamps to sample indices for separetely recorded events.

	Parameters:
	sigfile: raw signal file (Python Pickle) recorded with stream_recorder.py.
	eventfile: event file where events are indexed with LSL timestamps.

	Returns:
	events list, which can be used as an input to mne.io.RawArray.add_events().
	"""

	raw= qc.load_obj(sigfile)
	ts= raw['timestamps'].reshape(-1)
	ts_min= min(ts)
	ts_max= max(ts)
	events= []

	with open(eventfile) as f:
		for l in f:
			data= l.strip().split('\t')
			event_ts= float( data[0] )
			event_value= int( data[2] )
			# find the first index not smaller than ts
			next_index= np.searchsorted(ts, event_ts)
			if next_index >= len(ts):
				qc.print_c( '** WARNING: Event %d at time %.3f is out of time range (%.3f - %.3f).'% (event_value,event_ts,ts_min,ts_max), 'y' )
			else:
				events.append( [next_index, 0, event_value] )
			#print(events[-1])

	return events
示例#2
0
def search_lsl(ignore_markers=False):
    import pylsl, time

    # look for LSL servers
    amp_list = []
    amp_list_backup = []
    while True:
        streamInfos = pylsl.resolve_streams()
        if len(streamInfos) > 0:
            for index, si in enumerate(streamInfos):
                amp_serial = pylsl.StreamInlet(si).info().desc().child(
                    'acquisition').child_value('serial_number').strip()
                amp_name = si.name()
                if 'Markers' in amp_name:
                    amp_list_backup.append((index, amp_name, amp_serial))
                else:
                    amp_list.append((index, amp_name, amp_serial))
            break
        print('No server available yet on the network...')
        time.sleep(1)

    if ignore_markers is False:
        amp_list += amp_list_backup

    qc.print_c('-- List of servers --', 'W')
    for i, (index, amp_name, amp_serial) in enumerate(amp_list):
        if amp_serial == '': amp_ser = 'N/A'
        else: amp_ser = amp_serial
        qc.print_c('%d: %s (Serial %s)' % (i, amp_name, amp_ser), 'W')

    if len(amp_list) == 1:
        index = 0
        dong = 0
    else:
        dong = 1
        index = raw_input(
            'Amp index? Hit enter without index to select the first server.\n>> '
        ).strip()
        index = int(index)  # dong
        #amp_index, amp_name, amp_serial= [(0, u'g.USBamp-1',u'UB-2010.06.31')]
    #qc.shell()
    amp_index, amp_name, amp_serial = amp_list[index]
    #if dong == 1:
    #amp_index = 0
    #print('!!!!!!!!!!!!!!!')
    si = streamInfos[amp_index]
    assert amp_name == si.name()
    assert amp_serial == pylsl.StreamInlet(si).info().desc().child(
        'acquisition').child_value('serial_number').strip()
    print('Selected %s (Serial: %s)' % (amp_name, amp_serial))

    return amp_name, amp_serial
示例#3
0
def load_raw(rawfile, spfilter=None, spchannels=None, events_ext=None, multiplier=1):
	if not os.path.exists(rawfile):
		qc.print_c('# ERROR: File %s not found'% rawfile, 'r')
		sys.exit(-1)

	extension= rawfile.split('.')[-1]
	assert extension in ['fif','fiff'], 'only fif format is supported'
	raw= mne.io.Raw(rawfile, preload=True)

	if 'TRIGGER' in raw.ch_names:
		tch_name= 'TRIGGER'
	elif 'STI ' in raw.ch_names: # e.g. 'STI 014'
		tch_name= 'STI '
	elif 'Trig1' in raw.ch_names:
		tch_name = 'Trig1'
	else:
		raise RuntimeError, 'No trigger channel found.'

	# find a value changing from zero to a non-zero value
	tch= raw.ch_names.index(tch_name)
	events= mne.find_events(raw, stim_channel=tch_name, shortest_event=1, uint_cast=True)

	n_channels= raw._data.shape[0]
	eeg_channels= list(range(n_channels))
	eeg_channels.pop(tch)
	if multiplier != 1:
		raw._data[eeg_channels] *= multiplier

	# apply spatial filter
	if spfilter=='car':
		if not spchannels:
			raw._data[eeg_channels]= raw._data[eeg_channels] - np.mean( raw._data[eeg_channels], axis=0 )
		else:
			raw._data[spchannels]= raw._data[spchannels] - np.mean( raw._data[spchannels], axis=0 )
	elif spfilter=='laplacian':
		if type(spchannels) is not dict:
			raise RuntimeError, 'For Lapcacian, SP_CHANNELS must be of a form {CHANNEL:[NEIGHBORS], ...}'
		rawcopy= raw._data.copy()
		for src in spchannels:
			nei= spchannels[src]
			raw._data[src]= rawcopy[src] - np.mean( rawcopy[nei], axis=0 )
	elif spfilter=='bipolar':
		raw._data[1:] -= raw._data[spchannels]
	elif spfilter is None:
		pass
	else:
		qc.print_c('# ERROR: Unknown spatial filter', spfilter, 'r')
		sys.exit(-1)

	return raw, events
示例#4
0
    def init_loop(self):

        self.updating = False

        self.sr = StreamReceiver(window_size=1,
                                 buffer_size=10,
                                 amp_serial=self.amp_serial,
                                 amp_name=self.amp_name)
        srate = int(self.sr.sample_rate)
        #n_channels= self.sr.channels

        # 12 unsigned ints (4 bytes)
        ########## TODO: assumkng 32 samples chunk => make it read from LSL header
        data = [
            'EEG', srate, ['L', 'R'], 32,
            len(self.sr.get_eeg_channels()), 0,
            self.sr.get_trigger_channel(), None, None, None, None, None
        ]
        qc.print_c('Trigger channel is %d' % self.sr.get_trigger_channel(),
                   'G')

        self.config = {
            'id': data[0],
            'sf': data[1],
            'labels': data[2],
            'samples': data[3],
            'eeg_channels': data[4],
            'exg_channels': data[5],
            'tri_channels': data[6],
            'eeg_type': data[8],
            'exg_type': data[9],
            'tri_type': data[10],
            'lbl_type': data[11],
            'tim_size': 1,
            'idx_size': 1
        }

        self.tri = np.zeros(self.config['samples'])
        self.last_tri = 0
        self.eeg = np.zeros(
            (self.config['samples'], self.config['eeg_channels']),
            dtype=np.float)
        self.exg = np.zeros(
            (self.config['samples'], self.config['exg_channels']),
            dtype=np.float)
        self.ts_list = []
        self.ts_list_tri = []
示例#5
0
cnbidirs = [
    'libLSL', 'Triggers', 'StreamReceiver', 'StreamRecorder', 'StreamViewer',
    'Decoder', 'Utils', 'Glass'
]
cnbiroot = os.path.dirname(os.path.realpath(__file__)) + '/'
sys.path.append(cnbiroot)
for d in cnbidirs:
    sys.path.append(cnbiroot + d)

# check MNE version. MNE < 0.11 has an offset bug for BDF data.
import mne
mne_ver = mne.__version__.split('.')
if mne_ver[0] == '0' and float(mne_ver[1]) < 12:
    import q_common as qc
    qc.print_c(
        '\n\n*** WARNING: Your Python-MNE version is %s. Please upgrade to 0.12 or higher. ***\n'
        % mne.__version__, 'r')

# channel names (trigger channel is index 0 so that eeg channels start from index 1)
CAP = {
    'GTEC_16': [
        'TRIGGER', 'Fz', 'FC3', 'FC1', 'FCz', 'FC2', 'FC4', 'C3', 'C1', 'Cz',
        'C2', 'C4', 'CP3', 'CP1', 'CPz', 'CP2', 'CP4'
    ],
    'GTEC_16_INFO': ['stim'] + ['eeg'] * 16,
    'BIOSEMI_64': [
        'TRIGGER', 'Fp1', 'AF7', 'AF3', 'F1', 'F3', 'F5', 'F7', 'FT7', 'FC5',
        'FC3', 'FC1', 'C1', 'C3', 'C5', 'T7', 'TP7', 'CP5', 'CP3', 'CP1', 'P1',
        'P3', 'P5', 'P7', 'P9', 'PO7', 'PO3', 'O1', 'Iz', 'Oz', 'POz', 'Pz',
        'CPz', 'Fpz', 'Fp2', 'AF8', 'AF4', 'AFz', 'Fz', 'F2', 'F4', 'F6', 'F8',
        'FT8', 'FC6', 'FC4', 'FC2', 'FCz', 'Cz', 'C2', 'C4', 'C6', 'T8', 'TP8',
示例#6
0
 def print(self, msg):
     qc.print_c('[StreamViewer] %s' % msg, color='W')
示例#7
0
    def connect(self, find_any=True):
        """
        Run in child process
        """
        server_found = False
        amps = []
        channels = 0
        while server_found == False:
            if self.amp_name is None and self.amp_serial is None:
                self.print("Looking for a streaming server...")
            else:
                self.print("Looking for %s (Serial %s) ..." %
                           (self.amp_name, self.amp_serial))
            streamInfos = pylsl.resolve_streams()
            #print(streamInfos)
            if len(streamInfos) > 0:
                # For now, only 1 amp is supported by a single StreamReceiver object.
                for si in streamInfos:
                    #is_slave= ('true'==pylsl.StreamInlet(si).info().desc().child('amplifier').child('settings').child('is_slave').first_child().value() )
                    inlet = pylsl.StreamInlet(si)
                    amp_serial = inlet.info().desc().child(
                        'acquisition').child_value('serial_number')
                    amp_name = si.name()
                    #qc.print_c('Found %s (%s)'% (amp_name,amp_serial), 'G')

                    # connect to a specific amp only?
                    if self.amp_serial is not None and self.amp_serial != amp_serial:
                        continue

                    # connect to a specific amp only?
                    if self.amp_name is not None and self.amp_name != amp_name:
                        continue

                    # EEG streaming server only?
                    if self.eeg_only and si.type() != 'EEG':
                        continue

                    if 'USBamp' in amp_name:
                        self.print('Found USBamp streaming server %s (type %s, amp_serial %s) @ %s.'\
                            % (amp_name, si.type(), amp_serial, si.hostname()) )
                        self.lsl_tr_channel = 16
                        channels += si.channel_count()
                        ch_list = pu.lsl_channel_list(inlet)
                        amps.append(si)
                        server_found = True
                        break
                    elif 'BioSemi' in amp_name:
                        self.print('Found BioSemi streaming server %s (type %s, amp_serial %s) @ %s.'\
                            % (amp_name, si.type(), amp_serial, si.hostname()) )
                        self.lsl_tr_channel = 0  # or subtract -6684927? (value when trigger==0)
                        channels += si.channel_count()
                        ch_list = pu.lsl_channel_list(inlet)
                        amps.append(si)
                        server_found = True
                        break
                    elif 'SmartBCI' in amp_name:
                        self.print('Found SmartBCI streaming server %s (type %s, amp_serial %s) @ %s.'\
                            % (amp_name, si.type(), amp_serial, si.hostname()) )
                        self.lsl_tr_channel = 23
                        channels += si.channel_count()
                        ch_list = pu.lsl_channel_list(inlet)
                        amps.append(si)
                        server_found = True
                        break
                    elif 'StreamPlayer' in amp_name:
                        self.print('Found StreamPlayer streaming server %s (type %s, amp_serial %s) @ %s.'\
                            % (amp_name, si.type(), amp_serial, si.hostname()) )
                        self.lsl_tr_channel = 0
                        channels += si.channel_count()
                        ch_list = pu.lsl_channel_list(inlet)
                        amps.append(si)
                        server_found = True
                        break
                    elif 'openvibeSignal' in amp_name:
                        self.print('Found an Openvibe signal streaming server %s (type %s, amp_serial %s) @ %s.'\
                            % (amp_name, si.type(), amp_serial, si.hostname()) )
                        ch_list = pu.lsl_channel_list(inlet)
                        if 'TRIGGER' in ch_list:
                            self.lsl_tr_channel = ch_list.index('TRIGGER')
                        else:
                            self.lsl_tr_channel = None
                        channels += si.channel_count()
                        amps.append(si)
                        server_found = True
                        self.multiplier = 10**6
                        break
                    elif 'openvibeMarkers' in amp_name:
                        self.print('Found an Openvibe markers server %s (type %s, amp_serial %s) @ %s.'\
                            % (amp_name, si.type(), amp_serial, si.hostname()) )
                        ch_list = pu.lsl_channel_list(inlet)
                        if 'TRIGGER' in ch_list:
                            self.lsl_tr_channel = ch_list.index('TRIGGER')
                        else:
                            self.lsl_tr_channel = None
                        channels += si.channel_count()
                        amps.append(si)
                        server_found = True
                        #self.multiplier= 10**6
                        break
                    elif find_any:
                        self.print('Found a streaming server %s (type %s, amp_serial %s) @ %s.'\
                            % (amp_name, si.type(), amp_serial, si.hostname()) )
                        self.lsl_tr_channel = 0  # needs to be changed
                        channels += si.channel_count()
                        from IPython import embed
                        embed()
                        ch_list = pu.lsl_channel_list(inlet)
                        amps.append(si)
                        server_found = True
                        break
            time.sleep(1)

        self.amp_name = amp_name

        # define EEG channel indices
        self.lsl_eeg_channels = range(channels)
        if self.lsl_tr_channel is not None:
            self.print('Assuming trigger channel to be %d' %
                       self.lsl_tr_channel)
            self.lsl_eeg_channels.pop(self.lsl_tr_channel)
        self.lsl_eeg_channels = np.array(self.lsl_eeg_channels)
        self.tr_channel = 0  # trigger channel is always set to 0.
        self.eeg_channels = np.arange(
            1, channels)  # signal channels start from 1.

        # create new inlets to read from the stream
        inlets_master = []
        inlets_slaves = []
        for amp in amps:
            inlet = pylsl.StreamInlet(amp)
            inlets_master.append(inlet)
            '''
            if True==inlet.info().desc().child('amplifier').child('settings').child('is_slave').first_child().value():
                self.print('Slave amp: %s.'% amp.name() )
                inlets_slaves.append(inlet)
            else:
                self.print('Master amp: %s.'% amp.name() )
                inlets_master.append(inlet)
            '''
            self.buffers.append([])
            self.timestamps.append([])

        inlets = inlets_master + inlets_slaves
        sample_rate = amps[0].nominal_srate()
        self.print('Channels: %d' % channels)
        self.print('LSL Protocol version: %s' % amps[0].version())
        self.print('Source sampling rate: %.1f' % sample_rate)
        self.print('Unit multiplier: %.1f' % self.multiplier)

        # set member vars
        self.channels = channels  # includes trigger channel
        self.winsize = int(round(self.winsec * sample_rate))
        self.bufsize = int(round(self.bufsec * sample_rate))
        self.sample_rate = sample_rate
        self.connected = True
        self.inlets = inlets  # NOTE: not picklable!
        self.ch_list = ch_list

        for i, chn in enumerate(self.ch_list):
            if chn == 'TRIGGER' or 'STI ' in chn:
                self.ch_list.pop(i)
                self.ch_list = ['TRIGGER'] + self.ch_list
                break
        qc.print_c('self.ch_list %s' % self.ch_list, 'Y')

        # fill in initial buffer
        self.print('Waiting to fill initial buffer of length %d' %
                   (self.winsize))
        while len(self.timestamps[0]) < self.winsize:
            self.acquire()
            time.sleep(0.1)
        self.ready = True
        self.print('Start receiving stream data.')
示例#8
0
 def print(self, msg):
     qc.print_c('[StreamReceiver] %s' % msg, 'W')
示例#9
0
    import q_common as qc
    import mne

    amp_name, amp_serial = pu.search_lsl()
    sr = StreamReceiver(window_size=1,
                        buffer_size=1,
                        amp_serial=amp_serial,
                        eeg_only=False,
                        amp_name=amp_name)
    sfreq = sr.get_sample_rate()

    watchdog = qc.Timer()
    tm = qc.Timer(autoreset=True)
    trg_ch = sr.get_trigger_channel()
    qc.print_c('Trigger channel: %d' % trg_ch, 'G')

    psde= mne.decoding.PSDEstimator(sfreq=sfreq, fmin=1, fmax=50, bandwidth=None,\
        adaptive=False, low_bias=True, n_jobs=1, normalization='length', verbose=None)

    last_ts = 0

    while True:
        lsl_time = pylsl.local_clock()
        sr.acquire()
        window, tslist = sr.get_window()
        window = window.T

        # print event values
        tsnew = np.where(np.array(tslist) > last_ts)[0][0]
        trigger = np.unique(window[trg_ch, tsnew:])
示例#10
0
def load_raw_old(rawfile, spfilter=None, spchannels=None, events_ext=None):
    """
	** Deprecated function **
	Please use convert2fif to convert non-fif files to fif first.


	Returns raw data and events

	Supports gdf, bdf, fif, and Python raw format (pcl).
	Any non-fif file will be saved into .fif format in the fif/ directory after loading.

	Parameters:
	rawfile: (absolute) data file path
	spfilter: 'car' | 'laplacian' | None
	spchannels: None | list (for CAR) | dict (for LAPLACIAN)
		'car': channel indices used for CAR filtering. If None, use all channels except
			   the trigger channel (index 0).
		'laplacian': {channel:[neighbor1, neighbor2, ...], ...}
		*** Note ***
		Since PyCNBI puts trigger channel as index 0, data channel starts from index 1.
	events_mne: Add externally recorded events (e.g. software trigger).
				events_mne is of format: [ [sample_index1, 0, event_value1],... ]

	Returns:
	raw: mne.io.RawArray object. First channel (index 0) is always trigger channel.
	events: mne-compatible events numpy array object (N x [frame, 0, type])
	spfilter= {None | 'car' | 'laplacian'}

	"""

    if not os.path.exists(rawfile):
        qc.print_c('# ERROR: File %s not found' % rawfile, 'r')
        sys.exit(-1)

    rawfile = rawfile.replace('\\', '/')
    dirs = rawfile.split('/')
    if len(dirs) == 1: basedir = './'
    else: basedir = '/'.join(dirs[:-1]) + '/'
    extension = rawfile.split('.')[-1]
    basename = '.'.join(rawfile.split('.')[:-1])
    raw = None
    events = []

    if extension == 'pcl':
        data = qc.load_obj(rawfile)

        if type(data['signals']) == list:
            print('Converting into numpy format')
            signals_raw = np.array(
                data['signals'][0]).T  # to channels x samples
        else:
            signals_raw = data['signals'].T  # to channels x samples
        sample_rate = data['sample_rate']
        events_raw = data['events']

        # BioSemi or gtec?
        if data['channels'] == 17:
            # move the trigger channel to the first row
            if find_event_channel(signals_raw) != 16:
                qc.print_c(
                    '**** WARNING: Assuming GTEC_16 format. Double-check trigger channel !! *****',
                    'r')
            signals = np.concatenate(
                (signals_raw[16, :].reshape(1, -1), signals_raw[:16, :]))
            info = mne.create_info(CAP['GTEC_16'], sample_rate,
                                   CAP['GTEC_16_INFO'])
        elif data['channels'] >= 73:
            signals = signals_raw[:
                                  73, :]  # trigger channel is already the first row
            sigtrig = signals[0, :] - 1
            signals[0, :] = 0xFF & sigtrig.astype(
                int)  # keep only the low 8 bits
            info = mne.create_info(CAP['BIOSEMI_64'], sample_rate,
                                   CAP['BIOSEMI_64_INFO'])
        elif data['channels'] == 24:
            qc.print_c(
                '**** ASSUMING SmartBCI system with no trigger channel ****',
                'y')
            if True:
                # A1=9, A2=16
                ear_avg = (signals_raw[8] + signals_raw[15]) / 2.0
                signals = signals_raw - ear_avg
                trigger = np.zeros((1, signals_raw.shape[1]))
                signals = np.vstack((trigger, signals))
            else:
                signals = signals_raw[:
                                      24, :]  # trigger channel is already the first row
            sigtrig = signals[0, :]
            signals[0, :] = 0x00
            info = mne.create_info(CAP['SMARTBCI_24'], sample_rate,
                                   CAP['SMARTBCI_24_INFO'])
        else:  # ok, unknown format
            # guess trigger channel
            trig_ch = find_event_channel(signals_raw)
            if trig_ch is not None:
                qc.print_c(
                    'Found trigger channel %d. Moving to channel 0.' % trig_ch,
                    'y')
                signals = np.concatenate(
                    (signals_raw[[trig_ch]], signals_raw[:trig_ch],
                     signals_raw[trig_ch + 1:]),
                    axis=0)
                assert signals_raw.shape == signals.shape
                num_eeg_channels = data['channels'] - 1
            else:
                # assuming no trigger channel exists, add a trigger channel to index 0 for consistency.
                qc.print_c(
                    '**** Unrecognized number of channels (%d). Adding an event channel to index 0.'
                    % data['channels'], 'r')
                eventch = np.zeros([1, signals_raw.shape[1]])
                signals = np.concatenate((eventch, signals_raw), axis=0)
                num_eeg_channels = data['channels']

            ch_names = ['TRIGGER'] + [
                'CH%d' % (x + 1) for x in range(num_eeg_channels)
            ]
            ch_info = ['stim'] + ['eeg'] * num_eeg_channels
            info = mne.create_info(ch_names, sample_rate, ch_info)

    elif extension in ['fif', 'fiff']:
        raw = mne.io.Raw(rawfile, preload=True)

    elif extension in ['bdf', 'gdf']:
        # convert to mat using MATLAB (MNE's edf reader has an offset bug)
        matfile = basename + '.mat'
        if not os.path.exists(matfile):
            print('>> Converting input to mat file')
            run = "[sig,header]=sload('%s.%s'); save('%s.mat','sig','header');" % (
                basename, extension, basename)
            qc.matlab(run)
            if not os.path.exists(matfile):
                qc.print_c('>> ERROR: mat file convertion error.', 'r')
                sys.exit()

        mat = scipy.io.loadmat(matfile)
        os.remove(matfile)
        sample_rate = int(mat['header']['SampleRate'])
        nch = mat['sig'].shape[1]

        if extension == 'gdf':
            # Note: gdf might  have a software trigger channel
            if nch == 17:
                ch_names = CAP['GTEC_16']
                ch_info = CAP['GTEC_16_INFO'][:nch]
            else:
                ch_names = ['TRIGGER'] + ['ch%d' % x for x in range(1, nch)]
                ch_info = ['stim'] + ['eeg'] * (nch - 1)

            # read events from header
            '''
			Important:
				event position may have the same frame number for two consecutive events
				It might be due to the CNBI software trigger bug
			Example:
				f1.20121220.102907.offline.mi.mi_rhlh.gdf (Two 10201's in evpos)
			'''
            evtype = mat['header']['EVENT'][0][0][0]['TYP'][0]
            evpos = mat['header']['EVENT'][0][0][0]['POS'][0]
            for e in range(evtype.shape[0]):
                label = int(evtype[e])
                events.append([int(evpos[e][0]), 0, label])

        elif extension == 'bdf':
            # assume Biosemi always has the same number of channels
            if nch == 73:
                ch_names = CAP['BIOSEMI_64']
                extra_ch = nch - len(CAP['BIOSEMI_64_INFO'])
                extra_names = []
                for ch in range(extra_ch):
                    extra_names.append('EXTRA%d' % ch)
                ch_names = ch_names + extra_names
                ch_info = CAP['BIOSEMI_64_INFO'] + ['misc'] * extra_ch
            else:
                qc.print_c(
                    '****** load_raw(): WARNING: Unrecognized number of channels (%d) ******'
                    % nch, 'y')
                qc.print_c(
                    'The last channel will be assumed to be trigger. Press Enter to continue, or Ctrl+C to break.',
                    'r')
                raw_input()
                # Set the trigger to be channel 0 because later we will move it to channel 0.
                ch_names = ['TRIGGER'
                            ] + ['CH%d' % (x + 1) for x in range(nch - 1)]
                ch_info = ['stim'] + ['eeg'] * (nch - 1)

        # Move the event channel to 0 (for consistency)
        signals_raw = mat['sig'].T  # -> channels x samples
        signals = np.concatenate(
            (signals_raw[-1, :].reshape(1, -1), signals_raw[:-1, :]))

        # Note: Biosig's sload() sometimes returns bogus event values so we use the following for events
        bdf = mne.io.read_raw_edf(rawfile, preload=True)
        events = mne.find_events(bdf)
        signals[-1][:] = bdf._data[
            -1][:]  # overwrite with the correct event values

        info = mne.create_info(ch_names, sample_rate, ch_info)
    else:
        # unknown format
        qc.print_c(
            'ERROR: Unrecognized file extension %s. It should be [.pcl | .fif | .fiff | .gdf | .bdf]'
            % extension, 'r')
        sys.exit(-1)

    if raw is None:
        # signals= channels x samples
        raw = mne.io.RawArray(signals, info)

        # check if software trigger
        trigch = raw.info['ch_names'].index('TRIGGER')
        if events != [] and max(raw[trigch][0][0]) == 0:
            raw.add_events(events, stim_channel='TRIGGER')

        # external events with LSL timestamps
        if events_ext != None:
            if extension != 'pcl':
                qc.print_c(
                    '>> ERROR: external events can be only added to raw .pcl files',
                    'r')
                sys.exit(-1)
            events_index = event_timestamps_to_indices(rawfile, events_ext)
            raw.add_events(events_index, stim_channel='TRIGGER')

        qc.make_dirs(basedir + 'fif/')
        fifname = basedir + 'fif/' + basename.split('/')[-1] + '.fif'
        raw.save(fifname, overwrite=True, verbose=False)
        print('Saving to', fifname)

    # find a value changing from zero to a non-zero value
    events = mne.find_events(raw, stim_channel='TRIGGER', shortest_event=1)

    # apply spatial filter
    n_channels = raw._data.shape[0]
    if spfilter == 'car':
        if not spchannels:
            raw._data[1:] = raw._data[1:] - np.mean(raw._data[1:], axis=0)
        else:
            raw._data[spchannels] = raw._data[spchannels] - np.mean(
                raw._data[spchannels], axis=0)
    elif spfilter == 'laplacian':
        if type(spchannels) is not dict:
            raise RuntimeError, 'For Lapcacian, SP_CHANNELS must be of a form {CHANNEL:[NEIGHBORS], ...}'
        rawcopy = raw._data.copy()
        for src in spchannels:
            nei = spchannels[src]
            raw._data[src] = rawcopy[src] - np.mean(rawcopy[nei], axis=0)
    elif spfilter == 'bipolar':
        raw._data[1:] -= raw._data[spchannels]
    elif spfilter is None:
        pass
    else:
        qc.print_c('# ERROR: Unknown spatial filter', spfilter, 'r')
        sys.exit(-1)

    return raw, events
示例#11
0
def load_raw(rawfile,
             spfilter=None,
             spchannels=None,
             events_ext=None,
             multiplier=1):
    """
	Loads data from a fif-format file.
	You can convert non-fif files (.eeg, .bdf, .gdf, .pcl) to fif format.

	Parameters:
	rawfile: (absolute) data file path
	spfilter: 'car' | 'laplacian' | None
	spchannels: None | list (for CAR) | dict (for LAPLACIAN)
		'car': channel indices used for CAR filtering. If None, use all channels except
			   the trigger channel (index 0).
		'laplacian': {channel:[neighbor1, neighbor2, ...], ...}
		*** Note ***
		Since PyCNBI puts trigger channel as index 0, data channel starts from index 1.
	events_mne: Add externally recorded events (e.g. software trigger).
				events_mne is of format: [ [sample_index1, 0, event_value1],... ]
	multiplier: Multiply all values except triggers (to convert unit).

	Returns:
	raw: mne.io.RawArray object. First channel (index 0) is always trigger channel.
	events: mne-compatible events numpy array object (N x [frame, 0, type])
	spfilter= {None | 'car' | 'laplacian'}

	"""

    if not os.path.exists(rawfile):
        qc.print_c('# ERROR: File %s not found' % rawfile, 'r')
        sys.exit(-1)

    extension = rawfile.split('.')[-1]
    assert extension in ['fif', 'fiff'], 'only fif format is supported'
    raw = mne.io.Raw(rawfile, preload=True)

    if 'TRIGGER' in raw.ch_names:
        tch_name = 'TRIGGER'
    elif 'STI ' in raw.ch_names:  # e.g. 'STI 014'
        tch_name = 'STI '
    elif 'Trig1' in raw.ch_names:
        tch_name = 'Trig1'
    else:
        raise RuntimeError, 'No trigger channel found.'

    # find a value changing from zero to a non-zero value
    tch = raw.ch_names.index(tch_name)
    events = mne.find_events(raw,
                             stim_channel=tch_name,
                             shortest_event=1,
                             uint_cast=True)

    n_channels = raw._data.shape[0]
    eeg_channels = list(range(n_channels))
    eeg_channels.pop(tch)
    if multiplier != 1:
        raw._data[eeg_channels] *= multiplier

    # apply spatial filter
    if spfilter == 'car':
        if not spchannels:
            raw._data[eeg_channels] = raw._data[eeg_channels] - np.mean(
                raw._data[eeg_channels], axis=0)
        else:
            raw._data[spchannels] = raw._data[spchannels] - np.mean(
                raw._data[spchannels], axis=0)
    elif spfilter == 'laplacian':
        if type(spchannels) is not dict:
            raise RuntimeError, 'For Lapcacian, SP_CHANNELS must be of a form {CHANNEL:[NEIGHBORS], ...}'
        rawcopy = raw._data.copy()
        for src in spchannels:
            nei = spchannels[src]
            raw._data[src] = rawcopy[src] - np.mean(rawcopy[nei], axis=0)
    elif spfilter == 'bipolar':
        raw._data[1:] -= raw._data[spchannels]
    elif spfilter is None:
        pass
    else:
        qc.print_c('# ERROR: Unknown spatial filter', spfilter, 'r')
        sys.exit(-1)

    return raw, events
示例#12
0
def record(state, amp_name, amp_serial, eeg_only=False):
    # set data file name
    filename = time.strftime(OUT_DIR + "/%Y%m%d-%H%M%S-raw.pcl",
                             time.localtime())
    qc.print_c('\n>> Output file: %s' % (filename), 'W')

    # test writability
    try:
        qc.make_dirs(OUT_DIR)
        open(
            filename,
            'w').write('The data will written when the recording is finished.')
    except:
        qc.print_c(
            '\n*** ERROR: There was a problem writing file %s\n' % filename,
            'W')
        sys.exit(-1)

    # start a server for sending out data filename for software trigger
    outlet = cnbi_lsl.start_server('StreamRecorderInfo',
                                   channel_format='string',
                                   source_id=filename,
                                   stype='Markers')

    # connect to EEG stream server
    sr = receiver.StreamReceiver(amp_name=amp_name,
                                 amp_serial=amp_serial,
                                 eeg_only=eeg_only)

    # record start
    qc.print_c('\n>> Recording started (PID %d).' % os.getpid(), 'W')
    qc.print_c('\n>> Press Enter to stop recording', 'G')
    tm = qc.Timer(autoreset=True)
    next_sec = 1
    while state.value == 1:
        sr.acquire()
        if sr.get_buflen() > next_sec:
            duration = str(datetime.timedelta(seconds=int(sr.get_buflen())))
            print('RECORDING %s' % duration)
            next_sec += 1

        ######################################################
        #data, ts= sr.get_window()
        #tc= pylsl.local_clock()
        #tson= np.argmax( ts >= int(tc) )
        #print( ts[tson], tson, data[tson,0] )
        ######################################################

        tm.sleep_atleast(0.01)

    # record stop
    qc.print_c('>> Stop requested. Copying buffer', 'G')
    buffers, times = sr.get_buffer()

    # DO NOT INFER TRIGGER CHANNEL AUTOMATICALLY
    '''
	signal_idx= []
	for x in range(buffers.shape[1]):
		if x != sr.get_trigger_channel():
			signal_idx.append(x)
	signals= buffers[:, signal_idx]
	events= buffers[ :, sr.get_trigger_channel() ]
	'''
    signals = buffers
    events = None

    # channels = total channels from amp, including trigger channel
    data = {
        'signals': signals,
        'timestamps': times,
        'events': events,
        'sample_rate': sr.get_sample_rate(),
        'channels': sr.get_num_channels(),
        'ch_names': sr.get_channel_names()
    }
    qc.print_c('Saving data ...', 'W')
    qc.save_obj(filename, data)
    print('Saved to %s' % filename)

    import convert2fif as cf
    cf.pcl2fif(filename)
    qc.print_c('File saved and converted to fif format.', 'W')
示例#13
0
    qc.print_c('File saved and converted to fif format.', 'W')


if __name__ == '__main__':
    eeg_only = False

    if len(sys.argv) == 2:
        amp_name = sys.argv[1]
        amp_serial = None
    elif len(sys.argv) == 3:
        amp_name, amp_serial = sys.argv[1:3]
    else:
        amp_name, amp_serial = pu.search_lsl(ignore_markers=True)
    if amp_name == 'None':
        amp_name = None
    qc.print_c(
        'Connecting to a server %s (Serial %s)' % (amp_name, amp_serial), 'W')

    qc.print_c('\n>> Press Enter to start recording.', 'G')
    key = raw_input()
    state = mp.Value('i', 1)
    proc = mp.Process(target=record,
                      args=[state, amp_name, amp_serial, eeg_only])
    proc.start()

    raw_input('')
    state.value = 0
    qc.print_c('(main) Waiting for recorder process to finish.', 'W')
    proc.join(10)
    if proc.is_alive():
        qc.print_c(
            '>> ERROR: Recorder process not finihsing. Are you running from Spyder?',
示例#14
0
def cross_validate(data, cls_params):
    """
    Do cross-validation

    CV_PERFORM= ['LeaveOneOut' | 'StratifiedShuffleSplit' | 'KFold']
    """

    CV_PERFORM = 'KFold'
    CV_FOLDS = 6
    # parameters for StratifiedShuffleSplit only
    CV_TEST_RATIO = 0.2
    CV_SEED = 0

    acc_subject = {}
    scores_all = []
    for subject in data:
        gbp = cls_params[subject]
        cls1 = GradientBoostingClassifier(n_estimators=gbp['trees'], learning_rate=gbp['learning_rate'], max_depth=gbp['max_depth'], max_features=gbp['max_features'], subsample=gbp['subsample'], random_state=gbp['random_state'])
        cls2 = GradientBoostingClassifier(n_estimators=gbp['trees'], learning_rate=gbp['learning_rate'], max_depth=gbp['max_depth'], max_features=gbp['max_features'], subsample=gbp['subsample'], random_state=gbp['random_state'])
        qc.print_c('Parameters\nGB %s' % (gbp), 'W')

        X1 = data[subject]['X1']
        X2 = data[subject]['X2']
        Y = data[subject]['Y']
        if CV_PERFORM == 'LeaveOneOut':
            print('\n>> %s: %d-fold leave-one-out cross-validation' % (subject, ntrials))
            cv = LeaveOneOut()
        elif CV_PERFORM == 'StratifiedShuffleSplit':
            print('\n>> %s: %d-fold stratified cross-validation with test set ratio %.2f' %
                              (subject, CV_FOLDS, CV_TEST_RATIO))
            cv = StratifiedShuffleSplit(CV_FOLDS, test_size=CV_TEST_RATIO, random_state=CV_SEED)
        elif CV_PERFORM == 'KFold':
            cv = KFold(CV_FOLDS)

        label_set = list(np.unique(Y))
        scores = []
        num_labels = len(label_set)
        cms = np.zeros((num_labels, num_labels))
        cnum = 1

        if N_JOBS > 1:
            results = []
            pool = mp.Pool(mp.cpu_count())
            for train, test in cv.split(Y):
                p = pool.apply_async(fit_predict, [cls1, cls2, X1[train], X2[train], Y[train], X1[test], X2[test], Y[test], cnum, label_set])
                results.append(p)
                cnum += 1
            pool.close()
            pool.join()

            for r in results:
                score, cm = r.get()
                scores.append(score)
                cms += cm
        else:
            for train, test in cv.split(Y):
                score, cm = fit_predict(cls1, cls2, X1[train], X2[train], Y[train], X1[test], X2[test], Y[test], cnum, label_set)
                scores.append(score)
                cms += cm
                cnum += 1

        # Show confusion matrix
        cm_rate = cms.astype('float') / cms.sum(axis=1)[:, np.newaxis]
        cm_txt = '\nY: ground-truth, X: predicted\n'
        for l in label_set:
            cm_txt += '%4d\t' % l
        cm_txt += '\n'
        for r in cm_rate:
            for c in r:
                cm_txt += '%-4.3f\t' % c
            cm_txt += '\n'
        acc = np.mean(scores)
        print('Average accuracy: %.3f' % acc)
        print(cm_txt)
        acc_subject[subject] = acc
        scores_all += scores

    # Assuming every subject has equal number of trials
    for subject in acc_subject:
        print('%s: %.3f' % (subject, acc_subject[subject]))

    acc_all = np.mean(scores_all)
    print('Average accuracy over all subjects: %.3f' % acc_all)

    return acc_all, acc_subject