class MNEUtil(object): def __init__(self): self.config = ConfigProvider() self.fileUtil = FileUtil() def createMNEObjectFromCSV(self, filePath): eegData = self.fileUtil.getDtoFromCsv(filePath) return self.createMNEObjectFromEEGDto(eegData) def createMNEObjectFromEEGDto(self, eegDto): return self.createMNEObject(eegDto.getEEGData(), eegDto.getEEGHeader(), eegDto.getGyroData(), eegDto.getGyroHeader(), eegDto.filePath, eegDto.getSamplingRate()) def createMNEObject(self, eegData, eegHeader, gyroData, gyroHeader, filePath, samplingRate): info = self._createEEGInfo(eegHeader, gyroHeader, filePath, samplingRate) data = self._mergeData(eegData, gyroData) return mne.io.RawArray(data, info) def _mergeData(self, eegData, gyroData): if gyroData is None: return eegData return concatenate((eegData, gyroData), axis=0) def _createEEGInfo(self, eegChannelNames, gyroChannelNames, filePath, samplingRate): channelTypes = ["eeg"] * len(eegChannelNames) + ['misc'] * len( gyroChannelNames) channelNames = eegChannelNames + gyroChannelNames montage = mne.channels.read_montage("standard_1020") info = mne.create_info(channelNames, samplingRate, channelTypes, montage) info["description"] = filePath return info def createMNEObjectFromECGDto(self, ecgDto, resampleFac=None): info = self._createECGInfo(ecgDto.getECGHeader(), ecgDto.filePath, ecgDto.getSamplingRate()) ecgData = ecgDto.getECGData() if resampleFac is not None: ecgData = signal.resample(ecgData, resampleFac) return mne.io.RawArray(ecgData, info) def _createECGInfo(self, channelName, filePath, samplingRate): channelTypes = ["ecg"] info = mne.create_info([channelName], samplingRate, channelTypes) info["description"] = filePath return info def createMNEEpochsObject(self, eegData, clazz): raw = self.createMNEObjectFromEEGDto(eegData) return self.createMNEEpochsObjectFromRaw(raw, clazz) def createMNEEpochsObjectFromRaw(self, raw, clazz, duration=1): events = self._createEventsArray(raw, clazz, False) return mne.Epochs(raw, events=events, tmin=0.0, tmax=0.99, add_eeg_ref=True) def _createEventsArray(self, raw, clazz, overlapping=True, duration=1): if overlapping: duration = 0.5 return mne.make_fixed_length_events(raw, clazz, duration=duration) def addECGChannel(self, eegRaw, ecgRaw): if "ecg" in ecgRaw: return self._addChannel(eegRaw, ecgRaw) def addEOGChannel(self, eegRaw, eogRaw): if "eog" in eogRaw: return self._addChannel(eegRaw, eogRaw) def _addChannel(self, eegRaw, otherRaw): otherRaw = self.adjustSampleRate(eegRaw, otherRaw) otherRaw = self.adjustLength(eegRaw, otherRaw) return eegRaw.add_channels([otherRaw], force_update_info=True) def addICASources(self, raw, ica): icaRaw = ica.get_sources(raw) raw.add_channels([icaRaw]) return raw def adjustSampleRate(self, eegRaw, otherRaw): eegSFreq = eegRaw.info['sfreq'] otherSFreq = otherRaw.info['sfreq'] if eegSFreq != otherSFreq: otherRaw = otherRaw.resample(eegSFreq, npad='auto') return otherRaw def adjustLength(self, eegRaw, otherRaw): eegNTimes = eegRaw.n_times otherNTimes = otherRaw.n_times if eegNTimes != otherNTimes: eegSFreq = eegRaw.info['sfreq'] tMax = (eegRaw.n_times - 1) / eegSFreq otherRaw = otherRaw.crop(0, tMax) return otherRaw def markBadChannels(self, raw, channels): raw.info['bads'] = channels def interpolateBadChannels(self, raw): return raw.interpolate_bads() def createPicks(self, mneObj): return mne.pick_types(mneObj.info, meg=False, eeg=True, eog=False, stim=False, exclude='bads') def bandpassFilterData(self, mneObj): highFreq = self.config.getProcessingConfig().get("upperFreq") lowFreq = self.config.getProcessingConfig().get("lowerFreq") return self.filterData(mneObj, lowFreq, highFreq) def filterData(self, mneObj, lowFreq, highFreq): return mneObj.filter(lowFreq, highFreq, filter_length="auto", l_trans_bandwidth="auto", h_trans_bandwidth="auto", phase='zero', fir_window="hamming") def getEEGCannels(self, mneObj): return mneObj.copy().pick_types(meg=False, eeg=True) def getChannels(self, mneObj, channels): return mneObj.copy().pick_channels(channels) def cropChannels(self, mneObj, tmin, tmax): return mneObj.copy().crop(tmin, tmax - 1) def dropChannels(self, mneObj, channels): return mneObj.copy().drop_channels(channels) def calcPSD(self, raw, fmin, fmax, picks=None): return psd_welch(raw, fmin, fmax, picks=picks) def ICA(self, mneObj, icCount=None, random_state=None): picks = self.createPicks(mneObj) reject = dict(eeg=300) if icCount is None: icCount = len(picks) ica = ICA(n_components=icCount, method="fastica", random_state=random_state) ica.fit(mneObj, picks=picks, reject=reject) return ica def labelArtefact(self, templateICA, templateIC, icas, label): template = (0, templateIC) icas = [templateICA] + icas return corrmap(icas, template=template, threshold=0.85, label=label, plot=False, show=False, ch_type='eeg', verbose=True) def findCrossCorrelation(self, raw, ica=None): import matplotlib.pyplot as plt ch_names = raw.info["ch_names"] ch_idx = [ ch_names.index(id) for id in ch_names if id.startswith("ICA") ] cor_list = [] data = raw._data xChannel = data[ch_names.index("X")] for idx in ch_idx: chan = data[idx] cor = signal.correlate(xChannel, chan) plt.plot(cor, label=str(idx)) plt.legend() MNEPlotter().plotRaw(raw) plt.show()
class DataWidget(QtGui.QWidget): def __init__(self, dataUrls, maxFps): super(DataWidget, self).__init__() self.fileUtil = FileUtil() self._initData(dataUrls[0]) self.maxFps = maxFps self.curSecond = 0 self._initPlot() layout = QtGui.QVBoxLayout(self) layout.addWidget(self.toolbar) layout.addWidget(self.canvas) self.setObjectName("datawidget") def _initData(self, filePath): if self.fileUtil.isCSVFile(filePath): dto = self.fileUtil.getDtoFromCsv(filePath) else: dto = self.fileUtil.getDtoFromFif(filePath) self.eegHeader = dto.getEEGHeader() self.eegData = dto.getEEGData() self.numChannels = len(self.eegData) self.samplingRate = dto.getSamplingRate() self.length = len(self.eegData[0]) logging.info("plotter\t#%d\t%.2fHz" % (self.length, self.samplingRate)) def _initPlot(self): self.figure = plt.figure() self.canvas = FigureCanvas(self.figure) self.toolbar = NavigationToolbar(self.canvas, self) self.axes = [] for i, _ in enumerate(self.eegData): self.axes.append( self.figure.add_subplot(self.numChannels, 1, i + 1)) start, end = self._getRange() x_values = [x for x in range(start, end)] self.lines = [] for i, ax in enumerate(self.axes): data = self.eegData[i] line, = ax.plot(x_values, data[start:end], '-') self.lines.append(line) ax.set_xlim([start, end]) ax.set_ylim([min(data), max(data)]) ax.set_ylabel(self.eegHeader[i]) def show(self, curSecond): if self._isReplot(curSecond): self.plot() def plot(self): start, end = self._getRange() if self._isInDataRange(start, end): for i, line in enumerate(self.lines): line.set_ydata(self.eegData[i][start:end]) self.canvas.draw() else: logging.warn("no data found for index range [%d:%d]" % (start, end)) def _isInDataRange(self, start, end): return end < self.length def _getRange(self): start = int(self.curSecond * self.samplingRate) end = int(start + self.samplingRate) return start, end # TODO method does 2 things def _isReplot(self, curSecond): if curSecond != self.curSecond: self.curSecond = curSecond return True return False