fffiloni commited on
Commit
d1bb7db
·
verified ·
1 Parent(s): eeba86f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +169 -3
app.py CHANGED
@@ -720,6 +720,130 @@ def build_advanced_character_pack(
720
  return [], None, None, None, traceback.format_exc()
721
 
722
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
723
  def build_ui():
724
  with gr.Blocks(title="SCAIL-Pose Pack Builder") as demo:
725
  gr.Markdown(
@@ -788,6 +912,48 @@ def build_ui():
788
  outputs=[simple_ref_mask, simple_rendered, simple_mask_video, simple_zip, simple_status],
789
  )
790
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
791
  with gr.Tab("Advanced Character Pack"):
792
  gr.Markdown(
793
  "Create a canonical SCAIL-2 animation pack for multi-reference or multi-character cases. "
@@ -869,8 +1035,8 @@ def build_ui():
869
 
870
  with gr.Tab("Pack Format"):
871
  gr.Markdown(
872
- "The Simple Pack tab exports flat packs. The Advanced Character Pack tab exports a canonical "
873
- "character pack with explicit character folders.\n\n"
874
  "Simple animation pack:\n"
875
  "```text\n"
876
  "ref.png\n"
@@ -913,4 +1079,4 @@ def build_ui():
913
 
914
 
915
  if __name__ == "__main__":
916
- build_ui().queue(max_size=4).launch(show_error=True)
 
720
  return [], None, None, None, traceback.format_exc()
721
 
722
 
723
+ @spaces.GPU(duration=GPU_DURATION, size=GPU_SIZE)
724
+ def build_two_character_pack(
725
+ character_0_ref,
726
+ character_1_ref,
727
+ driving_video,
728
+ prompt,
729
+ sam3_text,
730
+ progress=gr.Progress(track_tqdm=True),
731
+ ):
732
+ try:
733
+ progress(0.0, desc="Checking SCAIL-Pose repo")
734
+ _require_repo_layout()
735
+ progress(0.04, desc="Preparing SAM3 weights")
736
+ sam3_model = _ensure_sam3_weights()
737
+
738
+ job_dir = _new_job_dir("two_character_animation")
739
+ pack_root = job_dir / "scail2_input_pack"
740
+ pack_root.mkdir(parents=True, exist_ok=True)
741
+
742
+ progress(0.08, desc="Preparing two-character inputs")
743
+ work_driving = job_dir / "driving.mp4"
744
+ _copy_video(driving_video, work_driving, "driving video")
745
+ shutil.copy2(work_driving, pack_root / "rendered_v2.mp4")
746
+
747
+ prepared_refs = [
748
+ CharacterRef(
749
+ character="character_0",
750
+ view="front",
751
+ path=_copy_reference_to_png(_as_path(character_0_ref, "character 0 reference"), pack_root / "characters" / "character_0" / "front.png"),
752
+ ),
753
+ CharacterRef(
754
+ character="character_1",
755
+ view="front",
756
+ path=_copy_reference_to_png(_as_path(character_1_ref, "character 1 reference"), pack_root / "characters" / "character_1" / "front.png"),
757
+ ),
758
+ ]
759
+ character_names = ["character_0", "character_1"]
760
+ primary = prepared_refs[0]
761
+ _build_advanced_metadata(pack_root, "animation", prompt, primary, character_names, sam3_text)
762
+
763
+ progress(0.12, desc="Loading SAM3 predictors")
764
+ video_predictor, image_predictor = _load_sam3_predictors(sam3_model)
765
+
766
+ from TrackSam3.track import get_mask_from_image, get_mask_from_image_via_video, get_mask_from_video
767
+ from NLFPoseExtract.v2_helper import save_colored_mask_image, write_colored_mask_video
768
+
769
+ text = _text_args(sam3_text)
770
+ colors = SCAIL_COLORS[:2]
771
+
772
+ progress(0.20, desc="Tracking two subjects left-to-right")
773
+ drv_masks, drv_colors = get_mask_from_video(
774
+ str(work_driving),
775
+ video_predictor,
776
+ max_targets=2,
777
+ sort_by="x",
778
+ fixed_colors=colors,
779
+ text=text,
780
+ )
781
+ if len(drv_masks) < 2:
782
+ raise RuntimeError(
783
+ f"SAM3 detected {len(drv_masks)} subject(s), but this mode expects two. "
784
+ "Try a clearer driving video or use the Advanced Character Pack."
785
+ )
786
+
787
+ import cv2
788
+
789
+ cap = cv2.VideoCapture(str(work_driving))
790
+ fps = cap.get(cv2.CAP_PROP_FPS)
791
+ cap.release()
792
+ fps_int = max(1, int(round(fps or 24)))
793
+ write_colored_mask_video(
794
+ drv_masks[:2],
795
+ drv_colors[:2],
796
+ str(pack_root / "rendered_mask_v2.mp4"),
797
+ fps_int,
798
+ bg_color=(255, 255, 255),
799
+ )
800
+
801
+ progress(0.55, desc="Generating reference masks")
802
+ logs = [
803
+ "Mode: two-character animation",
804
+ "Mapping: left-most driving subject -> character_0; next subject to the right -> character_1",
805
+ f"Driving tracks detected: {len(drv_masks)}",
806
+ ]
807
+ for ref in prepared_refs:
808
+ character_idx = character_names.index(ref.character)
809
+ color = drv_colors[character_idx]
810
+ mask_path = ref.path.with_name(f"{ref.view}_mask.png")
811
+ progress(None, desc=f"Masking {ref.character}")
812
+ try:
813
+ ref_masks, _ = get_mask_from_image_via_video(
814
+ str(ref.path),
815
+ video_predictor,
816
+ max_targets=1,
817
+ sort_by="x",
818
+ fixed_colors=[color],
819
+ text=text,
820
+ )
821
+ except Exception as exc:
822
+ logging.warning("Video-based image masking failed for %s, trying image predictor: %s", ref.path, exc)
823
+ ref_masks, _ = get_mask_from_image(
824
+ str(ref.path),
825
+ image_predictor,
826
+ max_targets=1,
827
+ sort_by="x",
828
+ fixed_colors=[color],
829
+ text=text,
830
+ )
831
+ if len(ref_masks) == 0:
832
+ raise RuntimeError(f"SAM3 did not detect a subject in {ref.character}.")
833
+ save_colored_mask_image([ref_masks[0]], [color], str(mask_path), bg_color=(255, 255, 255))
834
+ logs.append(f"- {ref.character}: {mask_path.relative_to(pack_root).as_posix()}")
835
+
836
+ progress(0.92, desc="Packaging two-character pack")
837
+ zip_path = _write_canonical_pack_zip(pack_root, "two_character_animation")
838
+ gallery = _advanced_gallery(pack_root)
839
+ progress(1.0, desc="Done")
840
+ status = f"Done. Two-character pack created at {zip_path}\n\n" + "\n".join(logs)
841
+ return gallery, str(pack_root / "rendered_v2.mp4"), str(pack_root / "rendered_mask_v2.mp4"), str(zip_path), status
842
+ except Exception:
843
+ logging.exception("Two-character pack generation failed")
844
+ return [], None, None, None, traceback.format_exc()
845
+
846
+
847
  def build_ui():
848
  with gr.Blocks(title="SCAIL-Pose Pack Builder") as demo:
849
  gr.Markdown(
 
912
  outputs=[simple_ref_mask, simple_rendered, simple_mask_video, simple_zip, simple_status],
913
  )
914
 
915
+ with gr.Tab("Two Characters"):
916
+ gr.Markdown(
917
+ "Fast path for the common two-character case. Upload two reference images and one driving video. "
918
+ "`Character 0` should match the left-most subject in the driving video; `Character 1` should match "
919
+ "the next subject to the right."
920
+ )
921
+ with gr.Row():
922
+ two_char0 = gr.Image(type="filepath", label="Character 0 reference")
923
+ two_char1 = gr.Image(type="filepath", label="Character 1 reference")
924
+ two_driving = gr.Video(label="Driving video")
925
+ with gr.Accordion("Optional settings", open=False):
926
+ two_prompt = gr.Textbox(label="Prompt for SCAIL-2", lines=3)
927
+ two_sam3_text = gr.Textbox(
928
+ value="human character",
929
+ label="SAM3 text prompt",
930
+ info="Use a broad prompt such as `human character`, or comma/newline-separated prompts for distinct subjects.",
931
+ )
932
+ two_run = gr.Button("Generate two-character pack", variant="primary")
933
+ two_gallery = gr.Gallery(
934
+ label="References and generated masks",
935
+ columns=4,
936
+ height=320,
937
+ selected_index=0,
938
+ preview=True,
939
+ )
940
+ with gr.Row():
941
+ two_rendered = gr.Video(label="Rendered / driving video")
942
+ two_mask_video = gr.Video(label="Driving mask video")
943
+ two_zip = gr.File(label="Download two-character SCAIL-2 pack")
944
+ two_status = gr.Textbox(label="Run logs", lines=14)
945
+ two_run.click(
946
+ build_two_character_pack,
947
+ inputs=[
948
+ two_char0,
949
+ two_char1,
950
+ two_driving,
951
+ two_prompt,
952
+ two_sam3_text,
953
+ ],
954
+ outputs=[two_gallery, two_rendered, two_mask_video, two_zip, two_status],
955
+ )
956
+
957
  with gr.Tab("Advanced Character Pack"):
958
  gr.Markdown(
959
  "Create a canonical SCAIL-2 animation pack for multi-reference or multi-character cases. "
 
1035
 
1036
  with gr.Tab("Pack Format"):
1037
  gr.Markdown(
1038
+ "The Simple Pack tab exports flat packs. The Two Characters and Advanced Character Pack tabs "
1039
+ "export canonical character packs with explicit character folders.\n\n"
1040
  "Simple animation pack:\n"
1041
  "```text\n"
1042
  "ref.png\n"
 
1079
 
1080
 
1081
  if __name__ == "__main__":
1082
+ build_ui().queue(max_size=4).launch(show_error=True)