Stable Video Diffusion完全ガイド!使い方から商用利用まで徹底解説

Stable-Video-Diffusion
押さえておきたいポイント
  • Stable Video DiffusionはImage-to-Video特化モデルであり、静止画の構図や被写体を保った短尺動画生成に強い
  • 商用利用の可否はMembershipではなくライセンス体系(Community / Enterprise)を基準に判断
  • 実運用ではモデル選択とパラメータ調整に加え、実行環境(ComfyUIなど)の選び方が品質と安定性を左右する

2023年11月、画像から動画を生成してくれるAI「Stable Video Diffusion」がGitHubで公開され、誰でも「画像を入力とした動画生成」ができるようになりました。これにより、任意の入力画像に沿った動画の生成が、とても簡単にできるんです…!

GitHubのスター数は、なんと25000を超えており、Stability AIが開発したAIの中でも大注目のプロジェクトです。

この記事ではStable Video Diffusionの使い方や、有効性の検証まで行います。本記事を熟読することで、Stable Video Diffusionの凄さを実感し、Image2Videoの魅力にとりつかれるでしょう。

ぜひ、最後までご覧ください。

\生成AIを活用して業務プロセスを自動化/

目次

Stable Video Diffusionの概要

Stable Video Diffusion(SVD)は、Stability AIによって開発されたImage2Videoモデルの一種です。画像生成AIのStable Diffusionをベースに作られており、入力画像から動画を生成できます。

具体的には、これまでStable Diffusionにおいて、プロンプトとしてテキストを入れていた部分に、画像を入力すれば、その画像の内容に即した動画が生成されるのです。

Stable Video Diffusionは、14フレームおよび25フレームを生成できる2種類の画像からビデオへのモデルとしてリリースされており、毎秒3〜30フレームのフレームレートで生成が可能となっています。

Stable Video Diffusionの性能表
参考:https://ja.stability.ai/blog/stable-video-diffusion

簡単に静止画から動きを生み出すことが可能になるため、さまざまなクリエイティブ作業に役立てることができるでしょう。

Stable Video Diffusionの仕組み

Stable Video Diffusionの訓練は、3つの段階に分かれています。

第一段階では、テキストから画像への事前学習です。第二段階は動画の事前学習、第三段階は高品質動画へのファインチューニングという流れで訓練が行われます。

スクロールできます
学習段階学習内容使用データ・特徴
第一段階テキストから画像への事前学習Stable Diffusionの技術を活用し、大規模かつ多様な画像データセットを用いて、基本となる空間的特徴を学習
第二段階動画の事前学習大量の低解像度動画データを使用して動画特有の特徴を学習。フィルタリングやカット検出により、静的シーンや低品質なクリップを除外
第三段階高品質動画へのファインチューニング高解像度かつ高品質な少量データセットで微調整を実施。視覚品質が高く、カメラ動作とキャプションの整合性が取れた動画を使用
Stable Video Diffusionの学習段階一覧表

この訓練過程を経ることでStable Video Diffusionは動画の生成を可能としています。

Stable Diffusion自体、元々は静止画像を生成することに長けています。その技術に「時間的要素」を組み込み、不自然な挙動にならないように学習したデータから前後のフレームの動き方を参照することによって、動画生成が可能です。

一方で、前後のフレームの動き方のみでは、カメラの動きを再現することができません。そこで、Low-Rank Adaptation(LoRA)という技術を使って、カメラの動きをコントロールします。

カメラの水平移動やズームイン、ズームアウトなどを学習させることによって、LoRAでカメラの動きを制御することが可能です。

Stable Video Diffusionのモデルバリエーションと選び方

Stable Video Diffusionは、静止画像を条件として短い動画クリップを生成するImage-to-Videoモデルです。複数のモデルバリエーションが公開されており、フレーム数・推奨解像度・安定性などがそれぞれ異なります。

svd / svd-xt / svd-xt-1-1の違い

Stable Video Diffusionには主に以下の3つのバージョンがあります。

スクロールできます
モデル生成フレーム数推奨解像度特徴
svd14フレーム576×1024ベースライン。軽量で試しやすい
svd-xt25フレーム576×1024滑らかさと時間的一貫性が向上
svd-xt-1-125フレーム576×1024安定性・出力品質が高く初期値でも扱いやすい
Stable Video Diffusionモデル一覧表

基本版となるStable Video Diffusionは、14フレームの動画を生成するバージョンで、比較的短いクリップ向けです。モデルカードでは14フレーム・解像度576×1024でトレーニングされたことが明記されています。

SVD-XTは、25フレームの動画を生成。14フレーム版の学習をベースにして、フレーム数と安定性が向上しています。

さらにSVD-XT-1-1は、25フレーム版を進化させたバージョンです。解像度576×1024で生成され、motion_bucket_idやfpsなどの調整値がデフォルト固定された状態でも安定した出力が可能。

動画の長さは4秒以下であり、どのバリエーションでも変わりません。また、推奨解像度は576×1024です。

得意な表現と苦手な表現

Stable Video Diffusionは静止画像からのモーション生成に特化しており、静止画の細部を保ちながら自然な動きを再現することができます。

特に被写体の周辺の点滅や揺れなどの処理は比較的滑らかです。 ただし、その一方で、複雑なカメラワークや長尺の時間的変化、大きく変形するようなアニメーション表現には不向きです。

また、動きの強さや背景の変化に対しては調整パラメータが直接関与しないと破綻しやすい側面があるため、用途によっては後処理や補間処理が必要なケースが出てきます。

初めて使う場合はどのモデルを選ぶべきか

