ジャコ Lab

プログラミング関連のメモ帳的ブログです

AnimateDiffVideoToVideoPipeline で Video-to-Video をする

AnimateDiffPipeline で Text-to-Video をしていましたが、 AnimateDiffVideoToVideoPipeline を使うと Video-to-Video ができるようになります。

huggingface.co

これは Hugging Face のドキュメントに使用方法が載っていましたので触ってみます。

はじめに

何気に Video-to-Video は初めてやるかもしれないです。
なんか前にやったような気がするなぁと思って過去記事を漁ってみましたが、
ControlNet の OpenPose で棒人間動画を使ったくらいかもしれないです。
(どちらにせよ、動画を扱うので似たような感じですが。。。)

zako-lab929.hatenablog.com

AnimateDiffVideoToVideoPipeline を使ってみる

事前準備

インプットとして動画を使うので動画をロードする部分が必要です。

import imageio
import requests
from io import BytesIO
from PIL import Image

def load_video(file_path: str):
    images = []

    if file_path.startswith(('http://', 'https://')):
        # If the file_path is a URL
        response = requests.get(file_path)
        response.raise_for_status()
        content = BytesIO(response.content)
        frames = imageio.get_reader(content)
    else:
        # Assuming it's a local file path
        frames = imageio.get_reader(file_path)

    for frame in frames:
        pil_image = Image.fromarray(frame)
        images.append(pil_image)

    return images
画像をロードするときはdiffusers.utilsload_image()だったけど動画版はないのかな?
動画版OpenPoseのときも imageio 使ってたね。今回は requests モジュールでダウンロードするところからやってるね

モデルのロード等 (MotionAdapter, AnimateDiffVideoToVideoPipeline, DDIMScheduler)

import torch
from diffusers import AnimateDiffVideoToVideoPipeline, DDIMScheduler, MotionAdapter

# モーションアダプターのロード
adapter = MotionAdapter.from_pretrained(
    "guoyww/animatediff-motion-adapter-v1-5-2",
    torch_dtype=torch.float16
)

# SD 1.5 系のモデルを AnimateDiffVideoToVideoPipeline でロード
pipe = AnimateDiffVideoToVideoPipeline.from_pretrained(
    "SG161222/Realistic_Vision_V5.1_noVAE",
    motion_adapter=adapter,
    torch_dtype=torch.float16
).to("cuda")

# スケジューラの設定
pipe.scheduler = DDIMScheduler.from_config(
    pipe.scheduler.config,
    clip_sample=False,
    timestep_spacing="linspace",
    beta_schedule="linear",
    steps_offset=1,
)

# enable memory savings
pipe.enable_vae_slicing()
pipe.enable_model_cpu_offload()
beta_schedule="linear"が付いてる!

インプット用の動画の準備

# 元動画(GIF)
init_image_url = "https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/diffusers/animatediff-vid2vid-input-1.gif"
init_frames = load_video(init_image_url)
動画といいつつ GIF 画像

パイプライン実行

import torch

# シード
generator = torch.Generator("cuda")
generator.seed()

# パイプライン実行
prompt = "panda playing a guitar, on a boat, in the ocean, high quality"
negative_prompt = "bad quality, worse quality"
frames = pipe(
    video=init_frames,
    prompt=prompt,
    negative_prompt=negative_prompt,
    guidance_scale=7.5,
    num_inference_steps=25,
    strength=0.5,
    generator=generator,
).frames[0]

実行結果

from diffusers.utils import export_to_gif
from datetime import datetime
from zoneinfo import ZoneInfo

# Asia/Tokyo タイムゾーンの現在時刻を YYYYMMDDhhmmss 形式で得る
formattedNow = datetime.now(tz=ZoneInfo("Asia/Tokyo")).strftime("%Y%m%d%H%M%S")

# 実行結果
export_to_gif(frames, f"animation_{formattedNow}_{generator.initial_seed()}.gif")

インプット画像アウトプット画像
(左) インプット | (右) アウトプット

ドキュメント通りの出力ではなかったね
strength が 0.5 のせいか、半分くらいパンダになったアライグマ?が出来ました

もう1つのサンプル動画でやってみる

インプット画像アウトプット画像
(左) インプット | (右) アウトプット

まとめ

とりあえず出力できました。