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

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +207 -141
app.py CHANGED
@@ -1,146 +1,212 @@
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)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import streamlit as st
2
+ import torch
3
+ import torch.nn as nn
4
+ import torch.nn.functional as F
5
  import pandas as pd
6
+ import numpy as np
7
+ from PIL import Image
8
+
9
+ # =========================
10
+ # 1) LIBRERIA OUTPUT: possibili nuovi usi
11
+ # Ognuno ha un "mini embedding" e un'immagine di esempio
12
+ # =========================
13
+ output_libreria = {
14
+ "ArmadioAnta": {
15
+ "embedding": [1.0, 0.0], # fittizio
16
+ "descrizione": "Riutilizzo come anta armadio",
17
+ "img": "armadio.jpg" # se ce l'hai, o lascialo vuoto
18
  },
19
+ "DockRigenerato": {
20
+ "embedding": [0.5, 0.5],
21
+ "descrizione": "Base dock con componenti EoL",
22
+ "img": "dock.jpg"
 
23
  },
24
+ "OggettoVetrina": {
25
+ "embedding": [0.0, 1.0],
26
+ "descrizione": "Pannello estetico o vetrina",
27
+ "img": "vetrina.jpg"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  }
29
+ }
30
+ # In un caso reale potresti mettere molte voci, con embeddings più ricchi
31
+ # e parametri più precisi
32
+
33
+ # =========================
34
+ # 2) MODELLO AI: un MLP
35
+ # Input: [volume, area, lunghezza, spessore, usura, (materiale cod?), + output_embedding(2)]
36
+ # Output: punteggio compatibilità (float)
37
+ # =========================
38
+ class SimpleMLP(nn.Module):
39
+ def __init__(self):
40
+ super().__init__()
41
+ self.fc1 = nn.Linear(8, 16)
42
+ self.fc2 = nn.Linear(16, 8)
43
+ self.fc3 = nn.Linear(8, 1)
44
+
45
+ def forward(self, x):
46
+ x = F.relu(self.fc1(x))
47
+ x = F.relu(self.fc2(x))
48
+ x = torch.sigmoid(self.fc3(x)) # punteggio tra 0 e 1
49
+ return x
50
+
51
+ # =========================
52
+ # Salviamo il modello in session_state per poterlo usare da più sezioni
53
+ # =========================
54
+ if "model" not in st.session_state:
55
+ st.session_state["model"] = SimpleMLP()
56
+ if "trained" not in st.session_state:
57
+ st.session_state["trained"] = False
58
+
59
+ # Ottimizziamo i param in session
60
+ optimizer = torch.optim.Adam(st.session_state["model"].parameters(), lr=1e-3)
61
+
62
+ # ============== FUNZIONE DI TRAIN FAKE ================
63
+ def train_fake_model(epochs=20, batch_size=32):
64
+ """
65
+ Genera un dataset fittizio di EoL + output => score
66
+ e allena un MLP in session_state["model"].
67
+ """
68
+ # Generiamo un dataset random con 1000 esempi
69
+ # EoL -> (vol, area, lunghezza, spessore, usura, materialeCod)
70
+ # Output -> 2D embedding
71
+ # Score -> random 0..1 ma correlato un po' a "logica" fittizia
72
+ n = 1000
73
+ rng = np.random.default_rng(42)
74
+ vol = rng.normal(50,15,n)
75
+ area = rng.normal(20,5,n)
76
+ lung = rng.normal(100,30,n)
77
+ spess = rng.uniform(0.5,5,n)
78
+ usura = rng.uniform(0,1,n)
79
+ materiale = rng.integers(0,3,n).astype(float)
80
+
81
+ # Scegli output a caso
82
+ out_keys = list(output_libreria.keys())
83
+ output_idx = rng.integers(0,len(out_keys),n)
84
+ # costruiamo embedding 2D
85
+ out_emb = []
86
+ for i in output_idx:
87
+ key = out_keys[i]
88
+ out_emb.append(output_libreria[key]["embedding"])
89
+ out_emb = np.array(out_emb)
90
+
91
+ # Score fittizio
92
+ # Esempio: se volume < 200 e usura < 0.5 => punteggio +
93
+ # + un offset se output = "ArmadioAnta" e spess < 3 => punteggio alto
94
+ base_score = 0.5 * np.ones(n)
95
+ base_score -= (usura * 0.3)
96
+ base_score -= ((vol - 200)/400)
97
+ # se spess < 3 e out=Armadio => +0.2
98
+ # se out=DockRigenerato => +0.1 random
99
+ for i in range(n):
100
+ if out_keys[output_idx[i]]=="ArmadioAnta" and spess[i]<3:
101
+ base_score[i]+=0.2
102
+ if out_keys[output_idx[i]]=="DockRigenerato":
103
+ base_score[i]+=rng.uniform(0,0.1)
104
+ # bounding punteggio
105
+ base_score = np.clip(base_score,0,1)
106
+
107
+ X = np.column_stack([vol,area,lung,spess,usura,materiale,out_emb])
108
+ y = base_score.reshape(-1,1)
109
+
110
+ # Torch
111
+ X_t = torch.tensor(X, dtype=torch.float32)
112
+ y_t = torch.tensor(y, dtype=torch.float32)
113
+
114
+ dataset = torch.utils.data.TensorDataset(X_t, y_t)
115
+ loader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=True)
116
+
117
+ model = st.session_state["model"]
118
+ # reinit param
119
+ def weights_init(m):
120
+ if isinstance(m, nn.Linear):
121
+ nn.init.xavier_uniform_(m.weight)
122
+ nn.init.zeros_(m.bias)
123
+ model.apply(weights_init)
124
+
125
+ lossf = nn.MSELoss()
126
+ for ep in range(epochs):
127
+ totloss=0
128
+ for xb,yb in loader:
129
+ optimizer.zero_grad()
130
+ pred = model(xb)
131
+ loss=lossf(pred,yb)
132
+ loss.backward()
133
+ optimizer.step()
134
+ totloss+=loss.item()
135
+ st.session_state["trained"] = True
136
+ return totloss
137
+
138
+ # ============== UI Streamlit ================
139
+ st.title("WEEKO – Flusso: EoL + Output-libreria + AI + Overlay Estetico")
140
+
141
+ # TAB: Train AI + Avanzamento
142
+ tab1, tab2, tab3 = st.tabs(["1) Train AI (finto)","2) Valuta EoL + Output","3) Overlay Estetico"])
143
+
144
+ with tab1:
145
+ st.subheader("Fase 1: Train fittizio del modello AI")
146
+ st.write("Generiamo un dataset casuale simulando molte componenti EoL e possibili output.")
147
+ if st.button("🏋️ Esegui training fittizio"):
148
+ final_loss = train_fake_model(epochs=20)
149
+ st.success(f"Training completato, loss={final_loss:.4f}")
150
+ if st.session_state["trained"]:
151
+ st.info("Modello AI pronto all'uso.")
152
+ else:
153
+ st.warning("Non hai ancora addestrato il modello fittizio, i punteggi saranno casuali.")
154
+
155
+ with tab2:
156
+ st.subheader("Fase 2: Inserisci EoL e scegli un Output")
157
+
158
+ # Carico eventuale foto EoL
159
+ file_eol = st.file_uploader("Foto EoL (opzionale)", type=["jpg","png"], key="eolFile2")
160
+ if file_eol:
161
+ st.image(file_eol, caption="Componente EoL", use_column_width=True)
162
+
163
+ # Dati EoL
164
+ vol_in = st.number_input("Volume (cm³)", 0.0, 1000.0, 100.0)
165
+ area_in = st.number_input("Area (cm²)", 0.0, 500.0, 30.0)
166
+ lung_in = st.number_input("Lunghezza (mm)", 0.0, 2000.0, 120.0)
167
+ spess_in = st.number_input("Spessore (mm)", 0.5, 10.0, 2.0)
168
+ usura_in = st.slider("Usura (0=nuovo,1=molto usurato)", 0.0,1.0,0.3)
169
+ mat_in = st.selectbox("Materiale", ["Plastica","Metallo","PCB"])
170
+ mat_code = {"Plastica":0.0,"Metallo":1.0,"PCB":2.0}[mat_in]
171
+
172
+ # Scegli un output
173
+ scelte = list(output_libreria.keys())
174
+ out_sel = st.selectbox("Scegli l'output-libreria", scelte)
175
+
176
+ if st.button("Calcola punteggio AI"):
177
+ model = st.session_state["model"]
178
+ embed = output_libreria[out_sel]["embedding"] # 2D
179
+ x_np = np.array([[vol_in, area_in, lung_in, spess_in, usura_in, mat_code, embed[0], embed[1]]], dtype=np.float32)
180
+ x_t = torch.from_numpy(x_np)
181
+ with torch.no_grad():
182
+ pred = model(x_t).item()
183
+ st.info(f"Punteggio di compatibilità con '{out_sel}': {pred:.3f}")
184
+ if pred>0.7:
185
+ st.success("Ottima compatibilità! Procedi con la rigenerazione.")
186
+ elif pred>0.4:
187
+ st.warning("Compatibilità media: valuta modifiche/manuale rifinitura.")
188
+ else:
189
+ st.error("Compatibilità bassa, meglio cambiare output o scartare.")
190
+
191
+ with tab3:
192
+ st.subheader("Fase 3: Overlay Estetico")
193
+ st.write("Carica la foto EoL e la foto dell'Output finale per un effetto wow. (Puramente estetico)")
194
+
195
+ col1, col2 = st.columns(2)
196
+ with col1:
197
+ file_eol_3 = st.file_uploader("Foto EoL", type=["jpg","png"], key="uplEol3")
198
+ with col2:
199
+ file_out_3 = st.file_uploader("Foto Oggetto Finale (es. Armadio?)", type=["jpg","png"], key="uplOut3")
200
+
201
+ alpha_val = st.slider("Trasparenza EoL", 0.0,1.0,0.5)
202
+
203
+ if file_eol_3 and file_out_3:
204
+ eol_img = Image.open(file_eol_3).convert("RGBA")
205
+ out_img = Image.open(file_out_3).convert("RGBA")
206
+ # ridimensioniamo la EoL all'immagine dell'oggetto
207
+ eol_img = eol_img.resize(out_img.size)
208
+
209
+ blend = Image.blend(out_img, eol_img, alpha_val)
210
+ st.image(blend, caption="Overlay EoL + Oggetto Finale", use_column_width=True)
211
+ else:
212
+ st.info("Attendi di caricare entrambe le immagini per vedere l'overlay.")