モデル選びは、用途・安定性・計算リソースのバランスで決めるとよいでしょう。

とりあえず試したい場合や軽く動かしたい場合は、svdがおすすめ。初めてStable Video Diffusionを触る人が最初に試すには、学習済みの動作が軽く環境要件が比較的低いこのモデルが適しています。結果の傾向を掴むのに十分な動画が生成できます。

ある程度滑らかな動画を生成したい場合は、svd-xtが向いています。フレーム数が多く動きの滑らかさが向上し、ベース版よりも時間的一貫性が高くなります。

ただし、生成負荷は若干上がります。安定性や出力品質を重視したい場合は、svd-xt-1-1が良いです。初期値でも比較的安定した生成ができ、fpsやmotion_bucket_idのデフォルト値が整えられているため、初心者でも安心感があります。長めの滑らかさと高い再現性を狙いたい場合に向いています。

Stable Video Diffusionのライセンス及び料金体系

非商用利用プロフェッショナルプランエンタープライズプラン
対象者個人利用および研究用年間収益1億円未満、機関投資1億円未満、月間アクティブユーザー100万人未満のクリエイター・開発者向けプロフェッショナルより大規模な企業向け
費用無料月額20ドル カスタム
※要問い合わせ
セルフホストモデル 非商用利用商用利用商用利用
特徴・新しいモデルへの早期アクセス
・パブリックコミュニティへのアクセス
・プロフェッショナル向けDiscordコミュニティ
・非商用プランのすべて
・カスタム価格
・エンタープライズ機能
・プロフェッショナルプランのすべて
Stable Video Diffusionのプラン一覧表

Stable Video Diffusionでは、STABLE VIDEO DIFFUSION NON-COMMERCIAL COMMUNITY LICENSE AGREEMENTというライセンスとなっています。

一部を抜粋して日本語に訳したものが下記になります。

Stability AI は、非独占的、全世界的、譲渡不可、サブライセンス不可、取り消し可能なロイヤリティフリーの制限付きライセンスをお客様に付与します。ソフトウェア製品に組み込まれている Stability AI の知的財産または Stability AI が所有するその他の権利に基づいて、商用または生産使用以外の目的でソフトウェア製品の複製、配布、派生作品を作成することはできません。    

基本的には無料で使えますが、無料版だと商用では使えないので注意してください。(詳しくは後述しています)
ライセンスは適宜更新されるようなので利用する前に必ずチェックしておきましょう。

なお、アイデアを動画にできる生成AIについて知りたい方はこちらの記事をご覧ください。

Stable Video Diffusionの商用利用は不可能?

Stable Video DiffusionをリリースしたStability AIの発表によると「Stable Video Diffusionは研究用であり、現段階では商用アプリケーションでの使用を意図していない」と書かれています。

参考:https://ja.stability.ai/blog/stable-video-diffusion

ただ、2023年12月にStability AIは「顧客が同社のモデルを商業利用する方法を標準化し、有償メンバーシッププランを開始する」と発表しています。有償メンバーシップは収益性とオープン性のバランスをとることを目的としており、ユーザーに商業利用する権利を付与する方法を「再定義」するものとして位置付けられています。

メンバーシップは、

  • Non-Commercial:個人・研究利用向けの無料プラン
  • Professional:機関投資家・クリエイター・開発者・新興企業向けの月額20ドルのプラン
  • Enterprise:Professinalプランよりも大きな顧客向けのカスタムプラン

の3種類が用意されています。

いずれのプランでも、SDXL(Stable Diffusion XL) TurboやStable Video Diffusion・大規模言語モデルの「Stable LM Zephyr 3B」など最新AIモデルへの早期アクセスが提供される予定ですが、商業利用が可能なのはProfessionalプランとEnterpriseプランのみとなるようです。

商用利用したい場合は、有料プランに登録する必要がありそうです。

2026年2月時点でのライセンスについては、Stable Video DiffusionはStability AIが定義するCore Modelsに含まれており、年間収益が1M USD未満の個人・小規模事業者であれば、Communityライセンスの範囲で商用利用が可能です。

一方、年間収益が1M USDを超える企業や大規模サービスでの利用については、Enterpriseライセンスの契約が必要。リリース当初に強調されていた「研究用途中心」という位置づけは現在の公式ライセンス体系にはそのまま当てはまらないため、商用可否の判断は最新のライセンスページおよびCore Modelsを基準に行うことが重要です。

Stable Video Diffusionの使い方

ではStable Video Diffusionを動かすための必要なスペックの紹介と準備をご説明します。

Stable Diffusionのローカルでの使い方はこちら

Stable Video Diffusionを動かすために必要なスペック

今回、Stable Video Diffusionを動かしたのは下記の環境です。

Google Colab(無料プラン)

GPU:T4

無料プランのT4でもStable Video Diffusionは動かすことができましたが、生成までに時間がかかりました。
メモリが上限に張り付きそうになったしていたため、ギリギリのラインで動いていたようです。

余裕があれば有料のGoogle Colab Proで、GPU:V100、ハイメモリの環境をおすすめします。

Stable Video Diffusionのセットアップ方法

Stable Video Diffusionを試すには様々な方法がありますが、今回は以下のツイートを参考にしました。なお、HuggingFaceのWRITEのトークンが必要になるため事前に準備しておきましょう。

まずは、以下のコードを実行して、Setupを完了させます。

#@title Setup

!nvidia-smi

!git clone https://github.com/Stability-AI/generative-models.git

# install required packages from pypi

# !pip3 install -r generative-models/requirements/pt2.txt

# manually install only necesarry packages for colab

