diff --git a/utils/data_processing/data_processing.py b/utils/data_processing/data_processing.py index 2026ad5..1ae0cd9 100644 --- a/utils/data_processing/data_processing.py +++ b/utils/data_processing/data_processing.py @@ -5,7 +5,8 @@ Created on Mon Apr 24 2023 @auth: Djalim Simaila @e-mail: djalim.simaila@inrae.fr """ -from utils.math import data_extraction +from utils.math import data_extraction as de +import numpy as np from utils.files.input import ScannedObject from utils.settings.SettingManager import SettingManager @@ -44,13 +45,13 @@ def get_raw_data(obj:ScannedObject, ndigits:int,delta_z:float=1,update_progress_ # Calculate the data for each discrete vertex for discrete_values in discrete_vertices: - mean_x ,mean_y, mean_z = data_extraction.get_x_y_z_mean(discrete_values) + mean_x ,mean_y, mean_z = de.get_x_y_z_mean(discrete_values) for x,y,z in discrete_values: data["X (en mm)"].append(round(x, ndigits)) data["Y (en mm)"].append(round(y, ndigits)) data["Z (en mm)"].append(round(z, ndigits)) - data["teta (en rad)"].append(round(data_extraction.get_teta_from_x_y(x,y,mean_x,mean_y), ndigits)) - data["rayon (en mm)"].append(round(data_extraction.get_radius_from_x_y(x,y,mean_x,mean_y), ndigits)) + data["teta (en rad)"].append(round(de.get_teta_from_x_y(x,y,mean_x,mean_y), ndigits)) + data["rayon (en mm)"].append(round(de.get_radius_from_x_y(x,y,mean_x,mean_y), ndigits)) data["Xi-Xmoy"].append(round(x-mean_x, ndigits)) data["Yi-Ymoy"].append(round(y-mean_y, ndigits)) update_progress_bar(int(progress/len(discrete_vertices)*100)) @@ -82,16 +83,52 @@ def get_discrete_data(obj:ScannedObject, ndigits:int, delta_z:float=1, update_pr discrete_vertices = obj.get_discrete_vertices(delta_z) progress = 0 for discrete_values in discrete_vertices: - x,y,z = data_extraction.get_x_y_z_mean(discrete_values) + x,y,z = de.get_x_y_z_mean(discrete_values) data["X moy (en mm)"].append(round(x, ndigits)) data["Y moy (en mm)"].append(round(y, ndigits)) data["Z moy (en mm)"].append(round(z, ndigits)) first = discrete_values[0] last = discrete_values[-1] data["Discretisation(en mm)"].append(round(last[2]-first[2],ndigits)) - data["Rayon moyen (en mm)"].append(round(data_extraction.get_mean_radius(discrete_values), ndigits)) - data["Rayon ecart type (en mm)"].append(round(data_extraction.get_radius_std(discrete_values), ndigits)) + data["Rayon moyen (en mm)"].append(round(de.get_mean_radius(discrete_values), ndigits)) + data["Rayon ecart type (en mm)"].append(round(de.get_radius_std(discrete_values), ndigits)) update_progress_bar(int(progress/len(discrete_vertices)*100)) progress += 1 return data +def get_advanced_data(discrete_data:dict, update_progress_bar= progressbar_placeholder): + """ + """ + # Tortusity + l = 0 + L = 0 + vertices = list(zip(discrete_data["X moy (en mm)"], discrete_data["Y moy (en mm)"], discrete_data["Z moy (en mm)"])) + for i in range(len(vertices)-1): + l += de.get_distance_between_two_vertices(vertices[i], vertices[i+1]) + L = de.get_distance_between_two_vertices(vertices[0], vertices[-1]) + T = l/L + update_progress_bar(10) + # Volume and surface + H = discrete_data["Z moy (en mm)"][-1] - discrete_data["Z moy (en mm)"][0] + R = de.get_mean([np.power(r,2) for r in discrete_data["Rayon moyen (en mm)"]]) + V = np.pi * R * H + S = 2 * np.pi * R * H + update_progress_bar(30) + # + R_mean = de.get_mean(discrete_data["Rayon moyen (en mm)"]) + R_mean_std = de.get_standard_deviation(discrete_data["Rayon moyen (en mm)"]) + mean_sigma_r_squared = de.get_mean([np.power(r,2) for r in discrete_data["Rayon ecart type (en mm)"]]) + sigma_r_tot = np.sqrt(np.power(R_mean_std,2) + mean_sigma_r_squared ) + MI_l = R_mean_std/R_mean + MI_p = np.sqrt(mean_sigma_r_squared)/R_mean + update_progress_bar(100) + return { + "Tortuosité":T, + "Volume":V, + "Surface":S, + "Moyenne des rayons moyens":R_mean, + "Ecart-type des rayons moyens":R_mean_std, + "Sigma r tot":sigma_r_tot, + "MI_l":MI_l, + "MI_p":MI_p + } diff --git a/utils/gui/pyqt/main_window/MainWindow.py b/utils/gui/pyqt/main_window/MainWindow.py index 6a22420..46b683f 100644 --- a/utils/gui/pyqt/main_window/MainWindow.py +++ b/utils/gui/pyqt/main_window/MainWindow.py @@ -10,9 +10,10 @@ from PyQt5 import QtWidgets from PyQt5.QtCore import QThread from PyQt5.QtWidgets import QFileDialog, QWidget from utils.files.input import ScannedObject +from utils.gui.pyqt.main_window.Workers.AdvancedDataWorker import AdvancedDataWorker from utils.gui.pyqt.settings.Settings import Settings from utils.gui.pyqt.about.AboutThis import AboutThis -from utils.math.data_extraction import get_radius_from_x_y, get_true_teta_from_x_y +from utils.math.data_extraction import get_mean, get_radius_from_x_y, get_true_teta_from_x_y from utils.settings.SettingManager import SettingManager from utils.graph2D.visplot_render import cross_section, render2D from utils.graph3D.visplot_render import render3D @@ -50,6 +51,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): "Coupe XZ", "Coupe YZ", "Evolution du rayon moyen", + "Difference entre le rayon moyen et la moyenne des rayons", "Coupe de la couche", "Difference entre le rayon de chaque points et le rayon moyen de la couche" ] @@ -57,9 +59,10 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): self.obj = None self.raw_data= None self.discrete_data = None + self.advanced_data = None self.completed_tasks = 0 - self.total_tasks = 2 + self.total_tasks = 3 self.combo_boxes = [ self.slot0ComboBox, @@ -245,6 +248,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): self.processdiscrete_worker.status.connect(self.set_status) self.processdiscrete_worker.progress.connect(self.update_progress_bar) self.processdiscrete_worker.processedData.connect(self.set_discrete_data) + self.processdiscrete_worker.processedData.connect(self.process_advanced_data) # Finished self.processdiscrete_worker.finished.connect(self.finish_analyse) self.processdiscrete_worker.finished.connect(self.processdiscrete_thread.quit) @@ -254,6 +258,30 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): # Start the thread self.processdiscrete_thread.start() + def process_advanced_data(self, discrete_data:dict): + """ + Start the analyse, create the thread and connect the signals. + """ + self.advanced_data_thread = QThread() + self.advanced_data_worker = AdvancedDataWorker("AdvancedDataProcessWorker", + discrete_data) + self.advanced_data_worker.moveToThread(self.advanced_data_thread) + # Connect the signals + # Start + self.advanced_data_thread.started.connect(self.advanced_data_worker.run) + # Progress + self.advanced_data_worker.status.connect(self.set_status) + self.advanced_data_worker.progress.connect(self.update_progress_bar) + self.advanced_data_worker.processedData.connect(self.set_advanced_data) + # Finished + self.advanced_data_worker.finished.connect(self.finish_analyse) + self.advanced_data_worker.finished.connect(self.advanced_data_thread.quit) + self.advanced_data_worker.finished.connect(self.advanced_data_worker.deleteLater) + self.advanced_data_thread.finished.connect(self.advanced_data_thread.deleteLater) + + # Start the thread + self.advanced_data_thread.start() + def set_obj(self,obj:ScannedObject): """ Set the obj to the main window @@ -267,10 +295,10 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): self.discrete_data = discrete_data layer = [str(i) for i in range(len(discrete_data["X moy (en mm)"]))] layer.insert(0,"Aucune couche") + self.layer_ComboBox.currentIndexChanged.disconnect(self.layer_changed) self.layer_ComboBox.clear() self.layer_ComboBox.addItems(layer) - - + self.layer_ComboBox.currentIndexChanged.connect(self.layer_changed) def set_raw_data(self,raw_data:dict): """ @@ -278,6 +306,21 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): """ self.raw_data = raw_data + def set_advanced_data(self,advanced_data:dict): + """ + Set the advanced data to the main window + """ + self.advanced_data = advanced_data + self.tortuosity.setValue(advanced_data["Tortuosité"]) + self.volume.setValue(advanced_data["Volume"]) + self.surface.setValue(advanced_data["Surface"]) + self.mean_r_mean.setValue(advanced_data["Moyenne des rayons moyens"]) + self.sigma_r_mean.setValue(advanced_data["Ecart-type des rayons moyens"]) + self.sigma_r_tot.setValue(advanced_data["Sigma r tot"]) + self.MI_l.setValue(advanced_data["MI_l"]) + self.MI_p.setValue(advanced_data["MI_p"]) + + def save_model(self): """ Save the model to a file @@ -339,21 +382,38 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): "Z (en mm)", False).native) if graph_type == "Evolution du rayon moyen": - current_slot.addWidget(render2D(list(zip(discrete_data['Z moy (en mm)'],discrete_data['Rayon moyen (en mm)'])), + current_slot.addWidget(render2D(list(zip(discrete_data['Rayon moyen (en mm)'],discrete_data['Z moy (en mm)'])), "Evolution du rayon moyen en fonction de Z", + "Rayon moyen (en mm)", + "Z (en mm)", + False).native) + if graph_type == "Difference entre le rayon moyen et la moyenne des rayons": + r_mean= get_mean(discrete_data['Rayon moyen (en mm)']) + current_slot.addWidget(render2D(list(zip(discrete_data['Rayon moyen (en mm)']-r_mean,discrete_data['Z moy (en mm)'])), + "Difference entre le rayon moyen et la moyenne en fonction de Z", + "Rayon moyen (en mm)", "Z (en mm)", - "Rayon moyen (en mm)\n", False).native) self.set_status("Graphs rendered!") def renderDiscreteGraphs(self,obj:ScannedObject,raw_data:dict,discrete_data:dict): """ """ - self.set_status("Renderingc discretes graphs... this may take a moment") if self.layer_ComboBox.currentText() == 'Aucune couche': return layer_nb = int(self.layer_ComboBox.currentText()) discretisation_value = self.discretisation_value_selector.value() + + self.interval_size.setValue(self.discrete_data["Discretisation(en mm)"][layer_nb]) + self.x_mean.setValue(self.discrete_data["X moy (en mm)"][layer_nb]) + self.y_mean.setValue(self.discrete_data["Y moy (en mm)"][layer_nb]) + self.z_mean.setValue(self.discrete_data["Z moy (en mm)"][layer_nb]) + self.r_mean.setValue(self.discrete_data["Rayon moyen (en mm)"][layer_nb]) + self.sigma_r.setValue(self.discrete_data["Rayon ecart type (en mm)"][layer_nb]) + + if not self.show_graph_checkbox.isChecked(): + return + self.set_status("Renderingc discretes graphs... this may take a moment") for slot in self.slots: current_slot = slot[0] graph_type = slot[1] diff --git a/utils/gui/pyqt/main_window/Workers/AdvancedDataWorker.py b/utils/gui/pyqt/main_window/Workers/AdvancedDataWorker.py new file mode 100644 index 0000000..48ac262 --- /dev/null +++ b/utils/gui/pyqt/main_window/Workers/AdvancedDataWorker.py @@ -0,0 +1,43 @@ +""" +Created on Wed Apr 26 2023 +@name: DiscreteDataWorker.py +@desc: A module to process discrete data in a thread +@auth: Djalim Simaila +@e-mail: djalim.simaila@inrae.fr +""" +from PyQt5.QtCore import pyqtSignal +from utils.gui.pyqt.main_window.Workers.Worker import Worker +from utils.data_processing.data_processing import get_advanced_data + +class AdvancedDataWorker(Worker): + """ + Worker to calculate the discrete data in a thread + + :param name: The name of the worker + :param obj: The scanned object + :param output_path: The path to save the output file + :param output_file_prefix: The prefix of the output file + :param delta_z: The delta z + + :ivar obj: The scanned object + :ivar delta_z: The delta z + :ivar output_path: The path to save the output file + :ivar output_file_prefix: The prefix of the output file + :ivar processedData: The signal to emit the result + """ + processedData = pyqtSignal(dict) + + def __init__(self,name:str, discrete_data:dict): + super().__init__(name) + self.discrete_data = discrete_data + + + def run(self): + """ + Run the analyse + """ + self.set_status("Calculating advanced data...") + advanced_data = get_advanced_data(self.discrete_data,self.update_progress) + self.processedData.emit(advanced_data) + self.set_status("Done") + self.finished.emit() \ No newline at end of file