AnalyseMorphologique/integration_tests/data_test.py
Djalim Simaila fb2bb6e9ce 🐛 fix(data_test.py): change variable name from teta_diffs to theta_diffs to improve semantics
 feat(data_processing.py): add function to calculate morphological indicators from discrete data
The variable name teta_diffs was changed to theta_diffs to improve semantics. A new function was added to calculate morphological indicators from discrete data. The function calculates Tortuosity, Volume, Surface, Mean radius, Standard deviation of radius, Sigma r tot, MI_l, and MI_p.

🔥 refactor(input.py): remove unused result_file_path parameter from ScannedObject constructor and from_xyz_file method
 feat(input.py): add encoding parameter to open method in from_obj_file and from_xyz_file methods
The result_file_path parameter was not being used in the ScannedObject constructor and from_xyz_file method, so it was removed to simplify the code. The encoding parameter was added to the open method in the from_obj_file and from_xyz_file methods to ensure that the files are opened with the correct encoding.

🐛 fix(output.py): add utf-8 encoding when writing to output file
 feat(output.py): remove unused import and function argument, improve code readability
The fix adds the utf-8 encoding when writing to the output file to avoid encoding issues. The feat removes the unused import and function argument to improve code readability. The function format_data now only takes the necessary arguments and the unused import is removed.

🐛 fix(main_window.py): fix typo in function name
 feat(main_window.py): add persistence to pre-processed data
The fix corrects a typo in the function name get_true_theta_from_x_y. The feat adds persistence to the pre-processed data by storing the raw data, discrete data, and advanced data in the main window. This avoids re-computation of the data when switching between tabs.

🎨 style(MainWindow.ui): add export_advanced_metrics button to the UI
🎨 style(UI_MainWindow.py): add export_advanced_metrics button to the UI
🎨 style(ressources_rc.py): update the resource file
🐛 fix(data_extraction.py): fix typo in function name get_mean_teta to get_mean_theta
The changes add a new button to the UI named "export_advanced_metrics" which allows the user to export variables. The resource file is updated to reflect the changes. The typo in the function name get_mean_teta is fixed to get_mean_theta.
2023-05-09 10:56:43 +02:00

264 lines
12 KiB
Python