!wget https://gist.githubusercontent.com/mkshing/4ad40699756d996ba6b3f7934e6ca532/raw/3f0094272c7a2bd3eb5f1a0db91bed582c9e8f01/requirements.txt

!pip3 install -r requirements.txt

!pip3 install -e generative-models

!pip3 install -e git+https://github.com/Stability-AI/datapipelines.git@main#egg=sdata

!pip3 install gradio

#@title Colab hack for SVD

# !pip uninstall -y numpy

# !pip install -U numpy

!mkdir -p /content/scripts/util/detection

!ln -s /content/generative-models/scripts/util/detection/p_head_v1.npz /content/scripts/util/detection/p_head_v1.npz

!ln -s /content/generative-models/scripts/util/detection/w_head_v1.npz /content/scripts/util/detection/w_head_v1.npz

次に、HuggingFaceのトークンをセットします。

#@title Login HuggingFace to download weights

#@markdown Please make sure to fill in the form in the model cards and accept it.

from huggingface_hub import login

login()

上記のコードを実行するとトークンを入力するエリアが表示されるので各自で発行したトークンを入力して「Login」をクリックします。

Huggigng Faceの認証画面

次に、以下のコードを実行して、モデルのcheckpointのダウンロードと、モデルのロードをしましょう。

# @title Download weights

import os

import subprocess

from huggingface_hub import hf_hub_download

version = "svd-xt" #@param ["svd", "svd-xt", "svd-xt-1-1"]

TYPE2PATH = {

    "svd": ["stabilityai/stable-video-diffusion-img2vid", "svd.safetensors"],

    "svd-xt": ["stabilityai/stable-video-diffusion-img2vid-xt", "svd_xt.safetensors"],

    "svd-xt-1-1": ["stabilityai/stable-video-diffusion-img2vid-xt-1-1", "svd_xt_1_1.safetensors"],

}

repo_id, fname = TYPE2PATH[version]

ckpt_dir = "/content/checkpoints"

ckpt_path = os.path.join(ckpt_dir, fname)

# @markdown This will take several minutes. <br>

# @markdown **Reference:**

# @markdown * `svd`: [stabilityai/stable-video-diffusion-img2vid](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid) for 14 frames generation

# @markdown * `svd-xt`: [stabilityai/stable-video-diffusion-img2vid-xt](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt) for 25 frames generation

# @markdown * `svd-xt-1-1`: [stabilityai/stable-video-diffusion-img2vid-xt-1-1](https://huggingface.co/stabilityai/stable-video-diffusion-img2vid-xt-1-1) for 25 frames generation with fixed conditioning at 6FPS and Motion Bucket Id 127

os.makedirs("checkpoints", exist_ok=True)

if os.path.exists(ckpt_path):

  print("Already downloaded")

else:

  hf_hub_download(

      repo_id=repo_id,

      filename=fname,

      local_dir=ckpt_dir,

  )

#@title Load Model

import sys

from omegaconf import OmegaConf

import torch

sys.path.append("generative-models")

from sgm.util import default, instantiate_from_config

from scripts.util.detection.nsfw_and_watermark_dectection import DeepFloydDataFiltering

def load_model(

    config: str,

    device: str,

    num_frames: int,

    num_steps: int,

    ckpt_path: str = None,

):

    config = OmegaConf.load(config)

    config.model.params.conditioner_config.params.emb_models[

        0

    ].params.open_clip_embedding_config.params.init_device = device

    config.model.params.sampler_config.params.num_steps = num_steps

    config.model.params.sampler_config.params.guider_config.params.num_frames = (

        num_frames

    )

    if ckpt_path is not None:

        config.model.params.ckpt_path = ckpt_path

        print(f"Changed `ckpt_path` to {ckpt_path}")

    with torch.device(device):

        model = instantiate_from_config(config.model).to(device).eval().requires_grad_(False)

    filter = DeepFloydDataFiltering(verbose=False, device=device)

    return model, filter

if version == "svd":

    num_frames = 14

    num_steps = 25

    # output_folder = default(output_folder, "outputs/simple_video_sample/svd/")

    model_config = "generative-models/scripts/sampling/configs/svd.yaml"

elif "svd-xt" in version:

    num_frames = 25

    num_steps = 30

    # output_folder = default(output_folder, "outputs/simple_video_sample/svd_xt/")

    model_config = "generative-models/scripts/sampling/configs/svd_xt.yaml"

else:

    raise ValueError(f"Version {version} does not exist.")

device = "cuda" if torch.cuda.is_available() else "cpu"

model, filter = load_model(

    model_config,

    device,

    num_frames,

    num_steps,

    ckpt_path,

)

# move models expect unet to cpu

model.conditioner.cpu()

model.first_stage_model.cpu()

# change the dtype of unet

model.model.to(dtype=torch.float16)

torch.cuda.empty_cache()

model = model.requires_grad_(False)

ちなみに、ここで「version」変数の値を「svd_xt」としています。svdでは14フレーム、svd_xtは25フレームで生成可能なモデルとなっていますので用途に応じたものを選択してください。

なお、より高性能な「svd-xt-1-1」を利用するには住所などを入力し、同意しないと使えませんのでご注意ください。

次に、以下のコードを実行して、動画生成を行うための関数を定義します。

# @title Sampling function

import math

import os

from glob import glob

from pathlib import Path

from typing import Optional

import cv2

import numpy as np

import torch

from einops import rearrange, repeat

from fire import Fire

from PIL import Image

from torchvision.transforms import ToTensor

from torchvision.transforms import functional as TF

