EnginDev commited on
Commit
0a4e24e
·
verified ·
1 Parent(s): 2d668f8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +1 -168
app.py CHANGED
@@ -1,168 +1 @@
1
- # app.py - Boostly SAM2 (AutomaticMaskGenerator, CPU friendly defaults)
2
- import os
3
- import urllib.request
4
- import time
5
- import traceback
6
-
7
- import numpy as np
8
- from PIL import Image
9
- import cv2
10
- import torch
11
- import gradio as gr
12
-
13
- print("=== Boostly SAM2 Space starting ===", time.asctime())
14
-
15
- # ---------------------------
16
- # Model download / settings
17
- # ---------------------------
18
- MODEL_URL = "https://dl.fbaipublicfiles.com/segment_anything/sam_vit_b_01ec64.pth"
19
- MODEL_PATH = "sam_vit_b.pth"
20
- MODEL_TYPE = "vit_b"
21
-
22
- # Download model if not present
23
- if not os.path.exists(MODEL_PATH):
24
- print("Model not found — downloading (this can take a while)...")
25
- urllib.request.urlretrieve(MODEL_URL, MODEL_PATH)
26
- print("Model downloaded.")
27
-
28
- device = "cuda" if torch.cuda.is_available() else "cpu"
29
- print("Device:", device)
30
-
31
- # Try to import segment_anything (needs to be in requirements.txt)
32
- try:
33
- from segment_anything import sam_model_registry, SamAutomaticMaskGenerator
34
- except Exception as e:
35
- print("ERROR importing segment_anything:", e)
36
- raise
37
-
38
- # Load model
39
- sam = sam_model_registry[MODEL_TYPE](checkpoint=MODEL_PATH)
40
- sam.to(device=device)
41
-
42
- # Automatic mask generator parameters (tweak for CPU)
43
- mask_generator = SamAutomaticMaskGenerator(
44
- sam,
45
- points_per_side=16, # lower -> fewer proposals (faster)
46
- pred_iou_thresh=0.86, # confidence threshold
47
- stability_score_thresh=0.7, # lower -> more masks
48
- min_mask_region_area=200 # remove tiny specks
49
- )
50
-
51
- print("Mask generator ready.")
52
-
53
- # ---------------------------
54
- # Helpers
55
- # ---------------------------
56
- def prepare_image(image, max_size=1024):
57
- """Convert and resize image to reduce memory on CPU."""
58
- if isinstance(image, np.ndarray):
59
- image_pil = Image.fromarray(image)
60
- else:
61
- image_pil = image
62
- if image_pil.mode != "RGB":
63
- image_pil = image_pil.convert("RGB")
64
- w, h = image_pil.size
65
- max_wh = max(w, h)
66
- if max_wh > max_size:
67
- scale = max_size / max_wh
68
- new_w, new_h = int(w * scale), int(h * scale)
69
- image_pil = image_pil.resize((new_w, new_h), Image.Resampling.LANCZOS)
70
- return image_pil, np.array(image_pil)
71
-
72
- def keep_top_masks(masks, top_k=12):
73
- """Sort masks by area and keep top_k largest ones."""
74
- if not masks:
75
- return masks
76
- areas = [np.sum(m["segmentation"]) for m in masks]
77
- idx = np.argsort(areas)[::-1] # descending
78
- top_idx = idx[:top_k]
79
- return [masks[i] for i in top_idx]
80
-
81
- def draw_masks_overlay(image_np, masks, draw_labels=True):
82
- overlay = image_np.copy()
83
- for i, mask in enumerate(masks):
84
- m = mask["segmentation"]
85
- color = np.random.randint(40, 230, size=(3,))
86
- alpha = 0.55
87
- overlay[m] = (overlay[m] * (1 - alpha) + color * alpha).astype(np.uint8)
88
- # Draw contour
89
- contours, _ = cv2.findContours(m.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
90
- cv2.drawContours(overlay, contours, -1, (255,255,255), 2)
91
- # Label
92
- if draw_labels:
93
- ys, xs = np.where(m)
94
- if len(xs) and len(ys):
95
- cx, cy = int(np.mean(xs)), int(np.mean(ys))
96
- cv2.putText(overlay, f"Obj {i+1}", (cx, cy), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255,255,255), 2)
97
- return overlay
98
-
99
- # ---------------------------
100
- # Main function: segment all
101
- # ---------------------------
102
- def segment_all_objects(image, max_size=1024, top_k=12, cpu_mode=True):
103
- """
104
- image: PIL or ndarray
105
- max_size: resize largest side to this for CPU
106
- top_k: keep top_k biggest masks (to reduce noise)
107
- cpu_mode: if True, uses conservative params (already set in mask_generator)
108
- """
109
- try:
110
- if image is None:
111
- return None, {"error": "Kein Bild hochgeladen"}
112
- # prepare image
113
- image_pil, image_np = prepare_image(image, max_size=max_size)
114
- h, w = image_np.shape[:2]
115
- print(f"Starting mask generation — image size: {w}x{h}, device: {device}")
116
- t0 = time.time()
117
-
118
- # generate masks (this is the heavy call)
119
- masks = mask_generator.generate(image_np)
120
-
121
- t1 = time.time()
122
- print(f"Generated {len(masks)} masks in {t1-t0:.1f}s")
123
-
124
- # filter: keep largest top_k masks
125
- masks = keep_top_masks(masks, top_k=top_k)
126
- print(f"Kept top {len(masks)} masks by area")
127
-
128
- # Convert overlay
129
- overlay = draw_masks_overlay(image_np, masks, draw_labels=True)
130
-
131
- metadata = {
132
- "success": True,
133
- "num_masks_generated": len(masks),
134
- "generation_time_s": float(t1 - t0),
135
- "image_size": [w, h],
136
- "device": device
137
- }
138
- return Image.fromarray(overlay), metadata
139
-
140
- except Exception as e:
141
- tb = traceback.format_exc()
142
- print("ERROR in segment_all_objects:", e)
143
- print(tb)
144
- return image, {"error": str(e), "trace": tb}
145
-
146
- # ---------------------------
147
- # Gradio UI
148
- # ---------------------------
149
- title = "Boostly SAM2 - AutomaticMaskGenerator"
150
- desc = "Upload an image — SAM AutomaticMaskGenerator will propose many segmentations. On CPU it's slower; use small images for tests."
151
-
152
- with gr.Blocks(title=title) as demo:
153
- gr.Markdown("# 🎨 Boostly - Auto segmentation (SAM)")
154
- with gr.Row():
155
- with gr.Column(scale=1):
156
- inp = gr.Image(type="pil", label="Upload image")
157
- max_size = gr.Slider(minimum=256, maximum=1600, value=1024, step=128, label="Max image side (resize)")
158
- top_k = gr.Slider(minimum=1, maximum=40, value=12, step=1, label="Keep top K masks (largest)")
159
- btn = gr.Button("Segment (Auto)")
160
- with gr.Column(scale=1):
161
- out_img = gr.Image(label="Segmented overlay")
162
- out_meta = gr.JSON(label="Metadata")
163
-
164
- btn.click(fn=segment_all_objects, inputs=[inp, max_size, top_k, gr.State(True)], outputs=[out_img, out_meta])
165
-
166
- if __name__ == "__main__":
167
- print("Launching Gradio app...")
168
- demo.launch(server_name="0.0.0.0", server_port=7860, show_error=True)
 
1
+ d78a30bf53e5e1fdfc7d05affd3fac335304ed94