chore: Refactor home callbacks to be more readable.

feat: Implementation of slider control
This commit is contained in:
2024-06-26 13:27:34 +02:00
parent c61a1e4c5e
commit bdbb75cca8
9 changed files with 1408 additions and 310 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@
/.vscode/
/3D_app.old/
3D_app/data.sqlite
1acquisition-ultrasons-cale-tofd_2024-06-07_0851/

View File

@ -1,31 +1,29 @@
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
from sklearn.datasets import make_swiss_roll
import igraph as ig
from numpy.linalg import norm
from sklearn.datasets import make_moons
import igraph as ig
from numpy.linalg import norm
from tqdm import tqdm
from sys import stdout
# import cupy as np
class GrowingNeuralGas:
def __init__(self, max_neurons, max_iter, max_age, eb, en, alpha, beta, l, dataset):
'''
"""
---------------------------------------------
Growing Neural Gas' Parameter Declarations
---------------------------------------------
1. max_iter ; Maximum # of iterations the
2. max_age ; Maximum # of age
1. max_iter ; Maximum # of iterations the
2. max_age ; Maximum # of age
3. eb ; fraction of distance betweeen nearest neuron and input signal
4. en ; fraction of distance betweeen neighboring neurons and input signal
5. alpha ; multiplying scalar for local error
6. beta ; multiplying scalar for global error
7. l
7. l
8. dataset
'''
"""
# Parameters declared by user
self.max_neurons = max_neurons
self.max_iter = max_iter
@ -37,15 +35,15 @@ class GrowingNeuralGas:
self.l = l
self.dataset_original = dataset.copy()
self.dataset = dataset.copy()
# Variable for tracking learning evolution
self.verts_evolve = []
self.edges_evolve = []
def initialize_gng(self):
'''
"""
Initialize Growing Neural Gas
'''
"""
# Get random datapoints from target dataset
t0 = np.random.randint(0, int(self.dataset.shape[0] / 2))
t1 = np.random.randint(int(self.dataset.shape[0] / 2), self.dataset.shape[0])
@ -54,45 +52,60 @@ class GrowingNeuralGas:
self.gng.add_vertex(weight=self.dataset[t0, :].astype(np.float64), error=0)
self.gng.add_vertex(weight=self.dataset[t1, :].astype(np.float64), error=0)
self.gng.add_edge(0, 1, age=0)
def learning_position(self):
for _ in range(self.l):
# Step 1. Get a random datapoint from target dataset
t = np.random.randint(0, self.dataset.shape[0])
random_input = self.dataset[t, :]
# Step 2. Find 2 nearest neuron from random_input
nearest_index = np.array([norm(weight[:2] - random_input[:2])**2 for weight in self.gng.vs['weight']]).argsort()
nearest_index = np.array(
[
norm(weight[:2] - random_input[:2]) ** 2
for weight in self.gng.vs["weight"]
]
).argsort()
neuron_s1 = self.gng.vs[nearest_index[0]]
neuron_s2 = self.gng.vs[nearest_index[1]]
neuron_s2 = self.gng.vs[nearest_index[1]]
# Step 3. Increase the age of all neighboring edges from nearest neuron (neuron_s1)
for edge_id in self.gng.incident(neuron_s1.index):
self.gng.es[edge_id]['age'] += 1
self.gng.es[edge_id]["age"] += 1
# Step 4. Add error to the nearest neuron
self.gng.vs[neuron_s1.index]['error'] += norm(neuron_s1['weight'][:2] - random_input[:2])
self.gng.vs[neuron_s1.index]["error"] += norm(
neuron_s1["weight"][:2] - random_input[:2]
)
# Step 5.1. Update position of nearest neuron
neuron_s1['weight'][:2] += (self.eb * (random_input[:2] - neuron_s1['weight'][:2]))
neuron_s1['weight'][2] += (self.eb * (random_input[2] - neuron_s1['weight'][2]))
neuron_s1["weight"][:2] += self.eb * (
random_input[:2] - neuron_s1["weight"][:2]
)
neuron_s1["weight"][2] += self.eb * (
random_input[2] - neuron_s1["weight"][2]
)
# Step 5.2. Update position of nearest neuron's neighbors
for neuron in self.gng.vs[self.gng.neighbors(neuron_s1.index)]:
neuron['weight'][:2] += (self.en * (random_input[:2] - neuron['weight'][:2]))
neuron['weight'][2] += (self.en * (random_input[2] - neuron['weight'][2]))
neuron["weight"][:2] += self.en * (
random_input[:2] - neuron["weight"][:2]
)
neuron["weight"][2] += self.en * (random_input[2] - neuron["weight"][2])
# Step 6. Update edge of nearest neurons
EDGE_FLAG = self.gng.get_eid(neuron_s1.index, neuron_s2.index, directed=False, error=False)
EDGE_FLAG = self.gng.get_eid(
neuron_s1.index, neuron_s2.index, directed=False, error=False
)
if EDGE_FLAG == -1:
self.gng.add_edge(neuron_s1.index, neuron_s2.index, age=0)
else:
self.gng.es[EDGE_FLAG]['age'] = 0
self.gng.es[EDGE_FLAG]["age"] = 0
# Step 7.1. Delete aging edge
for edge in self.gng.es:
src = edge.source
tgt = edge.target
if edge['age'] > self.max_age:
if edge["age"] > self.max_age:
self.gng.delete_edges(edge.index)
# Step 7.2. Delete isolated neuron
@ -102,7 +115,7 @@ class GrowingNeuralGas:
# Step 8. Reduce global error
for neuron in self.gng.vs:
neuron['error'] *= self.beta
neuron["error"] *= self.beta
# Step 9.1. Remove generated random input from target dataset
self.dataset = np.delete(self.dataset, t, axis=0)
@ -114,13 +127,20 @@ class GrowingNeuralGas:
# Adding new neuron from previous learning
# Get neuron q and f
if len(self.gng.vs) <= self.max_neurons:
error_index = np.array([error for error in self.gng.vs['error']]).argsort()
error_index = np.array([error for error in self.gng.vs["error"]]).argsort()
neuron_q = self.gng.vs[error_index[-1]]
error = np.array([(neuron['error'], neuron.index) for neuron in self.gng.vs[self.gng.neighbors(neuron_q.index)]])
error = np.array(
[
(neuron["error"], neuron.index)
for neuron in self.gng.vs[self.gng.neighbors(neuron_q.index)]
]
)
error = np.sort(error, axis=0)
neuron_f = self.gng.vs[int(error[-1, 1])]
# Add neuron between neuron q and f
self.gng.add_vertex(weight=(neuron_q['weight'] + neuron_f['weight']) / 2, error=0)
self.gng.add_vertex(
weight=(neuron_q["weight"] + neuron_f["weight"]) / 2, error=0
)
neuron_r = self.gng.vs[len(self.gng.vs) - 1]
# Delete edge between neuron q and f
self.gng.delete_edges(self.gng.get_eid(neuron_q.index, neuron_f.index))
@ -128,24 +148,30 @@ class GrowingNeuralGas:
self.gng.add_edge(neuron_q.index, neuron_r.index, age=0)
self.gng.add_edge(neuron_r.index, neuron_f.index, age=0)
# Update neuron error
neuron_q['error'] *= self.alpha
neuron_f['error'] *= self.alpha
neuron_r['error'] = neuron_q['error']
neuron_q["error"] *= self.alpha
neuron_f["error"] *= self.alpha
neuron_r["error"] = neuron_q["error"]
def learn(self):
# Initialize GNG
self.initialize_gng()
# GNG learning iteration
for iter, _ in zip(range(0, self.max_iter), tqdm(range(self.max_iter))):
# Track evolution
self.verts_evolve.append(np.array([neuron['weight'] for neuron in self.gng.vs]))
self.edges_evolve.append(np.array([(neuron.source + 1, neuron.target + 1) for neuron in self.gng.es]))
self.verts_evolve.append(
np.array([neuron["weight"] for neuron in self.gng.vs])
)
self.edges_evolve.append(
np.array(
[(neuron.source + 1, neuron.target + 1) for neuron in self.gng.es]
)
)
# Learn new posititon
self.learning_position()
self.update_neuron()
self.update_neuron()
return self.gng
def plot_gng(self, active1,active2, data_point_size=1, neuron_size=30):
def plot_gng(self, active1, active2, data_point_size=1, neuron_size=30):
"""
Function to plot the neurons and connections of the GNG in 3D space, with color representing the intensity.
Parameters:
@ -153,67 +179,103 @@ class GrowingNeuralGas:
- neuron_size: Size of the neurons in the plot.
"""
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax = fig.add_subplot(111, projection="3d")
# Plot the original data points
if (active1==True):
scatter = ax.scatter(self.dataset_original[:, 0], self.dataset_original[:, 1], self.dataset_original[:, 2], c=self.dataset_original[:, 3], s=data_point_size, cmap='viridis', alpha=0.5, label='Data')
if active1 == True:
scatter = ax.scatter(
self.dataset_original[:, 0],
self.dataset_original[:, 1],
self.dataset_original[:, 2],
c=self.dataset_original[:, 3],
s=data_point_size,
cmap="viridis",
alpha=0.5,
label="Data",
)
# Plot the neurons
neuron_positions = np.array([neuron['weight'] for neuron in self.gng.vs])
scatter=ax.scatter(neuron_positions[:, 0], neuron_positions[:, 1], neuron_positions[:, 2], c=neuron_positions[:, 3], s=neuron_size, cmap='viridis', label='Neurons')
fig.colorbar(scatter, ax=ax, label='Intensity')
neuron_positions = np.array([neuron["weight"] for neuron in self.gng.vs])
scatter = ax.scatter(
neuron_positions[:, 0],
neuron_positions[:, 1],
neuron_positions[:, 2],
c=neuron_positions[:, 3],
s=neuron_size,
cmap="viridis",
label="Neurons",
)
fig.colorbar(scatter, ax=ax, label="Intensity")
# Plot the connections
if(active2==True):
if active2 == True:
for edge in self.gng.es:
src, tgt = edge.source, edge.target
src_pos, tgt_pos = self.gng.vs[src]['weight'], self.gng.vs[tgt]['weight']
ax.plot([src_pos[0], tgt_pos[0]], [src_pos[1], tgt_pos[1]], [src_pos[2], tgt_pos[2]], 'k-', alpha=0.5)
src_pos, tgt_pos = (
self.gng.vs[src]["weight"],
self.gng.vs[tgt]["weight"],
)
ax.plot(
[src_pos[0], tgt_pos[0]],
[src_pos[1], tgt_pos[1]],
[src_pos[2], tgt_pos[2]],
"k-",
alpha=0.5,
)
ax.set_title('Growing Neural Gas Result')
ax.legend()
plt.show()
def plot_gng(x1,x2,active2, neuron_size=30):
"""
Function to plot the neurons and connections of the GNG in 3D space, with color representing the intensity.
Parameters:
- data_point_size: Size of the data points in the plot.
- neuron_size: Size of the neurons in the plot.
"""
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# Plot the original data points
#if (active1==True):
#scatter = ax.scatter(self.dataset_original[:, 0], self.dataset_original[:, 1], self.dataset_original[:, 2], c=self.dataset_original[:, 3], s=data_point_size, cmap='viridis', alpha=0.5, label='Data')
# Plot the neurons
neuron_positions = x1
scatter=ax.scatter(neuron_positions[:, 0], neuron_positions[:, 1], neuron_positions[:, 2], c=neuron_positions[:, 3], s=neuron_size, cmap='viridis', label='Neurons')
fig.colorbar(scatter, ax=ax, label='Intensity')
# Plot the connections
if(active2==True):
for edge in x2:
src, tgt = edge[0], edge[1]
src_pos, tgt_pos = x1[src][:], x1[tgt][:]
ax.plot([src_pos[0], tgt_pos[0]], [src_pos[1], tgt_pos[1]], [src_pos[2], tgt_pos[2]], 'k-', alpha=0.5)
ax.set_title('Growing Neural Gas Result')
ax.set_title("Growing Neural Gas Result")
ax.legend()
plt.show()
def position_point_espace(data, seuil,y):
tab = []
size_data = np.shape(data)
for i in range(size_data[0]):
for j in range(size_data[1]):
if data[i, j] > seuil:
tab.append([y,i, j, data[i, j]])
return tab
def plot_gng(x1, x2, active2, neuron_size=30):
"""
Function to plot the neurons and connections of the GNG in 3D space, with color representing the intensity.
Parameters:
- data_point_size: Size of the data points in the plot.
- neuron_size: Size of the neurons in the plot.
"""
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
# Plot the original data points
# if (active1==True):
# scatter = ax.scatter(self.dataset_original[:, 0], self.dataset_original[:, 1], self.dataset_original[:, 2], c=self.dataset_original[:, 3], s=data_point_size, cmap='viridis', alpha=0.5, label='Data')
# Plot the neurons
neuron_positions = x1
scatter = ax.scatter(
neuron_positions[:, 0],
neuron_positions[:, 1],
neuron_positions[:, 2],
c=neuron_positions[:, 3],
s=neuron_size,
cmap="viridis",
label="Neurons",
)
fig.colorbar(scatter, ax=ax, label="Intensity")
# Plot the connections
if active2 == True:
for edge in x2:
src, tgt = edge[0], edge[1]
src_pos, tgt_pos = x1[src][:], x1[tgt][:]
ax.plot(
[src_pos[0], tgt_pos[0]],
[src_pos[1], tgt_pos[1]],
[src_pos[2], tgt_pos[2]],
"k-",
alpha=0.5,
)
ax.set_title("Growing Neural Gas Result")
ax.legend()
plt.show()
def position_point_espace(data, seuil, y):
tab = []
size_data = np.shape(data)
for i in range(size_data[0]):
for j in range(size_data[1]):
if data[i, j] > seuil:
tab.append([y, i, j, data[i, j]])
return tab

View File

@ -35,3 +35,8 @@
* Ajustement de la fonction d'enregistrement et d'ouverture des datasets
* Ajout d'un bouton de sauvegarde pour les datasets filtrés
# V4.2
- Réorganisation des callbacks de `home.py`
- Ajout de popups pour contrôler l'affichage des sliders

View File

@ -7,6 +7,8 @@ from os.path import join
from util import lire_fichier_csv
from selection_filtre import switch_case
from callbacks.ascan import data_traits
from dash import dcc, html
import dash_bootstrap_components as dbc
# on définit le dossier et les fichiers à lire
dossier = "Dataset/Shear_transform"
@ -27,6 +29,11 @@ X, Y, Z = np.mgrid[0:dim_x, 0:dim_y, 0:dim_z]
def get_callbacks():
# -----------------------------------------------------------------------
# ---------------------- Callbacks pour le 3D plot ----------------------
# -----------------------------------------------------------------------
# callback pour le plot 3D
@callback(
[Output("3dplot", "figure"), Output("fade-3dplot", "is_in")],
[
@ -38,13 +45,13 @@ def get_callbacks():
)
def update_3dplot(iso_value, y_values, settings, is_in):
if settings["use_real_values"]:
y_min, y_max = y_values
y_min, y_max = y_values or [0, dim_y]
selected_volume = volume[0:dim_x, int(y_min) : int(y_max), 0:dim_z]
X, Y, Z = [
np.load("Dataset/npy/{}-values.npy".format(i)) for i in ["x", "y", "z"]
]
else:
y_min, y_max = y_values
y_min, y_max = y_values or [0, dim_y]
selected_volume = volume[0:dim_x, int(y_min) : int(y_max), 0:dim_z]
X, Y, Z = np.mgrid[
0 : selected_volume.shape[0],
@ -65,7 +72,6 @@ def get_callbacks():
colorscale="Jet",
)
)
return [fig, True]
# callback pour le plot 3D en plein écran
@ -75,6 +81,7 @@ def get_callbacks():
Input("iso-slider-fullscreen", "value"),
Input("y-slider-fullscreen", "value"),
],
prevent_initial_call=True,
)
def update_3dplot_fullscreen(iso_value, y_values):
y_min, y_max = y_values
@ -101,6 +108,166 @@ def get_callbacks():
return fig
# callback pour le plein écran du plot 3D
@callback(
Output("modal-3dplot", "is_open"),
[Input("fullscreen-button-3dplot", "n_clicks")],
[dash.dependencies.State("modal-3dplot", "is_open")],
)
def toggle_fullscreen_3dplot(n1, is_open):
if n1:
return not is_open
return is_open
# callback pour les paramètres du plot 3D
@callback(
Output("modal-settings-3dplot", "is_open"),
[
Input("settings-button-3dplot", "n_clicks"),
Input("save-settings-3dplot", "n_clicks"),
],
[dash.dependencies.State("modal-settings-3dplot", "is_open")],
)
def toggle_settings_3dplot(n1, n2, is_open):
if n1 or n2:
return not is_open
return is_open
@callback(
Output("3dplot-settings", "data"),
Input("save-settings-3dplot", "n_clicks"),
[
State("checklist-3dplot-card-settings", "value"),
State("checklist-3dplot-fullscreen-settings", "value"),
],
prevent_initial_call=True,
)
def save_settings_3dplot(n_clicks, card_settings, fullscreen_settings):
return {
"card_settings": card_settings,
"fullscreen_settings": fullscreen_settings,
}
@callback(
[
Output("iso-slider-container", "children"),
Output("y-slider-container", "children"),
Output("iso-slider-fullscreen-container", "children"),
Output("y-slider-fullscreen-container", "children"),
],
Input("3dplot-settings", "data"),
)
def update_3dplot_sliders(data):
iso_slider = dbc.Row(
[
dbc.Col(html.Span("Iso value: "), width="auto"),
dbc.Col(
dcc.Slider(
id="iso-slider",
min=0,
max=volume.max() / 2,
value=0,
step=1,
marks={
str(i): str(i)
for i in range(
0,
int(volume.max() / 2) + 1,
int((volume.max() / 2) / 20),
)
},
)
),
]
)
y_slider = dbc.Row(
[
dbc.Col(html.Span("Y crop: "), width="auto"),
dbc.Col(
dcc.RangeSlider(
id="y-slider",
min=0,
max=dim_y,
value=[0, dim_y],
marks={
str(i): str(i)
for i in range(0, dim_y + 1, max(1, int(dim_y / 20)))
},
)
),
]
)
iso_slider_fullscreen = dbc.Row(
[
dbc.Col(html.Span("Iso value: "), width="auto"),
dbc.Col(
dcc.Slider(
id="iso-slider-fullscreen",
min=0,
max=volume.max() / 2,
value=0,
step=1,
marks={
str(i): str(i)
for i in range(
0,
int(volume.max() / 2) + 1,
int((volume.max() / 2) / 20),
)
},
)
),
]
)
y_slider_fullscreen = dbc.Row(
[
dbc.Col(html.Span("Y crop: "), width="auto"),
dbc.Col(
dcc.RangeSlider(
id="y-slider-fullscreen",
min=0,
max=dim_y,
value=[0, dim_y],
marks={
str(i): str(i)
for i in range(0, dim_y + 1, max(1, int(dim_y / 20)))
},
)
),
]
)
if data is None:
return [None, None, None, None]
else:
if (
data["card_settings"] is None
or "iso_slider" not in data["card_settings"]
):
iso_slider = None
if data["card_settings"] is None or "y_slider" not in data["card_settings"]:
y_slider = None
if (
data["fullscreen_settings"] is None
or "iso_slider" not in data["fullscreen_settings"]
):
iso_slider_fullscreen = None
if (
data["fullscreen_settings"] is None
or "y_slider" not in data["fullscreen_settings"]
):
y_slider_fullscreen = None
return [
iso_slider,
y_slider,
iso_slider_fullscreen,
y_slider_fullscreen,
]
# ----------------------------------------------------------------------
# ---------------------- Callbacks pour le A-Scan ----------------------
# ----------------------------------------------------------------------
# callback pour le A-scan
@callback(
[Output("heatmap-ascan", "figure"), Output("fade-ascan", "is_in")],
@ -108,33 +275,213 @@ def get_callbacks():
Input("layer-slider-bscan-zx", "value"),
Input("layer-slider-bscan-xy", "value"),
],
[State("fade-ascan", "is_in"), State("store-settings", "data")],
[
State("crop-slider", "value"),
State("fade-ascan", "is_in"),
State("store-settings", "data"),
],
)
def update_heatmap_ascan(layer, layer1, is_in, settings):
def update_heatmap_ascan(layer, layer1, crop_values, is_in, settings):
y_min, y_max = crop_values or [0, dim_y]
layer = layer or 1
if settings["apply_sampling_everywhere"]:
data = volume
else:
data = pre_volume
fig = px.line(y=data[layer - 1, :, layer1], title="A-scan")
fig = px.line(
y=data[layer - 1, int(y_min) : int(y_max), layer1], title="A-scan"
)
return [fig, True]
# callback pour le A-scan en plein écran
@callback(
Output("heatmap-ascan-fullscreen", "figure"),
Input("layer-slider-ascan-fullscreen", "value"),
State("store-settings", "data")
[
Input("layer-slider-x-ascan-fullscreen", "value"),
Input("layer-slider-z-ascan-fullscreen", "value"),
Input("crop-slider-fullscreen", "value"),
],
State("store-settings", "data"),
)
def update_heatmap_ascan_fullscreen(layer, settings):
def update_heatmap_ascan_fullscreen(layer_x, layer_z, crop, settings):
y_min, y_max = crop or [0, dim_y]
layer_x = layer_x or 0
layer_z = layer_z or 0
if settings["apply_sampling_everywhere"]:
data = volume
else:
data = pre_volume
fig = px.line(y=data[layer - 1, :, 5], title="A-scan")
fig = px.line(
y=data[layer_x - 1, int(y_min) : int(y_max), layer_z], title="A-scan"
)
return fig
# callback pour le plein écran du A-scan
@callback(
Output("modal-ascan", "is_open"),
[Input("fullscreen-button-ascan", "n_clicks")],
[dash.dependencies.State("modal-ascan", "is_open")],
)
def toggle_fullscreen_ascan(n1, is_open):
if n1:
return not is_open
return is_open
# callback pour les paramètres du A-scan
@callback(
Output("modal-settings-ascan", "is_open"),
[
Input("settings-button-ascan", "n_clicks"),
Input("save-settings-ascan", "n_clicks"),
],
[dash.dependencies.State("modal-settings-ascan", "is_open")],
)
def toggle_settings_ascan(n1, n2, is_open):
if n1 or n2:
return not is_open
return is_open
@callback(
Output("ascan-settings", "data"),
Input("save-settings-ascan", "n_clicks"),
[
State("checklist-ascan-card-settings", "value"),
State("checklist-ascan-fullscreen-settings", "value"),
],
prevent_initial_call=True,
)
def save_settings_ascan(n_clicks, card_settings, fullscreen_settings):
return {
"card_settings": card_settings,
"fullscreen_settings": fullscreen_settings,
}
@callback(
[
Output("crop-slider-container", "children"),
Output("layer-slider-x-ascan-fullscreen-container", "children"),
Output("layer-slider-z-ascan-fullscreen-container", "children"),
Output("crop-slider-fullscreen-container", "children"),
],
Input("ascan-settings", "data"),
)
def update_ascan_sliders(data):
crop_slider = dbc.Row(
[
dbc.Col(
html.Span("Crop: ", className="mr-2"),
width="auto",
),
dbc.Col(
dcc.RangeSlider(
id="crop-slider",
min=0,
max=dim_y,
step=1,
value=[0, dim_y],
marks={
str(i): str(i)
for i in range(0, dim_y + 1, max(1, int(dim_y / 20)))
},
),
),
]
)
slider_x_fullscreen = dbc.Row(
[
dbc.Col(html.Span("Layer X: "), width="auto"),
dbc.Col(
dcc.Slider(
id="layer-slider-x-ascan-fullscreen",
min=0,
max=dim_x,
value=0,
step=1,
marks={
str(i): str(i)
for i in range(0, dim_x, max(1, int(dim_x / 50)))
},
)
),
]
)
slider_z_fullscreen = dbc.Row(
[
dbc.Col(html.Span("Layer Z: "), width="auto"),
dbc.Col(
dcc.Slider(
id="layer-slider-z-ascan-fullscreen",
min=1,
max=dim_z - 1,
value=1,
marks={
str(i): str(i)
for i in range(1, dim_z - 1, max(1, int(dim_z / 20)))
},
step=1,
)
),
]
)
crop_slider_fullscreen = dbc.Row(
[
dbc.Col(html.Span("Crop: "), width="auto"),
dbc.Col(
dcc.RangeSlider(
id="crop-slider-fullscreen",
min=0,
max=dim_y,
value=[0, dim_y],
marks={
str(i): str(i)
for i in range(
0,
dim_y + 1,
max(1, int(dim_y / 20)),
)
},
step=1,
)
),
]
)
if data is None:
return [None, None, None, None]
else:
if (
data["card_settings"] is None
or "crop_slider" not in data["card_settings"]
):
crop_slider = None
if (
data["fullscreen_settings"] is None
or "layer_slider_x" not in data["fullscreen_settings"]
):
slider_x_fullscreen = None
if (
data["fullscreen_settings"] is None
or "layer_slider_z" not in data["fullscreen_settings"]
):
slider_z_fullscreen = None
if (
data["fullscreen_settings"] is None
or "crop_slider" not in data["fullscreen_settings"]
):
crop_slider_fullscreen = None
return [
crop_slider,
slider_x_fullscreen,
slider_z_fullscreen,
crop_slider_fullscreen,
]
# -------------------------------------------------------------------------
# ---------------------- Callbacks pour le B-Scan ZX ----------------------
# -------------------------------------------------------------------------
# callback pour les B-scan XY
@callback(
[
@ -143,16 +490,21 @@ def get_callbacks():
Output("fade-bscan-xy", "is_in"),
],
[Input("layer-slider-bscan-zx", "value")],
[State("fade-bscan-zx", "is_in"), State("store-settings", "data")],
[
State("crop-slider", "value"),
State("fade-bscan-zx", "is_in"),
State("store-settings", "data"),
],
)
def update_heatmap_bscan_zx(layer, is_in, settings):
def update_heatmap_bscan_zx(layer, crop_values, is_in, settings):
y_min, y_max = crop_values or [0, dim_y]
if settings["apply_sampling_everywhere"]:
data = volume
else:
data = pre_volume
fig = px.imshow(
data[layer - 1, :, :],
data[layer - 1, int(y_min) : int(y_max), :],
color_continuous_scale="Jet",
aspect="auto",
title="B-scan ZX",
@ -164,7 +516,7 @@ def get_callbacks():
@callback(
Output("heatmap-bscan-zx-fullscreen", "figure"),
Input("layer-slider-bscan-zx-fullscreen", "value"),
State("store-settings", "data")
State("store-settings", "data"),
)
def update_heatmap_bscan_zx_fullscreen(layer, settings):
if settings["apply_sampling_everywhere"]:
@ -181,6 +533,128 @@ def get_callbacks():
return fig
# callback pour le plein écran du B-scan ZX
@callback(
Output("modal-bscan-zx", "is_open"),
[Input("fullscreen-button-bscan-zx", "n_clicks")],
[dash.dependencies.State("modal-bscan-zx", "is_open")],
)
def toggle_fullscreen_bscan_zx(n1, is_open):
if n1:
return not is_open
return is_open
# callback pour les paramètres du B-scan ZX
@callback(
Output("modal-settings-bscan-zx", "is_open"),
[
Input("settings-button-bscan-zx", "n_clicks"),
Input("save-settings-bscan-zx", "n_clicks"),
],
[dash.dependencies.State("modal-settings-bscan-zx", "is_open")],
)
def toggle_settings_bscan_zx(n1, n2, is_open):
if n1 or n2:
return not is_open
return is_open
@callback(
Output("bscan-zx-settings", "data"),
Input("save-settings-bscan-zx", "n_clicks"),
[
State("checklist-bscan-zx-card-settings", "value"),
State("checklist-bscan-zx-fullscreen-settings", "value"),
],
prevent_initial_call=True,
)
def save_settings_bscan_zx(n_clicks, card_settings, fullscreen_settings):
return {
"card_settings": card_settings,
"fullscreen_settings": fullscreen_settings,
}
@callback(
[
Output("layer-slider-bscan-zx-container", "children"),
Output("layer-slider-bscan-zx-fullscreen-container", "children"),
],
Input("bscan-zx-settings", "data"),
)
def update_bscan_zx_sliders(data):
slider = dbc.Row(
[
dbc.Col(html.Span("Layer: "), width="auto"),
dbc.Col(
dcc.Slider(
id="layer-slider-bscan-zx",
min=0,
max=dim_x - 1,
value=0,
step=1,
marks={
str(i): str(i)
for i in range(0, dim_x, max(1, int(dim_x / 20)))
},
)
),
]
)
x_slider_fullscreen = dbc.Row(
[
dbc.Col(html.Span("X layer: "), width="auto"),
dbc.Col(
dcc.Slider(
id="layer-slider-bscan-zx-fullscreen",
min=0,
max=dim_z,
value=0,
step=1,
marks={
str(i): str(i)
for i in range(0, dim_z, max(1, int(dim_z / 50)))
},
)
),
]
)
y_slider_fullscreen = dbc.Row(
[
dbc.Col(html.Span("Y layer: "), width="auto"),
dbc.Col(
dcc.Slider(
id="layer-slider-bscan-xy-fullscreen",
min=0,
max=dim_z,
value=0,
step=1,
marks={
str(i): str(i)
for i in range(0, dim_z, max(1, int(dim_z / 50)))
},
)
),
]
)
if data is None:
return [None, None]
else:
if data["card_settings"] is None or "x_slider" not in data["card_settings"]:
slider = None
if (
data["fullscreen_settings"] is None
or "layer_slider" not in data["fullscreen_settings"]
):
slider_fullscreen = None
return [
slider,
slider_fullscreen,
]
# -------------------------------------------------------------------------
# ---------------------- Callbacks pour le B-Scan XY ----------------------
# -------------------------------------------------------------------------
# callback pour les B-scan ZX
@callback(
[
@ -189,15 +663,22 @@ def get_callbacks():
Output("fade-bscan-zx", "is_in"),
],
[Input("layer-slider-bscan-xy", "value")],
[State("fade-bscan-xy", "is_in"), State("store-settings", "data")],
[
State("crop-slider", "value"),
State("fade-bscan-xy", "is_in"),
State("store-settings", "data"),
],
)
def update_heatmap_bscan_xy(layer, is_in, settings):
def update_heatmap_bscan_xy(layer, crop_values, is_in, settings):
y_min, y_max = crop_values or [0, dim_y]
if settings["apply_sampling_everywhere"]:
data = volume
else:
data = pre_volume
fig = go.Figure(data=go.Heatmap(z=data[:, :, layer], colorscale="Jet"))
fig = go.Figure(
data=go.Heatmap(z=data[:, int(y_min) : int(y_max), layer], colorscale="Jet")
)
fig.update_layout(title="B-scan XY")
return [fig, layer, True]
@ -219,28 +700,6 @@ def get_callbacks():
return fig
# callback pour le plein écran du plot 3D
@callback(
Output("modal-3dplot", "is_open"),
[Input("fullscreen-button-3dplot", "n_clicks")],
[dash.dependencies.State("modal-3dplot", "is_open")],
)
def toggle_fullscreen_3dplot(n1, is_open):
if n1:
return not is_open
return is_open
# callback pour le plein écran du A-scan
@callback(
Output("modal-ascan", "is_open"),
[Input("fullscreen-button-ascan", "n_clicks")],
[dash.dependencies.State("modal-ascan", "is_open")],
)
def toggle_fullscreen_ascan(n1, is_open):
if n1:
return not is_open
return is_open
# callback pour le plein écran du B-scan XY
@callback(
Output("modal-bscan-xy", "is_open"),
@ -252,17 +711,6 @@ def get_callbacks():
return not is_open
return is_open
# callback pour le plein écran du B-scan ZX
@callback(
Output("modal-bscan-zx", "is_open"),
[Input("fullscreen-button-bscan-zx", "n_clicks")],
[dash.dependencies.State("modal-bscan-zx", "is_open")],
)
def toggle_fullscreen_bscan_zx(n1, is_open):
if n1:
return not is_open
return is_open
@callback(
[
Output("heatmap-bscan-xy", "clickData"),
@ -278,6 +726,118 @@ def get_callbacks():
bscan_zx = clickData["points"][0]["y"]
return [clickData, bscan_zx]
# callback pour les paramètres du B-scan XY
@callback(
Output("modal-settings-bscan-xy", "is_open"),
[
Input("settings-button-bscan-xy", "n_clicks"),
Input("save-settings-bscan-xy", "n_clicks"),
],
[dash.dependencies.State("modal-settings-bscan-xy", "is_open")],
)
def toggle_settings_bscan_xy(n1, n2, is_open):
if n1 or n2:
return not is_open
return is_open
@callback(
Output("bscan-xy-settings", "data"),
Input("save-settings-bscan-xy", "n_clicks"),
[
State("checklist-bscan-xy-card-settings", "value"),
State("checklist-bscan-xy-fullscreen-settings", "value"),
],
prevent_initial_call=True,
)
def save_settings_bscan_xy(n_clicks, card_settings, fullscreen_settings):
return {
"card_settings": card_settings,
"fullscreen_settings": fullscreen_settings,
}
@callback(
[
Output("layer-slider-bscan-xy-container", "children"),
Output("layer-slider-bscan-xy-fullscreen-container", "children"),
],
Input("bscan-xy-settings", "data"),
)
def update_bscan_xy_sliders(data):
slider = dbc.Row(
[
dbc.Col(html.Span("Layer: "), width="auto"),
dbc.Col(
dcc.Slider(
id="layer-slider-bscan-xy",
min=1,
max=dim_z - 1,
value=0,
step=1,
marks={
str(i): str(i)
for i in range(1, dim_z, max(1, int(dim_z / 20)))
},
)
),
]
)
x_slider_fullscreen = dbc.Row(
[
dbc.Col(html.Span("X layer: "), width="auto"),
dbc.Col(
dcc.Slider(
id="layer-slider-bscan-xy-fullscreen",
min=0,
max=dim_x,
value=0,
step=1,
marks={
str(i): str(i)
for i in range(0, dim_x, max(1, int(dim_x / 50)))
},
)
),
]
)
y_slider_fullscreen = dbc.Row(
[
dbc.Col(html.Span("Y layer: "), width="auto"),
dbc.Col(
dcc.Slider(
id="layer-slider-bscan-zx-fullscreen",
min=0,
max=dim_x,
value=0,
step=1,
marks={
str(i): str(i)
for i in range(0, dim_x, max(1, int(dim_x / 50)))
},
)
),
]
)
if data is None:
return [None, None]
else:
if data["card_settings"] is None or "x_slider" not in data["card_settings"]:
slider = None
if (
data["fullscreen_settings"] is None
or "layer_slider" not in data["fullscreen_settings"]
):
slider_fullscreen = None
return [
slider,
slider_fullscreen,
]
# ---------------------------------------------------------------------------------
# ---------------------- Callbacks pour la selection de data ----------------------
# ---------------------------------------------------------------------------------
# callback pour la selection de data sur le B-scan ZX
@callback(
[
Output("heatmap-bscan-zx", "clickData"),
@ -293,6 +853,7 @@ def get_callbacks():
bscan_xy = clickData["points"][0]["x"]
return [clickData, bscan_xy]
# callback pour la selection de data sur le B-scan XY
@callback(
[
Output("heatmap-bscan-xy", "figure", allow_duplicate=True),
@ -308,7 +869,7 @@ def get_callbacks():
type="line",
x0=0 - 1,
y0=bscan_zx,
x1=dim_y,
x1=pre_volume.shape[1],
y1=bscan_zx,
line=dict(color="white", width=1),
)
@ -325,7 +886,7 @@ def get_callbacks():
x0=bscan_xy,
y0=0,
x1=bscan_xy,
y1=dim_y,
y1=pre_volume.shape[1],
line=dict(color="white", width=1),
)
@ -333,6 +894,10 @@ def get_callbacks():
return [fig, fig2, fig3]
# --------------------------------------------------------------------------------------
# ---------------------- Callbacks pour la gestion des parametres ----------------------
# --------------------------------------------------------------------------------------
@callback(
[Output("store-settings", "data"), Output("settings-apply", "n_clicks")],
[
@ -372,6 +937,8 @@ def get_callbacks():
@callback(
[
Output("crop-slider", "max"),
Output("crop-slider", "marks"),
Output("layer-slider-bscan-zx", "max"),
Output("layer-slider-bscan-zx", "marks"),
Output("layer-slider-bscan-xy", "max"),
@ -381,8 +948,8 @@ def get_callbacks():
Output("iso-slider", "marks"),
Output("y-slider", "max"),
Output("y-slider", "marks"),
Output("layer-slider-ascan-fullscreen", "max"),
Output("layer-slider-ascan-fullscreen", "marks"),
Output("layer-slider-x-ascan-fullscreen", "max"),
Output("layer-slider-x-ascan-fullscreen", "marks"),
Output("layer-slider-bscan-xy-fullscreen", "max"),
Output("layer-slider-bscan-xy-fullscreen", "marks"),
Output("layer-slider-bscan-zx-fullscreen", "max"),
@ -399,15 +966,21 @@ def get_callbacks():
)
def redef_data(data):
global volume, pre_volume, dim_x, dim_y, dim_z, X, Y, Z
volume = pre_volume[
:: data["echantillonage_x"],
:: data["echantillonage_y"],
:: data["echantillonage_z"],
]
if data["apply_sampling_everywhere"]:
volume = pre_volume[
:: data["echantillonage_x"],
:: data["echantillonage_y"],
:: data["echantillonage_z"],
]
else:
volume = pre_volume
dim_x, dim_y, dim_z = volume.shape
X, Y, Z = np.mgrid[0:dim_x, 0:dim_y, 0:dim_z]
return [
dim_y - 1,
{str(i): str(i) for i in range(1, dim_y + 1, max(1, int(dim_y / 20)))},
dim_x - 1,
{str(i): str(i) for i in range(0, dim_x, max(1, int(dim_x / 20)))},
dim_z - 1,
@ -445,6 +1018,10 @@ def get_callbacks():
"Apply",
]
# ------------------------------------------------------------------------
# ---------------------- Callbacks pour les filtres ----------------------
# ------------------------------------------------------------------------
@callback(
Output("store-filters", "data", allow_duplicate=True),
[Input("store-filters", "data"), Input("store-settings", "data")],
@ -499,6 +1076,10 @@ def get_callbacks():
]
return data
# ------------------------------------------------------------------------------------
# ---------------------- Callbacks pour la gestion des fichiers ----------------------
# ------------------------------------------------------------------------------------
@callback(
Output("store-files", "data", allow_duplicate=True),
Input("store-files", "data"),
@ -516,9 +1097,9 @@ def get_callbacks():
# Appliquer les nouveaux paramètres d'échantillonnage
volume = pre_volume[
::settings["echantillonage_x"],
::settings["echantillonage_y"],
::settings["echantillonage_z"],
:: settings["echantillonage_x"],
:: settings["echantillonage_y"],
:: settings["echantillonage_z"],
]
dim_x, dim_y, dim_z = volume.shape
X, Y, Z = np.mgrid[0:dim_x, 0:dim_y, 0:dim_z]
@ -526,9 +1107,9 @@ def get_callbacks():
# Mettre à jour les graphiques
update_3dplot(0, [0, dim_y // 2], settings, False)
update_heatmap_ascan(0, 0, False)
update_heatmap_bscan_zx(0, False)
update_heatmap_bscan_xy(0, False)
update_heatmap_ascan(0, 0, False, settings)
update_heatmap_bscan_zx(0, False, settings)
update_heatmap_bscan_xy(0, False, settings)
return None
@ -548,3 +1129,65 @@ def get_callbacks():
else:
np.save(join("Dataset/saves", filename), data_traits)
return True
# -------------------------------------------------------------------------------------
# ---------------------- Callbacks pour la gestion des données ------------------------
# -------------------------------------------------------------------------------------
@callback(
[
Output("heatmap-ascan", "figure", allow_duplicate=True),
Output("heatmap-bscan-xy", "figure", allow_duplicate=True),
Output("heatmap-bscan-zx", "figure", allow_duplicate=True),
],
Input("crop-slider", "value"),
[
State("store-bscan-zx-layer", "data"),
State("store-bscan-xy-layer", "data"),
State("store-settings", "data"),
],
prevent_initial_call=True,
)
def update_scans(layer, layer1, layer2, settings):
y_min, y_max = layer
if settings["apply_sampling_everywhere"]:
data = volume
else:
data = pre_volume
fig = px.line(
y=data[layer1 - 1, int(y_min) : int(y_max), layer2], title="A-scan"
)
fig2 = go.Figure(
data=go.Heatmap(
z=data[:, int(y_min) : int(y_max), layer2], colorscale="Jet"
)
)
fig2.add_shape(
type="line",
x0=0 - 1,
y0=layer1,
x1=y_max,
y1=layer1,
line=dict(color="white", width=1),
)
fig2.update_layout(
title="B-scan XY", autosize=True, dragmode="zoom", clickmode="event+select"
)
fig3 = px.imshow(
data[layer1 - 1, int(y_min) : int(y_max), :],
color_continuous_scale="Jet",
aspect="auto",
title="B-scan ZX",
)
fig3.add_shape(
type="line",
x0=layer2,
y0=0,
x1=layer2,
y1=y_max,
line=dict(color="white", width=1),
)
fig3.update_layout(autosize=True, dragmode="zoom", clickmode="event+select")
return [fig, fig2, fig3]

View File

@ -49,4 +49,33 @@ db.close()
timestamp_end = datetime.datetime.now()
totaltime = timestamp_end - timestamp_start
print('Done in', totaltime.microseconds / 1000, 'ms!')
print('Done in', totaltime.microseconds / 1000, 'ms!')
if input("Do you want to display the 3D volume? (y/n) ") == "y":
import plotly.graph_objects as go
# on récupère les données de la base de données pour les afficher
db = sqlite3.connect('data.sqlite')
cursor = db.cursor()
cursor.execute('SELECT * FROM volume')
rows = cursor.fetchall()
db.close()
volume = np.zeros((dim_x, dim_y, dim_z))
for row in rows:
x, y, z, value = row
volume[x, y, z] = value
fig = go.Figure(data=go.Volume(
x=X.flatten(),
y=Y.flatten(),
z=Z.flatten(),
value=volume.flatten(),
isomin=0,
isomax=volume.max(),
opacity=0.1, # needs to be small to see through all surfaces
surface_count=21, # needs to be a large number for good volume rendering
))
fig.show()
input("Press Enter to continue...")

View File

@ -1,7 +1,7 @@
import dash
from dash import dcc, html, ALL, DiskcacheManager
import dash_bootstrap_components as dbc
from os import listdir, mkdir
from os import listdir, mkdir, path
from os.path import isfile, join
import diskcache
from json import load
@ -351,7 +351,13 @@ 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"}),
dbc.Label(
id="opened-file",
style={
"marginLeft": "10px",
"padding": "0 auto",
},
),
],
className="ml-auto",
navbar=True,
@ -367,7 +373,7 @@ nav_bar = dbc.Navbar(
modal_open,
modal_save,
navmenu,
save_toast
save_toast,
],
align="center",
style={"width": "100%"},
@ -382,6 +388,7 @@ nav_bar = dbc.Navbar(
# on défini le layout de l'application
app.layout = dbc.Container(
[
dcc.Location(id="loc", refresh=False),
nav_bar,
dash.page_container,
dcc.Store(
@ -397,7 +404,12 @@ app.layout = dbc.Container(
),
dcc.Store(id="store-filters", data={}),
dcc.Store(id="store-files"),
dcc.Store(id="ascan-settings"),
dcc.Store(id="3dplot-settings"),
dcc.Store(id="bscan-zx-settings"),
dcc.Store(id="bscan-xy-settings"),
],
id="app-container",
fluid=True,
)
@ -405,4 +417,6 @@ get_callbacks()
# on lance l'application
if __name__ == "__main__":
app.run(debug=config["debug"] or False, port=config["port"] or "8051", threaded=True)
app.run(
debug=config["debug"] or False, port=config["port"] or "8051", threaded=True
)

View File

@ -74,7 +74,7 @@ layout = html.Div(
dbc.Select(
id="select-ascan-filter1",
options=[
{"label": "Transformer du Hilbert", "value": "1"},
{"label": "Transformée de Hilbert", "value": "1"},
],
value=1,
style={"margin-bottom": "15px"},

View File

@ -112,12 +112,26 @@ mesh_card = dbc.Fade(
className="card-title",
style={"textAlign": "left"},
),
dbc.Button(
html.I(className="bi bi-arrows-fullscreen"),
id="fullscreen-button-3dplot",
className="mb-3",
color="primary",
style={"marginBottom": "15px"},
html.Div(
[
dbc.Button(
html.I(className="bi bi-gear-wide"),
id="settings-button-3dplot",
className="mb-3",
color="secondary",
style={
"marginBottom": "15px",
"marginRight": "5px",
},
),
dbc.Button(
html.I(className="bi bi-arrows-fullscreen"),
id="fullscreen-button-3dplot",
className="mb-3",
color="primary",
style={"marginBottom": "15px"},
),
]
),
],
style={"display": "flex", "justifyContent": "space-between"},
@ -128,31 +142,39 @@ mesh_card = dbc.Fade(
config=config3DPlot,
style={"marginBottom": "15px"},
), # 'fig' is your 3D plotly figure
dcc.Slider(
id="iso-slider",
min=0,
max=volume.max() / 2,
value=0,
marks={
str(i): str(i)
for i in range(
0,
int(volume.max() / 2) + 1,
int(volume.max() / 20),
)
},
step=1,
html.Div(
dcc.Slider(
id="iso-slider",
min=0,
max=volume.max() / 2,
value=0,
marks={
str(i): str(i)
for i in range(
0,
int(volume.max() / 2) + 1,
int(volume.max() / 20),
)
},
step=1,
),
id="iso-slider-container",
),
dcc.RangeSlider(
id="y-slider",
min=0,
max=dim_y,
value=[0, dim_y / 2],
marks={
str(i): str(i)
for i in range(0, int(dim_y) + 1, max(1, int(dim_y / 20)))
},
step=1,
html.Div(
dcc.RangeSlider(
id="y-slider",
min=0,
max=dim_y,
value=[0, dim_y / 2],
marks={
str(i): str(i)
for i in range(
0, int(dim_y) + 1, max(1, int(dim_y / 20))
)
},
step=1,
),
id="y-slider-container",
),
dbc.Modal(
[
@ -165,38 +187,47 @@ mesh_card = dbc.Fade(
config=config3DPlot,
style={"marginBottom": "15px", "height": "90%"},
), # 'fig' is your 3D plotly figure
dcc.Slider(
id="iso-slider-fullscreen",
min=volume.min(),
max=volume.max() / 2,
value=volume.min(),
marks={
str(i): str(i)
for i in range(
int(volume.min()),
int(volume.max() / 2) + 1,
int(
(volume.max() / 2 - volume.min())
/ 20
),
)
},
step=1,
html.Div(
dcc.Slider(
id="iso-slider-fullscreen",
min=volume.min(),
max=volume.max() / 2,
value=volume.min(),
marks={
str(i): str(i)
for i in range(
int(volume.min()),
int(volume.max() / 2) + 1,
int(
(
volume.max() / 2
- volume.min()
)
/ 20
),
)
},
step=1,
),
id="iso-slider-fullscreen-container",
),
dcc.RangeSlider(
id="y-slider-fullscreen",
min=0,
max=dim_y,
value=[0, dim_y / 2],
marks={
str(i): str(i)
for i in range(
0,
int(dim_y) + 1,
max(1, int(dim_y / 50)),
)
},
step=1,
html.Div(
dcc.RangeSlider(
id="y-slider-fullscreen",
min=0,
max=dim_y,
value=[0, dim_y / 2],
marks={
str(i): str(i)
for i in range(
0,
int(dim_y) + 1,
max(1, int(dim_y / 50)),
)
},
step=1,
),
id="y-slider-fullscreen-container",
),
]
),
@ -204,7 +235,57 @@ mesh_card = dbc.Fade(
id="modal-3dplot",
fullscreen=True,
),
]
dbc.Modal(
[
dbc.ModalHeader(dbc.ModalTitle("3D plot settings")),
dbc.ModalBody(
[
html.Span("Card display: "),
dbc.Checklist(
options=[
{
"label": "Show iso slider",
"value": "iso_slider",
},
{
"label": "Show Y slider",
"value": "y_slider",
},
],
id="checklist-3dplot-card-settings",
),
html.Span("Fullscreen display: "),
dbc.Checklist(
options=[
{
"label": "Show iso slider",
"value": "iso_slider",
},
{
"label": "Show Y slider",
"value": "y_slider",
},
],
id="checklist-3dplot-fullscreen-settings",
),
]
),
dbc.ModalFooter(
[
dbc.Button(
"Save",
id="save-settings-3dplot",
className="ml-auto",
color="primary",
),
]
),
],
id="modal-settings-3dplot",
fullscreen=False,
),
],
style={"height": "610px"},
)
]
),
@ -226,12 +307,26 @@ Ascan_card = dbc.Fade(
className="card-title",
style={"textAlign": "left"},
),
dbc.Button(
html.I(className="bi bi-arrows-fullscreen"),
id="fullscreen-button-ascan",
className="mb-3",
color="primary",
style={"marginBottom": "15px"},
html.Div(
[
dbc.Button(
html.I(className="bi bi-gear-wide"),
id="settings-button-ascan",
className="mb-3",
color="secondary",
style={
"marginBottom": "15px",
"marginRight": "5px",
},
),
dbc.Button(
html.I(className="bi bi-arrows-fullscreen"),
id="fullscreen-button-ascan",
className="mb-3",
color="primary",
style={"marginBottom": "15px"},
),
]
),
],
style={"display": "flex", "justifyContent": "space-between"},
@ -241,6 +336,20 @@ Ascan_card = dbc.Fade(
config=configAScan,
style={"marginBottom": "15px"},
), # 'fig' is your 2D plotly figure
html.Div(
dcc.RangeSlider(
id="crop-slider",
min=0,
max=dim_y,
value=[0, dim_y],
marks={
str(i): str(i)
for i in range(0, dim_y + 1, max(1, int(dim_y / 20)))
},
step=1,
),
id="crop-slider-container",
),
dbc.Modal(
[
dbc.ModalHeader(dbc.ModalTitle("A-Scan")),
@ -251,18 +360,59 @@ Ascan_card = dbc.Fade(
config=configAScan,
style={"marginBottom": "15px"},
), # 'fig' is your 2D plotly figure
dcc.Slider(
id="layer-slider-ascan-fullscreen",
min=0,
max=dim_x,
value=0,
step=1,
marks={
str(i): str(i)
for i in range(
0, dim_x, max(1, int(dim_x / 50))
)
},
html.Div(
dcc.Slider(
id="layer-slider-x-ascan-fullscreen",
min=0,
max=dim_x,
value=0,
step=1,
marks={
str(i): str(i)
for i in range(
0,
dim_x,
max(1, int(dim_x / 50)),
)
},
),
id="layer-slider-x-ascan-fullscreen-container",
),
html.Div(
dcc.Slider(
id="layer-slider-z-ascan-fullscreen",
min=1,
max=dim_z,
value=1,
step=1,
marks={
str(i): str(i)
for i in range(
1,
dim_z,
max(1, int(dim_z / 20)),
)
},
),
id="layer-slider-z-ascan-fullscreen-container",
),
html.Div(
dcc.RangeSlider(
id="crop-slider-fullscreen",
min=0,
max=dim_y,
value=[0, dim_y],
marks={
str(i): str(i)
for i in range(
0,
dim_y + 1,
max(1, int(dim_y / 20)),
)
},
step=1,
),
id="crop-slider-fullscreen-container",
),
]
),
@ -270,8 +420,57 @@ Ascan_card = dbc.Fade(
id="modal-ascan",
fullscreen=True,
),
dbc.Modal(
[
dbc.ModalHeader(dbc.ModalTitle("A-Scan settings")),
dbc.ModalBody(
[
html.Span("Card display: "),
dbc.Checklist(
options=[
{
"label": "Show crop slider",
"value": "crop_slider",
},
],
id="checklist-ascan-card-settings",
),
html.Span("Fullscreen display: "),
dbc.Checklist(
options=[
{
"label": "Show layer slider X",
"value": "layer_slider_x",
},
{
"label": "Show layer slider Z",
"value": "layer_slider_z",
},
{
"label": "Show crop slider",
"value": "crop_slider",
},
],
id="checklist-ascan-fullscreen-settings",
),
]
),
dbc.ModalFooter(
[
dbc.Button(
"Save",
id="save-settings-ascan",
className="ml-auto",
color="primary",
),
]
),
],
id="modal-settings-ascan",
fullscreen=False,
),
],
style={"height": "599px"},
style={"height": "610px"},
)
],
),
@ -293,12 +492,26 @@ Bscan_card_xy = dbc.Fade(
className="card-title",
style={"textAlign": "left"},
),
dbc.Button(
html.I(className="bi bi-arrows-fullscreen"),
id="fullscreen-button-bscan-zx",
className="mb-3",
color="primary",
style={"marginBottom": "15px"},
html.Div(
[
dbc.Button(
html.I(className="bi bi-gear-wide"),
id="settings-button-bscan-zx",
className="mb-3",
color="secondary",
style={
"marginBottom": "15px",
"marginRight": "5px",
},
),
dbc.Button(
html.I(className="bi bi-arrows-fullscreen"),
id="fullscreen-button-bscan-zx",
className="mb-3",
color="primary",
style={"marginBottom": "15px"},
),
]
),
],
style={"display": "flex", "justifyContent": "space-between"},
@ -308,16 +521,19 @@ Bscan_card_xy = dbc.Fade(
config=configBScanZX,
style={"marginBottom": "15px"},
), # 'fig' is your 2D plotly figure
dcc.Slider(
id="layer-slider-bscan-zx",
min=0,
max=dim_x - 1,
value=0,
step=1,
marks={
str(i): str(i)
for i in range(0, dim_x, max(1, int(dim_x / 20)))
},
html.Div(
dcc.Slider(
id="layer-slider-bscan-zx",
min=0,
max=dim_x - 1,
value=0,
step=1,
marks={
str(i): str(i)
for i in range(0, dim_x, max(1, int(dim_x / 20)))
},
),
id="layer-slider-bscan-zx-container",
),
dbc.Modal(
[
@ -329,18 +545,23 @@ Bscan_card_xy = dbc.Fade(
config=configBScanXY,
style={"marginBottom": "15px", "height": "90%"},
), # 'fig' is your 2D plotly figure
dcc.Slider(
id="layer-slider-bscan-zx-fullscreen",
min=0,
max=dim_x,
value=0,
step=1,
marks={
str(i): str(i)
for i in range(
0, dim_x + 1, max(1, int(dim_x / 50))
)
},
html.Div(
dcc.Slider(
id="layer-slider-bscan-zx-fullscreen",
min=0,
max=dim_x,
value=0,
step=1,
marks={
str(i): str(i)
for i in range(
0,
dim_x + 1,
max(1, int(dim_x / 50)),
)
},
),
id="layer-slider-bscan-zx-fullscreen-container",
),
]
),
@ -348,6 +569,55 @@ Bscan_card_xy = dbc.Fade(
id="modal-bscan-zx",
fullscreen=True,
),
dbc.Modal(
[
dbc.ModalHeader(dbc.ModalTitle("B-Scan ZX settings")),
dbc.ModalBody(
[
html.Span("Card display: "),
dbc.Checklist(
options=[
{
"label": "Show X slider",
"value": "x_slider",
}
],
id="checklist-bscan-zx-card-settings",
),
html.Span("Fullscreen display: "),
dbc.Checklist(
options=[
{
"label": "Show X slider",
"value": "x_slider",
},
{
"label": "Show Y slider",
"value": "y_slider",
},
{
"label": "Show crop slider",
"value": "crop_slider",
},
],
id="checklist-bscan-zx-fullscreen-settings",
),
]
),
dbc.ModalFooter(
[
dbc.Button(
"Save",
id="save-settings-bscan-zx",
className="ml-auto",
color="primary",
),
]
),
],
id="modal-settings-bscan-zx",
fullscreen=False,
),
]
)
]
@ -370,12 +640,30 @@ Bscan_card_zx = dbc.Fade(
className="card-title",
style={"textAlign": "left"},
),
dbc.Button(
html.I(className="bi bi-arrows-fullscreen"),
id="fullscreen-button-bscan-xy",
className="mb-3",
color="primary",
style={"marginBottom": "15px"},
html.Div(
[
dbc.Button(
html.I(className="bi bi-gear-wide"),
id="settings-button-bscan-xy",
className="mb-3",
color="secondary",
style={
"marginBottom": "15px",
"marginRight": "5px",
},
),
dbc.Button(
html.I(className="bi bi-arrows-fullscreen"),
id="fullscreen-button-bscan-xy",
className="mb-3",
color="primary",
style={"marginBottom": "15px"},
),
],
style={
"display": "flex",
"justifyContent": "space-between",
},
),
],
style={"display": "flex", "justifyContent": "space-between"},
@ -385,16 +673,19 @@ Bscan_card_zx = dbc.Fade(
config=configBScanXY,
style={"marginBottom": "15px"},
), # 'fig' is your 2D plotly figure
dcc.Slider(
id="layer-slider-bscan-xy",
min=1,
max=dim_z - 1,
value=1,
step=1,
marks={
str(i): str(i)
for i in range(1, dim_z + 1, max(1, int(dim_z / 20)))
},
html.Div(
dcc.Slider(
id="layer-slider-bscan-xy",
min=1,
max=dim_z - 1,
value=1,
step=1,
marks={
str(i): str(i)
for i in range(1, dim_z + 1, max(1, int(dim_z / 20)))
},
),
id="layer-slider-bscan-xy-container",
),
dbc.Modal(
[
@ -406,18 +697,23 @@ Bscan_card_zx = dbc.Fade(
config=configBScanXY,
style={"marginBottom": "15px", "height": "90%"},
), # 'fig' is your 2D plotly figure
dcc.Slider(
id="layer-slider-bscan-xy-fullscreen",
min=1,
max=dim_z - 1,
value=1,
step=1,
marks={
str(i): str(i)
for i in range(
1, dim_z + 1, max(1, int(dim_z / 50))
)
},
html.Div(
dcc.Slider(
id="layer-slider-bscan-xy-fullscreen",
min=1,
max=dim_z - 1,
value=1,
step=1,
marks={
str(i): str(i)
for i in range(
1,
dim_z + 1,
max(1, int(dim_z / 50)),
)
},
),
id="layer-slider-bscan-xy-fullscreen-container",
),
],
),
@ -425,6 +721,55 @@ Bscan_card_zx = dbc.Fade(
id="modal-bscan-xy",
fullscreen=True,
),
dbc.Modal(
[
dbc.ModalHeader(dbc.ModalTitle("B-Scan XY settings")),
dbc.ModalBody(
[
html.Span("Card display: "),
dbc.Checklist(
options=[
{
"label": "Show X slider",
"value": "x_slider",
}
],
id="checklist-bscan-xy-card-settings",
),
html.Span("Fullscreen display: "),
dbc.Checklist(
options=[
{
"label": "Show X slider",
"value": "x_slider",
},
{
"label": "Show Y slider",
"value": "y_slider",
},
{
"label": "Show crop slider",
"value": "crop_slider",
},
],
id="checklist-bscan-xy-fullscreen-settings",
),
]
),
dbc.ModalFooter(
[
dbc.Button(
"Save",
id="save-settings-bscan-xy",
className="ml-auto",
color="primary",
),
]
),
],
id="modal-settings-bscan-xy",
fullscreen=False,
),
]
)
]
@ -449,4 +794,4 @@ layout = html.Div(
]
)
get_callbacks()
get_callbacks()

View File

@ -2,11 +2,10 @@ dash==2.17.0
dash_bootstrap_components==1.6.0
diskcache==5.6.3
matplotlib==3.8.4
numpy==1.26.4
numpy==2.0.0
pandas==2.2.2
plotly==5.22.0
progress==1.6
python_igraph==0.11.5
scikit_learn==1.5.0
scipy==1.13.1
tqdm==4.66.4