from sgm.inference.helpers import embed_watermark

from sgm.util import default, instantiate_from_config

def get_unique_embedder_keys_from_conditioner(conditioner):

    return list(set([x.input_key for x in conditioner.embedders]))

def get_batch(keys, value_dict, N, T, device, dtype=None):

    batch = {}

    batch_uc = {}

    for key in keys:

        if key == "fps_id":

            batch[key] = (

                torch.tensor([value_dict["fps_id"]])

                .to(device, dtype=dtype)

                .repeat(int(math.prod(N)))

            )

        elif key == "motion_bucket_id":

            batch[key] = (

                torch.tensor([value_dict["motion_bucket_id"]])

                .to(device, dtype=dtype)

                .repeat(int(math.prod(N)))

            )

        elif key == "cond_aug":

            batch[key] = repeat(

                torch.tensor([value_dict["cond_aug"]]).to(device, dtype=dtype),

                "1 -> b",

                b=math.prod(N),

            )

        elif key == "cond_frames":

            batch[key] = repeat(value_dict["cond_frames"], "1 ... -> b ...", b=N[0])

        elif key == "cond_frames_without_noise":

            batch[key] = repeat(

                value_dict["cond_frames_without_noise"], "1 ... -> b ...", b=N[0]

            )

        else:

            batch[key] = value_dict[key]

    if T is not None:

        batch["num_video_frames"] = T

    for key in batch.keys():

        if key not in batch_uc and isinstance(batch[key], torch.Tensor):

            batch_uc[key] = torch.clone(batch[key])

    return batch, batch_uc

def sample(

    input_path: str = "assets/test_image.png",  # Can either be image file or folder with image files

    resize_image: bool = False,

    num_frames: Optional[int] = None,

    num_steps: Optional[int] = None,

    fps_id: int = 6,

    motion_bucket_id: int = 127,

    cond_aug: float = 0.02,

    seed: int = 23,

    decoding_t: int = 14,  # Number of frames decoded at a time! This eats most VRAM. Reduce if necessary.

    device: str = "cuda",

    output_folder: Optional[str] = "/content/outputs",

    skip_filter: bool = False,

):

    """

    Simple script to generate a single sample conditioned on an image `input_path` or multiple images, one for each

    image file in folder `input_path`. If you run out of VRAM, try decreasing `decoding_t`.

    """

    torch.manual_seed(seed)

    path = Path(input_path)

    all_img_paths = []

    if path.is_file():

        if any([input_path.endswith(x) for x in ["jpg", "jpeg", "png"]]):

            all_img_paths = [input_path]

        else:

            raise ValueError("Path is not valid image file.")

    elif path.is_dir():

        all_img_paths = sorted(

            [

                f

                for f in path.iterdir()

                if f.is_file() and f.suffix.lower() in [".jpg", ".jpeg", ".png"]

            ]

        )

        if len(all_img_paths) == 0:

            raise ValueError("Folder does not contain any images.")

    else:

        raise ValueError

    all_out_paths = []

    for input_img_path in all_img_paths:

        with Image.open(input_img_path) as image:

            if image.mode == "RGBA":

                image = image.convert("RGB")

            if resize_image and image.size != (1024, 576):

                print(f"Resizing {image.size} to (1024, 576)")

                image = TF.resize(TF.resize(image, 1024), (576, 1024))

            w, h = image.size

            if h % 64 != 0 or w % 64 != 0:

                width, height = map(lambda x: x - x % 64, (w, h))

                image = image.resize((width, height))

                print(

                    f"WARNING: Your image is of size {h}x{w} which is not divisible by 64. We are resizing to {height}x{width}!"

                )

            image = ToTensor()(image)

            image = image * 2.0 - 1.0

        image = image.unsqueeze(0).to(device)

        H, W = image.shape[2:]

        assert image.shape[1] == 3

        F = 8

        C = 4

        shape = (num_frames, C, H // F, W // F)

        if (H, W) != (576, 1024):

            print(

                "WARNING: The conditioning frame you provided is not 576x1024. This leads to suboptimal performance as model was only trained on 576x1024. Consider increasing `cond_aug`."

            )

        if motion_bucket_id > 255:

            print(

                "WARNING: High motion bucket! This may lead to suboptimal performance."

            )

        if fps_id < 5:

            print("WARNING: Small fps value! This may lead to suboptimal performance.")

        if fps_id > 30:

            print("WARNING: Large fps value! This may lead to suboptimal performance.")

        value_dict = {}

        value_dict["motion_bucket_id"] = motion_bucket_id

        value_dict["fps_id"] = fps_id

        value_dict["cond_aug"] = cond_aug

        value_dict["cond_frames_without_noise"] = image

        value_dict["cond_frames"] = image + cond_aug * torch.randn_like(image)

        value_dict["cond_aug"] = cond_aug

        # low vram mode

        model.conditioner.cpu()

        model.first_stage_model.cpu()

        torch.cuda.empty_cache()

        model.sampler.verbose = True

        with torch.no_grad():

            with torch.autocast(device):

                model.conditioner.to(device)

                batch, batch_uc = get_batch(

                    get_unique_embedder_keys_from_conditioner(model.conditioner),

                    value_dict,

                    [1, num_frames],

                    T=num_frames,

                    device=device,

                )

                c, uc = model.conditioner.get_unconditional_conditioning(

                    batch,

                    batch_uc=batch_uc,

                    force_uc_zero_embeddings=[

                        "cond_frames",

                        "cond_frames_without_noise",

                    ],

                )

                model.conditioner.cpu()

                torch.cuda.empty_cache()

                # from here, dtype is fp16

                for k in ["crossattn", "concat"]:

                    uc[k] = repeat(uc[k], "b ... -> b t ...", t=num_frames)

                    uc[k] = rearrange(uc[k], "b t ... -> (b t) ...", t=num_frames)

                    c[k] = repeat(c[k], "b ... -> b t ...", t=num_frames)

                    c[k] = rearrange(c[k], "b t ... -> (b t) ...", t=num_frames)

                for k in uc.keys():

                    uc[k] = uc[k].to(dtype=torch.float16)

                    c[k] = c[k].to(dtype=torch.float16)

                randn = torch.randn(shape, device=device, dtype=torch.float16)

                additional_model_inputs = {}

                additional_model_inputs["image_only_indicator"] = torch.zeros(

                    2, num_frames

                ).to(device, )

                additional_model_inputs["num_video_frames"] = batch["num_video_frames"]

                for k in additional_model_inputs:

                    if isinstance(additional_model_inputs[k], torch.Tensor):

                        additional_model_inputs[k] = additional_model_inputs[k].to(dtype=torch.float16)

                def denoiser(input, sigma, c):

                    return model.denoiser(

                        model.model, input, sigma, c, **additional_model_inputs

                    )

                samples_z = model.sampler(denoiser, randn, cond=c, uc=uc)

                samples_z.to(dtype=model.first_stage_model.dtype)

                ##

                model.en_and_decode_n_samples_a_time = decoding_t

                model.first_stage_model.to(device)

                samples_x = model.decode_first_stage(samples_z)

                samples = torch.clamp((samples_x + 1.0) / 2.0, min=0.0, max=1.0)

                model.first_stage_model.cpu()

                torch.cuda.empty_cache()

                os.makedirs(output_folder, exist_ok=True)

                base_count = len(glob(os.path.join(output_folder, "*.mp4")))

                video_path = os.path.join(output_folder, f"{base_count:06d}.mp4")

                writer = cv2.VideoWriter(

                    video_path,

                    cv2.VideoWriter_fourcc(*"MP4V"),

                    fps_id + 1,

                    (samples.shape[-1], samples.shape[-2]),

                )

                samples = embed_watermark(samples)

                if not skip_filter:

                    samples = filter(samples)

                else:

                    print("WARNING: You have disabled the NSFW/Watermark filter. Please do not expose unfiltered results in services or applications open to the public.")

                vid = (

                    (rearrange(samples, "t c h w -> t h w c") * 255)

                    .cpu()

                    .numpy()

                    .astype(np.uint8)

                )

                for frame in vid:

                    frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)

                    writer.write(frame)

                writer.release()

                all_out_paths.append(video_path)

    return all_out_paths

