🚀 feat(data_processing.py): add function to calculate advanced data
🚀 feat(MainWindow.py): add support for calculating advanced data in a separate thread 🚀 feat(AdvancedDataWorker.py): add worker to calculate advanced data in a thread The `get_advanced_data` function was added to calculate advanced data from the discrete data. The `process_advanced_data` function was added to the `MainWindow` class to start a thread to calculate the advanced data. The `AdvancedDataWorker` class was added to calculate the advanced data in a separate thread. This allows the application to be more responsive and not freeze while the advanced data is being calculated.
This commit is contained in:
parent
2c2cae2d25
commit
dce613fd9e
@ -5,7 +5,8 @@ Created on Mon Apr 24 2023
|
|||||||
@auth: Djalim Simaila
|
@auth: Djalim Simaila
|
||||||
@e-mail: djalim.simaila@inrae.fr
|
@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.files.input import ScannedObject
|
||||||
from utils.settings.SettingManager import SettingManager
|
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
|
# Calculate the data for each discrete vertex
|
||||||
for discrete_values in discrete_vertices:
|
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:
|
for x,y,z in discrete_values:
|
||||||
data["X (en mm)"].append(round(x, ndigits))
|
data["X (en mm)"].append(round(x, ndigits))
|
||||||
data["Y (en mm)"].append(round(y, ndigits))
|
data["Y (en mm)"].append(round(y, ndigits))
|
||||||
data["Z (en mm)"].append(round(z, 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["teta (en rad)"].append(round(de.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["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["Xi-Xmoy"].append(round(x-mean_x, ndigits))
|
||||||
data["Yi-Ymoy"].append(round(y-mean_y, ndigits))
|
data["Yi-Ymoy"].append(round(y-mean_y, ndigits))
|
||||||
update_progress_bar(int(progress/len(discrete_vertices)*100))
|
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)
|
discrete_vertices = obj.get_discrete_vertices(delta_z)
|
||||||
progress = 0
|
progress = 0
|
||||||
for discrete_values in discrete_vertices:
|
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["X moy (en mm)"].append(round(x, ndigits))
|
||||||
data["Y moy (en mm)"].append(round(y, ndigits))
|
data["Y moy (en mm)"].append(round(y, ndigits))
|
||||||
data["Z moy (en mm)"].append(round(z, ndigits))
|
data["Z moy (en mm)"].append(round(z, ndigits))
|
||||||
first = discrete_values[0]
|
first = discrete_values[0]
|
||||||
last = discrete_values[-1]
|
last = discrete_values[-1]
|
||||||
data["Discretisation(en mm)"].append(round(last[2]-first[2],ndigits))
|
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 moyen (en mm)"].append(round(de.get_mean_radius(discrete_values), ndigits))
|
||||||
data["Rayon ecart type (en mm)"].append(round(data_extraction.get_radius_std(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))
|
update_progress_bar(int(progress/len(discrete_vertices)*100))
|
||||||
progress += 1
|
progress += 1
|
||||||
return data
|
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
|
||||||
|
}
|
||||||
|
|||||||
@ -10,9 +10,10 @@ from PyQt5 import QtWidgets
|
|||||||
from PyQt5.QtCore import QThread
|
from PyQt5.QtCore import QThread
|
||||||
from PyQt5.QtWidgets import QFileDialog, QWidget
|
from PyQt5.QtWidgets import QFileDialog, QWidget
|
||||||
from utils.files.input import ScannedObject
|
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.settings.Settings import Settings
|
||||||
from utils.gui.pyqt.about.AboutThis import AboutThis
|
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.settings.SettingManager import SettingManager
|
||||||
from utils.graph2D.visplot_render import cross_section, render2D
|
from utils.graph2D.visplot_render import cross_section, render2D
|
||||||
from utils.graph3D.visplot_render import render3D
|
from utils.graph3D.visplot_render import render3D
|
||||||
@ -50,6 +51,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
"Coupe XZ",
|
"Coupe XZ",
|
||||||
"Coupe YZ",
|
"Coupe YZ",
|
||||||
"Evolution du rayon moyen",
|
"Evolution du rayon moyen",
|
||||||
|
"Difference entre le rayon moyen et la moyenne des rayons",
|
||||||
"Coupe de la couche",
|
"Coupe de la couche",
|
||||||
"Difference entre le rayon de chaque points et le rayon moyen 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.obj = None
|
||||||
self.raw_data= None
|
self.raw_data= None
|
||||||
self.discrete_data = None
|
self.discrete_data = None
|
||||||
|
self.advanced_data = None
|
||||||
|
|
||||||
self.completed_tasks = 0
|
self.completed_tasks = 0
|
||||||
self.total_tasks = 2
|
self.total_tasks = 3
|
||||||
|
|
||||||
self.combo_boxes = [
|
self.combo_boxes = [
|
||||||
self.slot0ComboBox,
|
self.slot0ComboBox,
|
||||||
@ -245,6 +248,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
self.processdiscrete_worker.status.connect(self.set_status)
|
self.processdiscrete_worker.status.connect(self.set_status)
|
||||||
self.processdiscrete_worker.progress.connect(self.update_progress_bar)
|
self.processdiscrete_worker.progress.connect(self.update_progress_bar)
|
||||||
self.processdiscrete_worker.processedData.connect(self.set_discrete_data)
|
self.processdiscrete_worker.processedData.connect(self.set_discrete_data)
|
||||||
|
self.processdiscrete_worker.processedData.connect(self.process_advanced_data)
|
||||||
# Finished
|
# Finished
|
||||||
self.processdiscrete_worker.finished.connect(self.finish_analyse)
|
self.processdiscrete_worker.finished.connect(self.finish_analyse)
|
||||||
self.processdiscrete_worker.finished.connect(self.processdiscrete_thread.quit)
|
self.processdiscrete_worker.finished.connect(self.processdiscrete_thread.quit)
|
||||||
@ -254,6 +258,30 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
# Start the thread
|
# Start the thread
|
||||||
self.processdiscrete_thread.start()
|
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):
|
def set_obj(self,obj:ScannedObject):
|
||||||
"""
|
"""
|
||||||
Set the obj to the main window
|
Set the obj to the main window
|
||||||
@ -267,10 +295,10 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
self.discrete_data = discrete_data
|
self.discrete_data = discrete_data
|
||||||
layer = [str(i) for i in range(len(discrete_data["X moy (en mm)"]))]
|
layer = [str(i) for i in range(len(discrete_data["X moy (en mm)"]))]
|
||||||
layer.insert(0,"Aucune couche")
|
layer.insert(0,"Aucune couche")
|
||||||
|
self.layer_ComboBox.currentIndexChanged.disconnect(self.layer_changed)
|
||||||
self.layer_ComboBox.clear()
|
self.layer_ComboBox.clear()
|
||||||
self.layer_ComboBox.addItems(layer)
|
self.layer_ComboBox.addItems(layer)
|
||||||
|
self.layer_ComboBox.currentIndexChanged.connect(self.layer_changed)
|
||||||
|
|
||||||
|
|
||||||
def set_raw_data(self,raw_data:dict):
|
def set_raw_data(self,raw_data:dict):
|
||||||
"""
|
"""
|
||||||
@ -278,6 +306,21 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
"""
|
"""
|
||||||
self.raw_data = raw_data
|
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):
|
def save_model(self):
|
||||||
"""
|
"""
|
||||||
Save the model to a file
|
Save the model to a file
|
||||||
@ -339,21 +382,38 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow):
|
|||||||
"Z (en mm)",
|
"Z (en mm)",
|
||||||
False).native)
|
False).native)
|
||||||
if graph_type == "Evolution du rayon moyen":
|
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",
|
"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)",
|
"Z (en mm)",
|
||||||
"Rayon moyen (en mm)\n",
|
|
||||||
False).native)
|
False).native)
|
||||||
self.set_status("Graphs rendered!")
|
self.set_status("Graphs rendered!")
|
||||||
|
|
||||||
def renderDiscreteGraphs(self,obj:ScannedObject,raw_data:dict,discrete_data:dict):
|
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':
|
if self.layer_ComboBox.currentText() == 'Aucune couche':
|
||||||
return
|
return
|
||||||
layer_nb = int(self.layer_ComboBox.currentText())
|
layer_nb = int(self.layer_ComboBox.currentText())
|
||||||
discretisation_value = self.discretisation_value_selector.value()
|
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:
|
for slot in self.slots:
|
||||||
current_slot = slot[0]
|
current_slot = slot[0]
|
||||||
graph_type = slot[1]
|
graph_type = slot[1]
|
||||||
|
|||||||
43
utils/gui/pyqt/main_window/Workers/AdvancedDataWorker.py
Normal file
43
utils/gui/pyqt/main_window/Workers/AdvancedDataWorker.py
Normal file
@ -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()
|
||||||
Loading…
Reference in New Issue
Block a user