havan2605 commited on
Commit
e6d6db9
Β·
verified Β·
1 Parent(s): b416ab6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +48 -53
app.py CHANGED
@@ -6,10 +6,9 @@ from PIL import Image, ImageDraw, ImageFont
6
  import json
7
  import random
8
  from transformers import (
9
- AutoModelForZeroShotObjectDetection,
10
  AutoProcessor,
11
- SamModel,
12
- SamProcessor,
13
  DepthAnythingForDepthEstimation,
14
  )
15
 
@@ -22,18 +21,17 @@ print(f"Using device: {device}")
22
  # -----------------------------
23
  # Model loading
24
  # -----------------------------
25
- # 1. Grounding DINO (open-vocabulary object detection -> provides labels + boxes)
26
- GROUNDING_ID = "IDEA-Research/grounding-dino-tiny"
27
- g_processor = AutoProcessor.from_pretrained(GROUNDING_ID)
28
- g_model = AutoModelForZeroShotObjectDetection.from_pretrained(GROUNDING_ID).to(device).eval()
29
-
30
- # 2. SAM (segmentation using boxes from Grounding DINO as prompts)
31
- SAM_ID = "facebook/sam-vit-base"
32
- sam_processor = SamProcessor.from_pretrained(SAM_ID)
33
- sam_model = SamModel.from_pretrained(SAM_ID).to(device).eval()
34
-
35
- # 3. Depth Anything V2 (monocular depth estimation)
36
- DEPTH_ID = "depth-anything/Depth-Anything-V2-Small-hf"
37
  depth_processor = AutoProcessor.from_pretrained(DEPTH_ID)
38
  depth_model = DepthAnythingForDepthEstimation.from_pretrained(DEPTH_ID).to(device).eval()
39
 
@@ -41,35 +39,38 @@ depth_model = DepthAnythingForDepthEstimation.from_pretrained(DEPTH_ID).to(devic
41
  # -----------------------------
42
  # Pipeline steps
43
  # -----------------------------
44
- def detect_objects(image: Image.Image, text_prompt: str, threshold: float = 0.3):
45
- """Step 1: Open-vocabulary detection -> boxes + labels."""
46
- inputs = g_processor(images=image, text=text_prompt, return_tensors="pt").to(device)
47
- with torch.no_grad():
48
- outputs = g_model(**inputs)
49
- results = g_processor.post_process_grounded_object_detection(
50
- outputs,
51
- inputs.input_ids,
52
- threshold=threshold,
53
- text_threshold=threshold,
54
- target_sizes=[image.size[::-1]],
55
- )
56
- return results[0]
57
 
 
 
 
 
 
 
58
 
59
- def segment_with_sam(image: Image.Image, boxes):
60
- """Step 2: Use detected boxes as SAM prompts -> binary masks."""
61
- if len(boxes) == 0:
62
- return []
63
- input_boxes = [[[float(b[0]), float(b[1]), float(b[2]), float(b[3])] for b in boxes]]
64
- inputs = sam_processor(image, input_boxes=input_boxes, return_tensors="pt").to(device)
65
  with torch.no_grad():
66
- outputs = sam_model(**inputs)
67
- masks = sam_processor.image_processor.post_process_masks(
68
- outputs.pred_masks.cpu(),
69
- inputs["original_sizes"].cpu(),
70
- inputs["reshaped_input_sizes"].cpu(),
71
- )
72
- return masks[0] # [N, 1, H, W]
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
 
75
  def estimate_depth(image: Image.Image):
@@ -93,8 +94,7 @@ def estimate_depth(image: Image.Image):
93
  def average_depth_per_mask(depth_map, masks):
94
  """Step 4: Compute mean depth within each binary mask."""
95
  out = []
96
- for mask in masks:
97
- m = mask[0].numpy().astype(bool) # H x W
98
  if m.sum() == 0:
99
  out.append(None)
100
  else:
@@ -118,7 +118,7 @@ def render_visualization(image, boxes, labels, masks, avg_depths):
118
  continue
119
  color = colors[i % len(colors)]
120
  # Fill mask
121
- m = masks[i][0].numpy().astype(bool)
122
  rgba = np.zeros((m.shape[0], m.shape[1], 4), dtype=np.uint8)
123
  rgba[m] = color + (120,) # alpha
124
  m_img = Image.fromarray(rgba, mode="RGBA")
@@ -140,17 +140,12 @@ def run_pipeline(image, text_prompt, threshold):
140
 
141
  image_pil = Image.fromarray(image).convert("RGB")
142
 
143
- # Step 1 β€” Detection
144
- detection = detect_objects(image_pil, text_prompt, threshold)
145
- boxes = detection["boxes"].tolist()
146
- labels = detection["labels"]
147
 
148
  if len(boxes) == 0:
149
  return image, json.dumps({"error": "No objects detected"}), None
150
 
151
- # Step 2 β€” Segmentation
152
- masks = segment_with_sam(image_pil, boxes)
153
-
154
  # Step 3 β€” Depth estimation
155
  depth_map = estimate_depth(image_pil)
156
 
@@ -207,8 +202,8 @@ demo = gr.Interface(
207
  ],
208
  title="🧠 Object Depth Comparison Pipeline",
209
  description=(
210
- "Pipeline: Image β†’ Grounding DINO (boxes+labels) β†’ SAM (masks) β†’ "
211
- "Depth Anything V2 (depth map) β†’ Avg depth per mask β†’ JSON comparison.\n\n"
212
  "Tip: write the object classes you expect, e.g. `person, car`."
213
  )
214
  )
 