最後に、以下のコードを実行してください。

# @title Do the Run!

# @markdown Generation takes about 10 mins for 25 frames on T4 (Colab free plan). Please be patient...

# @markdown (V100 takes about 3 mins.)

import gradio as gr

import random

def infer(input_path: str, resize_image: bool, n_frames: int, n_steps: int, seed: str, decoding_t: int, fps_id: int, motion_bucket_id: int, cond_aug: float, skip_filter: bool = False) -> str:

  if seed == "random":

    seed = random.randint(0, 2**32)

  if version == "svd-xt-1-1":

    if fps_id != 6:

      print("[WARNING] svd-xt-1-1 was fine-tuned in fixed conditioning (`fps_id=6`, `motion_bucket_id=127`)! The performance may vary compared to SVD 1.0.")

    if motion_bucket_id != 127:

      print("[WARNING] svd-xt-1-1 was fine-tuned in fixed conditioning (`fps_id=6`, `motion_bucket_id=127`)! The performance may vary compared to SVD 1.0.")

  seed = int(seed)

  output_paths = sample(

    input_path=input_path,

    resize_image=resize_image,

    num_frames=n_frames,

    num_steps=n_steps,

    fps_id=fps_id,

    motion_bucket_id=motion_bucket_id,

    cond_aug=cond_aug,

    seed=seed,

    decoding_t=decoding_t,  # Number of frames decoded at a time! This eats most VRAM. Reduce if necessary.

    device=device,

    skip_filter=skip_filter,

  )

  return output_paths[0]

with gr.Blocks() as demo:

  with gr.Column():

    image = gr.Image(label="input image", type="filepath")

    resize_image = gr.Checkbox(label="resize to optimal size", value=True)

    btn = gr.Button("Run")

    with gr.Accordion(label="Advanced options", open=False):

      n_frames = gr.Number(precision=0, label="number of frames", value=num_frames)

      n_steps = gr.Number(precision=0, label="number of steps", value=num_steps)

      seed = gr.Text(value="random", label="seed (integer or 'random')",)

      decoding_t = gr.Number(precision=0, label="number of frames decoded at a time", value=2)

      fps_id = gr.Number(precision=0, label="frames per second", value=6)

      motion_bucket_id = gr.Number(precision=0, value=127, label="motion bucket id")

      cond_aug = gr.Number(label="condition augmentation factor", value=0.02)

      skip_filter = gr.Checkbox(value=False, label="skip nsfw/watermark filter")

  with gr.Column():

    video_out = gr.Video(label="generated video")

  examples = [

      ["https://user-images.githubusercontent.com/33302880/284758167-367a25d8-8d7b-42d3-8391-6d82813c7b0f.png"],

  ]

  inputs = [image, resize_image, n_frames, n_steps, seed, decoding_t, fps_id, motion_bucket_id, cond_aug, skip_filter]

  outputs = [video_out]

  btn.click(infer, inputs=inputs, outputs=outputs)

  gr.Examples(examples=examples, inputs=inputs, outputs=outputs, fn=infer)

  demo.queue().launch(debug=True, share=True, show_error=True)

