EnginDev commited on
Commit
d78a30b
·
verified ·
1 Parent(s): c96ea8b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +174 -154
app.py CHANGED
@@ -4,7 +4,7 @@ import numpy as np
4
  from PIL import Image
5
  import cv2
6
 
7
- print("🚀 Starting SAM2 App v2.0...")
8
 
9
  device = "cuda" if torch.cuda.is_available() else "cpu"
10
  print(f"📱 Using device: {device}")
@@ -54,66 +54,17 @@ def prepare_image(image, max_size=1024):
54
  return image_pil, image_np
55
 
56
  def refine_mask(mask, kernel_size=5):
 
57
  mask_uint8 = (mask > 0).astype(np.uint8) * 255
58
  kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernel_size, kernel_size))
59
  mask_closed = cv2.morphologyEx(mask_uint8, cv2.MORPH_CLOSE, kernel)
60
  mask_refined = cv2.morphologyEx(mask_closed, cv2.MORPH_OPEN, kernel)
61
  return mask_refined > 0
62
 
63
- def merge_nearby_masks(masks, scores, distance_threshold=100):
64
- """
65
- Kombiniert Masken die nah beieinander sind
66
- → Fisch + Flosse werden EINS!
67
- """
68
- if len(masks) == 0:
69
- return np.zeros_like(masks[0]) if len(masks) > 0 else None
70
-
71
- # Finde Zentren aller Masken
72
- centers = []
73
- for mask in masks:
74
- y_coords, x_coords = np.where(mask)
75
- if len(x_coords) > 0:
76
- center_x = int(np.mean(x_coords))
77
- center_y = int(np.mean(y_coords))
78
- centers.append((center_x, center_y))
79
- else:
80
- centers.append(None)
81
-
82
- # Gruppiere Masken die nah beieinander sind
83
- combined = np.zeros_like(masks[0], dtype=bool)
84
-
85
- for i, mask in enumerate(masks):
86
- if centers[i] is None:
87
- continue
88
-
89
- # Checke ob diese Maske nah an bereits kombinierten Masken ist
90
- if np.sum(combined) > 0:
91
- # Distanz zwischen Masken
92
- combined_coords = np.argwhere(combined)
93
- mask_coords = np.argwhere(mask)
94
-
95
- if len(combined_coords) > 0 and len(mask_coords) > 0:
96
- # Minimale Distanz zwischen den Masken
97
- min_dist = float('inf')
98
- for mc in mask_coords[::10]: # Sample alle 10 Punkte
99
- for cc in combined_coords[::10]:
100
- dist = np.linalg.norm(mc - cc)
101
- min_dist = min(min_dist, dist)
102
-
103
- # Wenn nah genug: kombinieren!
104
- if min_dist < distance_threshold:
105
- combined = combined | mask
106
- print(f" ✅ Merged mask {i} (dist: {min_dist:.1f})")
107
- else:
108
- # Erste Maske
109
- combined = mask
110
-
111
- return combined
112
-
113
  def segment_automatic(image, quality="high", merge_parts=True):
114
  """
115
- Automatische Segmentierung mit Multi-Point Detection
116
- merge_parts=True → Kombiniert getrennte Teile wie Flossen!
117
  """
118
  if image is None:
119
  return None, {"error": "Kein Bild hochgeladen"}
@@ -125,87 +76,84 @@ def segment_automatic(image, quality="high", merge_parts=True):
125
  image_pil, image_np = prepare_image(image)
126
  h, w = image_np.shape[:2]
127
 
128
- # MEHRERE Punkte für bessere Abdeckung!
129
  center_x, center_y = w // 2, h // 2
