pormungtai commited on
Commit
4ead73f
·
verified ·
1 Parent(s): 7b2c15e

Add generate_from_image_video: in-Space SAM3 masking (SCAIL-Pose e2e) + ultralytics/moviepy

Browse files
Files changed (2) hide show
  1. app.py +129 -0
  2. requirements.txt +3 -0
app.py CHANGED
@@ -1255,6 +1255,96 @@ def _sampling_controls(prefix: str = ""):
1255
  return steps, cfg, shift, seed, target_size, segment_len, segment_overlap
1256
 
1257
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1258
  def build_ui():
1259
  examples = _existing_examples()
1260
  example_names = list(examples.keys())
@@ -1371,6 +1461,41 @@ def build_ui():
1371
  outputs=[upload_output, upload_status],
1372
  )
1373
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1374
  with gr.Tab("Advanced Pack"):
1375
  gr.Markdown(
1376
  "Upload a `.zip` pack for multi-reference or multi-character inputs. "
@@ -1491,6 +1616,10 @@ if __name__ == "__main__":
1491
  _prepare_runtime_for_startup()
1492
  if PRELOAD_PIPELINE:
1493
  _prepare_pipeline_for_startup()
 
 
 
 
1494
  build_ui().queue(max_size=8).launch(
1495
  allowed_paths=[str(OUTPUT_DIR.resolve())],
1496
  show_error=True,
 
1255
  return steps, cfg, shift, seed, target_size, segment_len, segment_overlap
1256
 
1257
 
1258
+ # ============================================================================
1259
+ # NEW: generate_from_image_video — ทำ SAM3 mask บน GPU เอง (ส่งแค่รูป+วิดีโอ)
1260
+ # ใช้ SCAIL-Pose e2e (process_one e2e_mode → SAM3 ผ่าน ultralytics) → _run_scail_job
1261
+ # ============================================================================
1262
+ import tempfile as _tempfile
1263
+
1264
+ _SCAIL_POSE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "SCAIL-Pose")
1265
+ if _SCAIL_POSE_DIR not in sys.path:
1266
+ sys.path.insert(0, _SCAIL_POSE_DIR)
1267
+
1268
+ SAM3_MODEL_PATH = os.getenv(
1269
+ "SAM3_MODEL", os.path.join(_SCAIL_POSE_DIR, "pretrained_weights", "sam3.pt")
1270
+ )
1271
+
1272
+ _sam3_predictor = None
1273
+
1274
+
1275
+ def _get_sam3_predictor():
1276
+ global _sam3_predictor
1277
+ if _sam3_predictor is None:
1278
+ from ultralytics.models.sam import SAM3VideoSemanticPredictor
1279
+ overrides = dict(
1280
+ conf=0.25, task="segment", mode="predict", imgsz=640,
1281
+ model=SAM3_MODEL_PATH, half=True, save=False, verbose=False,
1282
+ )
1283
+ _sam3_predictor = SAM3VideoSemanticPredictor(overrides=overrides, new_det_thresh=1.0)
1284
+ return _sam3_predictor
1285
+
1286
+
1287
+ def _make_masks_e2e(subdir, video_name="driving.mp4",
1288
+ text=("human", "character"), max_persons=1):
1289
+ from NLFPoseExtract.process_animation_aio import process_one
1290
+ predictor = _get_sam3_predictor()
1291
+ process_one(
1292
+ subdir, video_name, e2e_mode=True, crop_kind=None,
1293
+ max_persons=int(max_persons), text=list(text),
1294
+ model_nlf=None, detector=None, predictor=predictor, image_predictor=None,
1295
+ )
1296
+ return (
1297
+ os.path.join(subdir, "ref_mask.jpg"),
1298
+ os.path.join(subdir, "rendered_mask_v2.mp4"),
1299
+ os.path.join(subdir, "rendered_v2.mp4"),
1300
+ )
1301
+
1302
+
1303
+ def _ensure_sam3():
1304
+ if os.path.exists(SAM3_MODEL_PATH):
1305
+ return
1306
+ os.makedirs(os.path.dirname(SAM3_MODEL_PATH), exist_ok=True)
1307
+ tok = os.getenv("HF_TOKEN") or os.getenv("HUGGING_FACE_HUB_TOKEN")
1308
+ f = hf_hub_download("facebook/sam3", "sam3.pt", token=tok)
1309
+ try:
1310
+ os.symlink(f, SAM3_MODEL_PATH)
1311
+ except OSError:
1312
+ shutil.copyfile(f, SAM3_MODEL_PATH)
1313
+
1314
+
1315
+ @spaces.GPU(duration=_duration_for_job, size=GPU_SIZE)
1316
+ def generate_from_image_video(
1317
+ image, pose_video, prompt, mode,
1318
+ sample_steps, guide_scale, sample_shift, seed,
1319
+ target_size, segment_len, segment_overlap,
1320
+ progress=gr.Progress(track_tqdm=True),
1321
+ ):
1322
+ try:
1323
+ if image is None or pose_video is None:
1324
+ raise RuntimeError("Missing required input(s): reference image / pose video")
1325
+ target_w, target_h = [int(v) for v in str(target_size).split("x")]
1326
+ subdir = _tempfile.mkdtemp(prefix="scail_iv_")
1327
+ ref_path = os.path.join(subdir, "ref.png")
1328
+ drv_path = os.path.join(subdir, "driving.mp4")
1329
+ shutil.copyfile(image, ref_path)
1330
+ shutil.copyfile(pose_video, drv_path)
1331
+ progress(0.05, desc="Masking with SAM3 on GPU ...")
1332
+ ref_mask, drv_mask, rendered = _make_masks_e2e(
1333
+ subdir, "driving.mp4", text=("human", "character"), max_persons=1,
1334
+ )
1335
+ progress(0.35, desc="Running SCAIL-2 generation ...")
1336
+ output = _run_scail_job(
1337
+ ref_path, ref_mask, rendered, drv_mask,
1338
+ prompt, (mode == "replacement"), target_h, target_w,
1339
+ sample_steps, guide_scale, sample_shift, seed,
1340
+ segment_len, segment_overlap, progress=progress,
1341
+ )
1342
+ return output, "Done."
1343
+ except Exception:
1344
+ logging.exception("generate_from_image_video failed")
1345
+ return None, traceback.format_exc()
1346
+
1347
+
1348
  def build_ui():
1349
  examples = _existing_examples()
1350
  example_names = list(examples.keys())
 
1461
  outputs=[upload_output, upload_status],
1462
  )