上記を実行すると、以下のような画面が出てくると思います。

画像アップロード画面

「ここに画像をドロップ」のところに任意の画像をアップロードして、「Run」をクリックすると、動画が生成されます。

主要パラメータ

Stable Video Diffusionには、複数のパラメータが存在しますが、生成動画の質を大きく左右するものは主に「動きと時間感覚」「画質・安定性」再現性と計算資源」の3つです。

動きと時間感覚を決めるパラメータ

動画としての体感的な動きは、主にfps_idとmotion_bucket_idによって制御されます。

fps_idは、生成される動画のフレームレートに相当する内部条件で、Stable Video Diffusionの公式サンプリング例でも指定が前提とされています。

値を低く設定すると1フレームあたりの変化が大きくなり、結果としてカクついた印象の動画になります。一方で高めに設定すると、動きは滑らかになりますが、計算負荷とVRAM消費が増える傾向があります。

motion_bucket_idは、フレーム間の変化量、いわば「どれくらい動かすか」をモデルに伝えるための指標。Stable Video Diffusionは短尺動画を前提に学習されているため、極端に大きな値を与えると、被写体の形状崩れや不自然な変形が発生しやすくなります。

画質・安定性に影響するパラメータ

画質と安定性の調整で中心になるのがnum_stepsとcond_augです。

公式コードでは、ステップ数はnum_stepsとしてサンプラー設定に代入され、cond_augは条件付けに渡される値として定義されています。

一般的に、ステップ数を増やすと生成に時間がかかる一方で出力が安定することが多いです。

cond_aug は条件画像側の揺らぎを与えるための項目として実装されているので、出力のばらつきや条件画像からの離れ方に影響し得ます。ただし「必ずこうなる」と断定できるわけではないため、変更は小刻みに行うのがよいでしょう。

再現性と計算資源に関わるパラメータ

生成結果を再現するために重要なのがseedです。

Stable Video Diffusionでは、公式コード内で乱数シードを固定する実装が行われており、同一条件下での再現生成が可能になっています。

またdecoding_tは、一度にデコードするフレーム数に関わる設定で、VRAM消費量に直結します。

安全対策・フィルタ・ウォーターマークについて

Stable Video Diffusionの公式サンプリングコードでは、生成後の出力に対してNSFWフィルタとウォーターマーク処理が適用される設計になっています。

DeepFloydDataFilteringによるコンテンツフィルタリングと、embed_watermarkによるウォーターマーク付与が実装されており、デフォルト設定ではこれらが有効です。

また、コード上ではskip_filter=Trueを指定することでフィルタを無効化することも可能ですが、その場合は生成結果の公開や商用利用における責任が利用者側に委ねられる形になります。

実務向けの調整手順とおすすめ順序

まずfps_idとmotion_bucket_idで動画の方向性と動きの強さを決めます。その後、stepsを調整して画質と安定性を確保し、cond_augで変化量を微調整

検証段階ではseedを固定し、条件変更の影響を正確に比較し、最後に、実行環境のVRAM制約に応じてdecoding_tを調整することで、品質と実行安定性のバランスを取るのがよいでしょう。

ローカル以外の代表的な実行方法

Stable Video Diffusionはローカル以外にも代表的な実行方法があります。ここではComfyUIとWebUI Forgeでの実行方法について解説をします。

ComfyUIでのStable Video Diffusion

では実際にComfyUIでStable Video Diffusionを使っていきたいと思います。

まずはモデルのダウンロード。14フレームの動画を作成する場合はこちら

Stable Video Diffusionのモデルダウンロード画面
参考:https://huggingface.co/stabilityai/stable-video-diffusion-img2vid/tree/main

25フレームで動画を作成する場合にはこちらです

ダウンロードしたモデルファイルは、ComfyUIのチェックポイント用フォルダに配置します。標準構成ではComfyUI/models/checkpointsに保存すれば認識されます。

モデルのダウンロードが終わったら次はワークフローをダウンロードしましょう。例が公開されているので、こちらからダウンロードするのがよいです。

ComfyUIを起動して、ダウンロードしたワークフローをインポートしましょう。

ComfyUI起動画面

ワークフローのインポートと画像の選択が終わったら実行をクリックして完了するのを待ちます。

実際に生成された動画がこちら。

WebUI Forgeでの利用

続いてWebUI Forgeでの利用方法です。

まずはStable Diffusion WebUI Forgeをダウンロードします。

ターミナルで下記を実行します。

git clone https://github.com/lllyasviel/stable-diffusion-webui-forge.git

クローンが完了したら起動していきます。

筆者はmacOSを使っているため、webui-macos-env.shの内容をwebui-user.shにコピペして内容を変えています。

また、ComfyUIを実行する時にダウンロードしたモデルを「forgeフォルダ」→「models」→「svd」フォルダに移動させておきましょう。

さらに拡張としてSVDが使えるように下記を入れておきます。

git clone https://github.com/licyk/sd_forge_hypertile_svd_z123 extensions/sd_forge_hypertile_svd_z123

ここまでできたら、stable-diffusion-webui-forgeフォルダまで移動したら、./webui.shを実行。そうすると自動的にForgeが起動します。

WebUI Forge画面

画像をアップロードして実行をします。

実際に生成された動画がこちら。

ComfyUIで生成した動画よりも崩れてしまっていますね。GPUを使えないからかもしれません。GPUがない・もしくはあっても性能が低いなどの場合、Forgeを使うよりComfyUIを使って動画を生成するのが良さそうです。

