Update EStiMo_GUI.py
updated comments
This commit is contained in:
parent
a97d4e22e6
commit
ad77c22a04
1 changed files with 8 additions and 65 deletions
|
@ -30,10 +30,8 @@ import datetime
|
||||||
import ctypes
|
import ctypes
|
||||||
import scipy
|
import scipy
|
||||||
import pywt
|
import pywt
|
||||||
# import queue
|
|
||||||
|
|
||||||
from cycler import cycler
|
from cycler import cycler
|
||||||
# from matplotlib.backend_bases import MouseButton
|
|
||||||
from PyQt5.QtCore import QTimer, Qt
|
from PyQt5.QtCore import QTimer, Qt
|
||||||
from PyQt5.QtGui import QImage, QPixmap, QIcon, QFont
|
from PyQt5.QtGui import QImage, QPixmap, QIcon, QFont
|
||||||
from PyQt5.QtWidgets import (QMainWindow, QFileDialog, QMessageBox, QCheckBox, QLineEdit, QWidget, QPushButton,
|
from PyQt5.QtWidgets import (QMainWindow, QFileDialog, QMessageBox, QCheckBox, QLineEdit, QWidget, QPushButton,
|
||||||
|
@ -154,19 +152,13 @@ class NeurOneOffline():
|
||||||
eeg_chn = np.arange(0,num_electr,1)
|
eeg_chn = np.arange(0,num_electr,1)
|
||||||
hdr = mne.io.read_raw_brainvision(tmp_path)
|
hdr = mne.io.read_raw_brainvision(tmp_path)
|
||||||
|
|
||||||
# hdr.set_channel_types({'EMGleft': 'emg', 'EOGright': 'eog'})
|
|
||||||
# hdr.set_montage(mne.channels.read_custom_montage('easycap-M10_63_NO.txt'))
|
|
||||||
# mrk_fullpath = tmp_path[:-4]+'vmrk'
|
|
||||||
# eeg_fullpath = tmp_path[:-4]+'eeg' #this two are made by hand instead of function.
|
|
||||||
#Maybe there is some func for this
|
|
||||||
# Annotations returns all events - stimA, stimB, stopA, stopB, start of experiment etc... We chose only stim
|
# Annotations returns all events - stimA, stimB, stopA, stopB, start of experiment etc... We chose only stim
|
||||||
stim = hdr.annotations.onset[np.logical_or(hdr.annotations.description=="Stimulus/A",
|
stim = hdr.annotations.onset[np.logical_or(hdr.annotations.description=="Stimulus/A",
|
||||||
hdr.annotations.description=="Stimulus/B")]
|
hdr.annotations.description=="Stimulus/B")]
|
||||||
# Separate stimA and stimB
|
# Separate stimA and stimB
|
||||||
stimA = hdr.annotations.onset[hdr.annotations.description=="Stimulus/A"]
|
stimA = hdr.annotations.onset[hdr.annotations.description=="Stimulus/A"]
|
||||||
stimB = hdr.annotations.onset[hdr.annotations.description=="Stimulus/B"]
|
stimB = hdr.annotations.onset[hdr.annotations.description=="Stimulus/B"]
|
||||||
#divide for stim A and B
|
|
||||||
#stimR = hdr.annotations.onset[hdr.annotations.description=='Response/R 16']
|
|
||||||
npts = hdr.n_times
|
npts = hdr.n_times
|
||||||
nfft = int(hdr.info['sfreq']) # Sampling rate [Hz]
|
nfft = int(hdr.info['sfreq']) # Sampling rate [Hz]
|
||||||
fs = int(hdr.info['sfreq']) # Sampling rate [Hz]
|
fs = int(hdr.info['sfreq']) # Sampling rate [Hz]
|
||||||
|
@ -335,8 +327,7 @@ class AppForm(QMainWindow):
|
||||||
10: 7.812 - 15.625 Hz
|
10: 7.812 - 15.625 Hz
|
||||||
11: 15.625 - 31.25 Hz
|
11: 15.625 - 31.25 Hz
|
||||||
"""
|
"""
|
||||||
band = band - 9 #weird way, but then call from function works well for particular band
|
band = band - 9
|
||||||
#but works correctly only for this set of features!
|
|
||||||
dwt_pw1, dwt_pw2, dwt_pw3, dwt_pw4 = self.dwt[:4]
|
dwt_pw1, dwt_pw2, dwt_pw3, dwt_pw4 = self.dwt[:4]
|
||||||
#sum of squares
|
#sum of squares
|
||||||
dwt_pw1 = np.sum(dwt_pw1**2)
|
dwt_pw1 = np.sum(dwt_pw1**2)
|
||||||
|
@ -380,8 +371,7 @@ class AppForm(QMainWindow):
|
||||||
'High Gamma FFT Power', 'Spectral entropy', 'Temporal entropy',
|
'High Gamma FFT Power', 'Spectral entropy', 'Temporal entropy',
|
||||||
'Line length', 'DWT Power 0-4 Hz', 'DWT 4-8 Hz',
|
'Line length', 'DWT Power 0-4 Hz', 'DWT 4-8 Hz',
|
||||||
'DWT 8-16 Hz', 'DWT 16-31 Hz','Variance','Correlation']
|
'DWT 8-16 Hz', 'DWT 16-31 Hz','Variance','Correlation']
|
||||||
# One last thing to use your new function is to add string with its name
|
|
||||||
# to the other First_window.py: variable features_names in class First_window
|
|
||||||
|
|
||||||
def all_params(self, passed_params = None, restarted=''):
|
def all_params(self, passed_params = None, restarted=''):
|
||||||
#reads values from TMS_protocol.txt file
|
#reads values from TMS_protocol.txt file
|
||||||
|
@ -562,10 +552,6 @@ class AppForm(QMainWindow):
|
||||||
self.last_sec = ss.filtfilt(A, B, self.last_sec)
|
self.last_sec = ss.filtfilt(A, B, self.last_sec)
|
||||||
self.last_sec = ss.detrend(self.last_sec, axis=1)
|
self.last_sec = ss.detrend(self.last_sec, axis=1)
|
||||||
|
|
||||||
# plt.figure()
|
|
||||||
# plt.plot(self.last_sec[self.included_ch].T)
|
|
||||||
# plt.plot(eog)
|
|
||||||
#that's stupid, move channel selection before!!!!!!!!!!!!!
|
|
||||||
if self.use_regression:
|
if self.use_regression:
|
||||||
self.last_sec = eye_reg(self.last_sec[self.included_ch], eog)
|
self.last_sec = eye_reg(self.last_sec[self.included_ch], eog)
|
||||||
return self.last_sec
|
return self.last_sec
|
||||||
|
@ -592,9 +578,7 @@ class AppForm(QMainWindow):
|
||||||
self.results[idx] = self.functions[feature](feature)
|
self.results[idx] = self.functions[feature](feature)
|
||||||
else:
|
else:
|
||||||
self.results[idx] = self.functions[feature]()
|
self.results[idx] = self.functions[feature]()
|
||||||
|
|
||||||
#Checks how many fields were filled already, so we know what stage are we on
|
|
||||||
#and where to save the data
|
|
||||||
x = np.where(self.feature1==None)[0][0]
|
x = np.where(self.feature1==None)[0][0]
|
||||||
y = np.where(self.feature1==None)[1][0]
|
y = np.where(self.feature1==None)[1][0]
|
||||||
|
|
||||||
|
@ -616,9 +600,6 @@ class AppForm(QMainWindow):
|
||||||
print("Calculation of features: {}".format(times1-times))
|
print("Calculation of features: {}".format(times1-times))
|
||||||
|
|
||||||
|
|
||||||
#if value is not within threshold values then background color is red (salmon), otherwise green
|
|
||||||
|
|
||||||
#checks if there is a need to change a color of the background
|
|
||||||
old_prv_state = self.previous_state.copy()
|
old_prv_state = self.previous_state.copy()
|
||||||
if not all(np.isnan(self.thr_1)):
|
if not all(np.isnan(self.thr_1)):
|
||||||
if self.results[0]>=self.thr_1[0] and self.results[0]<=self.thr_1[1]:
|
if self.results[0]>=self.thr_1[0] and self.results[0]<=self.thr_1[1]:
|
||||||
|
@ -724,9 +705,7 @@ class AppForm(QMainWindow):
|
||||||
#If there is a need to redraw we do that. draw() option is slower, but more robust
|
#If there is a need to redraw we do that. draw() option is slower, but more robust
|
||||||
if need_redraw:
|
if need_redraw:
|
||||||
self.canvasMap.draw()
|
self.canvasMap.draw()
|
||||||
#otherwise we can just update the line, or to be precise, I think it just
|
#otherwise we can just update the line, or to be precise,
|
||||||
#draws the line on the old one. In this application it's fine. Faster than previous method.
|
|
||||||
#I can think about blitting, so it could be even faster...
|
|
||||||
else:
|
else:
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
for j in range(2):
|
for j in range(2):
|
||||||
|
@ -740,8 +719,7 @@ class AppForm(QMainWindow):
|
||||||
def update(self):
|
def update(self):
|
||||||
"""Updates data, checks if something should be plotted"""
|
"""Updates data, checks if something should be plotted"""
|
||||||
time_start = time.time()
|
time_start = time.time()
|
||||||
# There were some problems with delay. This way it works, but probably it can be done better
|
|
||||||
# If the queue with data timer is sped up.
|
|
||||||
if self.q.qsize()>0:
|
if self.q.qsize()>0:
|
||||||
self.timer.setInterval(int(1*self.speed_general*0.97))
|
self.timer.setInterval(int(1*self.speed_general*0.97))
|
||||||
if self.q.qsize()<1 and self.timer.interval()!= int(self.speed_general*1.1):
|
if self.q.qsize()<1 and self.timer.interval()!= int(self.speed_general*1.1):
|
||||||
|
@ -827,13 +805,6 @@ class AppForm(QMainWindow):
|
||||||
if len(stim)>0:
|
if len(stim)>0:
|
||||||
for ind in stim[::-1]:
|
for ind in stim[::-1]:
|
||||||
size = self.data_len - (od+ind-int(int_from*self.Fs))
|
size = self.data_len - (od+ind-int(int_from*self.Fs))
|
||||||
# print('size:', size)
|
|
||||||
# print(len(self.loaded))
|
|
||||||
# Interpolation - pretty long line, but basically it chooses ranges and
|
|
||||||
# assign boundary value as a baseline and does that in (I guess) more optimal way than using loops
|
|
||||||
# self.loaded[:, od+ind-int(int_from*self.Fs):od+ind+int(int_to*self.Fs)] = np.outer(
|
|
||||||
# self.loaded[:,min(od+ind+int(int_to*self.Fs), 30000-1)], np.ones(min(size, int((int_from+int_to)*self.Fs))))
|
|
||||||
# this way is even easier...
|
|
||||||
self.loaded[:, od+ind-int(int_from*self.Fs):od+ind+int(int_to*self.Fs)] = self.loaded[:,min(od+ind+int(int_to*self.Fs), 30000-1)].reshape(-1, 1)
|
self.loaded[:, od+ind-int(int_from*self.Fs):od+ind+int(int_to*self.Fs)] = self.loaded[:,min(od+ind+int(int_to*self.Fs), 30000-1)].reshape(-1, 1)
|
||||||
# for i in range(self.loaded.shape[0]):
|
# for i in range(self.loaded.shape[0]):
|
||||||
# self.loaded[i, od+ind-int(int_from*self.Fs):od+ind+int(int_to*self.Fs)] = np.linspace(
|
# self.loaded[i, od+ind-int(int_from*self.Fs):od+ind+int(int_to*self.Fs)] = np.linspace(
|
||||||
|
@ -861,8 +832,6 @@ class AppForm(QMainWindow):
|
||||||
|
|
||||||
step9 = time.time()-time_start
|
step9 = time.time()-time_start
|
||||||
|
|
||||||
# If do_calibration is True and there were trigger recently then stop calibration
|
|
||||||
# TODO: THAT'S CONDITION REQUIRED FOR STARTING MEASURMENTS. PROBABLY IT WON'T WORK FOR SOME MORE EXTREME SETTINGS
|
|
||||||
# if self.do_calibration and len(stim_where[stim_where>(self.data_len-max(
|
# if self.do_calibration and len(stim_where[stim_where>(self.data_len-max(
|
||||||
# 2000, round(self.Fs*self.time_between_bursts*0.7, -3)))])>0:
|
# 2000, round(self.Fs*self.time_between_bursts*0.7, -3)))])>0:
|
||||||
if self.do_calibration and len(stim_where[stim_where>(self.data_len-max(2000, self.exp_time+1500))])>0:
|
if self.do_calibration and len(stim_where[stim_where>(self.data_len-max(2000, self.exp_time+1500))])>0:
|
||||||
|
@ -876,7 +845,6 @@ class AppForm(QMainWindow):
|
||||||
return 0 #ends run of this function so nothing else happens
|
return 0 #ends run of this function so nothing else happens
|
||||||
step10 = time.time()-time_start
|
step10 = time.time()-time_start
|
||||||
# If set number of stimuli is detected
|
# If set number of stimuli is detected
|
||||||
# doit --> set length of the measurment between bursts
|
|
||||||
# if self.doit==0 and sum(stim_where>self.data_len-max(
|
# if self.doit==0 and sum(stim_where>self.data_len-max(
|
||||||
# 2000, round(self.Fs*self.time_between_bursts*0.7, -3)))==10:
|
# 2000, round(self.Fs*self.time_between_bursts*0.7, -3)))==10:
|
||||||
print(sum(stim_where>self.data_len-max(2000, self.exp_time+1500)))
|
print(sum(stim_where>self.data_len-max(2000, self.exp_time+1500)))
|
||||||
|
@ -900,9 +868,6 @@ class AppForm(QMainWindow):
|
||||||
elif self.doit>0:
|
elif self.doit>0:
|
||||||
print(self.doit)
|
print(self.doit)
|
||||||
self.last_stim = stim_where[-1]
|
self.last_stim = stim_where[-1]
|
||||||
# In some situations last stimuli might be already from another train, while
|
|
||||||
# First 100ms of loaded signal belong to previous one. That is why this exception exists
|
|
||||||
# EDIT: not sure if it's still needed after other changes I made
|
|
||||||
if any(np.diff(stim_where[stim_where>self.data_len-max(
|
if any(np.diff(stim_where[stim_where>self.data_len-max(
|
||||||
2000, round(self.Fs*self.time_between_bursts*1.2, -3))])>1000):
|
2000, round(self.Fs*self.time_between_bursts*1.2, -3))])>1000):
|
||||||
print('UWAGA NA TO')
|
print('UWAGA NA TO')
|
||||||
|
@ -1015,7 +980,6 @@ class AppForm(QMainWindow):
|
||||||
self.button4.setText("Start calibration")
|
self.button4.setText("Start calibration")
|
||||||
self.button4.setStyleSheet('')
|
self.button4.setStyleSheet('')
|
||||||
#par = self.thr_parameter
|
#par = self.thr_parameter
|
||||||
#Need to clean the figure to prepare is for a different type of plot
|
|
||||||
for i in range(6):
|
for i in range(6):
|
||||||
self.axesMap[i//2,i%2].cla()
|
self.axesMap[i//2,i%2].cla()
|
||||||
|
|
||||||
|
@ -1120,8 +1084,7 @@ class AppForm(QMainWindow):
|
||||||
#calculate fft
|
#calculate fft
|
||||||
self.S = abs(np.fft.rfft(self.second_to_analyze))
|
self.S = abs(np.fft.rfft(self.second_to_analyze))
|
||||||
|
|
||||||
#this is a bit shady, but should work. check it out if doesn't!
|
|
||||||
#do dwt only if any features requires it
|
|
||||||
if any(feature_num in self.used_features for feature_num in [8,9,10,11]):
|
if any(feature_num in self.used_features for feature_num in [8,9,10,11]):
|
||||||
self.dwt = pywt.wavedec(self.second_to_analyze, 'db1', level=8)
|
self.dwt = pywt.wavedec(self.second_to_analyze, 'db1', level=8)
|
||||||
|
|
||||||
|
@ -1152,8 +1115,7 @@ class AppForm(QMainWindow):
|
||||||
self.cals = [self.f1_cal, self.f2_cal, self.f3_cal, self.f4_cal, self.f5_cal,
|
self.cals = [self.f1_cal, self.f2_cal, self.f3_cal, self.f4_cal, self.f5_cal,
|
||||||
self.f6_cal]
|
self.f6_cal]
|
||||||
|
|
||||||
#!!! Temporary, it's wrong but it's overwritten later. It's needed to check if all features are used,
|
|
||||||
#but there could be more optimal solution. Remove it at some point!
|
|
||||||
self.thr_1 = [np.min(self.f1_cal)-0.1*np.min(self.f1_cal),
|
self.thr_1 = [np.min(self.f1_cal)-0.1*np.min(self.f1_cal),
|
||||||
np.max(self.f1_cal)+0.1*np.max(self.f1_cal)]
|
np.max(self.f1_cal)+0.1*np.max(self.f1_cal)]
|
||||||
self.thr_2 = [np.min(self.f2_cal)-0.1*np.min(self.f2_cal),
|
self.thr_2 = [np.min(self.f2_cal)-0.1*np.min(self.f2_cal),
|
||||||
|
@ -1245,12 +1207,6 @@ class AppForm(QMainWindow):
|
||||||
tick.tick2line.set_visible(False)
|
tick.tick2line.set_visible(False)
|
||||||
tick.label1.set_visible(False)
|
tick.label1.set_visible(False)
|
||||||
tick.label2.set_visible(False)
|
tick.label2.set_visible(False)
|
||||||
|
|
||||||
# for tick in self.axes.yaxis.get_major_ticks():
|
|
||||||
# tick.tick1line.set_visible(False)
|
|
||||||
# tick.tick2line.set_visible(False)
|
|
||||||
# tick.label1.set_visible(False)
|
|
||||||
# tick.label2.set_visible(False)
|
|
||||||
|
|
||||||
self.axes.set_yticks(np.arange(1, (self.num_of_ch)*1.01, 1))
|
self.axes.set_yticks(np.arange(1, (self.num_of_ch)*1.01, 1))
|
||||||
self.axes.set_yticklabels(self.ch_names[::-1])
|
self.axes.set_yticklabels(self.ch_names[::-1])
|
||||||
|
@ -1304,14 +1260,6 @@ class AppForm(QMainWindow):
|
||||||
self.canvas.draw()
|
self.canvas.draw()
|
||||||
self.canvasMap.draw() #update canvas
|
self.canvasMap.draw() #update canvas
|
||||||
|
|
||||||
#NOT NEEDED
|
|
||||||
# for i in range(self.num_of_ch):
|
|
||||||
# self.axbackground = self.canvas.copy_from_bbox(self.axes.bbox)
|
|
||||||
|
|
||||||
# texts = []
|
|
||||||
# for ind,name in enumerate(self.ch_names):
|
|
||||||
# texts.append(self.axes.text(-0.03,1-(ind+1)/(len(self.ch_names)+1),
|
|
||||||
# name, transform=self.axes.transAxes, color='r', fontsize=13))
|
|
||||||
|
|
||||||
self.button1 = QPushButton("&Settings")
|
self.button1 = QPushButton("&Settings")
|
||||||
self.button1.setCheckable(False)
|
self.button1.setCheckable(False)
|
||||||
|
@ -1416,8 +1364,3 @@ if __name__ == '__main__':
|
||||||
form = First_window(AppForm) #AppForm()
|
form = First_window(AppForm) #AppForm()
|
||||||
form.show()
|
form.show()
|
||||||
sys.exit(app.exec_())
|
sys.exit(app.exec_())
|
||||||
|
|
||||||
# cut time different from both sides
|
|
||||||
# some deafult settings. Maybe remember last configuration?
|
|
||||||
# EMG and EOG - none, more than one?
|
|
||||||
# change names to final names
|
|
Loading…
Reference in a new issue