From 34982dd973b370cc45ad1b466622c71f6aa00ba5 Mon Sep 17 00:00:00 2001 From: Le Stagiaire Date: Mon, 17 Jun 2024 13:55:15 +0200 Subject: [PATCH] feat: Update volume slider range and default values chore: Refactor code for improved readability and maintainability --- .gitignore | 3 +- 3D_app/callbacks/ascan.py | 11 ++--- 3D_app/callbacks/home.py | 100 ++++++++++++++++++++++---------------- 3D_app/callbacks/main.py | 11 +++-- 3D_app/import_sqlite.py | 14 +++++- 3D_app/main.py | 32 +++++++++++- 3D_app/pages/home.py | 8 +-- README.md | 26 +++------- 8 files changed, 127 insertions(+), 78 deletions(-) diff --git a/.gitignore b/.gitignore index 91532e2..6d559b8 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ /express-app/ /react-app/ /.vscode/ -/3D_app.old/ \ No newline at end of file +/3D_app.old/ +3D_app/data.sqlite diff --git a/3D_app/callbacks/ascan.py b/3D_app/callbacks/ascan.py index 65da7c5..f12ce54 100644 --- a/3D_app/callbacks/ascan.py +++ b/3D_app/callbacks/ascan.py @@ -1,4 +1,3 @@ -from re import I from dash import callback, Input, Output, State from selection_filtre import switch_case import plotly.graph_objects as go @@ -185,16 +184,16 @@ def get_callbacks(): data_avec_traitement = switch_case( data_avec_traitement, int(select_filtre_1), - float(fs_filtre_1), - float(cutoff_filtre_1), + int(fs_filtre_1), + int(cutoff_filtre_1), int(order_filtre_1), int(windowsize_filtre_1), ) data_avec_traitement = switch_case( data_avec_traitement, int(select_filtre_2), - float(fs_filtre_2), - float(cutoff_filtre_2), + int(fs_filtre_2), + int(cutoff_filtre_2), int(order_filtre_2), int(windowsize_filtre_2), ) @@ -250,7 +249,7 @@ def get_callbacks(): data_sans_traitement_fft = np.fft.fft( volume[ int(select_ascan_y) - 1, - select_ascan_z[0] : select_ascan_z[1], + select_ascan_z[0] : int(select_ascan_z[1] / 2), int(select_ascan_x) - 1, ] ) diff --git a/3D_app/callbacks/home.py b/3D_app/callbacks/home.py index 04c7c3a..7ab715b 100644 --- a/3D_app/callbacks/home.py +++ b/3D_app/callbacks/home.py @@ -6,6 +6,7 @@ import plotly.express as px from os.path import join from util import lire_fichier_csv from selection_filtre import switch_case +from callbacks.ascan import data_traits # on définit le dossier et les fichiers à lire dossier = "Dataset/Shear_transform" @@ -107,11 +108,15 @@ def get_callbacks(): Input("layer-slider-bscan-zx", "value"), Input("layer-slider-bscan-xy", "value"), ], - [dash.dependencies.State("fade-ascan", "is_in")], - prevent_initial_call=True, + [State("fade-ascan", "is_in"), State("store-settings", "data")], ) - def update_heatmap_ascan(layer, layer1, is_in): - fig = px.line(y=volume[layer - 1, :, layer1], title="A-scan") + def update_heatmap_ascan(layer, layer1, is_in, settings): + if settings["apply_sampling_everywhere"]: + data = volume + else: + data = pre_volume + + fig = px.line(y=data[layer - 1, :, layer1], title="A-scan") return [fig, True] @@ -119,9 +124,15 @@ def get_callbacks(): @callback( Output("heatmap-ascan-fullscreen", "figure"), Input("layer-slider-ascan-fullscreen", "value"), + State("store-settings", "data") ) - def update_heatmap_ascan_fullscreen(layer): - fig = px.line(y=volume[layer - 1, :, 5], title="A-scan") + def update_heatmap_ascan_fullscreen(layer, settings): + if settings["apply_sampling_everywhere"]: + data = volume + else: + data = pre_volume + + fig = px.line(y=data[layer - 1, :, 5], title="A-scan") return fig # callback pour les B-scan XY @@ -132,12 +143,16 @@ def get_callbacks(): Output("fade-bscan-xy", "is_in"), ], [Input("layer-slider-bscan-zx", "value")], - [dash.dependencies.State("fade-bscan-zx", "is_in")], - prevent_initial_call=True, + [State("fade-bscan-zx", "is_in"), State("store-settings", "data")], ) - def update_heatmap_bscan_zx(layer, is_in): + def update_heatmap_bscan_zx(layer, is_in, settings): + if settings["apply_sampling_everywhere"]: + data = volume + else: + data = pre_volume + fig = px.imshow( - volume[layer - 1, :, :], + data[layer - 1, :, :], color_continuous_scale="Jet", aspect="auto", title="B-scan ZX", @@ -149,10 +164,16 @@ def get_callbacks(): @callback( Output("heatmap-bscan-zx-fullscreen", "figure"), Input("layer-slider-bscan-zx-fullscreen", "value"), + State("store-settings", "data") ) - def update_heatmap_bscan_zx_fullscreen(layer): + def update_heatmap_bscan_zx_fullscreen(layer, settings): + if settings["apply_sampling_everywhere"]: + data = volume + else: + data = pre_volume + fig = px.imshow( - volume[layer - 1, :, :], + data[layer - 1, :, :], color_continuous_scale="Jet", aspect="auto", title="B-scan ZX", @@ -168,11 +189,15 @@ def get_callbacks(): Output("fade-bscan-zx", "is_in"), ], [Input("layer-slider-bscan-xy", "value")], - [dash.dependencies.State("fade-bscan-xy", "is_in")], - prevent_initial_call=True, + [State("fade-bscan-xy", "is_in"), State("store-settings", "data")], ) - def update_heatmap_bscan_xy(layer, is_in): - fig = go.Figure(data=go.Heatmap(z=volume[:, :, layer], colorscale="Jet")) + def update_heatmap_bscan_xy(layer, is_in, settings): + if settings["apply_sampling_everywhere"]: + data = volume + else: + data = pre_volume + + fig = go.Figure(data=go.Heatmap(z=data[:, :, layer], colorscale="Jet")) fig.update_layout(title="B-scan XY") return [fig, layer, True] @@ -181,9 +206,15 @@ def get_callbacks(): @callback( Output("heatmap-bscan-xy-fullscreen", "figure"), Input("layer-slider-bscan-xy-fullscreen", "value"), + State("store-settings", "data"), ) - def update_heatmap_bscan_xy_fullscreen(layer): - fig = go.Figure(data=go.Heatmap(z=volume[:, :, layer], colorscale="Jet")) + def update_heatmap_bscan_xy_fullscreen(layer, settings): + if settings["apply_sampling_everywhere"]: + data = volume + else: + data = pre_volume + + fig = go.Figure(data=go.Heatmap(z=data[:, :, layer], colorscale="Jet")) fig.update_layout(title="B-scan XY") return fig @@ -232,22 +263,6 @@ def get_callbacks(): return not is_open return is_open - @callback( - [ - Output("3dplot", "clickData"), - Output("layer-slider-bscan-xy", "value"), - Output("layer-slider-bscan-zx", "value"), - ], - Input("3dplot", "clickData"), - ) - def display_3dplot_click_data(clickData): - if clickData is None: - return [None, 1, 1] - - bscan_xy = clickData["points"][0]["z"] - bscan_zx = clickData["points"][0]["x"] - return [clickData, bscan_xy, bscan_zx] - @callback( [ Output("heatmap-bscan-xy", "clickData"), @@ -288,7 +303,7 @@ def get_callbacks(): prevent_initial_call=True, ) def update_bscan_layers(bscan_xy, bscan_zx): - fig = go.Figure(data=go.Heatmap(z=volume[:, :, bscan_xy], colorscale="Jet")) + fig = go.Figure(data=go.Heatmap(z=pre_volume[:, :, bscan_xy], colorscale="Jet")) fig.add_shape( type="line", x0=0 - 1, @@ -300,7 +315,7 @@ def get_callbacks(): fig.update_layout(title="B-scan XY") fig2 = px.imshow( - volume[bscan_zx - 1, :, :], + pre_volume[bscan_zx - 1, :, :], color_continuous_scale="Jet", aspect="auto", title="B-scan ZX", @@ -314,7 +329,7 @@ def get_callbacks(): line=dict(color="white", width=1), ) - fig3 = px.line(y=volume[bscan_zx - 1, :, bscan_xy], title="A-scan") + fig3 = px.line(y=pre_volume[bscan_zx - 1, :, bscan_xy], title="A-scan") return [fig, fig2, fig3] @@ -329,6 +344,7 @@ def get_callbacks(): State("echantillonage-x", "value"), State("echantillonage-y", "value"), State("echantillonage-z", "value"), + State("apply-sampling-everywhere", "value"), ], prevent_initial_call=True, ) @@ -339,6 +355,7 @@ def get_callbacks(): echantillonage_x_value, echantillonage_y_value, echantillonage_z_value, + apply_sampling, ): if clicks != None and clicks == 1: return [ @@ -348,6 +365,7 @@ def get_callbacks(): "echantillonage_x": echantillonage_x_value, "echantillonage_y": echantillonage_y_value, "echantillonage_z": echantillonage_z_value, + "apply_sampling_everywhere": apply_sampling, }, 0, ] @@ -394,14 +412,14 @@ def get_callbacks(): {str(i): str(i) for i in range(0, dim_x, max(1, int(dim_x / 20)))}, dim_z - 1, {str(i): str(i) for i in range(1, dim_z + 1, max(1, int(dim_z / 20)))}, - volume.min(), + 0, volume.max() / 2, { str(i): str(i) for i in range( - int(volume.min()), + 0, int(volume.max() / 2) + 1, - int((volume.max() / 2 - volume.min()) / 10), + int(volume.max() / 20), ) }, dim_y, @@ -528,5 +546,5 @@ def get_callbacks(): if format == "raw": np.save(join("Dataset/saves", filename), pre_volume) else: - np.save(join("Dataset/saves", filename), volume) + np.save(join("Dataset/saves", filename), data_traits) return True diff --git a/3D_app/callbacks/main.py b/3D_app/callbacks/main.py index e1b3a04..8742420 100644 --- a/3D_app/callbacks/main.py +++ b/3D_app/callbacks/main.py @@ -42,12 +42,14 @@ def get_callbacks(): @callback( Output("open-modal", "is_open"), - [Input("open-button", "n_clicks"), Input("open-close", "n_clicks")], + [Input("open-button", "n_clicks"), Input("open-close", "n_clicks"), Input("open-refresh", "n_clicks")], [dash.dependencies.State("open-modal", "is_open")], ) - def toggle_open(n1, n2, is_open): + def toggle_open(n1, n2, n3, is_open): if n1 or n2: return not is_open + elif n3: + return True return is_open @@ -88,6 +90,7 @@ def get_callbacks(): [ Output("open-modal", "is_open", allow_duplicate=True), Output("store-files", "data"), + Output("opened-file", "children"), ], Input({"type": "file-item", "index": ALL}, "n_clicks"), State({"type": "file-item", "index": ALL}, "children"), @@ -96,11 +99,11 @@ def get_callbacks(): def open_file(n, filenames): ctx = dash.callback_context if not ctx.triggered or all(click is None for click in n): - return [None, ""] + return [None, "", "No file opened"] file_index = ctx.triggered[0]["prop_id"].split(".")[0] file_index = eval(file_index) filename = filenames[file_index["index"]] - return [False, filename] + return [False, filename, f"Opened file: {filename}"] @callback( diff --git a/3D_app/import_sqlite.py b/3D_app/import_sqlite.py index b92208f..3d3fd7b 100644 --- a/3D_app/import_sqlite.py +++ b/3D_app/import_sqlite.py @@ -2,6 +2,7 @@ import sqlite3 from util import * import numpy as np import datetime +from progress.bar import IncrementalBar timestamp_start = datetime.datetime.now() @@ -9,6 +10,7 @@ db = sqlite3.connect('data.sqlite') cursor = db.cursor() cursor.execute('CREATE TABLE IF NOT EXISTS volume (x INTEGER, y INTEGER, z INTEGER, value REAL)') +cursor.execute('CREATE TABLE IF NOT EXISTS properties (name TEXT, value TEXT)') # on définit le dossier et les fichiers à lire @@ -21,17 +23,25 @@ fichiers_selectionnes = [ # fichiers_selectionnes = ['Shear_x001-x101_y{:03d}_Rot00.csv'.format(i) for i in range(10, 62)] # on lit les fichiers et on les met dans un tableau -pre_volume = np.array(lire_fichier_csv(dossier, fichiers_selectionnes)) -volume = pre_volume[:, ::32, :] +volume = np.array(lire_fichier_csv(dossier, fichiers_selectionnes))[:, ::16, :] dim_x, dim_y, dim_z = volume.shape X, Y, Z = np.mgrid[0:dim_x, 0:dim_y, 0:dim_z] +bar = IncrementalBar('Processing', max=dim_x*dim_y*dim_z, suffix="%(percent).2f%% - %(index)d/%(max)d (%(eta).3fs remaining estimated)",) + cursor.execute('DELETE FROM volume') for x in range(dim_x): for y in range(dim_y): for z in range(dim_z): cursor.execute('INSERT INTO volume (x, y, z, value) VALUES (?, ?, ?, ?)', (x, y, z, volume[x, y, z])) + bar.next() +bar.finish() + +cursor.execute('DELETE FROM properties') +cursor.execute('INSERT INTO properties (name, value) VALUES (?, ?)', ('dim_x', str(dim_x))) +cursor.execute('INSERT INTO properties (name, value) VALUES (?, ?)', ('dim_y', str(dim_y))) +cursor.execute('INSERT INTO properties (name, value) VALUES (?, ?)', ('dim_z', str(dim_z))) db.commit() db.close() diff --git a/3D_app/main.py b/3D_app/main.py index 31f0f65..cdca6fc 100644 --- a/3D_app/main.py +++ b/3D_app/main.py @@ -6,21 +6,24 @@ from os.path import isfile, join import diskcache from json import load from callbacks.main import * +import numpy as np +from util import lire_fichier_csv +cache = diskcache.Cache("./cache") +background_callback_manager = DiskcacheManager(cache) # on crée l'application app = dash.Dash( __name__, external_stylesheets=[dbc.themes.DARKLY, dbc.icons.BOOTSTRAP], use_pages=True, + background_callback_manager=background_callback_manager, ) config = load(open("config.json", "r")) app.title = config["app_title"] -cache = diskcache.Cache("./cache") -background_callback_manager = DiskcacheManager(cache) print("Reloading...") @@ -32,6 +35,23 @@ if "saves" not in listdir("Dataset"): files = listdir("Dataset/saves") +# on définit le dossier et les fichiers à lire +dossier = "Dataset/Shear_transform" +fichiers_selectionnes = [ + "Shear_x001-x101_y{:03d}_Rot00_transform.csv".format(i) for i in range(10, 14) +] + +# valeurs d'échantillonage +echantillonage_x = 1 +echantillonage_y = 32 +echantillonage_z = 1 + +pre_volume = np.array(lire_fichier_csv(dossier, fichiers_selectionnes)) +volume = pre_volume[::echantillonage_x, ::echantillonage_y, ::echantillonage_z] +dim_x, dim_y, dim_z = volume.shape + +X, Y, Z = np.mgrid[0:dim_x, 0:dim_y, 0:dim_z] + # on lit le fichier modal.md pour le tuto with open("assets/modal.md", "r") as f: @@ -101,6 +121,12 @@ modal_settings = dbc.Modal( ), ] ), + dbc.Switch( + id="apply-sampling-everywhere", + label="Apply sampling on A-Scan and B-Scan", + value=True, + className="me-2", + ), ] ), dbc.ModalFooter( @@ -325,6 +351,7 @@ nav_bar = dbc.Navbar( dbc.NavItem(button_settings), dbc.NavItem(button_open), dbc.NavItem(button_save), + dbc.Label(id="opened-file", style={"marginLeft": "10px", "padding": "0 auto"}), ], className="ml-auto", navbar=True, @@ -365,6 +392,7 @@ app.layout = dbc.Container( "echantillonage_x": 1, "echantillonage_y": 32, "echantillonage_z": 1, + "apply_sampling_everywhere": True, }, ), dcc.Store(id="store-filters", data={}), diff --git a/3D_app/pages/home.py b/3D_app/pages/home.py index 029362d..12fc8c2 100644 --- a/3D_app/pages/home.py +++ b/3D_app/pages/home.py @@ -130,15 +130,15 @@ mesh_card = dbc.Fade( ), # 'fig' is your 3D plotly figure dcc.Slider( id="iso-slider", - min=volume.min(), + min=0, max=volume.max() / 2, - value=volume.min(), + value=0, marks={ str(i): str(i) for i in range( - int(volume.min()), + 0, int(volume.max() / 2) + 1, - int((volume.max() / 2 - volume.min()) / 10), + int(volume.max() / 20), ) }, step=1, diff --git a/README.md b/README.md index ea7fe4a..6a6026c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +Pour le README de l'app 3D, se référer à [3D_app/README.md](3D_app/README.md) # -----------------------Tache---------------------------- @@ -15,15 +16,10 @@ -Point of interest scan : caractérisation du ou des défauts qui seront représenter par des points d'intêrets. - -### Secondaire : +### Secondaire : Comparé les traitement entre pytohn et matlab en les correllant et dire qu'il sont à ..% différent et les superposés. - - - - # -----------------------Biblio-------------------------- Papier vue @@ -45,7 +41,6 @@ Low frequency ultrasonic dataset for pulse echo object detection in an isotropic https://www.ncbi.nlm.nih.gov/pmc/articles/PMC9108879/ étude dont il serait intéressant de récupérer le data set, proposer par Hassan Rabah - Dataset for structural health monitoring of pipelines using ultrasonic guided waves https://www.ncbi.nlm.nih.gov/pmc/articles/PMC9747640/ pareil, a voir pour récupérer le data set, proposer par Slavisa Jovanovic @@ -53,7 +48,7 @@ pareil, a voir pour récupérer le data set, proposer par Slavisa Jovanovic liste de projet de reconstruction 3D https://github.com/bluestyle97/awesome-3d-reconstruction-papers?tab=readme-ov-file#neural-surface -Cours MIT, Machine learning for inverse graphics par Prof, Sitzmann: +Cours MIT, Machine learning for inverse graphics par Prof, Sitzmann: https://www.scenerepresentations.org/courses/inverse-graphics-23/ Étude et développement de méthodes de caractérisation de défauts basées sur les reconstructions ultrasonores TFM: @@ -63,9 +58,9 @@ Slicer-IGT, add-on de slicer permettant la représentation 3D en temps réel. Site internet : https://www.slicerigt.org/wp/ GitHub : https://github.com/SlicerIGT/SlicerIGT -Réseau de neurone GNG : +Réseau de neurone GNG : GitHub : https://github.com/ansrivas/GNG/tree/master?tab=readme-ov-file -GitHub-1: https://github.com/rendchevi/growing-neural-gas/blob/master/README.md +GitHub-1: https://github.com/rendchevi/growing-neural-gas/blob/master/README.md Youtube_GitHub-1: https://www.youtube.com/watch?v=Dt73QWZQck4&t=2226s Reconstruction @@ -81,34 +76,29 @@ Technique TFM/FMC : présentation et travaux normatifs en cours: https://www.isgroupe.com/fr/blog/technique-tfm-mc-presentation-travaux-normatifs-en-cours # -----------------------Tuto------------------------------ - + apprendre openGL https://opengl.developpez.com/tutoriels/apprendre-opengl/ - camera https://opengl.developpez.com/tutoriels/apprendre-opengl/?page=camera le but de l'utilisation de openGl est d'in fine de développer un outil d'imagerie 3D pour représenter sous forme de voxel les set de data générer ou mesurer. - - # -----------------------Note------------------------------ - + k wave pour la simulation d'onde sonore, première introduction à la représentation de voxel dans Matlab. - OpenCV n'est pas forcement pertinent pour le moment l'outil à été élaborer surtout pour faire du traitement d'image. - Pour la suite du stage il sera intéressant voir nécessaire de se familiariser avec les méthode de filtrage pour ensuite en faire une implémentation FPGA si le temps nous le permet. Pour un préambule de solution, pourquoi ne pas imaginé un système capable de faire tourner un os (raspberry pi) et en parallèle d'un FPGA qui ferais le gros des calculs (de filtrage par exemple) Pour être très optimiste sur la suite de ce projet, imaginons un FPGA reconfigurable à la volée en fonction du type de filtrage que l'utilisateur souhaiterais utiliser dans sont système. -algorithme de contrôle vraiment nécessaire ? +algorithme de contrôle vraiment nécessaire ? d'abord penser à une méthode de reconstruction 3D en temps réel, quel 'artefact' complète un objet, lequel n'est qu'un bruit donc devra être filtré.