fal.aiでの実行

環境構築をするのは大変なケースも多く、まずはお試しで使ってみたい。という場合にはfal.aiを使うのがよいでしょう。fal.aiであれば煩雑な環境構築をする必要なく、画像から動画を生成することが可能です。

fal.aiのStable Video Diffusion画面
参考:https://fal.ai/models/fal-ai/stable-video

動画一本あたり0.075ドルで生成できます。ローカル環境の環境構築ができれば当然無料でStable Video Diffusionを使うことができますが、環境構築のハードルが高い・お試しで使いたいだけ、と言う方であればfal.aiをおすすめします。

Stable Video Diffusionを実際に使ってみた

ここでは、冒頭の導入文にある、以下の画像を動画化してみましょう。

アップロードして、Stable Video Diffusionで動かしてみた結果が、以下の通りです。

なんか、角度が変化しているだけに感じます。

お次はSVD-XT。

うーん、クオリティは悪くないんだけど、なんかカクカクしています。

なお、ByteDanceが開発した動画生成AIについて知りたい方はこちらの記事をご覧ください。

Stable Video Diffusionの推しポイントである高品質な動画生成は本当なのか?

Stable Video Diffusionの威力を確かめるために、Runwayの「Gen-2」と比較してみます。

そこで、先ほどのStable Video Diffusionへのタスクを、Gen-2にも行わせて、品質を比較してみようと思います。Gen-2は、以下のページから利用できます。

Gen-2: The Next Step Forward for Generative AI

Gen-2によって生成された動画は、以下の通りです。

細部までかなり自然ですね!ただ、人の手が歪んでいたりして、不自然な部分も見受けられました。

とはいえ、Stable Video DiffusionよりもGen-2の方が、まだまだ精度がよさそうです。

他の動画生成AIとの比較

Stable Video Diffusionは、研究起点のオープンモデルとして提供されている点が、商用SaaS型の動画生成AIと大きく異なります。

Image-to-Videoを前提とした設計で、入力画像の構図や被写体を比較的忠実に保ったまま短尺動画を生成できるのが特徴です。ここではStable Video Diffusionとそのほかの動画生成AIとの比較をしていきます。

Stable Video DiffusionとRunwayの違い

Runwayは、ブラウザ上で完結する商用動画生成サービスです。テキストから動画を生成できるほか、動画編集やエフェクト、背景除去など、完成物を作るための機能が一体化されています。

Stable Video Diffusionとの最大の違いは、設計思想と責任範囲です。

Runwayは「最終アウトプットを素早く作る」ことに最適化されており、モデル選択やパラメータ調整を意識せずに使える反面、内部挙動の細かい制御はできません。

一方でStable Video Diffusionはローカルや任意のUI上で動かせるため、入力画像の作り方や後処理を含めて自由度が高く、制作フローに組み込みやすい構成です

Stable Video DiffusionとPikaの違い

Pikaはテキスト指示による動画生成を強みとするサービスで、キャラクターやシーンを自然言語で操作できる点が特徴です。

Pikaは「テキストで動画を作りたい」というニーズに応える設計になっており、発想段階のラフ動画やSNS向け短編映像を素早く作る用途に向いています

Stable Video Diffusionはテキスト制御を前提としていないため、Pikaのようなプロンプト主導の操作感はありません。

その代わり、既存のビジュアル素材を起点に、動きだけを付与する用途では安定しやすく、実写素材やイラスト素材との相性が良い構成です。

用途別の選び分け

どの動画生成AIが適しているかは、「何を起点に」「どこまで作り込みたいか」で決まります。

アイデア出しやSNS向け動画を素早く作りたい場合は、RunwayやPikaのようなSaaS型が効率的でしょう。

一方で、すでに静止画素材や世界観が固まっていて、その延長として短い動画表現を作りたい場合には、Stable Video Diffusionが向いています

Image-to-Videoに特化しているため、被写体保持や構図の一貫性を重視する制作フローと相性が良く、ローカル実行による柔軟な運用も可能です。

Stability AIの動画系ラインナップの進化

Stable Video Diffusionの公開以降、Stability AIは動画・3D領域へとモデル展開を広げています。ここでは、公式に発表・公開されている関連モデルを整理します。

Stable Video 3D

Stable Video 3Dは、単一画像から複数視点の画像列を生成するモデルです。名前にVideoが含まれていますが、目的は動画生成というよりも3D再構成。

入力された1枚の画像から異なるカメラ角度のビューを生成し、それを3Dアセット生成やNeRF系ワークフローに活用する設計です。つまり、時間軸ではなく「視点軸」を拡張するモデルと言えます。

Stable Video 4D

Stable Video 4Dは、単一動画から動的な3Dシーンを再構成する研究モデルです。これはさらに一歩進み、「時間+3D空間」を同時に扱う4D表現を目指しています。

入力された動画から、被写体や空間の動きを含む三次元的な再構成を行う設計で、従来の2D動画生成とは方向性が異なります。映像を作るモデルというよりも、映像を立体的に復元する研究プロジェクトに近い立ち位置です。

Stable Video Diffusionの注意点

Stable Video Diffusionを使う上での注意点は次の2つです。

  • GPUの使いすぎ
  • 著作権

Stable Video Diffusionを使う際に、google colaboratoryで実装される時にはGPUの使いすぎに注意が必要です。動画を生成するにはかなりの時間がかかり、google colaboratoryは時間経過に応じてリソースが消費されます。

