Spaces:
Paused
Paused
Umut Kocasari Claude Opus 4.8 commited on
Commit ·
94c4dfa
1
Parent(s): dae4cfd
Default to one-by-one, robust video decode, add 40-img/1-img examples
Browse files- Default inference mode is now One-by-one (was Joint).
- Decode videos with cv2.VideoCapture instead of imageio's extension-based plugin
pick, which only yielded the first frame of webcam recordings; now the first
MAX_FRAMES frames are used (imageio fallback kept).
- Add two click-to-run gr.Examples (cache_examples=False so the build never runs
on the GPU-less builder): a 40-image sequence and a single image, vendored under
examples/seq40/ (first 40 frames of nersemble 109 EMO-1-shout+laugh), LFS-tracked.
Verified: examples build on gradio 6.19; cv2 extracts 40 frames from a 50-frame clip.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- .gitattributes +3 -0
- app.py +57 -8
- examples/seq40/frame_000001.png +3 -0
- examples/seq40/frame_000002.png +3 -0
- examples/seq40/frame_000003.png +3 -0
- examples/seq40/frame_000004.png +3 -0
- examples/seq40/frame_000005.png +3 -0
- examples/seq40/frame_000006.png +3 -0
- examples/seq40/frame_000007.png +3 -0
- examples/seq40/frame_000008.png +3 -0
- examples/seq40/frame_000009.png +3 -0
- examples/seq40/frame_000010.png +3 -0
- examples/seq40/frame_000011.png +3 -0
- examples/seq40/frame_000012.png +3 -0
- examples/seq40/frame_000013.png +3 -0
- examples/seq40/frame_000014.png +3 -0
- examples/seq40/frame_000015.png +3 -0
- examples/seq40/frame_000016.png +3 -0
- examples/seq40/frame_000017.png +3 -0
- examples/seq40/frame_000018.png +3 -0
- examples/seq40/frame_000019.png +3 -0
- examples/seq40/frame_000020.png +3 -0
- examples/seq40/frame_000021.png +3 -0
- examples/seq40/frame_000022.png +3 -0
- examples/seq40/frame_000023.png +3 -0
- examples/seq40/frame_000024.png +3 -0
- examples/seq40/frame_000025.png +3 -0
- examples/seq40/frame_000026.png +3 -0
- examples/seq40/frame_000027.png +3 -0
- examples/seq40/frame_000028.png +3 -0
- examples/seq40/frame_000029.png +3 -0
- examples/seq40/frame_000030.png +3 -0
- examples/seq40/frame_000031.png +3 -0
- examples/seq40/frame_000032.png +3 -0
- examples/seq40/frame_000033.png +3 -0
- examples/seq40/frame_000034.png +3 -0
- examples/seq40/frame_000035.png +3 -0
- examples/seq40/frame_000036.png +3 -0
- examples/seq40/frame_000037.png +3 -0
- examples/seq40/frame_000038.png +3 -0
- examples/seq40/frame_000039.png +3 -0
- examples/seq40/frame_000040.png +3 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip 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
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip 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 |
+
*.jpg filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
app.py
CHANGED
|
@@ -197,6 +197,28 @@ def _sniff_ext(path):
|
|
| 197 |
return ".png"
|
| 198 |
|
| 199 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
def _prepare_inputs(files, video, max_frames, workdir):
|
| 201 |
"""Normalize the upload (an image set OR a video) into an ordered list of
|
| 202 |
frame paths.
|
|
@@ -206,14 +228,21 @@ def _prepare_inputs(files, video, max_frames, workdir):
|
|
| 206 |
one): files are natural-sorted by their original name (temporal order),
|
| 207 |
capped, then copied as ``frame_XXXX.<ext>`` with a content-sniffed extension.
|
| 208 |
"""
|
| 209 |
-
from faceanything.io_utils import load_frame_paths
|
| 210 |
-
|
| 211 |
if video:
|
| 212 |
vpath = video if isinstance(video, str) else _to_entry(video)[0]
|
| 213 |
if vpath and os.path.exists(vpath):
|
| 214 |
-
paths
|
| 215 |
-
|
| 216 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 217 |
if paths:
|
| 218 |
return paths
|
| 219 |
|
|
@@ -576,10 +605,10 @@ def build_demo():
|
|
| 576 |
)
|
| 577 |
mode = gr.Radio(
|
| 578 |
choices=["Joint", "One-by-one"],
|
| 579 |
-
value="
|
| 580 |
label="Inference mode",
|
| 581 |
-
info="
|
| 582 |
-
"
|
| 583 |
)
|
| 584 |
remove_bg = gr.Checkbox(
|
| 585 |
value=True, label="Remove background",
|
|
@@ -650,6 +679,26 @@ def build_demo():
|
|
| 650 |
# slider — smoother than reloading a glb on every drag tick.
|
| 651 |
frame_slider.release(pick_frame, inputs=[frame_slider, view_state],
|
| 652 |
outputs=model3d)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 653 |
return demo
|
| 654 |
|
| 655 |
|
|
|
|
| 197 |
return ".png"
|
| 198 |
|
| 199 |
|
| 200 |
+
def _extract_video(video_path, max_frames, out_dir):
|
| 201 |
+
"""Decode the first ``max_frames`` frames of a video. Uses cv2.VideoCapture,
|
| 202 |
+
which (unlike imageio's extension-based plugin pick) robustly decodes webcam
|
| 203 |
+
recordings — those were yielding only a single frame otherwise."""
|
| 204 |
+
import cv2
|
| 205 |
+
|
| 206 |
+
os.makedirs(out_dir, exist_ok=True)
|
| 207 |
+
paths = []
|
| 208 |
+
cap = cv2.VideoCapture(video_path)
|
| 209 |
+
try:
|
| 210 |
+
while len(paths) < int(max_frames):
|
| 211 |
+
ok, frame = cap.read()
|
| 212 |
+
if not ok:
|
| 213 |
+
break
|
| 214 |
+
p = os.path.join(out_dir, f"frame_{len(paths):04d}.png")
|
| 215 |
+
cv2.imwrite(p, frame) # BGR ndarray -> correct-RGB PNG on disk
|
| 216 |
+
paths.append(p)
|
| 217 |
+
finally:
|
| 218 |
+
cap.release()
|
| 219 |
+
return paths
|
| 220 |
+
|
| 221 |
+
|
| 222 |
def _prepare_inputs(files, video, max_frames, workdir):
|
| 223 |
"""Normalize the upload (an image set OR a video) into an ordered list of
|
| 224 |
frame paths.
|
|
|
|
| 228 |
one): files are natural-sorted by their original name (temporal order),
|
| 229 |
capped, then copied as ``frame_XXXX.<ext>`` with a content-sniffed extension.
|
| 230 |
"""
|
|
|
|
|
|
|
| 231 |
if video:
|
| 232 |
vpath = video if isinstance(video, str) else _to_entry(video)[0]
|
| 233 |
if vpath and os.path.exists(vpath):
|
| 234 |
+
paths = _extract_video(vpath, max_frames,
|
| 235 |
+
os.path.join(workdir, "video_frames"))
|
| 236 |
+
if len(paths) <= 1: # fall back to imageio if cv2 read too few frames
|
| 237 |
+
from faceanything.io_utils import load_frame_paths
|
| 238 |
+
try:
|
| 239 |
+
alt, _ = load_frame_paths(
|
| 240 |
+
vpath, max_frames=int(max_frames), stride=1,
|
| 241 |
+
work_dir=os.path.join(workdir, "video_frames_io"))
|
| 242 |
+
if len(alt) > len(paths):
|
| 243 |
+
paths = alt
|
| 244 |
+
except Exception:
|
| 245 |
+
pass
|
| 246 |
if paths:
|
| 247 |
return paths
|
| 248 |
|
|
|
|
| 605 |
)
|
| 606 |
mode = gr.Radio(
|
| 607 |
choices=["Joint", "One-by-one"],
|
| 608 |
+
value="One-by-one",
|
| 609 |
label="Inference mode",
|
| 610 |
+
info="One-by-one: more surface detail, lower memory. "
|
| 611 |
+
"Joint (all-at-once): more 3D-consistent across frames.",
|
| 612 |
)
|
| 613 |
remove_bg = gr.Checkbox(
|
| 614 |
value=True, label="Remove background",
|
|
|
|
| 679 |
# slider — smoother than reloading a glb on every drag tick.
|
| 680 |
frame_slider.release(pick_frame, inputs=[frame_slider, view_state],
|
| 681 |
outputs=model3d)
|
| 682 |
+
|
| 683 |
+
# ---------------- examples (click to load + run) ----------------
|
| 684 |
+
ex40 = sorted(glob.glob(os.path.join(APP_DIR, "examples", "seq40", "*.png")))[:MAX_IMAGES]
|
| 685 |
+
if ex40:
|
| 686 |
+
gr.Examples(
|
| 687 |
+
examples=[
|
| 688 |
+
[ex40, None, "One-by-one", 504, True, 0, 100, 25, 0.01, 10, MAX_IMAGES],
|
| 689 |
+
[ex40[:1], None, "One-by-one", 504, True, 0, 100, 25, 0.01, 10, 1],
|
| 690 |
+
],
|
| 691 |
+
example_labels=[f"{len(ex40)}-image sequence", "Single image"],
|
| 692 |
+
inputs=[files, video, mode, process_res, remove_bg,
|
| 693 |
+
conf_percentile, n_tracks, track_k, track_threshold,
|
| 694 |
+
fps, max_frames],
|
| 695 |
+
outputs=[model3d, canonical_vid, depth_vid, normals_vid,
|
| 696 |
+
tracks2d_vid, tracks_zip, view_state, frame_slider, status],
|
| 697 |
+
fn=run,
|
| 698 |
+
run_on_click=True, # click runs the pipeline
|
| 699 |
+
cache_examples=False, # never pre-run at build (no GPU at build time)
|
| 700 |
+
label="Examples — click to load and run",
|
| 701 |
+
)
|
| 702 |
return demo
|
| 703 |
|
| 704 |
|
examples/seq40/frame_000001.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000002.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000003.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000004.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000005.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000006.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000007.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000008.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000009.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000010.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000011.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000012.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000013.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000014.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000015.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000016.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000017.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000018.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000019.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000020.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000021.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000022.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000023.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000024.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000025.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000026.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000027.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000028.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000029.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000030.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000031.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000032.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000033.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000034.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000035.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000036.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000037.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000038.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000039.png
ADDED
|
Git LFS Details
|
examples/seq40/frame_000040.png
ADDED
|
Git LFS Details
|