Ronny56 commited on
Commit
cddfd6c
·
verified ·
1 Parent(s): 3cbb007

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +160 -101
app.py CHANGED
@@ -1,21 +1,84 @@
1
  import gradio as gr
2
- from ultralytics import YOLO, SAM
3
  import numpy as np
4
  import cv2
5
 
6
- # Carica modelli
7
- print("Caricamento modelli...")
8
  detector = YOLO('best.pt')
9
- segmenter = SAM('mobile_sam.pt')
10
- print("Modelli caricati!")
11
 
12
- def detect_and_segment_balloons(image, confidence):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  if image is None:
14
  return None, {"error": "Nessuna immagine"}
15
 
16
  print(f"1. Rilevamento balloon con confidenza: {confidence}")
17
 
18
- # Step 1: Rileva balloon con YOLO
19
  detection_results = detector(image, conf=confidence, verbose=False)
20
 
21
  output = {
@@ -34,99 +97,89 @@ def detect_and_segment_balloons(image, confidence):
34
  annotated = image.copy()
35
  h, w = image.shape[:2]
36
 
37
- # Step 2: Per ogni balloon, usa SAM per segmentazione precisa
38
  for i in range(len(detection_results[0].boxes)):
39
  box = detection_results[0].boxes.xyxy[i].cpu().numpy()
40
  conf = float(detection_results[0].boxes.conf[i].cpu().numpy())
41
 
42
- print(f"3. Segmentazione balloon {i+1}...")
 
 
 
 
 
 
 
 
 
43
 
44
- try:
45
- # Usa SAM con il bounding box come prompt
46
- seg_results = segmenter(image, bboxes=[box], verbose=False)
47
 
48
- detection_data = {
49
- 'balloon_id': i + 1,
50
- 'box': {
51
- 'x1': int(box[0]),
52
- 'y1': int(box[1]),
53
- 'x2': int(box[2]),
54
- 'y2': int(box[3])
55
- },
56
- 'confidence': round(conf, 3)
57
- }
58
-
59
- # Estrai la mask precisa da SAM
60
- if seg_results[0].masks is not None and len(seg_results[0].masks.data) > 0:
61
- # Ottieni mask e converti tipo PRIMA di ridimensionare
62
- mask = seg_results[0].masks.data[0].cpu().numpy()
63
- mask = mask.astype(np.float32) # ✅ Converti a float32
64
-
65
- # Ridimensiona alle dimensioni originali
66
- mask_resized = cv2.resize(mask, (w, h), interpolation=cv2.INTER_LINEAR)
67
-
68
- # Converti in mask binaria (0/255)
69
- mask_binary = (mask_resized > 0.5).astype(np.uint8)
70
- mask_uint8 = mask_binary * 255
71
-
72
- # Trova contorno del balloon
73
- contours, _ = cv2.findContours(mask_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
74
 
75
- if len(contours) > 0:
76
- # Prendi il contorno più grande
77
- largest_contour = max(contours, key=cv2.contourArea)
78
-
79
- # Converti contorno in lista di punti
80
- polygon = largest_contour.reshape(-1, 2).tolist()
81
- detection_data['polygon'] = polygon
82
- detection_data['num_points'] = len(polygon)
83
-
84
- # ✅ MASK COME BACCHETTA MAGICA (0/255)
85
- detection_data['mask'] = mask_uint8.tolist()
86
- detection_data['mask_shape'] = [h, w]
87
-
88
- # Disegna contorno verde sull'immagine
89
- cv2.drawContours(annotated, [largest_contour], -1, (0, 255, 0), 2)
90
 
91
- # Aggiungi etichetta
92
- x, y = int(box[0]), int(box[1]) - 10
93
- cv2.putText(annotated, f"B{i+1}", (x, y),
94
- cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
95
-
96
- detection_data['has_mask'] = True
97
- print(f" ✅ Balloon {i+1} segmentato: {len(polygon)} punti")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  else:
99
  detection_data['has_mask'] = False
100
- print(f" ⚠️ Balloon {i+1}: nessun contorno trovato")
101
- else:
 
 
102
  detection_data['has_mask'] = False
103
- print(f" ⚠️ Balloon {i+1}: SAM non ha prodotto mask")
104
-
105
- output['detections'].append(detection_data)
106
-
107
- except Exception as e:
108
- print(f" ❌ Errore segmentazione balloon {i+1}: {str(e)}")
109
- # Aggiungi comunque detection senza mask
110
- output['detections'].append({
111
- 'balloon_id': i + 1,
112
- 'box': {
113
- 'x1': int(box[0]),
114
- 'y1': int(box[1]),
115
- 'x2': int(box[2]),
116
- 'y2': int(box[3])
117
- },
118
- 'confidence': round(conf, 3),
119
- 'has_mask': False,
120
- 'error': str(e)
121
- })
122
 
123
  print(f"4. Completato! {output['num_balloons']} balloon processati")
124
  return annotated, output
125
 
126
  # Interface Gradio
127
- with gr.Blocks(title="Balloon Segmentation") as demo:
128
- gr.Markdown("# 🎈 Segmentazione Precisa Balloon")
129
- gr.Markdown("**Rileva balloon e crea mask precise pixel-per-pixel (come bacchetta magica)**")
130
 
131
  with gr.Row():
132
  with gr.Column():
@@ -138,36 +191,42 @@ with gr.Blocks(title="Balloon Segmentation") as demo:
138
  step=0.05,
139
  label="🎯 Confidenza Detection"
140
  )
141
- segment_btn = gr.Button("✂️ Segmenta Balloon", variant="primary", size="lg")
 
 
 
 
142
 
143
  with gr.Column():
144
- output_image = gr.Image(label="✅ Balloon Segmentati (contorni verdi)")
145
- output_json = gr.JSON(label="📊 Dati con Mask Precise")
146
 
147
  gr.Markdown("""
148
  ### 📖 Formato Output per Ogni Balloon:
149
- - **mask**: Array 2D (0/255) - **USA QUESTO come selezione!**
150
- - **polygon**: Lista punti [x, y] del contorno
151
- - **box**: Rettangolo {x1, y1, x2, y2}
152
  - **mask_shape**: [altezza, larghezza] della mask
153
 
154
- ### 🎯 Come Usare nella Tua App React:
155
- La **mask** è identica alla selezione della bacchetta magica:
156
- - Pixel **255** = dentro il balloon (selezionato - bianco)
157
- - Pixel **0** = fuori dal balloon (non selezionato - nero)
158
 
159
- ### ⚙️ Note Tecniche:
160
- - **Detection**: YOLO trova i balloon (veloce)
161
- - **Segmentation**: SAM crea mask precise (più lento ma accurato)
162
- - Su CPU può richiedere 10-20 secondi per immagine
 
163
  """)
164
 
165
- segment_btn.click(
166
  fn=detect_and_segment_balloons,
167
- inputs=[input_image, confidence],
168
  outputs=[output_image, output_json]
169
  )
170
 
171
- print("✅ App di segmentazione avviata!")
172
  demo.launch()
173
 
 
 
1
  import gradio as gr
2
+ from ultralytics import YOLO
3
  import numpy as np
4
  import cv2
5
 
6
+ # Carica il modello detection
7
+ print("Caricamento modello...")
8
  detector = YOLO('best.pt')
9
+ print("Modello caricato!")
 
10
 
11
+ def get_inpaint_bboxes(xyxy, img):
12
+ """
13
+ Versione semplificata di get_inpaint_bboxes da comic-translate.
14
+ Crea mask precise del balloon usando image processing.
15
+ """
16
+ x1, y1, x2, y2 = [int(coord) for coord in xyxy]
17
+
18
+ # Estrai la regione del balloon
19
+ h, w = img.shape[:2]
20
+ x1 = max(0, x1)
21
+ y1 = max(0, y1)
22
+ x2 = min(w, x2)
23
+ y2 = min(h, y2)
24
+
25
+ balloon_region = img[y1:y2, x1:x2]
26
+
27
+ if balloon_region.size == 0:
28
+ return None
29
+
30
+ # Converti in grayscale
31
+ if len(balloon_region.shape) == 3:
32
+ gray = cv2.cvtColor(balloon_region, cv2.COLOR_RGB2GRAY)
33
+ else:
34
+ gray = balloon_region
35
+
36
+ # Threshold per isolare il testo (di solito nero su bianco)
37
+ # I balloon sono solitamente bianchi/chiari, il testo scuro
38
+ _, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
39
+
40
+ # Inverti se necessario (vogliamo testo = bianco, sfondo = nero)
41
+ # Conta pixel bianchi e neri per decidere
42
+ white_pixels = np.sum(binary == 255)
43
+ black_pixels = np.sum(binary == 0)
44
+
45
+ if white_pixels > black_pixels:
46
+ # Più bianco che nero = balloon chiaro con testo scuro
47
+ binary = cv2.bitwise_not(binary)
48
+
49
+ # Dilata leggermente per unire caratteri vicini
50
+ kernel = np.ones((3, 3), np.uint8)
51
+ dilated = cv2.dilate(binary, kernel, iterations=2)
52
+
53
+ # Trova contorni delle aree di testo
54
+ contours, _ = cv2.findContours(dilated, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
55
+
56
+ # Crea mask finale
57
+ mask = np.zeros_like(gray)
58
+
59
+ # Riempi tutti i contorni trovati
60
+ for contour in contours:
61
+ area = cv2.contourArea(contour)
62
+ # Filtra contorni troppo piccoli (rumore)
63
+ if area > 20: # Minimo 20 pixel
64
+ cv2.drawContours(mask, [contour], -1, 255, -1)
65
+
66
+ # Crea mask full-size dell'immagine originale
67
+ full_mask = np.zeros((h, w), dtype=np.uint8)
68
+ full_mask[y1:y2, x1:x2] = mask
69
+
70
+ return full_mask
71
+
72
+ def detect_and_segment_balloons(image, confidence, use_segmentation):
73
+ """
74
+ Rileva balloon e opzionalmente crea mask di segmentazione.
75
+ """
76
  if image is None:
77
  return None, {"error": "Nessuna immagine"}
78
 
79
  print(f"1. Rilevamento balloon con confidenza: {confidence}")
80
 
81
+ # Step 1: Detection con YOLO
82
  detection_results = detector(image, conf=confidence, verbose=False)
83
 
84
  output = {
 
97
  annotated = image.copy()
98
  h, w = image.shape[:2]
99
 
100
+ # Step 2: Per ogni balloon, crea mask di segmentazione
101
  for i in range(len(detection_results[0].boxes)):
102
  box = detection_results[0].boxes.xyxy[i].cpu().numpy()
103
  conf = float(detection_results[0].boxes.conf[i].cpu().numpy())
104
 
105
+ detection_data = {
106
+ 'balloon_id': i + 1,
107
+ 'box': {
108
+ 'x1': int(box[0]),
109
+ 'y1': int(box[1]),
110
+ 'x2': int(box[2]),
111
+ 'y2': int(box[3])
112
+ },
113
+ 'confidence': round(conf, 3)
114
+ }
115
 
116
+ if use_segmentation:
117
+ print(f"3. Segmentazione balloon {i+1}...")
 
118
 
119
+ try:
120
+ # Usa get_inpaint_bboxes per creare la mask
121
+ mask = get_inpaint_bboxes(box, image)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
+ if mask is not None and mask.any():
124
+ # Trova contorno del balloon dalla mask
125
+ contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
 
 
 
 
 
 
 
 
 
 
 
 
126
 
127
+ if len(contours) > 0:
128
+ # Prendi tutti i contorni (potrebbero essere più righe di testo)
129
+ all_polygons = []
130
+ for contour in contours:
131
+ if cv2.contourArea(contour) > 20: # Filtra rumore
132
+ polygon = contour.reshape(-1, 2).tolist()
133
+ all_polygons.append(polygon)
134
+
135
+ detection_data['polygons'] = all_polygons
136
+ detection_data['num_polygons'] = len(all_polygons)
137
+
138
+ # ✅ MASK COME BACCHETTA MAGICA (0/255)
139
+ detection_data['mask'] = mask.tolist()
140
+ detection_data['mask_shape'] = [h, w]
141
+
142
+ # Disegna contorni sull'immagine
143
+ cv2.drawContours(annotated, contours, -1, (0, 255, 0), 2)
144
+
145
+ # Aggiungi etichetta
146
+ x, y = int(box[0]), int(box[1]) - 10
147
+ cv2.putText(annotated, f"B{i+1}", (x, y),
148
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
149
+
150
+ detection_data['has_mask'] = True
151
+ print(f" ✅ Balloon {i+1} segmentato: {len(all_polygons)} poligoni")
152
+ else:
153
+ detection_data['has_mask'] = False
154
+ print(f" ⚠️ Balloon {i+1}: nessun contorno trovato")
155
  else:
156
  detection_data['has_mask'] = False
157
+ print(f" ⚠️ Balloon {i+1}: mask vuota")
158
+
159
+ except Exception as e:
160
+ print(f" ❌ Errore segmentazione balloon {i+1}: {str(e)}")
161
  detection_data['has_mask'] = False
162
+ detection_data['error'] = str(e)
163
+ else:
164
+ # Solo detection, niente segmentation
165
+ detection_data['has_mask'] = False
166
+ # Disegna solo bounding box
167
+ cv2.rectangle(annotated,
168
+ (int(box[0]), int(box[1])),
169
+ (int(box[2]), int(box[3])),
170
+ (255, 0, 0), 2)
171
+ cv2.putText(annotated, f"B{i+1}", (int(box[0]), int(box[1]) - 10),
172
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 2)
173
+
174
+ output['detections'].append(detection_data)
 
 
 
 
 
 
175
 
176
  print(f"4. Completato! {output['num_balloons']} balloon processati")
177
  return annotated, output
178
 
179
  # Interface Gradio
180
+ with gr.Blocks(title="Balloon Detection & Segmentation") as demo:
181
+ gr.Markdown("# 🎈 Rilevamento e Segmentazione Balloon (stile comic-translate)")
182
+ gr.Markdown("**Sistema leggero senza SAM - usa image processing per creare mask precise**")
183
 
184
  with gr.Row():
185
  with gr.Column():
 
191
  step=0.05,
192
  label="🎯 Confidenza Detection"
193
  )
194
+ use_segmentation = gr.Checkbox(
195
+ value=True,
196
+ label="✂️ Abilita Segmentazione (crea mask precise)"
197
+ )
198
+ process_btn = gr.Button("🔍 Processa", variant="primary", size="lg")
199
 
200
  with gr.Column():
201
+ output_image = gr.Image(label="✅ Risultato")
202
+ output_json = gr.JSON(label="📊 Dati Output")
203
 
204
  gr.Markdown("""
205
  ### 📖 Formato Output per Ogni Balloon:
206
+ - **box**: Rettangolo di detection {x1, y1, x2, y2}
207
+ - **mask**: Array 2D (0/255) - **mask precisa del testo** (come bacchetta magica!)
208
+ - **polygons**: Lista di poligoni che descrivono le aree di testo
209
  - **mask_shape**: [altezza, larghezza] della mask
210
 
211
+ ### 🎯 Come Funziona:
212
+ 1. **Detection**: YOLO trova i balloon (veloce, su CPU)
213
+ 2. **Segmentation**: Image processing crea mask del testo (velocissimo!)
214
+ 3. Nessun modello pesante come SAM = **molto più veloce**
215
 
216
+ ### 💡 Differenza con SAM:
217
+ - **Molto più veloce** (< 1 secondo vs 15-20 secondi)
218
+ - **Funziona bene su CPU** gratuita
219
+ - ⚠️ Segmenta il **testo** dentro il balloon, non il contorno del balloon
220
+ - Perfetto per **inpainting** (rimuovere testo)
221
  """)
222
 
223
+ process_btn.click(
224
  fn=detect_and_segment_balloons,
225
+ inputs=[input_image, confidence, use_segmentation],
226
  outputs=[output_image, output_json]
227
  )
228
 
229
+ print("✅ App avviata!")
230
  demo.launch()
231
 
232
+