Spaces:
Sleeping
Sleeping
Fix ZeroGPU SAM3 runtime and request diagnostics
Browse files
README.md
CHANGED
|
@@ -7,6 +7,7 @@ pinned: false
|
|
| 7 |
short_description: SAM3 open-vocabulary panoptic concept segmentation API
|
| 8 |
---
|
| 9 |
|
| 10 |
-
`api_panoptic(image, concepts, confidence, mask_threshold)` returns
|
| 11 |
-
detections with base64-encoded PNG masks.
|
| 12 |
-
|
|
|
|
|
|
| 7 |
short_description: SAM3 open-vocabulary panoptic concept segmentation API
|
| 8 |
---
|
| 9 |
|
| 10 |
+
`api_panoptic(image, concepts, confidence, mask_threshold)` returns a compressed
|
| 11 |
+
`.json.gz` file containing detections with base64-encoded PNG masks. File
|
| 12 |
+
transport avoids blocking Gradio's event channel with large inline mask JSON.
|
| 13 |
+
The Space requires an `HF_TOKEN` secret with access to `facebook/sam3`.
|
app.py
CHANGED
|
@@ -3,8 +3,11 @@
|
|
| 3 |
from __future__ import annotations
|
| 4 |
|
| 5 |
import base64
|
|
|
|
| 6 |
import io
|
|
|
|
| 7 |
import os
|
|
|
|
| 8 |
import traceback
|
| 9 |
|
| 10 |
import gradio as gr
|
|
@@ -33,6 +36,14 @@ def _encode_mask(mask_bool: np.ndarray) -> str:
|
|
| 33 |
return base64.b64encode(buffer.getvalue()).decode("ascii")
|
| 34 |
|
| 35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
def _gpu_duration(image, concepts, conf, mask_threshold=0.5) -> int:
|
| 37 |
del image, conf, mask_threshold
|
| 38 |
concept_count = len(
|
|
@@ -44,20 +55,24 @@ def _gpu_duration(image, concepts, conf, mask_threshold=0.5) -> int:
|
|
| 44 |
@spaces.GPU(duration=_gpu_duration)
|
| 45 |
def api_panoptic(image, concepts, conf, mask_threshold=0.5):
|
| 46 |
if image is None:
|
| 47 |
-
return
|
|
|
|
|
|
|
| 48 |
image = image.convert("RGB")
|
| 49 |
width, height = image.size
|
| 50 |
concept_list = [
|
| 51 |
value.strip() for value in str(concepts or "").split(",") if value.strip()
|
| 52 |
]
|
| 53 |
if not concept_list:
|
| 54 |
-
return
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
|
|
|
|
|
|
| 61 |
|
| 62 |
print(
|
| 63 |
f"SAM3 request: size={width}x{height} concepts={len(concept_list)}",
|
|
@@ -114,21 +129,30 @@ def api_panoptic(image, concepts, conf, mask_threshold=0.5):
|
|
| 114 |
traceback.print_exc()
|
| 115 |
raise
|
| 116 |
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 125 |
|
| 126 |
|
| 127 |
with gr.Blocks(title="SAM3 Panoptic") as demo:
|
| 128 |
gr.Markdown("# SAM3 Panoptic API")
|
| 129 |
with gr.Row():
|
| 130 |
input_image = gr.Image(type="pil", label="Image")
|
| 131 |
-
|
|
|
|
|
|
|
| 132 |
concept_text = gr.Textbox(
|
| 133 |
label="Concepts",
|
| 134 |
value="person, car, road, building, tree",
|
|
@@ -142,7 +166,7 @@ with gr.Blocks(title="SAM3 Panoptic") as demo:
|
|
| 142 |
gr.Button("Segment").click(
|
| 143 |
api_panoptic,
|
| 144 |
[input_image, concept_text, confidence, mask_threshold],
|
| 145 |
-
|
| 146 |
api_name="api_panoptic",
|
| 147 |
)
|
| 148 |
|
|
|
|
| 3 |
from __future__ import annotations
|
| 4 |
|
| 5 |
import base64
|
| 6 |
+
import gzip
|
| 7 |
import io
|
| 8 |
+
import json
|
| 9 |
import os
|
| 10 |
+
import tempfile
|
| 11 |
import traceback
|
| 12 |
|
| 13 |
import gradio as gr
|
|
|
|
| 36 |
return base64.b64encode(buffer.getvalue()).decode("ascii")
|
| 37 |
|
| 38 |
|
| 39 |
+
def _write_response_file(payload: dict) -> str:
|
| 40 |
+
descriptor, path = tempfile.mkstemp(prefix="sam3_", suffix=".json.gz")
|
| 41 |
+
os.close(descriptor)
|
| 42 |
+
with gzip.open(path, "wt", encoding="utf-8", compresslevel=6) as handle:
|
| 43 |
+
json.dump(payload, handle, separators=(",", ":"))
|
| 44 |
+
return path
|
| 45 |
+
|
| 46 |
+
|
| 47 |
def _gpu_duration(image, concepts, conf, mask_threshold=0.5) -> int:
|
| 48 |
del image, conf, mask_threshold
|
| 49 |
concept_count = len(
|
|
|
|
| 55 |
@spaces.GPU(duration=_gpu_duration)
|
| 56 |
def api_panoptic(image, concepts, conf, mask_threshold=0.5):
|
| 57 |
if image is None:
|
| 58 |
+
return _write_response_file(
|
| 59 |
+
{"error": "no image provided", "detections": []}
|
| 60 |
+
)
|
| 61 |
image = image.convert("RGB")
|
| 62 |
width, height = image.size
|
| 63 |
concept_list = [
|
| 64 |
value.strip() for value in str(concepts or "").split(",") if value.strip()
|
| 65 |
]
|
| 66 |
if not concept_list:
|
| 67 |
+
return _write_response_file(
|
| 68 |
+
{
|
| 69 |
+
"version": "5",
|
| 70 |
+
"model": MODEL_ID,
|
| 71 |
+
"width": width,
|
| 72 |
+
"height": height,
|
| 73 |
+
"detections": [],
|
| 74 |
+
}
|
| 75 |
+
)
|
| 76 |
|
| 77 |
print(
|
| 78 |
f"SAM3 request: size={width}x{height} concepts={len(concept_list)}",
|
|
|
|
| 129 |
traceback.print_exc()
|
| 130 |
raise
|
| 131 |
|
| 132 |
+
response_path = _write_response_file(
|
| 133 |
+
{
|
| 134 |
+
"version": "5",
|
| 135 |
+
"model": MODEL_ID,
|
| 136 |
+
"width": width,
|
| 137 |
+
"height": height,
|
| 138 |
+
"detections": detections,
|
| 139 |
+
}
|
| 140 |
+
)
|
| 141 |
+
print(
|
| 142 |
+
f"SAM3 response: detections={len(detections)} "
|
| 143 |
+
f"transport_bytes={os.path.getsize(response_path)}",
|
| 144 |
+
flush=True,
|
| 145 |
+
)
|
| 146 |
+
return response_path
|
| 147 |
|
| 148 |
|
| 149 |
with gr.Blocks(title="SAM3 Panoptic") as demo:
|
| 150 |
gr.Markdown("# SAM3 Panoptic API")
|
| 151 |
with gr.Row():
|
| 152 |
input_image = gr.Image(type="pil", label="Image")
|
| 153 |
+
output_file = gr.File(
|
| 154 |
+
type="filepath", label="Compressed detections (.json.gz)"
|
| 155 |
+
)
|
| 156 |
concept_text = gr.Textbox(
|
| 157 |
label="Concepts",
|
| 158 |
value="person, car, road, building, tree",
|
|
|
|
| 166 |
gr.Button("Segment").click(
|
| 167 |
api_panoptic,
|
| 168 |
[input_image, concept_text, confidence, mask_threshold],
|
| 169 |
+
output_file,
|
| 170 |
api_name="api_panoptic",
|
| 171 |
)
|
| 172 |
|