1463
 
1464
+ with gr.Tab("Image + Video (auto-mask)"):
1465
+ gr.Markdown(
1466
+ "Send only a **reference image** + **driving video**. "
1467
+ "The Space runs SAM3 masking on the GPU (SCAIL-Pose e2e) and generates."
1468
+ )
1469
+ iv_image = gr.Image(type="filepath", label="Reference image")
1470
+ iv_video = gr.Video(label="Driving / pose video")
1471
+ iv_prompt = gr.Textbox(label="Prompt", value="A young woman with natural body movement.")
1472
+ iv_mode = gr.Radio(["animation", "replacement"], value="animation", label="Mode")
1473
+ with gr.Row():
1474
+ iv_steps = gr.Slider(1, 50, value=8, step=1, label="Steps")
1475
+ iv_cfg = gr.Slider(1, 10, value=1.0, step=0.5, label="CFG (guide_scale)")
1476
+ iv_shift = gr.Slider(1, 10, value=3.0, step=0.5, label="Shift")
1477
+ with gr.Row():
1478
+ iv_seed = gr.Number(value=42, precision=0, label="Seed")
1479
+ iv_target = gr.Dropdown(
1480
+ ["512x896", "896x512", "704x1280", "1280x704"],
1481
+ value="512x896", label="Target size",
1482
+ )
1483
+ iv_segment_len = gr.Number(value=81, precision=0, label="Segment length")
1484
+ iv_segment_overlap = gr.Number(value=5, precision=0, label="Segment overlap")
1485
+ run_iv = gr.Button("Generate (auto-mask)", variant="primary")
1486
+ iv_output = gr.Video(label="Output")
1487
+ iv_status = gr.Textbox(label="Run status")
1488
+ run_iv.click(
1489
+ generate_from_image_video,
1490
+ inputs=[
1491
+ iv_image, iv_video, iv_prompt, iv_mode,
1492
+ iv_steps, iv_cfg, iv_shift, iv_seed,
1493
+ iv_target, iv_segment_len, iv_segment_overlap,
1494
+ ],
1495
+ outputs=[iv_output, iv_status],
1496
+ api_name="generate_from_image_video",
1497
+ )
1498
+
1499
  with gr.Tab("Advanced Pack"):
1500
  gr.Markdown(
1501
  "Upload a `.zip` pack for multi-reference or multi-character inputs. "
 
1616
  _prepare_runtime_for_startup()
1617
  if PRELOAD_PIPELINE:
1618
  _prepare_pipeline_for_startup()
1619
+ try:
1620
+ _ensure_sam3() # โหลด sam3.pt (ครั้งแรก) — ต้องมี secret HF_TOKEN ที่ได้สิทธิ์ facebook/sam3
1621
+ except Exception as _e:
1622
+ logging.warning("SAM3 weights not ready: %s", _e)
1623
  build_ui().queue(max_size=8).launch(
1624
  allowed_paths=[str(OUTPUT_DIR.resolve())],
1625
  show_error=True,
requirements.txt CHANGED
@@ -18,3 +18,6 @@ ftfy
18
  tqdm
19
  einops
20
  numpy>=1.23.5,<2
 
 
 
 
18
  tqdm
19
  einops
20
  numpy>=1.23.5,<2
21
+
22
+ ultralytics
23
+ moviepy>=1.0.3