Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -75,7 +75,6 @@ def get_spazio_attivo(main_space, alt_space):
|
|
| 75 |
def aggiorna_sliders(spazio_principale, spazio_altri):
|
| 76 |
spazio = get_spazio_attivo(spazio_principale, spazio_altri)
|
| 77 |
|
| 78 |
-
# FIX: Gestione corretta HUE (0-179) e visibilità
|
| 79 |
if spazio == "HSV":
|
| 80 |
return (gr.update(label="Tinta (H) Min", value=20, maximum=179, visible=True), gr.update(label="Tinta (H) Max", value=80, maximum=179, visible=True),
|
| 81 |
gr.update(label="Saturazione (S) Min", value=40, visible=True), gr.update(label="Saturazione (S) Max", value=255, visible=True),
|
|
@@ -116,17 +115,50 @@ def cattura_colore(image, evt: gr.SelectData, s_main, s_alt):
|
|
| 116 |
pixel_conv = converti_spazio_colore(pixel_img, spazio)[0][0]
|
| 117 |
v1, v2, v3 = [int(v) for v in pixel_conv]
|
| 118 |
|
| 119 |
-
# FIX: Limite HUE nel contagocce
|
| 120 |
max_v1 = 179 if spazio == "HSV" else 255
|
| 121 |
return (max(0, v1-25), min(max_v1, v1+25), max(0, v2-25), min(255, v2+25), max(0, v3-25), min(255, v3+25))
|
| 122 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
# ==========================================
|
| 124 |
# 2. MOTORE GEOMETRICO (FASE 1)
|
| 125 |
# ==========================================
|
| 126 |
def ordina_min_max(v1, v2): return int(min(v1, v2)), int(max(v1, v2))
|
| 127 |
|
| 128 |
def elabora_nastro(image, s_main, s_alt, c1_min, c1_max, c2_min, c2_max, c3_min, c3_max):
|
| 129 |
-
# FIX: Tolto l'output fantasma che causava il Ghost Button
|
| 130 |
if image is None: return None, None, "0", "In attesa...", None, gr.update(visible=False)
|
| 131 |
|
| 132 |
spazio = get_spazio_attivo(s_main, s_alt)
|
|
@@ -211,7 +243,7 @@ def applica_morfologia(mask, tipo, intensita):
|
|
| 211 |
return cv2.morphologyEx(m, cv2.MORPH_CLOSE, kernel)
|
| 212 |
return mask
|
| 213 |
|
| 214 |
-
def elabora_pianta(image, s_main, s_alt, c1_min, c1_max, c2_min, c2_max, c3_min, c3_max, morfo_tipo, morfo_int):
|
| 215 |
if image is None: return None, "0"
|
| 216 |
spazio = get_spazio_attivo(s_main, s_alt)
|
| 217 |
min1, max1 = ordina_min_max(c1_min, c1_max)
|
|
@@ -228,7 +260,12 @@ def elabora_pianta(image, s_main, s_alt, c1_min, c1_max, c2_min, c2_max, c3_min,
|
|
| 228 |
mask = cv2.inRange(img_conv, np.array([min1, min2, min3]), np.array([max1, max2, max3]))
|
| 229 |
|
| 230 |
mask_pulita = applica_morfologia(mask, morfo_tipo, morfo_int)
|
|
|
|
|
|
|
| 231 |
segmented = cv2.bitwise_and(image, image, mask=mask_pulita)
|
|
|
|
|
|
|
|
|
|
| 232 |
pixel_count = int(np.sum(mask_pulita == 255))
|
| 233 |
return segmented, f"{pixel_count} px"
|
| 234 |
|
|
@@ -240,7 +277,7 @@ def carica_fastsam():
|
|
| 240 |
from ultralytics import FastSAM
|
| 241 |
return FastSAM("FastSAM-s.pt")
|
| 242 |
|
| 243 |
-
def segmenta_ai_manuale(image, evt: gr.SelectData):
|
| 244 |
if image is None: return None, "Nessuna immagine"
|
| 245 |
x, y = evt.index
|
| 246 |
model = carica_fastsam()
|
|
@@ -252,64 +289,65 @@ def segmenta_ai_manuale(image, evt: gr.SelectData):
|
|
| 252 |
mask = (mask * 255).astype(np.uint8)
|
| 253 |
|
| 254 |
segmented = cv2.bitwise_and(image, image, mask=mask)
|
| 255 |
-
|
| 256 |
pixel_count = int(np.sum(mask == 255))
|
| 257 |
return segmented, f"{pixel_count} px"
|
| 258 |
return image, "Nessun oggetto trovato."
|
| 259 |
|
| 260 |
def segmenta_ai_automatico(image, quad_vertices, input_area_rif):
|
| 261 |
-
# FIX: Motore iterativo riprogettato con calcolo ExG e Grafica Avanzata
|
| 262 |
if image is None or quad_vertices is None:
|
| 263 |
return image, "Errore: Calibra il quadrilatero in Fase 1."
|
| 264 |
|
| 265 |
h, w = image.shape[:2]
|
| 266 |
-
|
| 267 |
-
# 1. Maschera del quadrilatero per annerire l'esterno
|
| 268 |
valid_area = np.zeros((h, w), dtype=np.uint8)
|
| 269 |
cv2.fillPoly(valid_area, [quad_vertices], 255)
|
| 270 |
|
| 271 |
-
#
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
segmented_mask_total = np.zeros((h, w), dtype=np.uint8)
|
| 276 |
instances = []
|
| 277 |
model = carica_fastsam()
|
| 278 |
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
|
|
|
|
| 293 |
results = model.predict(image, points=[[cx, cy]], labels=[1], device="cpu", verbose=False)
|
| 294 |
|
| 295 |
if len(results) > 0 and results[0].masks is not None:
|
| 296 |
mask = results[0].masks.data[0].cpu().numpy()
|
| 297 |
mask = cv2.resize(mask, (w, h), interpolation=cv2.INTER_NEAREST)
|
| 298 |
mask = (mask * 255).astype(np.uint8)
|
| 299 |
-
mask = cv2.bitwise_and(mask, valid_area)
|
| 300 |
|
| 301 |
-
#
|
| 302 |
-
new_pixels = cv2.bitwise_and(mask, cv2.bitwise_not(
|
| 303 |
-
if np.sum(new_pixels == 255) < 100:
|
|
|
|
|
|
|
|
|
|
| 304 |
|
| 305 |
instances.append(mask)
|
| 306 |
-
|
| 307 |
-
else:
|
|
|
|
|
|
|
| 308 |
|
| 309 |
if not instances:
|
| 310 |
-
return
|
| 311 |
|
| 312 |
-
#
|
| 313 |
img_float = image.astype(np.float32)
|
| 314 |
exg_img = 2 * img_float[:,:,1] - img_float[:,:,0] - img_float[:,:,2]
|
| 315 |
exg_img = np.clip(exg_img, 0, 255).astype(np.uint8)
|
|
@@ -320,35 +358,13 @@ def segmenta_ai_automatico(image, quad_vertices, input_area_rif):
|
|
| 320 |
if mean_exg > max_exg:
|
| 321 |
max_exg, plant_idx = mean_exg, i
|
| 322 |
|
| 323 |
-
#
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
if i == plant_idx:
|
| 331 |
-
plant_px_area = int(np.sum(mask == 255))
|
| 332 |
-
if contours:
|
| 333 |
-
c = max(contours, key=cv2.contourArea)
|
| 334 |
-
# Trova il punto più alto del contorno per agganciare la freccia senza coprire la foglia
|
| 335 |
-
topmost = tuple(c[c[:, :, 1].argmin()][0])
|
| 336 |
-
tail = (max(0, topmost[0] - 60), max(0, topmost[1] - 80)) # Coda esterna in alto a sx
|
| 337 |
-
|
| 338 |
-
# Freccia Gialla verso la pianta
|
| 339 |
-
cv2.arrowedLine(final_output, tail, topmost, (0, 255, 255), 3, tipLength=0.2)
|
| 340 |
-
|
| 341 |
-
# Testo Dati
|
| 342 |
-
text = f"Pianta, Area: {plant_px_area} px"
|
| 343 |
-
if input_area_rif and input_area_rif > 0:
|
| 344 |
-
quad_px_area = np.sum(valid_area == 255)
|
| 345 |
-
cm2 = (input_area_rif / quad_px_area) * plant_px_area
|
| 346 |
-
text += f" | {cm2:.2f} cm2"
|
| 347 |
-
|
| 348 |
-
cv2.putText(final_output, text, (max(0, tail[0]-40), max(0, tail[1]-10)),
|
| 349 |
-
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
|
| 350 |
-
|
| 351 |
-
return final_output, f"Elaborazione completata. {len(instances)} aree trovate."
|
| 352 |
|
| 353 |
# ==========================================
|
| 354 |
# 5. INTERFACCIA UTENTE (UI)
|
|
@@ -407,7 +423,6 @@ with gr.Blocks(theme=gr.themes.Default(primary_hue="green")) as app:
|
|
| 407 |
img_rif_in.select(fn=cattura_colore, inputs=[img_rif_in, sp_main_r, sp_altri_r], outputs=sliders_r)
|
| 408 |
|
| 409 |
for s in sliders_r + [sp_main_r, sp_altri_r]:
|
| 410 |
-
# FIX: Rimossa l'ultima variabile (bottone fantasma)
|
| 411 |
s.change(
|
| 412 |
fn=elabora_nastro,
|
| 413 |
inputs=[img_rif_in, sp_main_r, sp_altri_r] + sliders_r,
|
|
@@ -440,7 +455,7 @@ with gr.Blocks(theme=gr.themes.Default(primary_hue="green")) as app:
|
|
| 440 |
with gr.Row():
|
| 441 |
img_lat_in = gr.Image(label="Immagine Originale", interactive=True, height=350)
|
| 442 |
img_lat_out = gr.Image(label="Pianta Segmentata", height=350)
|
| 443 |
-
pixel_lat_out = gr.Textbox(label="
|
| 444 |
|
| 445 |
sliders_l = [l1_min, l1_max, l2_min, l2_max, l3_min, l3_max]
|
| 446 |
|
|
@@ -448,8 +463,11 @@ with gr.Blocks(theme=gr.themes.Default(primary_hue="green")) as app:
|
|
| 448 |
sp.change(fn=aggiorna_sliders, inputs=[sp_main_l, sp_altri_l], outputs=sliders_l)
|
| 449 |
img_lat_in.select(fn=cattura_colore, inputs=[img_lat_in, sp_main_l, sp_altri_l], outputs=sliders_l)
|
| 450 |
|
|
|
|
| 451 |
for s in sliders_l + [sp_main_l, sp_altri_l, morfo_tipo, morfo_int]:
|
| 452 |
-
s.change(fn=elabora_pianta,
|
|
|
|
|
|
|
| 453 |
|
| 454 |
# --- TAB 3: AI FASTSAM ---
|
| 455 |
with gr.Tab("🧠 Fase 3: AI (FastSAM)"):
|
|
@@ -460,11 +478,10 @@ with gr.Blocks(theme=gr.themes.Default(primary_hue="green")) as app:
|
|
| 460 |
with gr.Row():
|
| 461 |
img_ai_in = gr.Image(label="Clicca sull'oggetto", interactive=True, height=350)
|
| 462 |
img_ai_out = gr.Image(label="Risultato Rete Neurale", height=350)
|
| 463 |
-
pixel_ai_out = gr.Textbox(label="
|
| 464 |
|
| 465 |
-
img_ai_in.select(fn=segmenta_ai_manuale, inputs=[img_ai_in], outputs=[img_ai_out, pixel_ai_out])
|
| 466 |
|
| 467 |
-
# L'evento Auto-SAM ora riceve anche input_area_rif per il calcolo in cm2
|
| 468 |
btn_auto_sam.click(fn=segmenta_ai_automatico, inputs=[img_ai_in, quad_state, input_area_rif], outputs=[img_ai_out, pixel_ai_out])
|
| 469 |
|
| 470 |
img_rif_in.change(lambda: gr.update(visible=False), None, btn_auto_sam)
|
|
|
|
| 75 |
def aggiorna_sliders(spazio_principale, spazio_altri):
|
| 76 |
spazio = get_spazio_attivo(spazio_principale, spazio_altri)
|
| 77 |
|
|
|
|
| 78 |
if spazio == "HSV":
|
| 79 |
return (gr.update(label="Tinta (H) Min", value=20, maximum=179, visible=True), gr.update(label="Tinta (H) Max", value=80, maximum=179, visible=True),
|
| 80 |
gr.update(label="Saturazione (S) Min", value=40, visible=True), gr.update(label="Saturazione (S) Max", value=255, visible=True),
|
|
|
|
| 115 |
pixel_conv = converti_spazio_colore(pixel_img, spazio)[0][0]
|
| 116 |
v1, v2, v3 = [int(v) for v in pixel_conv]
|
| 117 |
|
|
|
|
| 118 |
max_v1 = 179 if spazio == "HSV" else 255
|
| 119 |
return (max(0, v1-25), min(max_v1, v1+25), max(0, v2-25), min(255, v2+25), max(0, v3-25), min(255, v3+25))
|
| 120 |
|
| 121 |
+
def disegna_etichetta_pianta(img, mask, quad_vertices, input_area_rif):
|
| 122 |
+
"""Funzione universale per disegnare contorni, freccia ed etichetta testuale."""
|
| 123 |
+
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
| 124 |
+
if not contours: return img
|
| 125 |
+
|
| 126 |
+
# Bordo Magenta
|
| 127 |
+
cv2.drawContours(img, contours, -1, (255, 0, 255), 2)
|
| 128 |
+
|
| 129 |
+
c = max(contours, key=cv2.contourArea)
|
| 130 |
+
topmost = tuple(c[c[:, :, 1].argmin()][0])
|
| 131 |
+
|
| 132 |
+
# Calcola coordinate coda della freccia (in alto a sinistra rispetto al top)
|
| 133 |
+
tail = (max(20, topmost[0] - 120), max(40, topmost[1] - 80))
|
| 134 |
+
cv2.arrowedLine(img, tail, topmost, (0, 255, 255), 3, tipLength=0.2)
|
| 135 |
+
|
| 136 |
+
px_area = int(np.sum(mask == 255))
|
| 137 |
+
text = f"Pianta: {px_area} px"
|
| 138 |
+
|
| 139 |
+
# Se abbiamo il quadrilatero e l'area di riferimento, calcola in cm2
|
| 140 |
+
if quad_vertices is not None and input_area_rif and input_area_rif > 0:
|
| 141 |
+
valid_area = np.zeros(img.shape[:2], dtype=np.uint8)
|
| 142 |
+
cv2.fillPoly(valid_area, [quad_vertices], 255)
|
| 143 |
+
quad_px_area = np.sum(valid_area == 255)
|
| 144 |
+
if quad_px_area > 0:
|
| 145 |
+
cm2 = (input_area_rif / quad_px_area) * px_area
|
| 146 |
+
text += f" | {cm2:.2f} cm2"
|
| 147 |
+
|
| 148 |
+
text_pos = (max(10, tail[0] - 50), max(20, tail[1] - 15))
|
| 149 |
+
|
| 150 |
+
# Testo con Outline nero per massima leggibilità
|
| 151 |
+
cv2.putText(img, text, text_pos, cv2.FONT_HERSHEY_SIMPLEX, 1.1, (0, 0, 0), 6)
|
| 152 |
+
cv2.putText(img, text, text_pos, cv2.FONT_HERSHEY_SIMPLEX, 1.1, (0, 255, 255), 3)
|
| 153 |
+
|
| 154 |
+
return img
|
| 155 |
+
|
| 156 |
# ==========================================
|
| 157 |
# 2. MOTORE GEOMETRICO (FASE 1)
|
| 158 |
# ==========================================
|
| 159 |
def ordina_min_max(v1, v2): return int(min(v1, v2)), int(max(v1, v2))
|
| 160 |
|
| 161 |
def elabora_nastro(image, s_main, s_alt, c1_min, c1_max, c2_min, c2_max, c3_min, c3_max):
|
|
|
|
| 162 |
if image is None: return None, None, "0", "In attesa...", None, gr.update(visible=False)
|
| 163 |
|
| 164 |
spazio = get_spazio_attivo(s_main, s_alt)
|
|
|
|
| 243 |
return cv2.morphologyEx(m, cv2.MORPH_CLOSE, kernel)
|
| 244 |
return mask
|
| 245 |
|
| 246 |
+
def elabora_pianta(image, s_main, s_alt, c1_min, c1_max, c2_min, c2_max, c3_min, c3_max, morfo_tipo, morfo_int, quad_state, input_area_rif):
|
| 247 |
if image is None: return None, "0"
|
| 248 |
spazio = get_spazio_attivo(s_main, s_alt)
|
| 249 |
min1, max1 = ordina_min_max(c1_min, c1_max)
|
|
|
|
| 260 |
mask = cv2.inRange(img_conv, np.array([min1, min2, min3]), np.array([max1, max2, max3]))
|
| 261 |
|
| 262 |
mask_pulita = applica_morfologia(mask, morfo_tipo, morfo_int)
|
| 263 |
+
|
| 264 |
+
# Se abbiamo isolato correttamente un oggetto e abbiamo dati, mettiamo l'etichetta grossa
|
| 265 |
segmented = cv2.bitwise_and(image, image, mask=mask_pulita)
|
| 266 |
+
if np.sum(mask_pulita == 255) > 500:
|
| 267 |
+
segmented = disegna_etichetta_pianta(segmented, mask_pulita, quad_state, input_area_rif)
|
| 268 |
+
|
| 269 |
pixel_count = int(np.sum(mask_pulita == 255))
|
| 270 |
return segmented, f"{pixel_count} px"
|
| 271 |
|
|
|
|
| 277 |
from ultralytics import FastSAM
|
| 278 |
return FastSAM("FastSAM-s.pt")
|
| 279 |
|
| 280 |
+
def segmenta_ai_manuale(image, evt: gr.SelectData, quad_vertices, input_area_rif):
|
| 281 |
if image is None: return None, "Nessuna immagine"
|
| 282 |
x, y = evt.index
|
| 283 |
model = carica_fastsam()
|
|
|
|
| 289 |
mask = (mask * 255).astype(np.uint8)
|
| 290 |
|
| 291 |
segmented = cv2.bitwise_and(image, image, mask=mask)
|
| 292 |
+
segmented = disegna_etichetta_pianta(segmented, mask, quad_vertices, input_area_rif)
|
| 293 |
pixel_count = int(np.sum(mask == 255))
|
| 294 |
return segmented, f"{pixel_count} px"
|
| 295 |
return image, "Nessun oggetto trovato."
|
| 296 |
|
| 297 |
def segmenta_ai_automatico(image, quad_vertices, input_area_rif):
|
|
|
|
| 298 |
if image is None or quad_vertices is None:
|
| 299 |
return image, "Errore: Calibra il quadrilatero in Fase 1."
|
| 300 |
|
| 301 |
h, w = image.shape[:2]
|
|
|
|
|
|
|
| 302 |
valid_area = np.zeros((h, w), dtype=np.uint8)
|
| 303 |
cv2.fillPoly(valid_area, [quad_vertices], 255)
|
| 304 |
|
| 305 |
+
# exclude_mask tiene traccia di tutto ciò che NON deve essere cliccato
|
| 306 |
+
exclude_mask = cv2.bitwise_not(valid_area)
|
| 307 |
+
|
|
|
|
|
|
|
| 308 |
instances = []
|
| 309 |
model = carica_fastsam()
|
| 310 |
|
| 311 |
+
for step in range(5):
|
| 312 |
+
if step == 0:
|
| 313 |
+
# Step 1: Click esatto nel baricentro del nastro
|
| 314 |
+
M = cv2.moments(valid_area)
|
| 315 |
+
cx, cy = int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"])
|
| 316 |
+
else:
|
| 317 |
+
# Step 2-5: Distance Transform per trovare il "Polo dell'inaccessibilità"
|
| 318 |
+
allowed_area = cv2.bitwise_not(exclude_mask)
|
| 319 |
+
dist = cv2.distanceTransform(allowed_area, cv2.DIST_L2, 5)
|
| 320 |
+
_, max_val, _, max_loc = cv2.minMaxLoc(dist)
|
| 321 |
+
|
| 322 |
+
if max_val < 5: break # Se il punto più lontano è a meno di 5 pixel dal bordo, abbiamo esplorato tutto
|
| 323 |
+
cx, cy = max_loc
|
| 324 |
|
| 325 |
+
# Inferenza
|
| 326 |
results = model.predict(image, points=[[cx, cy]], labels=[1], device="cpu", verbose=False)
|
| 327 |
|
| 328 |
if len(results) > 0 and results[0].masks is not None:
|
| 329 |
mask = results[0].masks.data[0].cpu().numpy()
|
| 330 |
mask = cv2.resize(mask, (w, h), interpolation=cv2.INTER_NEAREST)
|
| 331 |
mask = (mask * 255).astype(np.uint8)
|
| 332 |
+
mask = cv2.bitwise_and(mask, valid_area)
|
| 333 |
|
| 334 |
+
# Controllo anti-loop infinito
|
| 335 |
+
new_pixels = cv2.bitwise_and(mask, cv2.bitwise_not(exclude_mask))
|
| 336 |
+
if np.sum(new_pixels == 255) < 100:
|
| 337 |
+
# Se la maschera non porta nulla di nuovo, marca il punto esplorato e riprova
|
| 338 |
+
cv2.circle(exclude_mask, (cx, cy), 20, 255, -1)
|
| 339 |
+
continue
|
| 340 |
|
| 341 |
instances.append(mask)
|
| 342 |
+
exclude_mask = cv2.bitwise_or(exclude_mask, mask) # Aggiungi la foglia alle zone da non cliccare
|
| 343 |
+
else:
|
| 344 |
+
# Se SAM fallisce su quel punto, non cliccarlo più
|
| 345 |
+
cv2.circle(exclude_mask, (cx, cy), 20, 255, -1)
|
| 346 |
|
| 347 |
if not instances:
|
| 348 |
+
return cv2.bitwise_and(image, image, mask=valid_area), "Nessuna segmentazione AI riuscita."
|
| 349 |
|
| 350 |
+
# Seleziona la vera pianta usando l'ExG medio
|
| 351 |
img_float = image.astype(np.float32)
|
| 352 |
exg_img = 2 * img_float[:,:,1] - img_float[:,:,0] - img_float[:,:,2]
|
| 353 |
exg_img = np.clip(exg_img, 0, 255).astype(np.uint8)
|
|
|
|
| 358 |
if mean_exg > max_exg:
|
| 359 |
max_exg, plant_idx = mean_exg, i
|
| 360 |
|
| 361 |
+
# Produci l'output ESCLUSIVO per la pianta vincitrice
|
| 362 |
+
best_mask = instances[plant_idx]
|
| 363 |
+
final_output = cv2.bitwise_and(image, image, mask=best_mask)
|
| 364 |
+
final_output = disegna_etichetta_pianta(final_output, best_mask, quad_vertices, input_area_rif)
|
| 365 |
+
|
| 366 |
+
final_px = int(np.sum(best_mask == 255))
|
| 367 |
+
return final_output, f"Esplorazione AI completata. Pianta isolata ({final_px} px)."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 368 |
|
| 369 |
# ==========================================
|
| 370 |
# 5. INTERFACCIA UTENTE (UI)
|
|
|
|
| 423 |
img_rif_in.select(fn=cattura_colore, inputs=[img_rif_in, sp_main_r, sp_altri_r], outputs=sliders_r)
|
| 424 |
|
| 425 |
for s in sliders_r + [sp_main_r, sp_altri_r]:
|
|
|
|
| 426 |
s.change(
|
| 427 |
fn=elabora_nastro,
|
| 428 |
inputs=[img_rif_in, sp_main_r, sp_altri_r] + sliders_r,
|
|
|
|
| 455 |
with gr.Row():
|
| 456 |
img_lat_in = gr.Image(label="Immagine Originale", interactive=True, height=350)
|
| 457 |
img_lat_out = gr.Image(label="Pianta Segmentata", height=350)
|
| 458 |
+
pixel_lat_out = gr.Textbox(label="Dati Segmentazione")
|
| 459 |
|
| 460 |
sliders_l = [l1_min, l1_max, l2_min, l2_max, l3_min, l3_max]
|
| 461 |
|
|
|
|
| 463 |
sp.change(fn=aggiorna_sliders, inputs=[sp_main_l, sp_altri_l], outputs=sliders_l)
|
| 464 |
img_lat_in.select(fn=cattura_colore, inputs=[img_lat_in, sp_main_l, sp_altri_l], outputs=sliders_l)
|
| 465 |
|
| 466 |
+
# Aggiunti quad_state e input_area_rif per generare l'etichetta in cm2
|
| 467 |
for s in sliders_l + [sp_main_l, sp_altri_l, morfo_tipo, morfo_int]:
|
| 468 |
+
s.change(fn=elabora_pianta,
|
| 469 |
+
inputs=[img_lat_in, sp_main_l, sp_altri_l] + sliders_l + [morfo_tipo, morfo_int, quad_state, input_area_rif],
|
| 470 |
+
outputs=[img_lat_out, pixel_lat_out])
|
| 471 |
|
| 472 |
# --- TAB 3: AI FASTSAM ---
|
| 473 |
with gr.Tab("🧠 Fase 3: AI (FastSAM)"):
|
|
|
|
| 478 |
with gr.Row():
|
| 479 |
img_ai_in = gr.Image(label="Clicca sull'oggetto", interactive=True, height=350)
|
| 480 |
img_ai_out = gr.Image(label="Risultato Rete Neurale", height=350)
|
| 481 |
+
pixel_ai_out = gr.Textbox(label="Dati Segmentazione AI")
|
| 482 |
|
| 483 |
+
img_ai_in.select(fn=segmenta_ai_manuale, inputs=[img_ai_in, quad_state, input_area_rif], outputs=[img_ai_out, pixel_ai_out])
|
| 484 |
|
|
|
|
| 485 |
btn_auto_sam.click(fn=segmenta_ai_automatico, inputs=[img_ai_in, quad_state, input_area_rif], outputs=[img_ai_out, pixel_ai_out])
|
| 486 |
|
| 487 |
img_rif_in.change(lambda: gr.update(visible=False), None, btn_auto_sam)
|