EnginDev commited on
Commit
a2e8b0f
·
verified ·
1 Parent(s): 2b59476

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +181 -134
app.py CHANGED
@@ -3,18 +3,36 @@ import torch
3
  import numpy as np
4
  from PIL import Image
5
  import cv2
6
- from transformers import SamModel, SamProcessor
7
  import json
8
 
9
- # SAM Model laden
10
- print("Lade SAM Model...")
11
- processor = SamProcessor.from_pretrained("facebook/sam-vit-huge")
12
- model = SamModel.from_pretrained("facebook/sam-vit-huge")
13
  device = "cuda" if torch.cuda.is_available() else "cpu"
14
- model.to(device)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
  def generate_colors(n):
17
- """Generiere verschiedene Farben für Masken"""
18
  colors = []
19
  for i in range(n):
20
  hue = int(180 * i / max(n, 1))
@@ -23,72 +41,33 @@ def generate_colors(n):
23
  return colors
24
 
25
  def segment_automatic(image):
26
- """Automatische Segmentierung - Hauptobjekt in der Mitte"""
27
  if image is None:
28
  return None, {"error": "Kein Bild hochgeladen"}
29
 
30
- # Bild vorbereiten
31
- image_np = np.array(image)
32
- h, w = image_np.shape[:2]
33
-
34
- # Mittelpunkt als Input (da Objekt zentral sein sollte)
35
- input_points = [[[w//2, h//2]]]
36
-
37
- # SAM Processing
38
- inputs = processor(image, input_points=input_points, return_tensors="pt").to(device)
39
-
40
- with torch.no_grad():
41
- outputs = model(**inputs)
42
-
43
- # Masken extrahieren
44
- masks = processor.image_processor.post_process_masks(
45
- outputs.pred_masks.cpu(),
46
- inputs["original_sizes"].cpu(),
47
- inputs["reshaped_input_sizes"].cpu()
48
- )[0]
49
-
50
- # Beste Maske nehmen
51
- scores = outputs.iou_scores.cpu().numpy()[0]
52
- best_mask_idx = np.argmax(scores)
53
- best_mask = masks[best_mask_idx].numpy().squeeze()
54
-
55
- # Farbige Overlay erstellen
56
- overlay = image_np.copy()
57
- color = [255, 0, 100] # Pink
58
- overlay[best_mask] = overlay[best_mask] * 0.5 + np.array(color) * 0.5
59
-
60
- # Metadata
61
- metadata = {
62
- "mode": "automatic",
63
- "num_masks": 1,
64
- "score": float(scores[best_mask_idx]),
65
- "mask_shape": best_mask.shape,
66
- "object_detected": True
67
- }
68
-
69
- return Image.fromarray(overlay.astype(np.uint8)), metadata
70
-
71
- def segment_all_objects(image):
72
- """Alle Objekte segmentieren - für manuelle Auswahl"""
73
- if image is None:
74
- return None, {"error": "Kein Bild hochgeladen"}
75
-
76
- image_np = np.array(image)
77
- h, w = image_np.shape[:2]
78
-
79
- # Grid von Punkten für Segmentierung
80
- grid_points = []
81
- step = max(h, w) // 8 # 8x8 Grid
82
- for y in range(step, h, step):
83
- for x in range(step, w, step):
84
- grid_points.append([x, y])
85
-
86
- all_masks = []
87
- all_scores = []
88
-
89
- # Segmentiere jeden Punkt
90
- for point in grid_points[:10]: # Limitiere auf 10 für Performance
91
- inputs = processor(image, input_points=[[point]], return_tensors="pt").to(device)
92
 
93
  with torch.no_grad():
94
  outputs = model(**inputs)
@@ -101,108 +80,176 @@ def segment_all_objects(image):
101
 
102
  scores = outputs.iou_scores.cpu().numpy()[0]
103
  best_idx = np.argmax(scores)
 
104
 
105
- all_masks.append(masks[best_idx].numpy().squeeze())
106
- all_scores.append(scores[best_idx])
107
-
108
- # Kombiniere alle Masken mit verschiedenen Farben
109
- overlay = image_np.copy()
110
- colors = generate_colors(len(all_masks))
111
-
112
- for mask, color in zip(all_masks, colors):
113
- overlay[mask] = overlay[mask] * 0.6 + np.array(color) * 0.4
114
-
115
- metadata = {
116
- "mode": "multi_object",
117
- "num_masks": len(all_masks),
118
- "avg_score": float(np.mean(all_scores)),
119
- "masks_data": [
120
- {
121
- "id": i,
122
- "score": float(score),
123
- "area": int(mask.sum())
124
- } for i, (mask, score) in enumerate(zip(all_masks, all_scores))
125
- ]
126
- }
127
-
128
- return Image.fromarray(overlay.astype(np.uint8)), metadata
129
 
130
- def segment_with_points(image, points_json):
131
- """Segmentierung mit benutzerdefinierten Punkten"""
132
  if image is None:
133
  return None, {"error": "Kein Bild hochgeladen"}
134
 
135
- image_np = np.array(image)
136
-
137
  try:
138
- # Parse JSON input
139
- points_data = json.loads(points_json) if isinstance(points_json, str) else points_json
140
- input_points = [points_data.get("points", [[image_np.shape[1]//2, image_np.shape[0]//2]])]
141
 
142
- inputs = processor(image, input_points=input_points, return_tensors="pt").to(device)
 
 
 
143
 
144
- with torch.no_grad():
145
- outputs = model(**inputs)
146
 
147
- masks = processor.image_processor.post_process_masks(
148
- outputs.pred_masks.cpu(),
149
- inputs["original_sizes"].cpu(),
150
- inputs["reshaped_input_sizes"].cpu()
151
- )[0]
 
 
 
152
 
153
- scores = outputs.iou_scores.cpu().numpy()[0]
154
- best_idx = np.argmax(scores)
155
- best_mask = masks[best_idx].numpy().squeeze()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
 
157
  overlay = image_np.copy()
158
- color = [0, 255, 100] # Grün
159
- overlay[best_mask] = overlay[best_mask] * 0.5 + np.array(color) * 0.5
 
 
160
 
161
  metadata = {
162
- "mode": "custom_points",
163
- "points": input_points[0],
164
- "score": float(scores[best_idx]),
165
- "success": True
 
166
  }
167
 
168
- return Image.fromarray(overlay.astype(np.uint8)), metadata
169
 
170
  except Exception as e:
 
171
  return image, {"error": str(e)}
172
 
173
  # Gradio Interface
174
- with gr.Blocks(title="SAM2 Segmentierung API") as demo:
 
175
  gr.Markdown("""
176
  # 🎨 SAM2 Bild Segmentierung
177
- ### Keine Training nötig - Zero-Shot Object Segmentation!
178
  """)
179
 
180
- with gr.Tab("🤖 Automatisch (Hauptobjekt)"):
 
 
181
  with gr.Row():
182
  with gr.Column():
183
  input_auto = gr.Image(type="pil", label="Bild hochladen")
184
- btn_auto = gr.Button("Objekt erkennen", variant="primary")
 
185
  with gr.Column():
186
  output_auto = gr.Image(label="Segmentiertes Bild")
187
  json_auto = gr.JSON(label="Metadata")
188
 
189
- btn_auto.click(segment_automatic, inputs=input_auto, outputs=[output_auto, json_auto])
 
 
 
 
190
 
191
  with gr.Tab("🎯 Mehrere Objekte"):
 
 
192
  with gr.Row():
193
  with gr.Column():
194
  input_multi = gr.Image(type="pil", label="Bild hochladen")
195
- btn_multi = gr.Button("Alle Objekte erkennen", variant="primary")
 
196
  with gr.Column():
197
  output_multi = gr.Image(label="Segmentierte Bereiche")
198
  json_multi = gr.JSON(label="Metadata")
199
 
200
- btn_multi.click(segment_all_objects, inputs=input_multi, outputs=[output_multi, json_multi])
201
-
202
- with gr.Tab("✋ Custom (mit Punkten)"):
203
- with gr.Row():
204
- with gr.Column():
205
- input_custom = gr.Image(type="pil", label="Bild hochladen")
206
- points_input = gr.Textbox(
207
- label="Punkte (JSON)",
208
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  import numpy as np
4
  from PIL import Image
5
  import cv2
 
6
  import json
7
 
8
+ print("🚀 Starting SAM2 App...")
9
+
10
+ # Device Setup
 
11
  device = "cuda" if torch.cuda.is_available() else "cpu"
12
+ print(f"📱 Using device: {device}")
13
+
14
+ # Lazy Loading
15
+ model = None
16
+ processor = None
17
+
18
+ def load_model():
19
+ global model, processor
20
+ if model is None:
21
+ print("📦 Loading SAM model...")
22
+ try:
23
+ from transformers import SamModel, SamProcessor
24
+
25
+ model_name = "facebook/sam-vit-base"
26
+ processor = SamProcessor.from_pretrained(model_name)
27
+ model = SamModel.from_pretrained(model_name)
28
+ model.to(device)
29
+ print("✅ Model loaded successfully!")
30
+ except Exception as e:
31
+ print(f"❌ Error loading model: {e}")
32
+ raise e
33
+ return model, processor
34
 
35
  def generate_colors(n):
 
36
  colors = []
37
  for i in range(n):
38
  hue = int(180 * i / max(n, 1))
 
41
  return colors
42
 
43
  def segment_automatic(image):
 
44
  if image is None:
45
  return None, {"error": "Kein Bild hochgeladen"}
46
 
47
+ try:
48
+ model, processor = load_model()
49
+
50
+ if isinstance(image, np.ndarray):
51
+ image_pil = Image.fromarray(image)
52
+ else:
53
+ image_pil = image
54
+
55
+ image_np = np.array(image_pil)
56
+ h, w = image_np.shape[:2]
57
+
58
+ # Resize wenn zu groß
59
+ max_size = 1024
60
+ if max(h, w) > max_size:
61
+ scale = max_size / max(h, w)
62
+ new_h, new_w = int(h * scale), int(w * scale)
63
+ image_pil = image_pil.resize((new_w, new_h))
64
+ image_np = np.array(image_pil)
65
+ h, w = new_h, new_w
66
+
67
+ # Mittelpunkt
68
+ input_points = [[[w//2, h//2]]]
69
+
70
+ inputs = processor(image_pil, input_points=input_points, return_tensors="pt").to(device)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
 
72
  with torch.no_grad():
73
  outputs = model(**inputs)
 
80
 
81
  scores = outputs.iou_scores.cpu().numpy()[0]
82
  best_idx = np.argmax(scores)
83
+ best_mask = masks[best_idx].numpy().squeeze()
84
 
85
+ overlay = image_np.copy()
86
+ color = np.array([255, 0, 100])
87
+ overlay[best_mask] = (overlay[best_mask] * 0.5 + color * 0.5).astype(np.uint8)
88
+
89
+ metadata = {
90
+ "mode": "automatic",
91
+ "success": True,
92
+ "score": float(scores[best_idx]),
93
+ "image_size": [h, w],
94
+ "device": device
95
+ }
96
+
97
+ return Image.fromarray(overlay), metadata
98
+
99
+ except Exception as e:
100
+ print(f"❌ Error: {e}")
101
+ return image, {"error": str(e)}
 
 
 
 
 
 
 
102
 
103
+ def segment_multi(image):
 
104
  if image is None:
105
  return None, {"error": "Kein Bild hochgeladen"}
106
 
 
 
107
  try:
108
+ model, processor = load_model()
 
 
109
 
110
+ if isinstance(image, np.ndarray):
111
+ image_pil = Image.fromarray(image)
112
+ else:
113
+ image_pil = image
114
 
115
+ image_np = np.array(image_pil)
116
+ h, w = image_np.shape[:2]
117
 
118
+ # Resize
119
+ max_size = 1024
120
+ if max(h, w) > max_size:
121
+ scale = max_size / max(h, w)
122
+ new_h, new_w = int(h * scale), int(w * scale)
123
+ image_pil = image_pil.resize((new_w, new_h))
124
+ image_np = np.array(image_pil)
125
+ h, w = new_h, new_w
126
 
127
+ # Grid von Punkten
128
+ all_masks = []
129
+ all_scores = []
130
+
131
+ grid_points = [
132
+ [w//4, h//4], [w//2, h//4], [3*w//4, h//4],
133
+ [w//4, h//2], [w//2, h//2], [3*w//4, h//2],
134
+ [w//4, 3*h//4], [w//2, 3*h//4], [3*w//4, 3*h//4]
135
+ ]
136
+
137
+ for point in grid_points[:6]:
138
+ inputs = processor(image_pil, input_points=[[point]], return_tensors="pt").to(device)
139
+
140
+ with torch.no_grad():
141
+ outputs = model(**inputs)
142
+
143
+ masks = processor.image_processor.post_process_masks(
144
+ outputs.pred_masks.cpu(),
145
+ inputs["original_sizes"].cpu(),
146
+ inputs["reshaped_input_sizes"].cpu()
147
+ )[0]
148
+
149
+ scores = outputs.iou_scores.cpu().numpy()[0]
150
+ best_idx = np.argmax(scores)
151
+
152
+ all_masks.append(masks[best_idx].numpy().squeeze())
153
+ all_scores.append(scores[best_idx])
154
 
155
  overlay = image_np.copy()
156
+ colors = generate_colors(len(all_masks))
157
+
158
+ for mask, color in zip(all_masks, colors):
159
+ overlay[mask] = (overlay[mask] * 0.6 + np.array(color) * 0.4).astype(np.uint8)
160
 
161
  metadata = {
162
+ "mode": "multi_object",
163
+ "success": True,
164
+ "num_masks": len(all_masks),
165
+ "avg_score": float(np.mean(all_scores)),
166
+ "device": device
167
  }
168
 
169
+ return Image.fromarray(overlay), metadata
170
 
171
  except Exception as e:
172
+ print(f"❌ Error: {e}")
173
  return image, {"error": str(e)}
174
 
175
  # Gradio Interface
176
+ with gr.Blocks(title="SAM2 Segmentation API", theme=gr.themes.Soft()) as demo:
177
+
178
  gr.Markdown("""
179
  # 🎨 SAM2 Bild Segmentierung
180
+ ### Zero-Shot Object Segmentation - Kein Training nötig!
181
  """)
182
 
183
+ with gr.Tab("🤖 Automatisch"):
184
+ gr.Markdown("Erkennt automatisch das Hauptobjekt in der Bildmitte")
185
+
186
  with gr.Row():
187
  with gr.Column():
188
  input_auto = gr.Image(type="pil", label="Bild hochladen")
189
+ btn_auto = gr.Button("🚀 Objekt erkennen", variant="primary", size="lg")
190
+
191
  with gr.Column():
192
  output_auto = gr.Image(label="Segmentiertes Bild")
193
  json_auto = gr.JSON(label="Metadata")
194
 
195
+ btn_auto.click(
196
+ fn=segment_automatic,
197
+ inputs=input_auto,
198
+ outputs=[output_auto, json_auto]
199
+ )
200
 
201
  with gr.Tab("🎯 Mehrere Objekte"):
202
+ gr.Markdown("Segmentiert mehrere Bereiche im Bild")
203
+
204
  with gr.Row():
205
  with gr.Column():
206
  input_multi = gr.Image(type="pil", label="Bild hochladen")
207
+ btn_multi = gr.Button("🚀 Alle Objekte erkennen", variant="primary", size="lg")
208
+
209
  with gr.Column():
210
  output_multi = gr.Image(label="Segmentierte Bereiche")
211
  json_multi = gr.JSON(label="Metadata")
212
 
213
+ btn_multi.click(
214
+ fn=segment_multi,
215
+ inputs=input_multi,
216
+ outputs=[output_multi, json_multi]
217
+ )
218
+
219
+ with gr.Tab("📡 API Info"):
220
+ gr.Markdown("""
221
+ ### API Nutzung für Lovable/externe Apps
222
+
223
+ **Endpoint:** `https://dein-space-name.hf.space/api/predict`
224
+
225
+ **Beispiel (JavaScript):**
226
+ ```javascript
227
+ const response = await fetch('https://dein-space.hf.space/api/predict', {
228
+ method: 'POST',
229
+ headers: {'Content-Type': 'application/json'},
230
+ body: JSON.stringify({
231
+ data: [imageBase64],
232
+ fn_index: 0 // 0 = Auto, 1 = Multi
233
+ })
234
+ });
235
+
236
+ const result = await response.json();
237
+ console.log(result.data); // [segmentedImage, metadata]
238
+ ```
239
+
240
+ **Python:**
241
+ ```python
242
+ from gradio_client import Client
243
+
244
+ client = Client("dein-username/dein-space")
245
+ result = client.predict("image.jpg", api_name="/predict")
246
+ ```
247
+ """)
248
+
249
+ if __name__ == "__main__":
250
+ print("🌐 Launching Gradio Interface...")
251
+ demo.launch(
252
+ server_name="0.0.0.0",
253
+ server_port=7860,
254
+ show_error=True
255
+ )