valegro commited on
Commit
d10a21e
·
verified ·
1 Parent(s): d648346

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +134 -159
app.py CHANGED
@@ -1,171 +1,146 @@
1
  import streamlit as st
2
  import pandas as pd
3
- import numpy as np
4
- import torch
5
- import torch.nn as nn
6
- import torch.nn.functional as F
7
- from torch.utils.data import DataLoader, TensorDataset
8
 
9
- from PIL import Image
10
-
11
- # =========================
12
- # STRUTTURE TARGET (compatibilità)
13
- # =========================
14
  strutture_target = {
15
- "Dock-Rigenerato": {"max_volume": 250, "max_area": 60, "max_lunghezza": 160},
16
- "Oggetto-Vetrina": {"max_volume": 200, "max_area": 80, "max_lunghezza": 130},
17
- "Tool educativo": {"max_volume": 300, "max_area": 100, "max_lunghezza": 200},
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  }
19
 
20
- def valuta_compatibilità(row, target):
 
 
 
 
21
  score = 0
22
- if row[0] <= target["max_volume"]: score += 1
23
- if row[1] <= target["max_area"]: score += 1
24
- if row[2] <= target["max_lunghezza"]: score += 1
 
 
 
25
  return score
26
 
27
- def assegna_struttura(x):
28
- # x = [volume, area, lunghezza, materiale, rigidita, usura]
29
- punteggi = {k: valuta_compatibilità(x, v) for k,v in strutture_target.items()}
 
 
30
  best = max(punteggi, key=punteggi.get)
31
  return best
32
 
33
- # =========================
34
- # VAE DEFINIZIONE
35
- # =========================
36
- class VAE(nn.Module):
37
- def __init__(self, input_dim=6, latent_dim=2):
38
- super(VAE, self).__init__()
39
- self.fc1 = nn.Linear(input_dim, 12)
40
- self.fc21 = nn.Linear(12, latent_dim)
41
- self.fc22 = nn.Linear(12, latent_dim)
42
- self.fc3 = nn.Linear(latent_dim, 12)
43
- self.fc4 = nn.Linear(12, input_dim)
44
-
45
- def encode(self, x):
46
- h1 = F.relu(self.fc1(x))
47
- return self.fc21(h1), self.fc22(h1)
48
-
49
- def reparameterize(self, mu, logvar):
50
- std = torch.exp(0.5 * logvar)
51
- eps = torch.randn_like(std)
52
- return mu + eps * std
53
-
54
- def decode(self, z):
55
- h3 = F.relu(self.fc3(z))
56
- return self.fc4(h3)
57
-
58
- def forward(self, x):
59
- mu, logvar = self.encode(x)
60
- z = self.reparameterize(mu, logvar)
61
- return self.decode(z), mu, logvar
62
-
63
- def vae_loss(recon_x, x, mu, logvar):
64
- mse = F.mse_loss(recon_x, x, reduction='sum')
65
- kld = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())
66
- return mse + kld
67
-
68
- # ============== STREAMLIT SETUP ==============
69
- st.set_page_config(page_title="Weeko AI + Overlay", layout="wide")
70
- st.title("✨ WEEKO – OR6.3 Demo: AI dimensionale + Overlay Estetico")
71
-
72
- tabs = st.tabs(["🧠 AI Dimensionale", "🎨 Overlay Estetico"])
73
-
74
- # --------------------------------------------
75
- # TAB 1: AI Dimensionale (VAE)
76
- # --------------------------------------------
77
- with tabs[0]:
78
- st.subheader("AI Dimensionale con VAE (6 feature)")
79
-
80
- st.markdown("""
81
- **Scenario**: Abbiamo 6 feature: [volume, area, lunghezza, materiale, rigidità, usura].
82
- - Addestriamo al volo un piccolo VAE su dati simulati
83
- - Visualizziamo la ricostruzione e assegniamo a ciascun sample la struttura più compatibile
84
- """)
85
-
86
- if st.button("🏋️ Train VAE su dati dummy"):
87
- # 1) Creiamo dataset fittizio
88
- torch.manual_seed(42)
89
- n = 200
90
- volume = torch.randn(n,1)*10 + 50
91
- area = torch.randn(n,1)*5 + 20
92
- lung = torch.randn(n,1)*30 + 100
93
- materiale = torch.randint(0,3,(n,1)).float()
94
- rigidita = torch.rand(n,1)
95
- usura = torch.rand(n,1)
96
- data = torch.cat([volume, area, lung, materiale, rigidita, usura], dim=1)
97
-
98
- dataset = TensorDataset(data)
99
- loader = DataLoader(dataset, batch_size=32, shuffle=True)
100
-
101
- # 2) Inizializziamo e train
102
- vae = VAE()
103
- optimizer = torch.optim.Adam(vae.parameters(), lr=1e-3)
104
- n_epochs = 30
105
- losses = []
106
- for epoch in range(n_epochs):
107
- total_loss = 0
108
- for (batch,) in loader:
109
- optimizer.zero_grad()
110
- recon, mu, logvar = vae(batch)
111
- loss = vae_loss(recon, batch, mu, logvar)
112
- loss.backward()
113
- optimizer.step()
114
- total_loss += loss.item()
115
- losses.append(total_loss)
116
-
117
- st.success("Addestramento completato. Ultimo epoch loss: {:.2f}".format(losses[-1]))
118
-
119
- # 3) Generiamo riconstruction
120
- data_np = data.numpy()
121
- with torch.no_grad():
122
- recon, _, _ = vae(data)
123
- recon_np = recon.numpy()
124
-
125
- # 4) Assegniamo struttura a original e ricostruito
126
- df = pd.DataFrame(data_np, columns=["volume","area","lunghezza","materiale","rigidita","usura"])
127
- df["struttura_compat"] = df.apply(lambda row: assegna_struttura(row.values), axis=1)
128
-
129
- df_rec = pd.DataFrame(recon_np, columns=["volume","area","lunghezza","materiale","rigidita","usura"])
130
- df_rec["struttura_compat"] = df_rec.apply(lambda row: assegna_struttura(row.values), axis=1)
131
-
132
- st.write("### Esempio di 10 sample originali (Sx) e ricostruiti (Dx)")
133
- joined = pd.concat([
134
- df.head(10).round(2),
135
- df_rec.head(10).round(2)
136
- ], axis=1, keys=["Original","Ricostruito"])
137
- st.dataframe(joined)
138
-
139
- st.write("Struttura compatibile (originale) vs (ricostruito):")
140
- st.dataframe(pd.DataFrame({
141
- "Original": df["struttura_compat"].head(10).values,
142
- "Ricostruito": df_rec["struttura_compat"].head(10).values
143
- }))
144
-
145
- # --------------------------------------------
146
- # TAB 2: Overlay Estetico
147
- # --------------------------------------------
148
- with tabs[1]:
149
- st.subheader("Overlay Estetico: Componente EoL su Oggetto Finale")
150
-
151
- st.write("Carica due foto: (1) EoL (es. lamiera), (2) oggetto finale. Regola la trasparenza per un 'effetto wow'.")
152
-
153
- col1, col2 = st.columns(2)
154
- with col1:
155
- eol_file = st.file_uploader("Foto EoL (jpg/png)", type=["jpg","png"], key="eol_upload")
156
- with col2:
157
- final_file = st.file_uploader("Oggetto finale (jpg/png)", type=["jpg","png"], key="final_upload")
158
-
159
- if eol_file and final_file:
160
- from PIL import Image
161
- lamiera_img = Image.open(eol_file).convert("RGBA")
162
- oggetto_img = Image.open(final_file).convert("RGBA")
163
-
164
- # ridimensiona la lamiera alle dimensioni dell'oggetto
165
- lamiera_img = lamiera_img.resize(oggetto_img.size)
166
-
167
- alpha = st.slider("Trasparenza componente EoL", 0.0, 1.0, 0.5)
168
- blended = Image.blend(oggetto_img, lamiera_img, alpha)
169
- st.image(blended, caption="Overlay lamiera + oggetto finale", use_column_width=True)
170
- else:
171
- st.info("Attendi di caricare entrambe le immagini")
 
