class Chart(QChartView): def __init__(self, barCount: int): super().__init__() self.chart = QChart() self.setChart(self.chart) self.setRenderHint(QPainter.Antialiasing) self.chart.setAnimationOptions(QChart.SeriesAnimations) self.chart.setBackgroundVisible(True) self.chart.legend().setVisible(False) self.series = QBarSeries() self.chart.addSeries(self.series) self.barValues = QBarSet('') self.series.append(self.barValues) for i in range(barCount): self.barValues << 0. self.xAxis = QBarCategoryAxis() self.chart.addAxis(self.xAxis, Qt.AlignBottom) self.series.attachAxis(self.xAxis) self.xAxis.setTitleText('yPlus ranges') self.yAxis = QValueAxis() self.chart.addAxis(self.yAxis, Qt.AlignLeft) self.series.attachAxis(self.yAxis) self.yAxis.setTitleText('% of surface area') self.yAxis.setRange(0, 100) def setBarRanges(self, pois: List[float]): for i in range(len(pois)): if i == 0: tag = 'lt ' + str(pois[0]) elif i == len(pois) - 1: tag = 'gt ' + str(pois[-1]) else: tag = str(pois[i]) + ' - ' + str(pois[i + 1]) if not self.xAxis.count(): self.xAxis.append(tag) else: self.xAxis.replace(self.xAxis.at(i), tag) def setBarValues(self, values: List[float]): assert len(values) == self.barValues.count() for i in range(len(values)): if not self.barValues.count(): self.barValues.insert(i, 0.) else: self.barValues.replace(i, values[i])
class QtBarChart(QChartView): def __init__(self, spec): super().__init__(None) self.spec = spec self.chart = QChart() # self.chart.setTitle(str(self.spec.variables)) self.chart.legend().hide() self.mainset = QBarSet("") self.mainset.append([0 for i in range(len(spec.variables))]) self.mainset.setColor( QColor(spec.color[0], spec.color[1], spec.color[2])) self.series = QBarSeries() self.series.append(self.mainset) self.setMinimumWidth(400) self.setMinimumHeight(230) self.axis_x = QBarCategoryAxis() self.axis_y = QValueAxis() self.axis_x.append(spec.variables) self.chart.addSeries(self.series) self.chart.setAxisX(self.axis_x, self.series) self.chart.setAxisY(self.axis_y, self.series) self.setChart(self.chart) self.setRenderHint(QPainter.Antialiasing) self._updates_per_second = 10 self._dataset = [] def clear(self): self._dataset = [] def update_data(self, dataset): data = [] for d in dataset: data.append(d) self._dataset = data def redraw(self): if len(self._dataset) > 0: for i in range(len(self._dataset)): self.mainset.replace(i, self._dataset[i]) self.axis_y.setRange(0, max(self._dataset))
class QtHistogram(QChartView): def __init__(self, spec): super().__init__(None) self.spec = spec self.chart = QChart() self.chart.setTitle(self.spec.variable) self.chart.legend().hide() self.mainset = QBarSet("") self.mainset.append([0] * len(spec.bins)) self.mainset.setColor( QColor(spec.color[0], spec.color[1], spec.color[2])) self.series = QBarSeries() self.series.append(self.mainset) self.setMinimumWidth(400) self.setMinimumHeight(230) self.y_ranges = [0, 1, 5, 10, 25, 50, 100, 250, 500, 1000] self.max_y = 1000 self.max_y_range = 1000 self.lookback = 30 self.recent_max_y = deque([self.max_y_range] * self.lookback) font = QtGui.QFont() font.setPixelSize(10) self.axis_x = QBarCategoryAxis() self.axis_x.setLabelsAngle(-90) self.axis_x.setLabelsFont(font) self.axis_y = QValueAxis() self.axis_y.setRange(0, self.max_y) self.axis_x.append(map(str, spec.bins)) self.chart.addSeries(self.series) self.chart.setAxisX(self.axis_x, self.series) self.chart.setAxisY(self.axis_y, self.series) self.setChart(self.chart) self.setRenderHint(QPainter.Antialiasing) self._updates_per_second = 10 self._dataset = [] def clear(self): self._dataset = [] def update_data(self, dataset): data = [] for d in dataset: data.append(d) self._dataset = data def redraw(self): if len(self._dataset) > 0: for i in range(len(self._dataset)): self.mainset.replace(i, self._dataset[i]) # Calculate max of current values max_y_range = max(self._dataset) # Store max value self.recent_max_y.appendleft(max_y_range) if len(self.recent_max_y) > self.lookback: self.recent_max_y.pop() # Set max based on the last 30 max values, # to avoid flickering self.max_y_range = max(self.recent_max_y) y_range = bisect.bisect_left(self.y_ranges, self.max_y_range) if y_range < len(self.y_ranges): self.max_y = self.y_ranges[y_range] elif max_y_range > self.max_y: self.max_y += self.max_y elif max_y_range < self.max_y / 2: self.max_y = self.max_y / 2 self.axis_y.setRange(0, self.max_y)
class initial(QDialog): def __init__(self): super(initial, self).__init__() loadUi("main.ui", self) # definir elementos de la UI y acciones/funciones asociadas # creamos sets para mostrar resultados en el barchart self.setRecall = QBarSet("Recalls") self.setRecall.append([0, 0, 0]) self.setAccurracy = QBarSet("Accurracy") self.setAccurracy.append([0, 0, 0]) self.series = QBarSeries() # Elementos Tab Entrenamiento: # =========================== # Btn Insertar primeros datos entrenamiento self.trainingAddFilesBtn.clicked.connect( self.insertarNoticiasEntrenamientoDespoblacion) # Btn Insertar segundos datos entrenamiento self.trainingAddFilesBtn2.clicked.connect( self.insertarNoticiasEntrenamientoNoDespoblacion) # Btn Preprocesamiento de texto self.procesarTextoBtn.clicked.connect(self.procesarTextoEntrenamiento) # ComboBox selector de Modelo a entrenar # self.chooseModelComboBox.activated.connect(self.elegirModeloEntrenamiento) # añadir elementos al comboBox y sus valores asociados self.chooseModelComboBox.addItem("KNN", 1) self.chooseModelComboBox.addItem("Naive Bayes", 2) self.chooseModelComboBox.addItem("Decision Tree", 3) # Btn para entrenar el modelo seleccionado self.trainModelBtn.clicked.connect(self.entrenarModelo) # Elementos Tab Testeo: # ==================== # Btn Insertar Datos Testeo self.testingFilesBtn.clicked.connect(self.insertarNoticiasTesteo) # Btn Seleccionar Modelo self.selectTestModelBtn.clicked.connect(self.elegirModeloTesteo) # Btn Mostrar Resultados self.testBtn.clicked.connect(self.mostrarResultados) # Tab Testeo #self.tabTest.clicked.connect(self.abrirTabTesteo) # nombre excel self.nombreresultadoexcel = ':)' # funciones # ========= # abrir tab testeo def abrirTabTesteo(self): self.stepTipsField.setPlainText( "En esta pestaña puede realizar el testeo sobre un nuevo set de datos para un modelo ya existente." ) # abrir dialog window para seleccionar los datos de entrenamiento def insertarNoticiasEntrenamientoDespoblacion(self): del desp[:] print(desp) # cambiar texto campo descripcion self.stepTipsField.setPlainText( "Seleccionamos los directorios donde tenemos los archivos de texto que utilizaremos para entrenar nuestro modelo." ) # abrir ventana seleccion archivos desp.append(self.openDialogBox()) print(desp) #cambiar self.procesarTextoBtn a habilitado self.trainingAddFilesBtn2.setEnabled(True) # abrir dialog window para seleccionar los segundos datos de entrenamiento def insertarNoticiasEntrenamientoNoDespoblacion(self): del no_despoblacion[:] # cambiar texto campo descripcion self.stepTipsField.setPlainText( "Seleccionamos los directorios donde tenemos los archivos de texto que utilizaremos para entrenar nuestro modelo." ) # abrir ventana seleccion archivos no_despoblacion.append(self.openDialogBox()) #cambiar self.procesarTextoBtn a habilitado self.procesarTextoBtn.setEnabled(True) # aplicar preprocesamiento de texto def procesarTextoEntrenamiento(self): # cambiar texto campo descripcion self.stepTipsField.setPlainText( "El preprocesamiento a realizar consta de 4 etapas:\n1. Tokenizar: separar las palabras que componen un texto, obteniendo como resultado una secuencia de tokens.\n2. Normalización: se pasa a minúsculas tdoos los tokens.\n3.Filtrado de stopwords: en esta etapa eliminamos aquellas palabras con poco valor semántico, denominadas stopwords.\n4.Stemming: extraemos el lexema de los tokens restantes (un ejemplo sería ‘cas-’ para la palabra ‘casero’)" ) del noticias[:] del clases[:] # bucle inserción de noticias mediante open ingresar_noticias(desp[0], noticias) ingresar_noticias(no_despoblacion[0], noticias) ingresar_noticias(desp[0], despoblacion) # Procesamiento de texto texto_procesado(processed_text_entrenamiento, noticias) # Creación de arreglo de clases crea_clases(clases, processed_text_entrenamiento, despoblacion) # cambiar self.trainModelBtn a habilitado self.trainModelBtn.setEnabled(True) # cambiar texto campo descripcion self.stepTipsField.setPlainText( "El preprocesamiento a realizar consta de 4 etapas:\n1. Tokenizar: separar las palabras que componen un texto, obteniendo como resultado una secuencia de tokens.\n2. Normalización: se pasa a minúsculas tdoos los tokens.\n3.Filtrado de stopwords: en esta etapa eliminamos aquellas palabras con poco valor semántico, denominadas stopwords.\n4.Stemming: extraemos el lexema de los tokens restantes (un ejemplo sería ‘cas-’ para la palabra ‘casero’).\n====================\nEl preprocesamiento ha acabado" ) # cambiar self.procesarTextoBtn a deshabilitado self.procesarTextoBtn.setEnabled(False) # abrir ventana seleccion archivos def openDialogBox(self): filenames = QFileDialog.getOpenFileNames() return filenames[0] # mostrar resultados testeo en nueva tabla def mostrarResultados(self): # cambiar texto campo descripcion self.stepTipsField.setPlainText( "A continuación se muestra una tabla con los resultados de la clasificación realizada por el modelo seleccionado." ) # para ocupar toda la tabla self.tableWidgetshowTest.horizontalHeader().setSectionResizeMode( QtWidgets.QHeaderView.Stretch) # resetear tabla self.tableWidgetshowTest.setRowCount(0) nombre = self.nombreresultadoexcel # mostrar contenido xlsx documento = xlrd.open_workbook(nombre + '.xlsx') df = documento.sheet_by_index(0) self.tableWidgetshowTest.setRowCount((df.nrows) - 1) self.tableWidgetshowTest.setColumnCount(2) for x in range(1, df.nrows): for y in range(2): print('x: ' + df.cell_value(x, y - 1)) item = QTableWidgetItem() nombreArchivo = df.cell_value(x, y - 1).split("/") item.setText(nombreArchivo[len(nombreArchivo) - 1]) self.tableWidgetshowTest.setItem(x - 1, y - 1, item) # insertar archivos fase testeo def insertarNoticiasTesteo(self): # cambiar texto campo descripcion self.stepTipsField.setPlainText( "Seleccione los archivos que utilizará durante la fase de testeo.") # abrir ventana seleccion archivos filepaths = self.openDialogBox() #ingresar noticias ingresar_noticias(filepaths, nuevas) #Procesamiento de texto texto_procesado(processed_text_testeo, nuevas) # cambiar self.selectTestModelBtn a deshabilitado self.selectTestModelBtn.setEnabled(True) # cambiar self.testingFilesBtn a habilitado # self.testingFilesBtn.setEnabled(False) # seleccionar modelo fase testeo def elegirModeloTesteo(self): # cambiar texto campo descripcion self.stepTipsField.setPlainText( "Seleccione el diccionario .pk y modelo correspondiente .pk1.") # abrir ventana seleccion archivos modelopath = self.openDialogBox() if (len(modelopath) == 2): # cargar diccionario cv1 = cargar_modelo(modelopath[0]) # cargar modelo modelo = cargar_modelo(modelopath[1]) # aplicar tfidf X_testcv = tfid_fit(processed_text_testeo, cv1) # insertar predicciones predicciones = [] for i in X_testcv: predicciones.append(prediccion(modelo, i)) # crear dataframe df = pd.DataFrame(data=predicciones, index=nuevas) # nombrar archivo y exportar a excel archivo = modelopath[0] new_archivo = archivo.replace('modelos', 'resultados') nombre = new_archivo[:len(new_archivo) - 3] self.nombreresultadoexcel = nombre df.to_excel(nombre + ".xlsx", "Sheet1") # cambiar self.testBtn a habilitado self.testBtn.setEnabled(True) # cambiar texto campo descripcion self.stepTipsField.setPlainText( "Resultados exportados a la carpeta resultados en formato Excel." ) # aplicar modelo NaiveBayes entrenamiento def entrenamientoNaiveBayes(self): # Proceso TFIDF X_traincv = cv.fit_transform(processed_text_entrenamiento) # Partición de datos X_train, X_test, Y_train, Y_test = train_test_split(X_traincv, clases, test_size=0.15, random_state=324) #Modelos naive = naive_bayes(X_train, Y_train) print(naive) print( "####################### Test Score ##############################\n" ) test_score(naive, X_train, Y_train) # Creamos los datos a testear Y_true_naive, Y_pred_naive = datos_test(Y_test, naive, X_test) # Datos de los modelos print( "###################### Accuracy ###############################\n" ) accuracy(Y_true_naive, Y_pred_naive) self.setAccurracy.replace( 1, accuracy_score(Y_true_naive, Y_pred_naive) * 100) print( "####################### Recall ##############################\n") print(recall_score(Y_true_naive, Y_pred_naive, average='macro')) self.setRecall.replace( 1, recall_score(Y_true_naive, Y_pred_naive, average='macro') * 100) a = "Modelo Naive-Bayes\n==================\nRecall:" + str( recall_score(Y_true_naive, Y_pred_naive, average='macro')) + "\nAccuracy: " + str( accuracy_score(Y_true_naive, Y_pred_naive)) self.stepTipsField.setPlainText(a) #llamar a funcion para actualizar los valores del Barchart self.appendResults() print( "\n###################### Matriz de confusion ###############################\n" ) matrizconf(Y_true_naive, Y_pred_naive) #Guardamos modelo now = datetime.now() # dd/mm/YY H:M:S dt_string = now.strftime("dia_%d-%m-%Y,hora_%H-%M-%S") guardar_modelo('modelos/naive_' + dt_string, naive) with open('modelos/naive_' + dt_string + '.pk', 'wb') as f: pickle.dump(cv, f) # aplicar modelo Decision Tree entrenamiento def entrenamientoArbolDecision(self): # Proceso TFIDF X_traincv = cv.fit_transform(processed_text_entrenamiento) #Partición de datos X_train, X_test, Y_train, Y_test = train_test_split(X_traincv, clases, test_size=0.15, random_state=324) #Modelos tree = decision_tree(X_train, Y_train) print( "####################### Test Score ##############################\n" ) test_score(tree, X_train, Y_train) #Creamos los datos a testear Y_true_tree, Y_pred_tree = datos_test(Y_test, tree, X_test) #Datos de los modelos print( "###################### Accuracy ###############################\n" ) accuracy(Y_true_tree, Y_pred_tree) #incluir nueva accurracy al set de resultados de NaiveBayes #self.setDTrees.append(accuracy_score(Y_true_tree, Y_pred_tree)*100) self.setAccurracy.replace( 2, accuracy_score(Y_true_tree, Y_pred_tree) * 100) #llamar a funcion para actualizar los valores del Barchart print( "####################### Recall ##############################\n") print(recall_score(Y_true_tree, Y_pred_tree, average='macro')) #self.setDTrees.append(recall_score(Y_true_tree, Y_pred_tree, average='macro')*100) self.setRecall.replace( 2, recall_score(Y_true_tree, Y_pred_tree, average='macro') * 100) a = "Modelo Arbol Decision\n=====================\nRecall:" + str( recall_score(Y_true_tree, Y_pred_tree, average='macro')) + "\nAccuracy: " + str( accuracy_score(Y_true_tree, Y_pred_tree)) self.stepTipsField.setPlainText(a) self.appendResults() #Matriz confusion print( "\n###################### Matriz de confusion ###############################\n" ) matrizconf(Y_true_tree, Y_pred_tree) now = datetime.now() # dd/mm/YY H:M:S dt_string = now.strftime("dia_%d-%m-%Y,hora_%H-%M-%S") guardar_modelo('modelos/tree_' + dt_string, tree) with open('modelos/tree_' + dt_string + '.pk', 'wb') as f: pickle.dump(cv, f) # aplicar modelo KNN def entrenamientoKnn(self): # Proceso TFIDF X_traincv = cv.fit_transform(processed_text_entrenamiento) #Partición de datos X_train, X_test, Y_train, Y_test = train_test_split(X_traincv, clases, test_size=0.15, random_state=324) #Modelos modknn = knn(X_train, Y_train) print( "####################### Test Score ##############################\n" ) test_score(modknn, X_train, Y_train) #Creamos los datos a testear Y_true_knn, Y_pred_knn = datos_test(Y_test, modknn, X_test) #Datos de los modelos print( "###################### Accuracy ###############################\n" ) accuracy(Y_true_knn, Y_pred_knn) #llamar a funcion para actualizar los valores del Barchart self.setAccurracy.replace(0, accuracy_score(Y_true_knn, Y_pred_knn) * 100) print( "####################### Recall ##############################\n") print(recall_score(Y_true_knn, Y_pred_knn, average='macro')) #self.setDTrees.append(recall_score(Y_true_knn, Y_pred_knn, average='macro')*100) self.setRecall.replace( 0, recall_score(Y_true_knn, Y_pred_knn, average='macro') * 100) a = "Modelo KNN\n===============\nRecall:" + str( recall_score(Y_true_knn, Y_pred_knn, average='macro')) + "\nAccuracy: " + str( accuracy_score(Y_true_knn, Y_pred_knn)) self.stepTipsField.setPlainText(a) self.appendResults() #Matriz confusion print( "\n###################### Matriz de confusion ###############################\n" ) matrizconf(Y_true_knn, Y_pred_knn) #Guardamos modelo now = datetime.now() # dd/mm/YY H:M:S dt_string = now.strftime("dia_%d-%m-%Y,hora_%H-%M-%S") guardar_modelo('modelos/knn_' + dt_string, modknn) with open('modelos/knn_' + dt_string + '.pk', 'wb') as f: pickle.dump(cv, f) # comprobar modelo seleccionado en comboBox def entrenarModelo(self): #cambiar texto en self.stepTipsField self.stepTipsField.setPlainText("Entrenando el modelo seleccionado...") #tomar valor actual del comboBox modelSelect = self.chooseModelComboBox.currentData() #no existe switch en python (o.o) if modelSelect == 1: self.entrenamientoKnn() if modelSelect == 2: self.entrenamientoNaiveBayes() if modelSelect == 3: self.entrenamientoArbolDecision() # add resultados entrenamiento y actualizar barchart def appendResults(self): #clear de series self.series = QBarSeries() #add sets de Accurracy y Recall de todos los modelos procesados a series self.series.append(self.setAccurracy) self.series.append(self.setRecall) # crear nuevo Chart chart = QChart() # add series al nuevo Chart chart.addSeries(self.series) chart.setTitle("Precisiones de Modelos") chart.setAnimationOptions(QChart.SeriesAnimations) # parametro QChart modelosEjeX = ('KNN', 'Naive Bayes', 'Decision Trees') # parametros ejeX ejeX = QBarCategoryAxis() ejeX.append(modelosEjeX) # parametros ejeY ejeY = QValueAxis() chart.addAxis(ejeX, Qt.AlignBottom) chart.addAxis(ejeY, Qt.AlignLeft) # leyenda Barchart chart.legend().setVisible(True) chart.legend().setAlignment(Qt.AlignBottom) # Mostrar ventana Barchart self.QChartView = QChartView(chart) self.QChartView.resize(600, 600) self.QChartView.show()
class BandPowerGraph(QWidget): def __init__(self, name: str): super().__init__() self.band_power_chart = QChart() self.band_power_chart.setAnimationOptions(QChart.SeriesAnimations) self.channel_band_power_set = QBarSet("Band Power") self.channel_band_power_set.append(1) self.channel_band_power_set.append(1) self.channel_band_power_set.append(1) self.channel_band_power_set.append(1) self.channel_band_power_set.append(1) self.bands_axis = QBarCategoryAxis() self.bands_axis.append("Delta (1 - 3 Hz)") self.bands_axis.append("Theta (4 - 7 Hz)") self.bands_axis.append("Alpha (8 - 13 Hz)") self.bands_axis.append("Beta (13 - 30 Hz)") self.bands_axis.append("Gamma (30 - 100)") self.power_axis = QValueAxis() self.chart_series = QBarSeries() self.chart_series.append(self.channel_band_power_set) self.band_power_chart.addSeries(self.chart_series) self.band_power_chart.setTitle(f"<h1>Band Power For {name}</h1>") self.band_power_chart.addAxis(self.bands_axis, Qt.AlignBottom) self.band_power_chart.addAxis(self.power_axis, Qt.AlignLeft) self.chart_series.attachAxis(self.bands_axis) self.chart_series.attachAxis(self.power_axis) self.chart_view = QChartView(self.band_power_chart) self.chart_view.setRenderHint(QPainter.Antialiasing) self.root_layout = QStackedLayout() self.setLayout(self.root_layout) self.root_layout.addWidget(self.chart_view) def set_name(self, name: str): self.band_power_chart.setTitle(f"<h1>Band Power For {name}</h1>") def update_values(self, data: np.ndarray, fft_window_size: float = 0): eeg_data = utils.EegData(data) feature_extractor = eeg_data.feature_extractor(0, global_config.SAMPLING_RATE) self.channel_band_power_set.replace( 0, feature_extractor.average_band_amplitude(utils.FrequencyBand.delta_freq_band(), fft_window_size) ) self.channel_band_power_set.replace( 1, feature_extractor.average_band_amplitude(utils.FrequencyBand.theta_freq_band(), fft_window_size) ) self.channel_band_power_set.replace( 2, feature_extractor.average_band_amplitude(utils.FrequencyBand.alpha_freq_band(), fft_window_size) ) self.channel_band_power_set.replace( 3, feature_extractor.average_band_amplitude(utils.FrequencyBand.beta_freq_band(), fft_window_size) ) self.channel_band_power_set.replace( 4, feature_extractor.average_band_amplitude(utils.FrequencyBand.gama_freq_band(), fft_window_size) ) def auto_adjust_axis(self): utils.auto_adjust_axis(self.power_axis, [self.channel_band_power_set], 0.1)
class ResonanceFrequencyFinder(QMainWindow): # AVAILABLE_WINDOW_SIZES = ["5 Sec", "8 Sec", "10 Sec", "15 Sec", "20 Sec"] DEFAULT_GRAPH_PADDING = 2 DEFAULT_FFT_WINDOW_SIZE = 5 DEFAULT_SAMPLE_PADDING = 2 DEFAULT_RECORDING_DURATION = 30 + DEFAULT_SAMPLE_PADDING DEFAULT_MIN_FREQUENCY = 17 DEFAULT_MAX_FREQUENCY = 35 DEFAULT_FREQUENCY_STEP = 2 # Used to create a band for which the average frequency amplitude is computed DEFAULT_FREQUENCY_PADDING = 0.2 DEFAULT_BANDPASS_MIN = 8 DEFAULT_BANDPASS_MAX = 40 DEFAULT_C3_CHANNEL_INDEX = 4 DEFAULT_CZ_CHANNEL_INDEX = 2 DEFAULT_C4_CHANNEL_INDEX = 0 def __init__(self, board: BoardShim): super().__init__() self.setGeometry(0, 0, 1800, 900) self.setWindowTitle("Resonance-Like Frequency") self.board = board self.recording_progress_dialog = None self.eeg_data_buffer = utils.EegData() self.reading_timer = QTimer() self.recording = False self.recording_reference = False self.reference_eeg_data = utils.EegData() self.index_generator = utils.FrequencyIndexGenerator(global_config.SAMPLING_RATE) self.eeg_sample_count = 0 self.root_widget = QWidget() self.root_layout = QGridLayout() self.root_widget.setLayout(self.root_layout) self.setCentralWidget(self.root_widget) title = QLabel("<h1>Resonance Frequency Finder</h1>") title.setAlignment(Qt.AlignCenter) self.root_layout.addWidget(title, 0, 0, 1, 3) # window_size_label = QLabel("window size: ") # window_size_label.setAlignment(Qt.AlignRight) # self.window_size_combo_box = QComboBox() # self.window_size_combo_box.addItems(self.AVAILABLE_WINDOW_SIZES) self.root_directory_label = QLabel() self.select_root_directory = QPushButton("Select/Change") self.select_root_directory.clicked.connect(self.pick_root_directory) self.record_btn = QPushButton("Record") self.record_btn.setEnabled(False) self.record_btn.clicked.connect(self.record_clicked) self.record_reference_btn = QPushButton("Record Reference") self.record_reference_btn.clicked.connect(self.record_reference_clicked) # self.root_layout.addWidget(utils.construct_horizontal_box([ # window_size_label, self.window_size_combo_box, self.record_btn # ]), 1, 0, 1, 3) self.load_results_btn = QPushButton("Load Existing Data") self.load_results_btn.clicked.connect(self.load_existing_data) self.root_layout.addWidget(utils.construct_horizontal_box([ self.record_btn, self.record_reference_btn, self.root_directory_label, self.select_root_directory, self.load_results_btn ]), 1, 0, 1, 3) self.current_freq_label = QLabel() self.root_layout.addWidget(utils.construct_horizontal_box([self.current_freq_label]), 2, 0, 1, 3) self.frequency_slider = QSlider() self.frequency_slider.setRange(self.DEFAULT_MIN_FREQUENCY, self.DEFAULT_MAX_FREQUENCY) self.frequency_slider.setSingleStep(self.DEFAULT_FREQUENCY_STEP) self.frequency_slider.setTickInterval(self.DEFAULT_FREQUENCY_STEP) self.frequency_slider.valueChanged.connect(self.update_freq_label) self.frequency_slider.setTickPosition(QSlider.TicksBelow) self.frequency_slider.setOrientation(Qt.Horizontal) min_freq_label = QLabel(f"<b>{self.DEFAULT_MIN_FREQUENCY} Hz</b>") max_freq_label = QLabel(f"<b>{self.DEFAULT_MAX_FREQUENCY} Hz</b>") self.root_layout.addWidget(utils.construct_horizontal_box([ min_freq_label, self.frequency_slider, max_freq_label ]), 3, 0, 1, 3) self.c3_amplitude_bar_set = QBarSet("Electrode C3") self.cz_amplitude_bar_set = QBarSet("Electrode Cz") self.c4_amplitude_bar_set = QBarSet("Electrode C4") self.frequencies = [] for freq in range(self.DEFAULT_MIN_FREQUENCY, self.DEFAULT_MAX_FREQUENCY + 1, self.DEFAULT_FREQUENCY_STEP): self.frequencies.append(f"{freq} Hz") self.c3_amplitude_bar_set.append(1) self.cz_amplitude_bar_set.append(1) self.c4_amplitude_bar_set.append(1) self.freq_axis = QBarCategoryAxis() self.freq_axis.append(self.frequencies) self.amplitude_axis = QValueAxis() self.amplitude_axis.setRange(0, 4) self.freq_chart = QChart() self.freq_chart.setAnimationOptions(QChart.SeriesAnimations) self.electrodes_data_series = QBarSeries() self.electrodes_data_series.append(self.c3_amplitude_bar_set) self.electrodes_data_series.append(self.cz_amplitude_bar_set) self.electrodes_data_series.append(self.c4_amplitude_bar_set) self.freq_chart.addSeries(self.electrodes_data_series) self.freq_chart.setTitle("<h1>Frequency Amplitude Increase</h1>") self.freq_chart.addAxis(self.freq_axis, Qt.AlignBottom) self.freq_chart.addAxis(self.amplitude_axis, Qt.AlignLeft) self.electrodes_data_series.attachAxis(self.amplitude_axis) self.electrodes_data_series.attachAxis(self.freq_axis) self.frequency_amplitude_graph = QChartView(self.freq_chart) self.frequency_amplitude_graph.setRenderHint(QPainter.Antialiasing) self.root_layout.addWidget(self.frequency_amplitude_graph, 4, 0, 15, 3) self.auto_adjust_axis() def update_freq_label(self): self.current_freq_label.setText("Selected Frequency: {} Hz".format(self.frequency_slider.value())) def pick_root_directory(self): path = QFileDialog.getExistingDirectory(self, "Root Directory...") self.root_directory_label.setText(path) def record_clicked(self, reference: bool = False): # selected_window_text = self.window_size_combo_box.currentText() # window_size_text = selected_window_text.replace(" Sec", "") # window_size = -1 # # if utils.is_integer(window_size_text): # window_size = int(window_size_text) # else: # print("Invalid window size...") # return # window_size_in_samples = window_size * SAMPLING_RATE recording_duration_in_samples = self.DEFAULT_RECORDING_DURATION * global_config.SAMPLING_RATE if not reference and (self.frequency_slider.value() - self.DEFAULT_MIN_FREQUENCY) % self.DEFAULT_FREQUENCY_STEP != 0: err = QErrorMessage(self) err.showMessage("Invalid Frequency Selected") err.exec() return self.recording_progress_dialog = \ QProgressDialog("Reading EEG data from board...", "Stop Recording", 0, int(recording_duration_in_samples), self) self.recording_progress_dialog.setWindowTitle("Reading Data, Please Wait...") self.recording_progress_dialog.setWindowModality(Qt.WindowModal) self.recording_progress_dialog.show() if reference: self.recording_reference = True else: self.recording = True self.eeg_data_buffer.clear() self.board.start_stream() self.reading_timer = QTimer() self.reading_timer.timeout.connect(self.read_data) self.reading_timer.start(100) def record_reference_clicked(self): print("Record reference clicked") if self.reference_eeg_data.get_channel_data(0).shape[0] > 0: self.reference_eeg_data.clear() self.record_clicked(reference=True) def read_data(self): if not self.recording and not self.recording_reference: return recording_duration_in_samples = self.recording_progress_dialog.maximum() if self.recording_reference: if self.reference_eeg_data.get_channel_data(0).shape[0] > recording_duration_in_samples or\ self.recording_progress_dialog.wasCanceled(): self.stop_recording(True) return if self.recording: if self.recording_progress_dialog.wasCanceled() or\ self.eeg_data_buffer.get_channel_data(0).shape[0] > recording_duration_in_samples: self.stop_recording(self.recording_reference) return if self.board.get_board_data_count() > 0: raw_data = self.board.get_board_data() raw_eeg_data = utils.extract_eeg_data(raw_data, global_config.BOARD_ID) self.eeg_sample_count += raw_eeg_data.shape[1] path = self.root_directory_label.text() if path != "": full_path = path + "/" + global_config.RESONANCE_DATA_FILE_NAME DataFilter.write_file(raw_eeg_data, full_path, "a") # c3 = raw_eeg_data[self.DEFAULT_C3_CHANNEL_INDEX, :] # cz = raw_eeg_data[self.DEFAULT_CZ_CHANNEL_INDEX, :] # c4 = raw_eeg_data[self.DEFAULT_C4_CHANNEL_INDEX, :] if self.recording_reference: self.reference_eeg_data.append_data(raw_eeg_data) print(f"reference size: {self.reference_eeg_data.sample_count()}") self.recording_progress_dialog.setValue(self.reference_eeg_data.get_channel_data(0).shape[0]) else: self.eeg_data_buffer.append_data(raw_eeg_data) print(f"data size: {self.eeg_data_buffer.sample_count()}") self.recording_progress_dialog.setValue(self.eeg_data_buffer.get_channel_data(0).shape[0]) def load_existing_data(self): path = QFileDialog.getExistingDirectory(self, "Root Directory...") if path == "": return filter_settings = utils.FilterSettings(global_config.SAMPLING_RATE, self.DEFAULT_BANDPASS_MIN, self.DEFAULT_BANDPASS_MAX) frequencies, eeg_data, reference_data = utils.load_slice_and_filter_resonance_data(path, filter_settings) print(frequencies) size = len(frequencies) x = np.arange(size) x_ticks = [] plot_data = np.zeros((3, size)) for i in range(size): current_eeg_data = eeg_data[i] freq = frequencies[i] x_ticks.append(f"{freq} Hz") freq_band = utils.FrequencyBand( freq - self.DEFAULT_FREQUENCY_PADDING, freq + self.DEFAULT_FREQUENCY_PADDING) reference_c3_extractor = reference_data.feature_extractor( self.DEFAULT_C3_CHANNEL_INDEX, global_config.SAMPLING_RATE ) reference_cz_extractor = reference_data.feature_extractor( self.DEFAULT_CZ_CHANNEL_INDEX, global_config.SAMPLING_RATE ) reference_c4_extractor = reference_data.feature_extractor( self.DEFAULT_C4_CHANNEL_INDEX, global_config.SAMPLING_RATE ) data_c3_extractor = current_eeg_data.feature_extractor( self.DEFAULT_C3_CHANNEL_INDEX, global_config.SAMPLING_RATE ) data_cz_extractor = current_eeg_data.feature_extractor( self.DEFAULT_CZ_CHANNEL_INDEX, global_config.SAMPLING_RATE ) data_c4_extractor = current_eeg_data.feature_extractor( self.DEFAULT_C4_CHANNEL_INDEX, global_config.SAMPLING_RATE ) c3_diff, cz_diff, c4_diff = self.amplitude_diff(freq_band, reference_c3_extractor, reference_cz_extractor, reference_c4_extractor, data_c3_extractor, data_cz_extractor, data_c4_extractor) plot_data[0, i] = c3_diff plot_data[1, i] = cz_diff plot_data[2, i] = c4_diff plt.figure() plt.title("Amplitude Increase") plt.bar(x, plot_data[0], width=0.25, label="C3 amplitude increase") plt.bar(x + 0.25, plot_data[1], width=0.25, label="Cz amplitude increase") plt.bar(x + 0.50, plot_data[2], width=0.25, label="C4 amplitude increase") plt.xticks(x + 0.25, x_ticks) plt.ylabel("Average Band Amplitude") plt.legend(loc="best") plt.show() def amplitude_diff(self, freq_band, ref_c3_extractor, ref_cz_extractor, ref_c4_extractor, data_c3_extractor, data_cz_extractor, data_c4_extractor): ref_c3_amplitude = ref_c3_extractor.average_band_amplitude(freq_band, self.DEFAULT_FFT_WINDOW_SIZE) ref_cz_amplitude = ref_cz_extractor.average_band_amplitude(freq_band, self.DEFAULT_FFT_WINDOW_SIZE) ref_c4_amplitude = ref_c4_extractor.average_band_amplitude(freq_band, self.DEFAULT_FFT_WINDOW_SIZE) data_c3_amplitude = data_c3_extractor.average_band_amplitude(freq_band, self.DEFAULT_FFT_WINDOW_SIZE) data_cz_amplitude = data_cz_extractor.average_band_amplitude(freq_band, self.DEFAULT_FFT_WINDOW_SIZE) data_c4_amplitude = data_c4_extractor.average_band_amplitude(freq_band, self.DEFAULT_FFT_WINDOW_SIZE) c3_diff = data_c3_amplitude - ref_c3_amplitude cz_diff = data_cz_amplitude - ref_cz_amplitude c4_diff = data_c4_amplitude - ref_c4_amplitude return c3_diff, cz_diff, c4_diff # return data_c3_amplitude, data_cz_amplitude, data_c4_amplitude def stop_recording(self, reference: bool = False): if self.reading_timer is not None: self.reading_timer.deleteLater() self.board.stop_stream() self.recording = False self.recording_reference = False self.recording_progress_dialog.setValue(self.recording_progress_dialog.maximum()) recording_duration_in_samples = self.recording_progress_dialog.maximum() selected_freq = self.frequency_slider.value() if reference: sample_count = min(self.reference_eeg_data.get_channel_data(0).shape[0], recording_duration_in_samples) sample_count -= global_config.SAMPLING_RATE * self.DEFAULT_SAMPLE_PADDING self.index_generator.add_slice(0, self.eeg_sample_count - sample_count, self.eeg_sample_count) else: sample_count = min(self.eeg_data_buffer.get_channel_data(0).shape[0], recording_duration_in_samples) sample_count -= global_config.SAMPLING_RATE * self.DEFAULT_SAMPLE_PADDING self.index_generator.add_slice(selected_freq, self.eeg_sample_count - sample_count, self.eeg_sample_count) if self.root_directory_label.text() != "": self.index_generator.write_to_file(self.root_directory_label.text()) QApplication.beep() start = self.DEFAULT_SAMPLE_PADDING * global_config.SAMPLING_RATE if reference: print(f"reference size: {self.reference_eeg_data.sample_count()}") self.record_btn.setEnabled(True) # self.record_reference_btn.setEnabled(False) self.reference_eeg_data.filter_all_channels( global_config.SAMPLING_RATE, self.DEFAULT_BANDPASS_MIN, self.DEFAULT_BANDPASS_MAX, True ) self.reference_eeg_data = utils.EegData(self.reference_eeg_data.to_row_array()[:, start:]) print("Reference data saved...") else: print("Stopping the recording...") print("Filtering data...") self.eeg_data_buffer.filter_all_channels( global_config.SAMPLING_RATE, self.DEFAULT_BANDPASS_MIN, self.DEFAULT_BANDPASS_MAX, subtract_average=True ) self.eeg_data_buffer = utils.EegData(self.eeg_data_buffer.to_row_array()[:, start:]) print(f"data size: {self.eeg_data_buffer.sample_count()}") reference_c3_extractor = self.reference_eeg_data.feature_extractor( self.DEFAULT_C3_CHANNEL_INDEX, global_config.SAMPLING_RATE ) reference_cz_extractor = self.reference_eeg_data.feature_extractor( self.DEFAULT_CZ_CHANNEL_INDEX, global_config.SAMPLING_RATE ) reference_c4_extractor = self.reference_eeg_data.feature_extractor( self.DEFAULT_C4_CHANNEL_INDEX, global_config.SAMPLING_RATE ) data_c3_extractor = self.eeg_data_buffer.feature_extractor( self.DEFAULT_C3_CHANNEL_INDEX, global_config.SAMPLING_RATE ) data_cz_extractor = self.eeg_data_buffer.feature_extractor( self.DEFAULT_CZ_CHANNEL_INDEX, global_config.SAMPLING_RATE ) data_c4_extractor = self.eeg_data_buffer.feature_extractor( self.DEFAULT_C4_CHANNEL_INDEX, global_config.SAMPLING_RATE ) for i in range(self.c3_amplitude_bar_set.count()): current_freq = int(self.frequencies[i].replace(" Hz", "")) if current_freq == selected_freq: freq_band = utils.FrequencyBand( current_freq - self.DEFAULT_FREQUENCY_PADDING, current_freq + self.DEFAULT_FREQUENCY_PADDING) c3_diff, cz_diff, c4_diff = self.amplitude_diff(freq_band, reference_c3_extractor,reference_cz_extractor, reference_c4_extractor, data_c3_extractor, data_cz_extractor, data_c4_extractor) print(f"C3 diff = {c3_diff}") print(f"Cz diff = {cz_diff}") print(f"C4 diff = {c4_diff}") self.c3_amplitude_bar_set.replace(i, c3_diff) self.cz_amplitude_bar_set.replace(i, cz_diff) self.c4_amplitude_bar_set.replace(i, c4_diff) utils.auto_adjust_axis(self.amplitude_axis, [self.c3_amplitude_bar_set, self.cz_amplitude_bar_set, self.c4_amplitude_bar_set], self.DEFAULT_GRAPH_PADDING) def auto_adjust_axis(self): # Adjust the range so that everything is visible and add some gaps c3_min = sys.maxsize cz_min = sys.maxsize c4_min = sys.maxsize c3_max = -sys.maxsize cz_max = -sys.maxsize c4_max = -sys.maxsize for i in range(self.c3_amplitude_bar_set.count()): c3_min = min(c3_min, self.c3_amplitude_bar_set.at(i)) cz_min = min(cz_min, self.cz_amplitude_bar_set.at(i)) c4_min = min(c4_min, self.c4_amplitude_bar_set.at(i)) c3_max = max(c3_max, self.c3_amplitude_bar_set.at(i)) cz_max = max(cz_max, self.cz_amplitude_bar_set.at(i)) c4_max = max(c4_max, self.c4_amplitude_bar_set.at(i)) print("c3 min = {}, cz min = {}, c4 min = {}".format(c3_min, cz_min, c4_min)) print("c3 max = {}, cz max = {}, c4 max = {}".format(c3_max, cz_max, c4_max)) axis_min = min(0, c3_min, cz_min, c4_min) - self.DEFAULT_GRAPH_PADDING axis_max = max(0, c3_max, cz_max, c4_max) + self.DEFAULT_GRAPH_PADDING print("axis min = {}, axis max = {}".format(axis_min, axis_max)) self.amplitude_axis.setMin(axis_min) self.amplitude_axis.setMax(axis_max)