Metamorph / run_morphing.py
nalin0503
try to correct subprocess spawn and cuda 12.5.1 with cudnn 9...
3f0cc27
raw
history blame
8.46 kB
import os
import sys
import time
import subprocess
import argparse
from FILM import process_keyframes
import multiprocessing as mp
mp.set_start_method("spawn", force=True)
def parse_arguments():
parser = argparse.ArgumentParser(
description="Orchestrate DiffMorpher || LCM-LoRa || LCM, and FILM for smooth morphing between two images.")
# ------------------- DIFFMORPHER ARGS -------------------
parser.add_argument(
"--model_path", type=str, default="stabilityai/stable-diffusion-2-1-base",
help="Pretrained model to use for DiffMorpher (default: %(default)s)"
)
parser.add_argument(
"--image_path_0", type=str, required=True,
help="Path of the first image"
)
parser.add_argument(
"--prompt_0", type=str, default="",
help="Prompt describing the first image (default: %(default)s)"
)
parser.add_argument(
"--image_path_1", type=str, required=True,
help="Path of the second image"
)
parser.add_argument(
"--prompt_1", type=str, default="",
help="Prompt describing the second image (default: %(default)s)"
)
parser.add_argument(
"--output_path", type=str, default="./results",
help="Output folder for DiffMorpher keyframes/gif (default: %(default)s)"
)
parser.add_argument(
"--save_lora_dir", type=str, default="./lora",
help="Directory to save LoRA outputs (default: %(default)s)"
)
parser.add_argument(
"--load_lora_path_0", type=str, default="",
help="Path to LoRA checkpoint for image 0 (default: %(default)s)"
)
parser.add_argument(
"--load_lora_path_1", type=str, default="",
help="Path to LoRA checkpoint for image 1 (default: %(default)s)"
)
parser.add_argument(
"--use_adain", action="store_true",
help="Use AdaIN in DiffMorpher pipeline"
)
parser.add_argument(
"--use_reschedule", action="store_true",
help="Use reschedule sampling in DiffMorpher"
)
parser.add_argument(
"--lamb", type=float, default=0.6,
help="Lambda for self-attention replacement in DiffMorpher (default: %(default)s)"
)
parser.add_argument(
"--fix_lora_value", type=float, default=None,
help="Fix LoRA value in DiffMorpher (default: LoRA interpolation)"
)
parser.add_argument(
"--save_inter", action="store_true",
help="Save intermediate frames as individual images (e.g. .png) in DiffMorpher"
)
parser.add_argument(
"--num_frames", type=int, default=16,
help="Number of keyframes to generate (default: %(default)s)"
)
parser.add_argument(
"--duration", type=int, default=100,
help="Duration of each keyframe in the final .gif (default: %(default)s ms)"
)
parser.add_argument(
"--no_lora", action="store_true",
help="Disable LoRA usage in DiffMorpher"
)
parser.add_argument(
"--use_lcm", action="store_true",
help="Enable LCM-LoRA acceleration for faster sampling"
)
# ------------------- FILM ARGS -------------------
parser.add_argument(
"--use_film", action="store_true",
help="Flag to indicate whether to run FILM after generating keyframes"
)
parser.add_argument(
"--film_input_folder", type=str, default="",
help="Folder containing keyframes for FILM. If empty, will use DiffMorpher output folder."
)
parser.add_argument(
"--film_output_folder", type=str, default="./FILM_Results",
help="Folder where FILM’s final interpolated video is saved (default: %(default)s)"
)
parser.add_argument(
"--film_fps", type=int, default=40,
help="FPS for the final video - 'Pseudo-Playback-Speed', since total frames are same (default: %(default)s)"
)
parser.add_argument(
"--film_num_recursions", type=int, default=3,
help="Number of recursive interpolations to perform in FILM (default: %(default)s)"
)
return parser.parse_args()
def run_diffmorpher(args):
"""
Calls DiffMorpher's main.py via subprocess using the CLI arguments.
Expects `Image-Morpher/` to be a submodule in the current repo.
"""
diffmorpher_script = os.path.join("Image-Morpher", "main.py")
cmd = [
sys.executable, diffmorpher_script,
"--model_path", args.model_path,
"--image_path_0", args.image_path_0,
"--prompt_0", args.prompt_0,
"--image_path_1", args.image_path_1,
"--prompt_1", args.prompt_1,
"--output_path", args.output_path,
"--save_lora_dir", args.save_lora_dir,
"--lamb", str(args.lamb),
"--num_frames", str(args.num_frames),
"--duration", str(args.duration),
]
if args.load_lora_path_0:
cmd += ["--load_lora_path_0", args.load_lora_path_0]
if args.load_lora_path_1:
cmd += ["--load_lora_path_1", args.load_lora_path_1]
if args.use_adain:
cmd.append("--use_adain")
if args.use_reschedule:
cmd.append("--use_reschedule")
if args.fix_lora_value is not None:
cmd += ["--fix_lora_value", str(args.fix_lora_value)]
if args.no_lora:
cmd.append("--no_lora")
# ---- Always add --save_inter to ensure keyframes are saved ----
cmd.append("--save_inter")
# ---- Add LCM-LoRA flag if set ----
if args.use_lcm:
cmd.append("--use_lcm")
print("[INFO] Running DiffMorpher with command:")
print(" ".join(cmd))
start = time.time()
subprocess.run(cmd, check=True)
end = time.time()
print(f"[INFO] DiffMorpher completed in {end - start:.2f} seconds.")
def create_simple_video_from_keyframes(keyframes_folder, output_folder, fps=40):
"""
If the user does NOT want FILM, we still make a basic video from keyframes.
Assumes frames are saved as .png or .jpg in keyframes_folder.
"""
import cv2
from glob import glob
from datetime import datetime
os.makedirs(output_folder, exist_ok=True)
images = sorted(glob(os.path.join(keyframes_folder, "*.png")))
if not images:
images = sorted(glob(os.path.join(keyframes_folder, "*.jpg")))
if not images:
print(f"[WARN] No .png or .jpg frames found in {keyframes_folder}.")
return
# Prepare video writer
first_frame = cv2.imread(images[0])
height, width, _ = first_frame.shape
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
out_video_path = os.path.join(output_folder, f"simple_morph_{timestamp}.mp4")
out = cv2.VideoWriter(out_video_path, fourcc, fps, (width, height))
for img_path in images:
frame = cv2.imread(img_path)
out.write(frame)
out.release()
print(f"[INFO] Basic morphing video saved at: {out_video_path}")
def main():
args = parse_arguments()
overall_start_time = time.time()
# 1) Run DiffMorpher to generate keyframes
run_diffmorpher(args)
# 2) Determine the folder containing the keyframes
# If user didn’t explicitly give `--film_input_folder`, use `args.output_path`
keyframes_folder = args.film_input_folder if args.film_input_folder else args.output_path
# 3) If user wants to use FILM, perform high-quality interpolation on the keyframes
if args.use_film:
print("[INFO] Running FILM to enhance the keyframes...")
start_film_time = time.time()
# from FILM.py:
process_keyframes(
input_folder=keyframes_folder,
output_folder=args.film_output_folder,
fps=args.film_fps,
num_recursions=args.film_num_recursions
)
end_film_time = time.time()
print(f"[INFO] FILM interpolation completed in {end_film_time - start_film_time:.2f} seconds.")
else:
# 4) If user does NOT want FILM, create a simple .mp4 from the keyframes
print("[INFO] Skipping FILM interpolation. Creating a basic video from DiffMorpher keyframes...")
create_simple_video_from_keyframes(
keyframes_folder=keyframes_folder,
output_folder=args.film_output_folder,
fps=args.film_fps
)
# 5) Print total execution time
overall_end_time = time.time()
print(f"[INFO] Entire pipeline completed in {overall_end_time - overall_start_time:.2f} seconds.")
if __name__ == "__main__":
main()