6
  import json
7
  import random
8
  from transformers import (
 
9
  AutoProcessor,
10
+ Sam3Model,
11
+ Sam3Processor,
12
  DepthAnythingForDepthEstimation,
13
  )
14
 
 
21
  # -----------------------------
22
  # Model loading
23
  # -----------------------------
24
+ # 1. SAM3 (promptable concept segmentation -> detects + segments objects from a
25
+ # text prompt in a single step, replacing the old Grounding DINO + SAM combo).
26
+ # NOTE: facebook/sam3 is a gated Meta model on the Hub β€” the environment needs
27
+ # a HF token that has accepted the model license (e.g. `huggingface-cli login`
28
+ # or the `HF_TOKEN` env var) or `from_pretrained` will fail to download it.
29
+ SAM3_ID = "facebook/sam3"
30
+ sam3_processor = Sam3Processor.from_pretrained(SAM3_ID)
31
+ sam3_model = Sam3Model.from_pretrained(SAM3_ID).to(device).eval()
32
+
33
+ # 2. Depth Anything V2 Large (monocular depth estimation)
34
+ DEPTH_ID = "depth-anything/Depth-Anything-V2-Large-hf"
 
35
  depth_processor = AutoProcessor.from_pretrained(DEPTH_ID)
36
  depth_model = DepthAnythingForDepthEstimation.from_pretrained(DEPTH_ID).to(device).eval()
37
 
 
39
  # -----------------------------
40
  # Pipeline steps
41
  # -----------------------------
42
+ def detect_and_segment(image: Image.Image, text_prompt: str, threshold: float = 0.3):
43
+ """Step 1+2: SAM3 promptable concept segmentation -> boxes + masks + labels.
 
 
 
 
 
 
 
 
 
 
 
44
 
45
+ Runs one SAM3 forward pass per requested class, reusing the image's vision
46
+ embeddings so the (expensive) vision backbone only runs once per image.
47
+ """
48
+ class_names = [c.strip() for c in text_prompt.split(",") if c.strip()]
49
+ if not class_names:
50
+ return [], [], []
51
 
52
+ img_inputs = sam3_processor(images=image, return_tensors="pt").to(device)
 
 
 
 
 
53
  with torch.no_grad():
54
+ vision_embeds = sam3_model.get_vision_features(pixel_values=img_inputs.pixel_values)
55
+
56
+ all_boxes, all_labels, all_masks = [], [], []
57
+ for class_name in class_names:
58
+ text_inputs = sam3_processor(text=class_name, return_tensors="pt").to(device)
59
+ with torch.no_grad():
60
+ outputs = sam3_model(vision_embeds=vision_embeds, **text_inputs)
61
+ result = sam3_processor.post_process_instance_segmentation(
62
+ outputs,
63
+ threshold=threshold,
64
+ mask_threshold=0.5,
65
+ target_sizes=img_inputs.get("original_sizes").tolist(),
66
+ )[0]
67
+
68
+ for box, mask in zip(result["boxes"], result["masks"]):
69
+ all_boxes.append([float(v) for v in box.tolist()])
70
+ all_labels.append(class_name)
71
+ all_masks.append(mask.cpu().numpy().astype(bool))
72
+
73
+ return all_boxes, all_labels, all_masks
74
 
75
 
76
  def estimate_depth(image: Image.Image):
 
94
  def average_depth_per_mask(depth_map, masks):
95
  """Step 4: Compute mean depth within each binary mask."""
96
  out = []
97
+ for m in masks: # each mask is already an H x W bool array
 
98
  if m.sum() == 0:
99
  out.append(None)
100
  else:
 
118
  continue
119
  color = colors[i % len(colors)]
120
  # Fill mask
121
+ m = masks[i] # already an H x W bool array
122
  rgba = np.zeros((m.shape[0], m.shape[1], 4), dtype=np.uint8)
123
  rgba[m] = color + (120,) # alpha
124
  m_img = Image.fromarray(rgba, mode="RGBA")
 
140
 
141
  image_pil = Image.fromarray(image).convert("RGB")
142
 
143
+ # Step 1+2 β€” SAM3 detection + segmentation
144
+ boxes, labels, masks = detect_and_segment(image_pil, text_prompt, threshold)
 
 
145
 
146
  if len(boxes) == 0:
147
  return image, json.dumps({"error": "No objects detected"}), None
148
 
 
 
 
149
  # Step 3 β€” Depth estimation
150
  depth_map = estimate_depth(image_pil)
151
 
 
202
  ],
203
  title="🧠 Object Depth Comparison Pipeline",
204
  description=(
205
+ "Pipeline: Image β†’ SAM3 (text-prompted boxes+masks per class) β†’ "
206
+ "Depth Anything V2 Large (depth map) β†’ Avg depth per mask β†’ JSON comparison.\n\n"
207
  "Tip: write the object classes you expect, e.g. `person, car`."
208
  )
209
  )