そのため、気づいたらリソースが空になってしまったということにならないように、注意しましょう。

また、Stable Video Diffusionで生成された動画の著作権自体は問題ありませんが、使用した画像に関しては著作権の問題が発生することがあります。

クリエイターが作成した画像を使ってしまった、企業のロゴやキャラクターの画像を使って動画を生成してしまった、ということにならないよう、元の画像の著作権については注意しておくのがいいでしょう。

活用事例

今回解説する事例において、弊社がX(旧Twitter)で発見した参考となるツイートを紹介させていただいております。取り下げなどのご連絡は、contact@weel.co.jp からご連絡ください。

Stable Diffusionを使った事例として、コカコーラのCMがあります。

KDDIのCMでもStable Diffusionが活用されています。

近年では、CMに生成AIを活用する事例が増えてきており、今後もますます増加すると考えられます。

生成AIを使ってCMを作ることで、コストが下がり、生成AIを使わなかったときに比べて短時間で作成することができます。

また、非現実的な映像を実現することもできるので、視聴者に対してよりインパクトを与えることも可能。

StableVideo Diffusionに関するQ&A

ここではStable Video Diffusionを使う上で、よくある質問をまとめておきます。

Stable Video Diffusionでどのような動画が生成できますか?

静止画を元にした動画生成が可能です。

Stable Video Diffusionを使うのに必要なパソコンのスペックは?

RTX30シリーズや40シリーズのGPU、VRAMは16GB以上が望ましいです。google colaboratoryで動かす場合には、T4でも可能ですがやはりA100の方が生成時間は短縮できます。

動画生成にはどのくらいの時間かかりますか?

動画の長さや解像度、フレーム数、GPUによって異なりますが、1秒間に24フレームの動画を生成する場合には、数分から数時間です。

Stable Video Diffusionは商用利用可能?

商用利用する場合にはprofessional以上のプランに契約する必要があります.

動画の解像度は指定できますか?

指定できます。解像度を高くすればするだけ、生成時間はかかります。

モデルがロードされません

モデルのファイルが見つからないか破損している、指定したバージョンと異なっている場合があります。正しいパスとバージョンを確認、再ダウンロードを試してください。

CUDA Out of Memoryと表示されます。

メモリ不足です。入力解像度が高すぎたりフレーム数が多い、GPUメモリが少ない場合に生じます。解像度やフレーム数を下げる、バッチサイズを減らすなどで対応をしましょう。

フレーム生成の途中で処理が止まります。

VRAM不足が考えられるので、フレーム数を減らして再度生成してみてください。

動画が真っ黒、またはほとんど動きません。

入力画像が不適切か、motion系パラメータが低すぎる可能性があります。画像を差し替えるか、動き関連の設定を見直してください。

被写体が途中で崩れたり、形が変わってしまいます。

Stable Video Diffusionは短尺向けのモデルです。動きを強くしすぎると破綻しやすくなるため、変化量を抑える設定が有効です。

StableVideo Diffusionで簡単に静止画から動画を生成してみましょう

Stable Video Diffusion(SVD)は、Stability AIによって開発されたImage2Videoモデルの一種で、入力画像から動画を生成できます。

誰でも無料で利用可能できるAIツールであり、画像を入力すれば、その画像の内容に即した動画が生成されるのです。使い方は簡単で、本記事のコードをそのまま実行すれば、実行できます。

実際に試したところ、角度が動いているだけだったり、カクカクしていたりしました。加えて、比較検証したところ、やはりStable Video DiffusionよりもGen-2の方が、まだまだ精度がよさそうです。

数年後には、『アーサー・C・クラークの小説』のように、誰でもアーティストになれる時代が来るかもしれないですね。

最後に

いかがだったでしょうか?

Stable Video Diffusionの登場により、静止画から動画生成のハードルが大きく下がりました。ビジュアルコンテンツ制作の新たな可能性を、貴社のプロダクトや事業にどう活かせるか、最適な活用方法を検討してみましょう。

株式会社WEELは、自社・業務特化の効果が出るAIプロダクト開発が強みです!

開発実績として、

・新規事業室での「リサーチ」「分析」「事業計画検討」を70%自動化するAIエージェント
・社内お問い合わせの1次回答を自動化するRAG型のチャットボット
・過去事例や最新情報を加味して、10秒で記事のたたき台を作成できるAIプロダクト
・お客様からのメール対応の工数を80%削減したAIメール
・サーバーやAI PCを活用したオンプレでの生成AI活用
・生徒の感情や学習状況を踏まえ、勉強をアシストするAIアシスタント

などの開発実績がございます。

生成AIを活用したプロダクト開発の支援内容は、以下のページでも詳しくご覧いただけます。
➡︎株式会社WEELのサービスを詳しく見る。

まずは、「無料相談」にてご相談を承っておりますので、ご興味がある方はぜひご連絡ください。
➡︎生成AIを使った業務効率化、生成AIツールの開発について相談をしてみる。

生成AIを社内で活用していきたい方へ
無料相談

「生成AIを社内で活用したい」「生成AIの事業をやっていきたい」という方に向けて、生成AI社内セミナー・勉強会をさせていただいております。

セミナー内容や料金については、ご相談ください。

また、サービス紹介資料もご用意しておりますので、併せてご確認ください。

投稿者

  • WEEL Media部

    株式会社WEELが運営する生成系AI関連メディア「生成AI Media」は、AIの専門家によるWebメディアです。 AIに特化した編集部がAIの活用方法、導入事例、ニュース、トレンド情報を発信しています。

  • URLをコピーしました!
  • URLをコピーしました!
目次