🚀 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:
Djalim Simaila 2023-05-04 13:28:03 +02:00
parent 2c2cae2d25
commit dce613fd9e
3 changed files with 154 additions and 14 deletions

View File

@ -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
}

View File

@ -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]

View 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()