130
- points = [
131
- [center_x, center_y], # Mitte
132
- [center_x, center_y - h//4], # Oben
133
- [center_x, center_y + h//4], # Unten
134
- [center_x - w//4, center_y], # Links
135
- [center_x + w//4, center_y] # Rechts
136
- ]
137
 
138
- all_masks = []
139
- all_scores = []
 
 
 
 
 
140
 
141
- print(f"🎯 Using {len(points)} detection points...")
 
 
142
 
143
- for idx, point in enumerate(points):
144
- inputs = processor(
145
- image_pil,
146
- input_points=[[point]],
147
- input_labels=[[1]],
148
- return_tensors="pt"
149
- ).to(device)
150
-
151
- with torch.no_grad():
152
- outputs = model(**inputs, multimask_output=True)
153
-
154
- masks = processor.image_processor.post_process_masks(
155
- outputs.pred_masks.cpu(),
156
- inputs["original_sizes"].cpu(),
157
- inputs["reshaped_input_sizes"].cpu()
158
- )[0]
159
 
160
- scores = outputs.iou_scores.cpu().numpy()
161
- if scores.ndim > 1:
162
- scores = scores.flatten()
 
 
 
 
 
 
 
 
163
 
164
- # Beste Maske von diesem Punkt
 
 
 
165
  best_idx = np.argmax(scores)
166
-
167
  if masks.ndim == 4:
168
- mask = masks[0, best_idx].numpy()
169
  else:
170
- mask = masks[best_idx].numpy()
171
-
172
- # Nur gute Masken
173
- if scores[best_idx] > 0.6:
174
- all_masks.append(mask > 0)
175
- all_scores.append(scores[best_idx])
176
- print(f" ✅ Point {idx}: Score {scores[best_idx]:.3f}")
177
-
178
- print(f"✅ Got {len(all_masks)} quality masks")
179
-
180
- # MERGE: Kombiniere nahe Masken
181
- if merge_parts and len(all_masks) > 0:
182
- combined_mask = merge_nearby_masks(all_masks, all_scores, distance_threshold=100)
183
- else:
184
- combined_mask = np.zeros((h, w), dtype=bool)
185
- for mask in all_masks:
186
- combined_mask = combined_mask | mask
187
 
188
- # Refinement
189
  if quality == "high":
190
- combined_mask = refine_mask(combined_mask, kernel_size=9)
 
191
 
192
- # Overlay
193
  overlay = image_np.copy()
194
  color = np.array([255, 80, 180]) # Rosa/Pink
195
 
196
- mask_float = combined_mask.astype(float)
197
  if quality == "high":
198
- mask_float = cv2.GaussianBlur(mask_float, (7, 7), 0)
199
 
 
200
  for c in range(3):
201
  overlay[:, :, c] = (
202
  overlay[:, :, c] * (1 - mask_float * 0.65) +
203
  color[c] * mask_float * 0.65
204
  )
205
 
206
- # Kontur
207
  contours, _ = cv2.findContours(
208
- combined_mask.astype(np.uint8),
209
  cv2.RETR_EXTERNAL,
210
  cv2.CHAIN_APPROX_SIMPLE
211
  )
@@ -215,18 +163,16 @@ def segment_automatic(image, quality="high", merge_parts=True):
215
  "success": True,
216
  "mode": "automatic_plus" if merge_parts else "automatic",
217
  "quality": quality,
218
- "detection_points": len(points),
219
- "masks_found": len(all_masks),
220
- "avg_score": float(np.mean(all_scores)) if all_scores else 0,
221
- "all_scores": [float(s) for s in all_scores],
222
  "image_size": [w, h],
223
- "mask_area": int(np.sum(combined_mask)),
224
- "mask_percentage": float(np.sum(combined_mask) / (h * w) * 100),
225
  "num_contours": len(contours),
226
  "device": device
227
  }
228
 
229
- print(f"✅ Segmentation complete! {len(contours)} contours")
230
  return Image.fromarray(overlay.astype(np.uint8)), metadata
231
 
232
  except Exception as e:
@@ -235,15 +181,17 @@ def segment_automatic(image, quality="high", merge_parts=True):
235
  return image, {"error": str(e)}
236
 
237
  def segment_multi_dense(image, density="medium"):
238
- """Multi-Object mit dichterem Grid"""
239
  if image is None:
240
  return None, {"error": "Kein Bild"}
241
 
242
  try:
 
243
  model, processor = load_model()
244
  image_pil, image_np = prepare_image(image)
245
  h, w = image_np.shape[:2]
246
 
 
247
  if density == "high":
248
  grid_size = 5
249
  elif density == "medium":
@@ -251,6 +199,7 @@ def segment_multi_dense(image, density="medium"):
251
  else:
252
  grid_size = 3
253
 
 
254
  points = []
255
  for i in range(1, grid_size + 1):
256
  for j in range(1, grid_size + 1):
@@ -258,11 +207,12 @@ def segment_multi_dense(image, density="medium"):
258
  y = int(h * j / (grid_size + 1))
259
  points.append([x, y])
260
 
261
- print(f"📍 Using {len(points)} points for segmentation")
262
 
263
  all_masks = []
264
  all_scores = []
265
 
 
266
  for idx, point in enumerate(points):
267
  inputs = processor(
268
  image_pil,
@@ -288,14 +238,17 @@ def segment_multi_dense(image, density="medium"):
288
  else:
289
  mask = masks[best_idx].numpy()
290
 
 
291
  if scores[best_idx] > 0.7:
292
  all_masks.append(refine_mask(mask))
293
  all_scores.append(scores[best_idx])
294
 
295
  print(f"✅ Got {len(all_masks)} quality masks")
296
 
 
297
  overlay = image_np.copy()
298
 
 
299
  colors = []
300
  for i in range(len(all_masks)):
301
  hue = int(180 * i / max(len(all_masks), 1))
@@ -303,13 +256,15 @@ def segment_multi_dense(image, density="medium"):
303
  color_rgb = cv2.cvtColor(color_hsv, cv2.COLOR_HSV2RGB)[0][0]
304
  colors.append(color_rgb)
305
 
 
306
  for mask, color, score in zip(all_masks, colors, all_scores):
307
- alpha = 0.4 + (score - 0.7) * 0.2
308
  overlay[mask] = (
309
  overlay[mask] * (1 - alpha) +
310
  np.array(color) * alpha
311
  ).astype(np.uint8)
312
 
 
313
  contours, _ = cv2.findContours(
314
  mask.astype(np.uint8),
315
  cv2.RETR_EXTERNAL,
@@ -328,6 +283,7 @@ def segment_multi_dense(image, density="medium"):
328
  "scores": [float(s) for s in all_scores]
329
  }
330
 
 
331
  return Image.fromarray(overlay), metadata
332
 
333
  except Exception as e:
@@ -340,11 +296,10 @@ demo = gr.Blocks(title="SAM2 Boostly", theme=gr.themes.Soft())
340
 
341
  with demo:
342
  gr.Markdown("# 🎨 SAM2 Segmentierung - Boostly Edition")
343
- gr.Markdown("### Zero-Shot Object Segmentation powered by Meta SAM")
344
 
345
  with gr.Tab("🤖 Automatisch PLUS"):
346
- gr.Markdown("**Intelligent Multi-Point Detection** - Erkennt und kombiniert alle Objektteile!")
347
- gr.Markdown("✨ Perfekt für: Fische mit Flossen, Menschen mit Armen/Beinen, komplexe Objekte")
348
 
349
  with gr.Row():
350
  with gr.Column():
@@ -353,32 +308,41 @@ with demo:
353
  quality_radio = gr.Radio(
354
  choices=["high", "fast"],
355
  value="high",
356
- label="Qualität"
 
357
  )
358
 
359
  merge_checkbox = gr.Checkbox(
360
  value=True,
361
- label="✅ Teile zusammenfügen (Empfohlen!)"
 
362
  )
363
 
364
  btn_auto = gr.Button("🚀 Segmentieren", variant="primary", size="lg")
365
 
366
  gr.Markdown("""
367
- **So funktioniert's:**
368
- - 🎯 5 Erkennungspunkte (Mitte + 4 Richtungen)
369
- - 🔗 Intelligentes Merging naher Bereiche
370
- - 🎨 Fisch + Flosse = EIN zusammenhängendes Objekt!
 
371
  """)
372
 
373
  with gr.Column():
374
- output_auto = gr.Image(label="✨ Ergebnis")
375
- json_auto = gr.JSON(label="📊 Details")
376
 
377
  btn_auto.click(
378
  fn=segment_automatic,
379
  inputs=[input_auto, quality_radio, merge_checkbox],
380
  outputs=[output_auto, json_auto]
381
  )
 
 
 
 
 
 
382
 
383
  with gr.Tab("🎯 Multi-Region"):
384
  gr.Markdown("**Grid-basierte Segmentierung** - Für mehrere separate Objekte")
@@ -386,23 +350,28 @@ with demo:
386
  with gr.Row():
387
  with gr.Column():
388
  input_multi = gr.Image(type="pil", label="📸 Bild hochladen")
 
389
  density_radio = gr.Radio(
390
  choices=["high", "medium", "low"],
391
  value="medium",
392
- label="Punkt-Dichte"
 
393
  )
394
- btn_multi = gr.Button("🎯 Alle Bereiche", variant="primary", size="lg")
 
395
 
396
  gr.Markdown("""
397
  **Grid-Größen:**
398
- - 🔥 High: 5x5 = 25 Punkte
399
- - ⚡ Medium: 4x4 = 16 Punkte
400
  - 💨 Low: 3x3 = 9 Punkte
 
 
401
  """)
402
 
403
  with gr.Column():
404
- output_multi = gr.Image(label="✨ Ergebnis")
405
- json_multi = gr.JSON(label="📊 Details")
406
 
407
  btn_multi.click(
408
  fn=segment_multi_dense,
@@ -410,35 +379,86 @@ with demo:
410
  outputs=[output_multi, json_multi]
411
  )
412
 
413
- with gr.Tab("📡 API Integration"):
414
- gr.Markdown("### API Endpoint für Lovable/externe Apps")
415
  gr.Code(
416
  "https://EnginDev-Boostly.hf.space/api/predict",
417
- label="API URL"
418
  )
419
 
420
- gr.Markdown("### JavaScript Beispiel")
421
  gr.Code('''
422
- const response = await fetch('https://EnginDev-Boostly.hf.space/api/predict', {
 
 
 
 
 
 
 
 
 
 
 
 
423
  method: 'POST',
424
  headers: {'Content-Type': 'application/json'},
425
  body: JSON.stringify({
426
- data: [imageBase64, "high", true], // [image, quality, merge]
427
- fn_index: 0 // 0 = Automatisch PLUS, 1 = Multi-Region
428
  })
429
- });
 
 
 
 
 
 
 
 
430
 
431
- const result = await response.json();
432
- const segmentedImage = result.data[0]; // Base64 Image
433
- const metadata = result.data[1]; // JSON Details
434
  ''', language="javascript")
435
 
436
- gr.Markdown("### Parameter")
437
- gr.Markdown("- **fn_index**: 0 = Automatisch PLUS, 1 = Multi-Region")
438
- gr.Markdown("- **quality**: 'high' (präzise) oder 'fast' (schnell)")
439
- gr.Markdown("- **merge**: true (Teile kombinieren) oder false")
440
- gr.Markdown("- **density**: 'high', 'medium', 'low' (nur Multi-Region)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
441
 
442
  if __name__ == "__main__":
443
- print("🌐 Launching Boostly SAM2...")
444
  demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)
 
4
  from PIL import Image
5
  import cv2
6
 
7
+ print("🚀 Starting SAM2 App v2.1 - OPTIMIZED...")
8
 
9
  device = "cuda" if torch.cuda.is_available() else "cpu"
10
  print(f"📱 Using device: {device}")
 
54
  return image_pil, image_np
55
 
56
  def refine_mask(mask, kernel_size=5):
57
+ """Glättet Maskenkanten"""
58
  mask_uint8 = (mask > 0).astype(np.uint8) * 255
59
  kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (kernel_size, kernel_size))
60
  mask_closed = cv2.morphologyEx(mask_uint8, cv2.MORPH_CLOSE, kernel)
61
  mask_refined = cv2.morphologyEx(mask_closed, cv2.MORPH_OPEN, kernel)
62
  return mask_refined > 0
63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  def segment_automatic(image, quality="high", merge_parts=True):
65
  """
66
+ OPTIMIERTE Automatische Segmentierung
67
+ Schnell & präzise - kombiniert mehrere Masken
68
  """
69
  if image is None:
70
  return None, {"error": "Kein Bild hochgeladen"}
 
76
  image_pil, image_np = prepare_image(image)
77
  h, w = image_np.shape[:2]
78
 
 
79
  center_x, center_y = w // 2, h // 2
 
 
 
 
 
 
 
80
 
81
+ # Single point inference mit multimask_output
82
+ inputs = processor(
83
+ image_pil,
84
+ input_points=[[[center_x, center_y]]],
85
+ input_labels=[[1]],
86
+ return_tensors="pt"
87
+ ).to(device)
88
 
89
+ print("🧠 Running inference...")
90
+ with torch.no_grad():
91
+ outputs = model(**inputs, multimask_output=True)
92
 
93
+ masks = processor.image_processor.post_process_masks(
94
+ outputs.pred_masks.cpu(),
95
+ inputs["original_sizes"].cpu(),
96
+ inputs["reshaped_input_sizes"].cpu()
97
+ )[0]
98
+
99
+ scores = outputs.iou_scores.cpu().numpy()
100
+ if scores.ndim > 1:
101
+ scores = scores.flatten()
102
+
103
+ print(f"✅ Got {len(scores)} masks with scores: {scores}")
104
+
105
+ # SMART MERGING: Kombiniere alle guten Masken
106
+ if merge_parts:
107
+ combined_mask = np.zeros((h, w), dtype=bool)
108
+ masks_used = 0
109
 
110
+ for idx, score in enumerate(scores):
111
+ if score > 0.5: # Nur Masken mit gutem Score
112
+ if masks.ndim == 4:
113
+ mask = masks[0, idx].numpy()
114
+ else:
115
+ mask = masks[idx].numpy()
116
+
117
+ # OR-Kombination (super schnell!)
118
+ combined_mask = combined_mask | (mask > 0)
119
+ masks_used += 1
120
+ print(f" ✅ Added mask {idx} (score: {score:.3f})")
121
 
122
+ final_mask = combined_mask
123
+ print(f"🔗 Combined {masks_used} masks into one!")
124
+ else:
125
+ # Nur beste Maske
126
  best_idx = np.argmax(scores)
 
127
  if masks.ndim == 4:
128
+ final_mask = masks[0, best_idx].numpy() > 0
129
  else:
130
+ final_mask = masks[best_idx].numpy() > 0
131
+ masks_used = 1
132
+ print(f"✅ Using best mask (score: {scores[best_idx]:.3f})")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
+ # Refinement für glatte Kanten
135
  if quality == "high":
136
+ print("🎨 Refining mask...")
137
+ final_mask = refine_mask(final_mask, kernel_size=7)
138
 
139
+ # Overlay erstellen
140
  overlay = image_np.copy()
141
  color = np.array([255, 80, 180]) # Rosa/Pink
142
 
143
+ mask_float = final_mask.astype(float)
144
  if quality == "high":
145
+ mask_float = cv2.GaussianBlur(mask_float, (5, 5), 0)
146
 
147
+ # Farbiges Overlay
148
  for c in range(3):
149
  overlay[:, :, c] = (
150
  overlay[:, :, c] * (1 - mask_float * 0.65) +
151
  color[c] * mask_float * 0.65
152
  )
153
 
154
+ # Gelbe Kontur zeichnen
155
  contours, _ = cv2.findContours(
156
+ final_mask.astype(np.uint8),
157
  cv2.RETR_EXTERNAL,
158
  cv2.CHAIN_APPROX_SIMPLE
159
  )
 
163
  "success": True,
164
  "mode": "automatic_plus" if merge_parts else "automatic",
165
  "quality": quality,
166
+ "masks_combined": masks_used,
167
+ "all_scores": scores.tolist(),
 
 
168
  "image_size": [w, h],
169
+ "mask_area": int(np.sum(final_mask)),
170
+ "mask_percentage": float(np.sum(final_mask) / (h * w) * 100),
171
  "num_contours": len(contours),
172
  "device": device
173
  }
174
 
175
+ print("✅ Segmentation complete!")
176
  return Image.fromarray(overlay.astype(np.uint8)), metadata
177
 
178
  except Exception as e:
 
181
  return image, {"error": str(e)}
182
 
183
  def segment_multi_dense(image, density="medium"):
184
+ """Multi-Object Segmentierung mit Grid"""
185
  if image is None:
186
  return None, {"error": "Kein Bild"}
187
 
188
  try:
189
+ print(f"🎯 Starting multi-region segmentation (density: {density})...")
190
  model, processor = load_model()
191
  image_pil, image_np = prepare_image(image)
192
  h, w = image_np.shape[:2]
193
 
194
+ # Grid-Größe basierend auf Density
195
  if density == "high":
196
  grid_size = 5
197
  elif density == "medium":
 
199
  else:
200
  grid_size = 3
201
 
202
+ # Grid-Punkte generieren
203
  points = []
204
  for i in range(1, grid_size + 1):
205
  for j in range(1, grid_size + 1):
 
207
  y = int(h * j / (grid_size + 1))
208
  points.append([x, y])
209
 
210
+ print(f"📍 Using {len(points)} grid points ({grid_size}x{grid_size})...")
211
 
212
  all_masks = []
213
  all_scores = []
214
 
215
+ # Segmentiere jeden Punkt
216
  for idx, point in enumerate(points):
217
  inputs = processor(
218
  image_pil,
 
238
  else:
239
  mask = masks[best_idx].numpy()
240
 
241
+ # Nur Masken mit gutem Score
242
  if scores[best_idx] > 0.7:
243
  all_masks.append(refine_mask(mask))
244
  all_scores.append(scores[best_idx])
245
 
246
  print(f"✅ Got {len(all_masks)} quality masks")
247
 
248
+ # Overlay mit verschiedenen Farben
249
  overlay = image_np.copy()
250
 
251
+ # HSV-basierte Farbgenerierung
252
  colors = []
253
  for i in range(len(all_masks)):
254
  hue = int(180 * i / max(len(all_masks), 1))
 
256
  color_rgb = cv2.cvtColor(color_hsv, cv2.COLOR_HSV2RGB)[0][0]
257
  colors.append(color_rgb)
258
 
259
+ # Masken anwenden
260
  for mask, color, score in zip(all_masks, colors, all_scores):
261
+ alpha = 0.4 + (score - 0.7) * 0.2 # Höherer Score = stärkere Farbe
262
  overlay[mask] = (
263
  overlay[mask] * (1 - alpha) +
264
  np.array(color) * alpha
265
  ).astype(np.uint8)
266
 
267
+ # Kontur
268
  contours, _ = cv2.findContours(
269
  mask.astype(np.uint8),
270
  cv2.RETR_EXTERNAL,
 
283
  "scores": [float(s) for s in all_scores]
284
  }
285
 
286
+ print("✅ Multi-region complete!")
287
  return Image.fromarray(overlay), metadata
288
 
289
  except Exception as e:
 
296
 
297
  with demo:
298
  gr.Markdown("# 🎨 SAM2 Segmentierung - Boostly Edition")
299
+ gr.Markdown("### ⚡ Optimierte Zero-Shot Object Segmentation")
300
 
301
  with gr.Tab("🤖 Automatisch PLUS"):
302
+ gr.Markdown("**Smart Multi-Mask Combining** - Kombiniert automatisch alle Objektteile!")
 
303
 
304
  with gr.Row():
305
  with gr.Column():
 
308
  quality_radio = gr.Radio(
309
  choices=["high", "fast"],
310
  value="high",
311
+ label="⚙️ Qualität",
312
+ info="High = präzisere Kanten, Fast = schneller"
313
  )
314
 
315
  merge_checkbox = gr.Checkbox(
316
  value=True,
317
+ label="🔗 Teile zusammenfügen",
318
+ info="Kombiniert alle erkannten Bereiche (Fisch + Flosse = 1 Objekt)"
319
  )
320
 
321
  btn_auto = gr.Button("🚀 Segmentieren", variant="primary", size="lg")
322
 
323
  gr.Markdown("""
324
+ **✨ Funktionsweise:**
325
+ - SAM generiert 3 verschiedene Masken
326
+ - Wenn "Teile zusammenfügen" AN: Alle kombiniert → vollständiges Objekt
327
+ - Wenn AUS: Nur präziseste Maske
328
+ - ⚡ Optimiert: ~10-30 Sekunden statt 25 Minuten!
329
  """)
330
 
331
  with gr.Column():
332
+ output_auto = gr.Image(label="✨ Segmentiertes Bild")
333
+ json_auto = gr.JSON(label="📊 Metadata")
334
 
335
  btn_auto.click(
336
  fn=segment_automatic,
337
  inputs=[input_auto, quality_radio, merge_checkbox],
338
  outputs=[output_auto, json_auto]
339
  )
340
+
341
+ gr.Examples(
342
+ examples=[],
343
+ inputs=input_auto,
344
+ label="💡 Tipp: Objekt sollte zentral im Bild sein"
345
+ )
346
 
347
  with gr.Tab("🎯 Multi-Region"):
348
  gr.Markdown("**Grid-basierte Segmentierung** - Für mehrere separate Objekte")
 
350
  with gr.Row():
351
  with gr.Column():
352
  input_multi = gr.Image(type="pil", label="📸 Bild hochladen")
353
+
354
  density_radio = gr.Radio(
355
  choices=["high", "medium", "low"],
356
  value="medium",
357
+ label="📊 Punkt-Dichte",
358
+ info="Mehr Punkte = mehr Details, aber langsamer"
359
  )
360
+
361
+ btn_multi = gr.Button("🎯 Alle Bereiche segmentieren", variant="primary", size="lg")
362
 
363
  gr.Markdown("""
364
  **Grid-Größen:**
365
+ - 🔥 High: 5x5 = 25 Erkennungspunkte
366
+ - ⚡ Medium: 4x4 = 16 Punkte (empfohlen)
367
  - 💨 Low: 3x3 = 9 Punkte
368
+
369
+ Jedes Objekt bekommt eigene Farbe!
370
  """)
371
 
372
  with gr.Column():
373
+ output_multi = gr.Image(label="✨ Segmentiertes Bild")
374
+ json_multi = gr.JSON(label="📊 Metadata")
375
 
376
  btn_multi.click(
377
  fn=segment_multi_dense,
 
379
  outputs=[output_multi, json_multi]
380
  )
381
 
382
+ with gr.Tab("📡 API Dokumentation"):
383
+ gr.Markdown("### 🔗 API Endpoint")
384
  gr.Code(
385
  "https://EnginDev-Boostly.hf.space/api/predict",
386
+ label="Base URL"
387
  )
388
 
389
+ gr.Markdown("### 📝 JavaScript Integration (für Lovable)")
390
  gr.Code('''
391
+ // Segmentation Service
392
+ const HUGGINGFACE_API = 'https://EnginDev-Boostly.hf.space';
393
+
394
+ async function segmentImage(imageFile, mode = 'automatic') {
395
+ // File zu Base64 konvertieren
396
+ const base64 = await new Promise((resolve) => {
397
+ const reader = new FileReader();
398
+ reader.onloadend = () => resolve(reader.result);
399
+ reader.readAsDataURL(imageFile);
400
+ });
401
+
402
+ // API Call
403
+ const response = await fetch(`${HUGGINGFACE_API}/api/predict`, {
404
  method: 'POST',
405
  headers: {'Content-Type': 'application/json'},
406
  body: JSON.stringify({
407
+ data: [base64, "high", true], // [image, quality, merge]
408
+ fn_index: mode === 'automatic' ? 0 : 1
409
  })
410
+ });
411
+
412
+ const result = await response.json();
413
+
414
+ return {
415
+ segmentedImage: result.data[0], // Base64 segmentiertes Bild
416
+ metadata: result.data[1] // JSON mit Details
417
+ };
418
+ }
419
 
420
+ // Verwendung:
421
+ const result = await segmentImage(myImageFile, 'automatic');
422
+ console.log('Mask covers:', result.metadata.mask_percentage + '%');
423
  ''', language="javascript")
424
 
425
+ gr.Markdown("### ⚙️ Parameter")
426
+ gr.Markdown("""
427
+ **fn_index:**
428
+ - `0` = Automatisch PLUS (empfohlen für einzelne Objekte)
429
+ - `1` = Multi-Region (für mehrere Objekte)
430
+
431
+ **quality:**
432
+ - `"high"` = Präzise Kanten, Gaussian Blur, Refinement (~20-30s)
433
+ - `"fast"` = Schneller, weniger Nachbearbeitung (~10-15s)
434
+
435
+ **merge (nur fn_index=0):**
436
+ - `true` = Kombiniert alle Masken → vollständiges Objekt
437
+ - `false` = Nur beste Maske → nur Hauptteil
438
+
439
+ **density (nur fn_index=1):**
440
+ - `"high"` = 5x5 Grid = 25 Punkte
441
+ - `"medium"` = 4x4 Grid = 16 Punkte
442
+ - `"low"` = 3x3 Grid = 9 Punkte
443
+ """)
444
+
445
+ gr.Markdown("### 📊 Response Format")
446
+ gr.Code('''
447
+ {
448
+ "data": [
449
+ "data:image/png;base64,iVBORw0KGgo...", // Segmentiertes Bild
450
+ {
451
+ "success": true,
452
+ "mode": "automatic_plus",
453
+ "masks_combined": 3,
454
+ "mask_percentage": 12.5,
455
+ "num_contours": 1,
456
+ "all_scores": [0.998, 0.583, 0.864]
457
+ }
458
+ ]
459
+ }
460
+ ''', language="json")
461
 
462
  if __name__ == "__main__":
463
+ print("🌐 Launching Boostly SAM2 v2.1...")
464
  demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)