"""
This module gives functions to assist tests
"""
import os
import time
import numpy as np
from main import get_discrete_data, get_raw_data
from utils.files import output
from utils.files.input import ScannedObject, parse_result_file
def check_discrete_data(expected_file: str, actual_file: str, ndigits=7, eps=0.00001, silent: bool = False) -> dict:
"""
Compares the output of the main process with the reference output for the same file,
This function assume the passed paramters are the discretised one and not the raw,
this function will not test that for the user>
:param expected_file: The reference file, the one we assume is correct
:param actual_file: The output of the main process
:param ndigits: The number of values to keep after the dot
:param eps: the degree of precision we consider two values different
:return: dict containing the average difference for each variable
"""
output_file = "check_discrete_data_full.txt"
minimal_output_file = "check_discrete_data_minimal.txt"
minimal_output_data = ""
output_data = ""
expected = []
list_x_diffs, list_y_diffs, list_z_diffs, list_r_diffs, list_std_diffs = [], [], [], [], []
with open(expected_file, "r") as f:
expected = f.readlines()[1:]
expected = [line.replace(',', '.').split('\t') for line in expected]
expected = [[float(x) for x in line] for line in expected]
actual = []
with open(actual_file, "r") as f:
actual = f.readlines()[1:]
actual = [line.split('\t') for line in actual]
actual = [[float(x) for x in line if x != '\n'] for line in actual]
for point in range(min(len(expected), len(actual))):
# Calculate the difference for each variable
x_diff, y_diff, z_diff, r_diff, std_diff = round(abs(expected[point][0] - actual[point][0]), ndigits), round(abs(expected[point][1] - actual[point][1]), ndigits), round(
abs(expected[point][2] - actual[point][2]), ndigits), round(abs(expected[point][3] - actual[point][3]), ndigits), round(abs(expected[point][4] - actual[point][4]), ndigits)
# Add the difference to their respective list
list_x_diffs.append(x_diff)
list_y_diffs.append(y_diff)
list_z_diffs.append(z_diff)
list_r_diffs.append(r_diff)
list_std_diffs.append(std_diff)
# Format the line
line = f"{str(point).rjust(4)}:\t X: {str(x_diff).rjust(8)}\t Y: {str(y_diff).rjust(8)}\t Z: {str(z_diff).rjust(8)}\t R: {str(r_diff).rjust(8)}\t STD: {str(std_diff).rjust(8)}"
output_data += line + "\n"
# If one of the values is greater than epsilon, add the line to the minimal output file
if x_diff > eps or y_diff > eps or z_diff > eps or r_diff > eps or std_diff > eps:
minimal_output_data += line + "\n"
# Save output to file
output.save_output_file(output_file, output_data)
output.save_output_file(minimal_output_file, minimal_output_data)
# Print the results
if not silent:
x = round(sum(list_x_diffs)/len(expected), ndigits)
y = round(sum(list_y_diffs)/len(expected), ndigits)
z = round(sum(list_z_diffs)/len(expected), ndigits)
r = round(sum(list_r_diffs)/len(expected), ndigits)
std = round(sum(list_std_diffs)/len(expected), ndigits)
print()
print("Analyse données discretisées:")
print(f"difference moyenne X: {x}")
print(f"difference moyenne Y: {y}")
print(f"difference moyenne Z: {z}")
print(f"difference moyenne R: {r}")
print(f"difference moyenne STD: {std}")
print(f"diff globale des fichiers: {(x+y+z+r+std)/5}")
print(f"Voir {output_file} pour plus de détails")
print(f"Voir {minimal_output_file} pour les différences significatives")
print("_"*80)
return {
"x": x,
"y": y,
"z": z,
"r": r,
"std": std}
def check_raw_data(expected_file, actual_file, ndigits=7, eps=0.00001, silent: bool = False) -> dict:
"""
Compares the output of the main process with the reference output for the same file,
This function assume the passed paramters are the raw one and not the discretised,
this function will not test that for the user.
:param expected_file: The reference file, the one we assume is correct
:param actual_file: The output of the main process
:param ndigits: The number of values to keep after the dot
:param eps: the degree of precision we consider two values different
:return: dict containing the average difference for each variable
"""
output_file = "check_raw_data_full.txt"
minimal_output_file = "check_raw_data_minimal.txt"
minimal_output_data = ""
output_data = ""
expected = []
list_x_diffs, list_y_diffs, list_z_diffs, list_theta_diffs, list_radius_diffs, list_xmoy_diffs, list_ymoy_diffs = [], [], [], [], [], [], []
with open(expected_file, "r") as f:
expected = f.readlines()[1:]
expected = [line.replace(',', '.').split('\t') for line in expected]
expected = [[float(x) for x in line] for line in expected]
actual = []
with open(actual_file, "r") as f:
actual = f.readlines()[1:]
actual = [line.split('\t') for line in actual]
actual = [[float(x) for x in line if x != '\n'] for line in actual]
for i in range(min(len(expected), len(actual))):
# Calculate the absolute difference between expected and actual
x_diff, y_diff, z_diff, t_diff, r_diff, xmoy_diff, ymoy_diff = round(abs(expected[i][0] - actual[i][0]), ndigits), round(abs(expected[i][1] - actual[i][1]), ndigits), round(abs(expected[i][2] - actual[i][2]), ndigits), round(
abs(expected[i][3] - actual[i][3]), ndigits), round(abs(expected[i][4] - actual[i][4]), ndigits), round(abs(expected[i][5] - actual[i][5]), ndigits), round(abs(expected[i][6] - actual[i][6]), ndigits)
# Add each difference to its respctive list
list_x_diffs.append(x_diff)
list_y_diffs.append(y_diff)
list_z_diffs.append(z_diff)
list_theta_diffs.append(t_diff)
list_radius_diffs.append(r_diff)
list_xmoy_diffs.append(xmoy_diff)
list_ymoy_diffs.append(ymoy_diff)
# Format the line
line = f"{str(i).rjust(4)}:\t X: {str(x_diff).rjust(8)}\t Y: {str(y_diff).rjust(8)}\t Z: {str(z_diff).rjust(8)}\t T: {str(t_diff).rjust(8)}\t R: {str(r_diff).rjust(8)}\t Xmoy: {str(xmoy_diff).rjust(8)}\t Ymoy: {str(ymoy_diff).rjust(8)}"
output_data += line + "\n"
# If the diff is greater than epsilon, add it to the minimal output file
if x_diff > eps:
minimal_output_data += f"{i} : X diff {x_diff}\n"
if y_diff > eps:
minimal_output_data += f"{i} : Y diff {y_diff}\n"
if z_diff > eps:
minimal_output_data += f"{i} : Z diff {z_diff}\n"
if t_diff > eps:
minimal_output_data += f"{i} : theta diff{ t_diff}\n"
if r_diff > eps:
minimal_output_data += f"{i} : R diff {r_diff}\n"
if xmoy_diff > eps:
minimal_output_data += f"{i} : Xi-Xmoy diff {xmoy_diff}\n"
if ymoy_diff > eps:
minimal_output_data += f"{i} : Yi-Ymoy diff{ymoy_diff}\n"
# Save output to file
output.save_output_file(output_file, output_data)
output.save_output_file(minimal_output_file, minimal_output_data)
# Print the output
if not silent:
sum_x_diff = round(sum(list_x_diffs)/len(expected), ndigits)
sum_y_diff = round(sum(list_y_diffs)/len(expected), ndigits)
sum_z_diff = round(sum(list_z_diffs)/len(expected), ndigits)
sum_theta_diff = round(sum(list_theta_diffs)/len(expected), ndigits)
sum_radius_diff = round(sum(list_radius_diffs)/len(expected), ndigits)
sum_xmoy_diff = round(sum(list_xmoy_diffs)/len(expected), ndigits)
sum_ymoy_diffs = round(sum(list_ymoy_diffs)/len(expected), ndigits)
print()
print("Analyse données brutes :")
print(f"diff moyenne de x : {sum_x_diff}")
print(f"diff moyenne de y : {sum_y_diff}")
print(f"diff moyenne de z : {sum_z_diff}")
print(f"diff moyenne de t : {sum_theta_diff}")
print(f"diff moyenne de r : {sum_radius_diff}")
print(f"diff moyenne de xmoy : {sum_xmoy_diff}")
print(f"diff moyenne de ymoy : {sum_ymoy_diffs}")
print(
f"diff gloabale des fichiers : {(sum_x_diff+sum_y_diff+sum_z_diff+sum_theta_diff+sum_radius_diff+sum_xmoy_diff+sum_ymoy_diffs)/7}")
print(f"Voir {output_file} pour plus de détails")
print(f"Voir {minimal_output_file} pour les différences significatives")
print("_"*80)
return {"x": list_x_diffs,
"y": list_y_diffs,
"z": list_z_diffs,
"theta": list_theta_diffs,
"radius": list_radius_diffs,
"xmoy": list_xmoy_diffs,
"ymoy": list_ymoy_diffs}
def check_consistency(obj: ScannedObject, discretised_file_path: str):
"""
This function takes a obj file, and checks if the discretised result file
respect what should be the correct discritisation
:param obj: The object to check
:param discretised_file_path: The discetised file output
"""
obj.export_xyz("verification.txt")
mean_list = []
moyx, moyy, moyz = parse_result_file(discretised_file_path)
moy = moyy
verticies = obj.get_vertices(sort=True)
position = 0
while position < len(verticies) and len(moy) > 0:
xi_value = verticies[position][0]
yi_value = verticies[position][1]
zi_value = verticies[position][2]
mean_list.append(yi_value)
mean = np.mean(mean_list)
print(f"searching for {moy[0]}, currently at {position}", end="\r")
if abs(mean - moy[0]) < 0.000001:
moy.pop(0)
mean_list = []
copyposition = position
if int(verticies[copyposition][2]) >= int(verticies[copyposition+1][2]):
while int(verticies[copyposition][2]) == int(verticies[copyposition-1][2]):
copyposition -= 1
# Position +1 pour l'allignement des indices avec le numero de ligne
# Position - copyposition + 1 car on se deplace seulement si, la condition est fasse aka, on bouge pas si on est aussi de la ou on doit etre
print("line position :", position+1, "|should have stoped :", position - copyposition + 1,
"position higher| Z difference :", verticies[position][2] - verticies[copyposition-1][2])
position += 1
def test_get_raw_data(obj: ScannedObject, expected_file: str, ndigits: int = 6, eps: float = 0.0001):
"""
Test the get_raw_data function, mesure how long it takes to run it then
checks if the values match with the exepected values
:param obj: the object to calculate the data from
:param expected_file: reference output to check data against
:param ndigits: The number of digits to keep after the comma
:param eps: the degree of precision we consider two values different
"""
# Calculate raw data and save it in a file
now = time.time()
data = get_raw_data(obj, ndigits)
print()
print("Time to calculate raw data: ", time.time() - now)
output.save_output_file('tmp_analyse_brute.txt', output.format_data(data, '\t', [
"X (en mm)", "Y (en mm)", "Z (en mm)", "theta (en rad)", "rayon (en mm)", "Xi-Xmoy", "Yi-Ymoy"]))
check_raw_data(expected_file, 'tmp_analyse_brute.txt', ndigits, eps)
os.remove('tmp_analyse_brute.txt')
def test_get_discrete_data(obj: ScannedObject, expected_file: str, ndigits: int = 6, eps: float = 0.0001):
"""
Test the get_discrete_data function mesure how long it takes to run it then
checks if the values match with the exepected values
:param obj: the object to calculate the data from
:param expected_file: reference output to check data against
:param ndigits: The number of digits to keep after the comma
:param eps: the degree of precision we consider two values different
"""
# Calculate discrete data and save it in a file
now = time.time()
data = get_discrete_data(obj, ndigits)
print()
print("Time to calculate discrete data: ", time.time() - now)
output.save_output_file('tmp_analyse_discrete.txt', output.format_data(data, '\t', [
"X moy (en mm)", "Y moy (en mm)", "Z moy (en mm)", "Rayon moyen (en mm)", "Rayon ecart type (en mm)"]))
check_discrete_data(
expected_file, 'tmp_analyse_discrete.txt', ndigits, eps)
os.remove('tmp_analyse_discrete.txt')