This commit is contained in:
Pablerdo
2025-04-19 05:07:06 -04:00
parent f5751707f6
commit 28a6095b31
+126 -2
View File
@@ -1589,6 +1589,8 @@ class SVCFly:
# If no frames were generated, return the input
return (images, video_path or "")
print(f"Found {len(frame_files)} frames in {frames_path}")
# Load frames and convert to tensor in ComfyUI format (BCHW)
frames = []
for frame_path in frame_files:
@@ -1632,13 +1634,135 @@ class SVCFly:
)
return target_c2ws, target_Ks
class SVCFly_Bash:
"""ComfyUI node for Stable Virtual Camera Flythrough generation using the bash script."""
def __init__(self):
# Create an abort event for rendering control
self.abort_event = threading.Event()
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"images": ("IMAGE",),
"seed": ("INT", {"default": 23, "min": 0, "max": 0xffffffffffffffff}),
"cfg": ("FLOAT", {"default": 2.0, "min": 1.0, "max": 5.0, "step": 0.1}),
"camera_scale": ("FLOAT", {"default": 2.0, "min": 0.1, "max": 10.0, "step": 0.1}),
"num_frames": ("INT", {"default": 30, "min": 5, "max": 120, "step": 1}),
},
"optional": {
"chunk_strategy": (["interp-gt", "interp"], {"default": "interp-gt"}),
}
}
RETURN_TYPES = ("IMAGE", "STRING")
RETURN_NAMES = ("video_frames", "video_path")
FUNCTION = "generate_fly"
CATEGORY = "StableVirtualCamera"
def generate_fly(self, images, seed, cfg, camera_scale, num_frames, chunk_strategy="interp-gt"):
import os
import tempfile
import subprocess
import shutil
from glob import glob
import imageio.v3 as iio
import torch
from einops import rearrange
# Convert from ComfyUI format (BCHW) to HWC format for saving
if len(images.shape) == 4 and images.shape[1] == 3 and images.shape[0] >= 1:
# Input is in BCHW format, convert to BHWC first
print(f"Converting input tensor of shape {images.shape} from BCHW to BHWC")
images_to_save = rearrange(images, "b c h w -> b h w c").cpu().numpy()
else:
# Assume already in BHWC format
images_to_save = images.cpu().numpy()
# Create a timestamp-based name for unique folders
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
# Create a temporary directory to hold the input images
temp_dir = os.path.join(WORK_DIR, f"input_{timestamp}")
os.makedirs(temp_dir, exist_ok=True)
print(f"Saving {len(images_to_save)} images to {temp_dir}")
# Save each image to the temp directory
for i, img in enumerate(images_to_save):
# Convert from [0,1] float to [0,255] uint8
img_np = (img * 255).astype(np.uint8)
img_path = os.path.join(temp_dir, f"{i:03d}.png")
iio.imwrite(img_path, img_np)
# Define output directory
output_dir = os.path.join(WORK_DIR, f"output_{timestamp}")
# Create the command to run demo.py
cmd = [
"python", "demo.py",
"--data_path", temp_dir,
"--task", "img2trajvid",
"--cfg", f"{cfg},2.0", # First value is for view synthesis, second is for test-time conditioning
"--camera_scale", str(camera_scale),
"--use_traj_prior", "True",
"--chunk_strategy", chunk_strategy,
"--seed", str(seed),
"--num_targets", str(num_frames),
"--save_subdir", f"output_{timestamp}"
]
print(f"Running command: {' '.join(cmd)}")
try:
# Run the command
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
print(f"Command output: {result.stdout}")
# Find the output directory - it should be in WORK_DIR/img2trajvid/output_{timestamp}
results_dir = os.path.join(WORK_DIR, "img2trajvid", f"output_{timestamp}")
# Find the video file
video_files = glob(os.path.join(results_dir, "*.mp4"))
video_path = video_files[0] if video_files else ""
if not video_path:
print("No video was generated.")
return (images, "")
print(f"Generated video: {video_path}")
# Instead of loading all frames, which can be memory-intensive,
# just return the input images and the video path
# ComfyUI will display the video using the path
# Clean up temporary input directory
shutil.rmtree(temp_dir, ignore_errors=True)
return (images, video_path)
except subprocess.CalledProcessError as e:
print(f"Error running demo.py: {e}")
print(f"STDERR: {e.stderr}")
# Clean up
shutil.rmtree(temp_dir, ignore_errors=True)
return (images, "")
except Exception as e:
print(f"Unexpected error: {e}")
# Clean up
shutil.rmtree(temp_dir, ignore_errors=True)
return (images, "")
# Register the node class for ComfyUI
NODE_CLASS_MAPPINGS = {
"SVCFly": SVCFly
"SVCFly": SVCFly,
"SVCFly_Bash": SVCFly_Bash
}
NODE_DISPLAY_NAME_MAPPINGS = {
"SVCFly": "SVC Flythrough Generator"
"SVCFly": "SVC Flythrough Generator",
"SVCFly_Bash": "SVC Flythrough Generator (Bash)"
}
if __name__ == "__main__":