1
  import streamlit as st
2
  import pandas as pd
3
+ import plotly.graph_objects as go
 
 
 
 
4
 
5
+ # ========== STRUTTURE TARGET ==========
6
+ # Ognuna ha soglie (volume, area, lunghezza) e dimensioni LxWxH per il bounding box dimostrativo
 
 
 
7
  strutture_target = {
8
+ "Dock-Rigenerato": {
9
+ "max_volume": 250,
10
+ "max_area": 60,
11
+ "max_lunghezza": 160,
12
+ "size": (200, 80, 60) # dimensioni d'esempio (mm)
13
+ },
14
+ "Oggetto-Vetrina": {
15
+ "max_volume": 200,
16
+ "max_area": 80,
17
+ "max_lunghezza": 130,
18
+ "size": (150, 100, 80)
19
+ },
20
+ "Tool educativo": {
21
+ "max_volume": 300,
22
+ "max_area": 100,
23
+ "max_lunghezza": 200,
24
+ "size": (250, 120, 100)
25
+ },
26
  }
27
 
28
+ # ========== FUNZIONI COMPATIBILITÀ E BOUNDING BOX ==========
29
+
30
+ def valuta_compatibilita(eol, target):
31
+ # eol = dizionario {volume, area, lunghezza} (float)
32
+ # target = dict con max_volume, max_area, max_lunghezza
33
  score = 0
34
+ if eol["volume"] <= target["max_volume"]:
35
+ score += 1
36
+ if eol["area"] <= target["max_area"]:
37
+ score += 1
38
+ if eol["lunghezza"] <= target["max_lunghezza"]:
39
+ score += 1
40
  return score
41
 
42
+ def assegna_struttura(eol):
43
+ punteggi = {}
44
+ for nome, tdict in strutture_target.items():
45
+ punteggi[nome] = valuta_compatibilita(eol, tdict)
46
+ # Trova la struttura con punteggio più alto
47
  best = max(punteggi, key=punteggi.get)
48
  return best
49
 
