Spaces:
Sleeping
Sleeping
Rawal Khirodkar commited on
Commit ·
bb6c108
1
Parent(s): 1cefb98
Seg: add hero banner + 2-col layout (input | annotated overlay) + hover-pop CSS + 29 classes
Browse files- .gitattributes +1 -0
- app.py +71 -37
- assets/sapiens2.gif +3 -0
.gitattributes
CHANGED
|
@@ -34,3 +34,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
*.png filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
*.png filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
*.gif filter=lfs diff=lfs merge=lfs -text
|
app.py
CHANGED
|
@@ -30,6 +30,7 @@ _ = SegEstimator
|
|
| 30 |
|
| 31 |
ASSETS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets")
|
| 32 |
CONFIGS_DIR = os.path.join(ASSETS_DIR, "configs")
|
|
|
|
| 33 |
|
| 34 |
SEG_MODELS = {
|
| 35 |
"0.4B": {
|
|
@@ -57,13 +58,12 @@ DEFAULT_SIZE = "1B"
|
|
| 57 |
|
| 58 |
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
|
| 59 |
|
| 60 |
-
#
|
| 61 |
_CLASS_LABELS = {cid: meta["name"].replace("_", " ") for cid, meta in DOME_CLASSES_29.items()}
|
| 62 |
_CLASS_COLORS_RGB = {cid: meta["color"] for cid, meta in DOME_CLASSES_29.items()}
|
| 63 |
_CLASS_COLORS_HEX = {
|
| 64 |
_CLASS_LABELS[cid]: "#{:02x}{:02x}{:02x}".format(*meta["color"])
|
| 65 |
for cid, meta in DOME_CLASSES_29.items()
|
| 66 |
-
if cid != 0 # background skipped in AnnotatedImage
|
| 67 |
}
|
| 68 |
|
| 69 |
|
|
@@ -105,14 +105,14 @@ def _segment(image_bgr: np.ndarray, model) -> np.ndarray:
|
|
| 105 |
|
| 106 |
|
| 107 |
def _label_map_to_annotations(label_map: np.ndarray) -> List[Tuple[np.ndarray, str]]:
|
| 108 |
-
"""Convert
|
| 109 |
|
| 110 |
-
|
| 111 |
"""
|
| 112 |
annotations: List[Tuple[np.ndarray, str]] = []
|
| 113 |
for cid in np.unique(label_map):
|
| 114 |
cid = int(cid)
|
| 115 |
-
if cid
|
| 116 |
continue
|
| 117 |
mask = (label_map == cid)
|
| 118 |
if not mask.any():
|
|
@@ -146,7 +146,7 @@ def predict(image: Image.Image, size: str, opacity: float):
|
|
| 146 |
label_map = _segment(image_bgr, model) # (H, W)
|
| 147 |
|
| 148 |
annotations = _label_map_to_annotations(label_map)
|
| 149 |
-
annotated = (image_pil, annotations)
|
| 150 |
|
| 151 |
overlay_bgr = _label_map_to_overlay(image_bgr, label_map, float(opacity))
|
| 152 |
overlay_rgb = cv2.cvtColor(overlay_bgr, cv2.COLOR_BGR2RGB)
|
|
@@ -166,37 +166,71 @@ EXAMPLES = sorted(
|
|
| 166 |
if n.lower().endswith((".jpg", ".jpeg", ".png"))
|
| 167 |
)
|
| 168 |
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 178 |
with gr.Row():
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
color_map=_CLASS_COLORS_HEX,
|
| 194 |
-
height=560,
|
| 195 |
-
show_legend=True,
|
| 196 |
-
)
|
| 197 |
-
with gr.Accordion("Static overlay + raw labels", open=False):
|
| 198 |
-
out_img = gr.Image(label="Color overlay (PNG)", type="pil")
|
| 199 |
-
out_npy = gr.File(label="Raw labels (.npy uint8, class indices 0–28)")
|
| 200 |
|
| 201 |
run.click(predict, inputs=[inp, size, opacity], outputs=[out_annot, out_img, out_npy])
|
| 202 |
|
|
@@ -205,4 +239,4 @@ if __name__ == "__main__":
|
|
| 205 |
if torch.cuda.is_available():
|
| 206 |
torch.backends.cuda.matmul.allow_tf32 = True
|
| 207 |
torch.backends.cudnn.allow_tf32 = True
|
| 208 |
-
demo.launch(share=False)
|
|
|
|
| 30 |
|
| 31 |
ASSETS_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "assets")
|
| 32 |
CONFIGS_DIR = os.path.join(ASSETS_DIR, "configs")
|
| 33 |
+
HERO_GIF = os.path.join(ASSETS_DIR, "sapiens2.gif")
|
| 34 |
|
| 35 |
SEG_MODELS = {
|
| 36 |
"0.4B": {
|
|
|
|
| 58 |
|
| 59 |
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
|
| 60 |
|
| 61 |
+
# All 29 named classes (Background is class 0).
|
| 62 |
_CLASS_LABELS = {cid: meta["name"].replace("_", " ") for cid, meta in DOME_CLASSES_29.items()}
|
| 63 |
_CLASS_COLORS_RGB = {cid: meta["color"] for cid, meta in DOME_CLASSES_29.items()}
|
| 64 |
_CLASS_COLORS_HEX = {
|
| 65 |
_CLASS_LABELS[cid]: "#{:02x}{:02x}{:02x}".format(*meta["color"])
|
| 66 |
for cid, meta in DOME_CLASSES_29.items()
|
|
|
|
| 67 |
}
|
| 68 |
|
| 69 |
|
|
|
|
| 105 |
|
| 106 |
|
| 107 |
def _label_map_to_annotations(label_map: np.ndarray) -> List[Tuple[np.ndarray, str]]:
|
| 108 |
+
"""Convert (H, W) class-id map → AnnotatedImage's [(bool_mask, label), ...] list.
|
| 109 |
|
| 110 |
+
Includes all 29 named classes (Background as well) so the legend is complete.
|
| 111 |
"""
|
| 112 |
annotations: List[Tuple[np.ndarray, str]] = []
|
| 113 |
for cid in np.unique(label_map):
|
| 114 |
cid = int(cid)
|
| 115 |
+
if cid not in _CLASS_LABELS:
|
| 116 |
continue
|
| 117 |
mask = (label_map == cid)
|
| 118 |
if not mask.any():
|
|
|
|
| 146 |
label_map = _segment(image_bgr, model) # (H, W)
|
| 147 |
|
| 148 |
annotations = _label_map_to_annotations(label_map)
|
| 149 |
+
annotated = (image_pil, annotations)
|
| 150 |
|
| 151 |
overlay_bgr = _label_map_to_overlay(image_bgr, label_map, float(opacity))
|
| 152 |
overlay_rgb = cv2.cvtColor(overlay_bgr, cv2.COLOR_BGR2RGB)
|
|
|
|
| 166 |
if n.lower().endswith((".jpg", ".jpeg", ".png"))
|
| 167 |
)
|
| 168 |
|
| 169 |
+
CUSTOM_CSS = """
|
| 170 |
+
#hero { text-align: center; padding: 24px 0 12px; }
|
| 171 |
+
#hero img { max-width: 360px; width: 100%; border-radius: 12px;
|
| 172 |
+
box-shadow: 0 8px 28px rgba(0,0,0,0.18); }
|
| 173 |
+
#title { text-align: center; font-size: 44px; font-weight: 700; margin: 14px 0 0;
|
| 174 |
+
background: linear-gradient(90deg, #1d4ed8 0%, #6d28d9 50%, #be185d 100%);
|
| 175 |
+
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
|
| 176 |
+
background-clip: text; }
|
| 177 |
+
#subtitle { text-align: center; font-size: 16px; color: #64748b;
|
| 178 |
+
letter-spacing: 0.08em; margin: 4px 0 12px; text-transform: uppercase; }
|
| 179 |
+
#tagline { text-align: center; font-size: 16px; color: #475569;
|
| 180 |
+
max-width: 720px; margin: 4px auto 18px; line-height: 1.55; }
|
| 181 |
+
#badges { text-align: center; margin: 0 0 28px; }
|
| 182 |
+
#badges img { display: inline-block; margin: 0 4px; height: 28px; vertical-align: middle; }
|
| 183 |
+
|
| 184 |
+
/* AnnotatedImage hover pop: when a region is highlighted, lift + brighten it */
|
| 185 |
+
#seg-out .layer-wrap canvas { transition: transform 180ms ease, filter 180ms ease; }
|
| 186 |
+
#seg-out .layer-wrap.active canvas { transform: translateY(-3px) scale(1.01);
|
| 187 |
+
filter: brightness(1.18) drop-shadow(0 6px 12px rgba(0,0,0,0.25)); }
|
| 188 |
+
#seg-out .legend-item { transition: transform 140ms ease; }
|
| 189 |
+
#seg-out .legend-item:hover { transform: translateY(-1px); }
|
| 190 |
+
"""
|
| 191 |
+
|
| 192 |
+
HEADER_HTML = """
|
| 193 |
+
<div id="hero"><img src="/gradio_api/file=assets/sapiens2.gif" alt="Sapiens2"/></div>
|
| 194 |
+
<div id="title">Sapiens2: Body-Part Segmentation</div>
|
| 195 |
+
<div id="subtitle">ICLR 2026 · 29-class semantic segmentation</div>
|
| 196 |
+
<div id="tagline">Hover any predicted region to see its class name. Pick a model size and an
|
| 197 |
+
overlay opacity, then click Run.</div>
|
| 198 |
+
<div id="badges">
|
| 199 |
+
<a href="https://github.com/facebookresearch/sapiens2"><img src="https://img.shields.io/badge/Code-GitHub-181717?logo=github&logoColor=white"></a>
|
| 200 |
+
<a href="https://huggingface.co/facebook/sapiens2"><img src="https://img.shields.io/badge/Models-HuggingFace-FF9D00?logo=huggingface&logoColor=white"></a>
|
| 201 |
+
<a href="https://openreview.net/pdf?id=IVAlYCqdvW"><img src="https://img.shields.io/badge/Paper-OpenReview-94DD15?logo=adobeacrobatreader&logoColor=white"></a>
|
| 202 |
+
<a href="https://rawalkhirodkar.github.io/sapiens2"><img src="https://img.shields.io/badge/Project-Page-007FFF?logo=googlechrome&logoColor=white"></a>
|
| 203 |
+
</div>
|
| 204 |
+
"""
|
| 205 |
+
|
| 206 |
+
with gr.Blocks(title="Sapiens2 Seg", theme=gr.themes.Soft(), css=CUSTOM_CSS) as demo:
|
| 207 |
+
gr.HTML(HEADER_HTML)
|
| 208 |
+
|
| 209 |
+
with gr.Row(equal_height=True):
|
| 210 |
+
inp = gr.Image(label="Input (RGB)", type="pil", height=540)
|
| 211 |
+
out_annot = gr.AnnotatedImage(
|
| 212 |
+
label="Body parts — hover to highlight",
|
| 213 |
+
color_map=_CLASS_COLORS_HEX,
|
| 214 |
+
height=540,
|
| 215 |
+
show_legend=True,
|
| 216 |
+
elem_id="seg-out",
|
| 217 |
+
)
|
| 218 |
+
|
| 219 |
with gr.Row():
|
| 220 |
+
size = gr.Radio(
|
| 221 |
+
choices=list(SEG_MODELS.keys()),
|
| 222 |
+
value=DEFAULT_SIZE,
|
| 223 |
+
label="Model size",
|
| 224 |
+
scale=2,
|
| 225 |
+
)
|
| 226 |
+
opacity = gr.Slider(0.0, 1.0, value=0.5, step=0.05, label="Overlay opacity", scale=2)
|
| 227 |
+
run = gr.Button("Run", variant="primary", size="lg", scale=1)
|
| 228 |
+
|
| 229 |
+
gr.Examples(examples=EXAMPLES, inputs=inp, examples_per_page=14)
|
| 230 |
+
|
| 231 |
+
with gr.Accordion("Static overlay + raw labels", open=False):
|
| 232 |
+
out_img = gr.Image(label="Color overlay (PNG)", type="pil")
|
| 233 |
+
out_npy = gr.File(label="Raw labels (.npy uint8, class indices 0–28)")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
|
| 235 |
run.click(predict, inputs=[inp, size, opacity], outputs=[out_annot, out_img, out_npy])
|
| 236 |
|
|
|
|
| 239 |
if torch.cuda.is_available():
|
| 240 |
torch.backends.cuda.matmul.allow_tf32 = True
|
| 241 |
torch.backends.cudnn.allow_tf32 = True
|
| 242 |
+
demo.launch(share=False, allowed_paths=[ASSETS_DIR])
|
assets/sapiens2.gif
ADDED
|
Git LFS Details
|