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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +158 -139
app.py CHANGED
@@ -1,152 +1,171 @@
1
  import streamlit as st
2
  import pandas as pd
3
  import numpy as np
4
- import plotly.graph_objects as go
 
 
 
5
 
6
- # ========== STRUTTURE TARGET =============
7
- # A scopo illustrativo: dimensioni fisse x,y,z (mm)
 
 
 
8
  strutture_target = {
9
- "Dock-Rigenerato": {
10
- "max_volume": 250,
11
- "max_area": 60,
12
- "max_lunghezza": 160,
13
- "size": (200,80,60) # L x W x H in mm
14
- },
15
- "Oggetto-Vetrina": {
16
- "max_volume": 200,
17
- "max_area": 80,
18
- "max_lunghezza": 130,
19
- "size": (150,100,80)
20
- },
21
- "Tool educativo": {
22
- "max_volume": 300,
23
- "max_area": 100,
24
- "max_lunghezza": 200,
25
- "size": (250,120,100)
26
- },
27
  }
28
 
29
- def valuta_compat(row, target):
30
  score = 0
31
- if row["volume"] <= target["max_volume"]: score += 1
32
- if row["area"] <= target["max_area"]: score += 1
33
- if row["lunghezza"] <= target["max_lunghezza"]: score += 1
34
  return score
35
 
36
- def assegna_struttura(row):
37
- punteggi = {k: valuta_compat(row, v) for k, v in strutture_target.items()}
 
38
  best = max(punteggi, key=punteggi.get)
39
  return best
40
 
41
- # ---- FUNZIONE HELPER PER COSTRUIRE I 8 VERTICI DI UN PARALLELEPIPEDO
42
- def parallelepipedo_vertices(dx, dy, dz, offset=(0,0,0)):
43
- """
44
- Ritorna la lista di (x,y,z) dei vertici di un parallelepipedo
45
- con dimensioni dx, dy, dz e offset.
46
- """
47
- ox, oy, oz = offset
48
- return [
49
- (ox, oy, oz),
50
- (ox+dx, oy, oz),
51
- (ox+dx, oy+dy, oz),
52
- (ox, oy+dy, oz),
53
- (ox, oy, oz+dz),
54
- (ox+dx, oy, oz+dz),
55
- (ox+dx, oy+dy, oz+dz),
56
- (ox, oy+dy, oz+dz),
57
- ]
58
-
59
- # Per disegnare le linee degli spigoli:
60
- edges = [
61
- (0,1),(1,2),(2,3),(3,0),
62
- (4,5),(5,6),(6,7),(7,4),
63
- (0,4),(1,5),(2,6),(3,7)
64
- ]
65
-
66
- def traccia_box(fig, verts, color="blue"):
67
- # aggiunge le linee degli edges
68
- for (i,j) in edges:
69
- x_c = [verts[i][0], verts[j][0]]
70
- y_c = [verts[i][1], verts[j][1]]
71
- z_c = [verts[i][2], verts[j][2]]
72
- fig.add_trace(go.Scatter3d(
73
- x=x_c, y=y_c, z=z_c,
74
- mode='lines',
75
- line=dict(color=color, width=5),
76
- showlegend=False
77
- ))
78
-
79
- # ========== STREAMLIT APP =============
80
- st.set_page_config(page_title="Weeko EoL - bounding box", layout="wide")
81
- st.title("✨ WEEKO – OR6.2 Demo: Riutilizzo estetico e bounding box 3D")
82
-
83
- st.markdown("**Obiettivo**: dare funzionalità estetica a un oggetto EoL, valutando compatibilità geometrica, e visualizzare box 3D dell'oggetto e della struttura target.")
84
-
85
- # 1) Carico foto EoL (opzionale)
86
- st.subheader("1) Carica una foto del componente EoL")
87
- foto_file = st.file_uploader("Foto (jpg/png)", type=["jpg","png"])
88
- if foto_file:
89
- st.image(foto_file, caption="Foto componente EoL", use_column_width=True)
90
-
91
- # 2) Inserimento dati
92
- st.subheader("2) Inserisci i dati caratteristici (volume cm³, area cm², lunghezza mm...)")
93
- volume = st.number_input("Volume (cm³)", 0.0, 500.0, 100.0)
94
- area = st.number_input("Area (cm²)", 0.0, 300.0, 50.0)
95
- lung = st.number_input("Lunghezza (mm)", 0.0, 500.0, 120.0)
96
- mat = st.selectbox("Materiale", ["Plastica", "Metallo", "PCB"])
97
- rig = st.slider("Rigidità (0=flessibile, 1=rigido)", 0.0, 1.0, 0.5)
98
- usu = st.slider("Usura (0=nuovo, 1=molto usurato)", 0.0, 1.0, 0.3)
99
-
100
- df_eol = pd.DataFrame([{
101
- "volume": volume,
102
- "area": area,
103
- "lunghezza": lung,
104
- "materiale": {"Plastica":0,"Metallo":1,"PCB":2}[mat],
105
- "rigidita": rig,
106
- "usura": usu
107
- }])
108
-
109
- # 3) Calcolo compatibilità e visualizzazione box 3D
110
- if st.button("Valuta e Visualizza"):
111
- best_struttura = assegna_struttura(df_eol.loc[0])
112
- st.success(f"Struttura più compatibile: **{best_struttura}**")
113
-
114
- # Logica bounding box per EoL
115
- # => semplificazione: supponiamo un parallelepipedo.
116
- # area ~ L * W, volume ~ L * W * H
117
- # Se ho lunghezza e area, stimo W = area / lunghezza
118
- # e H = volume / (L * W)
119
-
120
- # Attento a zero division:
121
- epsilon = 1e-3
122
- L_eol = max(lung, epsilon)
123
- W_eol = max(area / L_eol, epsilon)
124
- H_eol = max(volume / (L_eol * W_eol), epsilon)
125
-
126
- # bounding box EoL
127
- offset_eol = (0,0,0)
128
- verts_eol = parallelepipedo_vertices(L_eol, W_eol, H_eol, offset_eol)
129
-
130
- # bounding box Struttura
131
- box_target = strutture_target[best_struttura]["size"] # (L, W, H)
132
- # Mettiamola a fianco spostando offset di +L_eol + 20
133
- offset_tgt = (L_eol + 20, 0, 0)
134
- verts_target = parallelepipedo_vertices(box_target[0], box_target[1], box_target[2], offset_tgt)
135
-
136
- fig = go.Figure()
137
- # Aggiunge box EoL in blu
138
- traccia_box(fig, verts_eol, color="blue")
139
- # Aggiunge box struttura in rosso
140
- traccia_box(fig, verts_target, color="red")
141
-
142
- fig.update_layout(
143
- width=800, height=600,
144
- title="Confronto bounding box: EoL (blu) vs Struttura (rossa)",
145
- scene=dict(
146
- xaxis_title="X (mm)",
147
- yaxis_title="Y (mm)",
148
- zaxis_title="Z (mm)",
149
- )
150
- )
151
-
152
- st.plotly_chart(fig, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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")