50
+ def parallelepipedo_vertices(dx, dy, dz, offset=(0,0,0)):
51
+ ox, oy, oz = offset
52
+ return [
53
+ (ox, oy, oz),
54
+ (ox+dx, oy, oz),
55
+ (ox+dx, oy+dy, oz),
56
+ (ox, oy+dy, oz),
57
+ (ox, oy, oz+dz),
58
+ (ox+dx, oy, oz+dz),
59
+ (ox+dx, oy+dy, oz+dz),
60
+ (ox, oy+dy, oz+dz),
61
+ ]
62
+
63
+ edges = [
64
+ (0,1),(1,2),(2,3),(3,0),
65
+ (4,5),(5,6),(6,7),(7,4),
66
+ (0,4),(1,5),(2,6),(3,7)
67
+ ]
68
+
69
+ def add_box_to_figure(fig, verts, color="blue"):
70
+ for (i,j) in edges:
71
+ x_c = [verts[i][0], verts[j][0]]
72
+ y_c = [verts[i][1], verts[j][1]]
73
+ z_c = [verts[i][2], verts[j][2]]
74
+ fig.add_trace(go.Scatter3d(
75
+ x=x_c, y=y_c, z=z_c,
76
+ mode='lines',
77
+ line=dict(color=color, width=5),
78
+ showlegend=False
79
+ ))
80
+
81
+ # ========== STREAMLIT APP ==========
82
+ st.set_page_config(page_title="Weeko - EoL Valutazione Al Contrario", layout="wide")
83
+ st.title("🔧 Weeko EoL – Analisi dimensionale + bounding box 3D")
84
+
85
+ st.write("Carica una **foto** del tuo componente EoL e inserisci manualmente le sue caratteristiche. \
86
+ L'app troverà la struttura più compatibile e mostrerà un confronto 3D semplificato con bounding box.")
87
+
88
+ # 1) Carico foto EoL (opzionale)
89
+ foto = st.file_uploader("Carica una foto (opzionale)", type=["jpg","png"])
90
+ if foto:
91
+ st.image(foto, caption="Foto EoL caricata", use_column_width=True)
92
+
93
+ st.subheader("Inserisci dati EoL")
94
+ volume = st.number_input("Volume (cm³)", 0.0, 1000.0, 120.0)
95
+ area = st.number_input("Area (cm²)", 0.0, 300.0, 50.0)
96
+ lunghezza = st.number_input("Lunghezza (mm)", 0.0, 500.0, 130.0)
97
+ rigidita = st.slider("Rigidità (0=flessibile, 1=rigido)", 0.0, 1.0, 0.5)
98
+ usura = st.slider("Usura (0=nuovo, 1=molto usurato)", 0.0, 1.0, 0.3)
99
+ materiale_sc = st.selectbox("Materiale", ["Plastica","Metallo","PCB"])
100
+
101
+ # Quando clicco "Valuta"
102
+ if st.button("Valuta compatibilità e Visualizza 3D"):
103
+ eol_data = {
104
+ "volume": volume,
105
+ "area": area,
106
+ "lunghezza": lunghezza,
107
+ "materiale": {"Plastica":0,"Metallo":1,"PCB":2}[materiale_sc],
108
+ "rigidita": rigidita,
109
+ "usura": usura
110
+ }
111
+
112
+ best_struttura = assegna_struttura(eol_data)
113
+ st.success(f"La struttura più compatibile è: **{best_struttura}**")
114
+
115
+ # ---- Creiamo bounding box EoL in modo semplificato
116
+ # area ~ L * W, volume ~ L * W * H
117
+ # Se area e lunghezza => W = area / lunghezza
118
+ # => H = volume / (L * W)
119
+ # NB: zero-check
120
+ eps = 1e-6
121
+ L_eol = max(lunghezza, eps)
122
+ W_eol = max(area / L_eol, eps)
123
+ H_eol = max(volume / (L_eol * W_eol), eps)
124
+
125
+ offset_eol = (0,0,0)
126
+ verts_eol = parallelepipedo_vertices(L_eol, W_eol, H_eol, offset_eol)
127
+
128
+ # ---- bounding box della struttura target
129
+ starget = strutture_target[best_struttura]["size"] # (L, W, H)
130
+ # Mettiamo un offset di + (L_eol + 20) in X, per vederli affiancati
131
+ offset_tgt = (L_eol+20, 0, 0)
132
+ verts_tgt = parallelepipedo_vertices(starget[0], starget[1], starget[2], offset_tgt)
133
+
134
+ fig = go.Figure()
135
+ add_box_to_figure(fig, verts_eol, color="blue") # EoL box
136
+ add_box_to_figure(fig, verts_tgt, color="red") # Struttura box
137
+ fig.update_layout(
138
+ width=800, height=600,
139
+ title="Confronto EoL (blu) vs Struttura (rossa)",
140
+ scene=dict(
141
+ xaxis_title="X (mm)",
142
+ yaxis_title="Y (mm)",
143
+ zaxis_title="Z (mm)",
144
+ )
145
+ )
146
+ st.plotly_chart(fig, use_container_width=True)