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
Files changed (3) hide show
  1. .gitattributes +1 -0
  2. app.py +71 -37
  3. 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
- # Class id readable label, RGB color tuple, "#rrggbb" hex (for gr.AnnotatedImage color_map).
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 a (H, W) class-id map into AnnotatedImage's [(mask, label), ...] format.
109
 
110
- Each non-background class present in the prediction yields one boolean mask.
111
  """
112
  annotations: List[Tuple[np.ndarray, str]] = []
113
  for cid in np.unique(label_map):
114
  cid = int(cid)
115
- if cid == 0 or cid not in _CLASS_LABELS: # skip Background
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) # gr.AnnotatedImage value tuple
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
- with gr.Blocks(title="Sapiens2 Seg", theme=gr.themes.Soft()) as demo:
170
- gr.Markdown(
171
- "# Sapiens2: Body-Part Segmentation\n"
172
- "### ICLR 2026\n"
173
- "29-class per-pixel body-part segmentation. Hover over the predicted regions to highlight each part.\n\n"
174
- "[Code](https://github.com/facebookresearch/sapiens2) · "
175
- "[Models](https://huggingface.co/facebook/sapiens2) · "
176
- "[Paper](https://openreview.net/pdf?id=IVAlYCqdvW)"
177
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
178
  with gr.Row():
179
- with gr.Column(scale=1):
180
- inp = gr.Image(label="Input", type="pil", height=420)
181
- with gr.Row():
182
- size = gr.Radio(
183
- choices=list(SEG_MODELS.keys()),
184
- value=DEFAULT_SIZE,
185
- label="Model size",
186
- )
187
- opacity = gr.Slider(0.0, 1.0, value=0.5, step=0.05, label="Overlay opacity")
188
- run = gr.Button("Run", variant="primary")
189
- gr.Examples(examples=EXAMPLES, inputs=inp, examples_per_page=14)
190
- with gr.Column(scale=2):
191
- out_annot = gr.AnnotatedImage(
192
- label="Body parts (hover to highlight)",
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

  • SHA256: 1b5207fe975943c81239f0b587d320b5d949ebfe5e477ad07ed516a292f16292
  • Pointer size: 132 Bytes
  • Size of remote file: 3.65 MB