0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

EbSynthってなんだ?〜1枚のキーフレームで動画を変身させるVFXツールの全貌と実践ガイド〜

0
Posted at

この記事の対象読者

  • 動画編集やVFXに興味があり、スタイル転送を試してみたい方
  • Pythonの基礎文法を理解しており、画像処理に挑戦したい方
  • Stable DiffusionやComfyUIなどの画像生成AIと組み合わせたワークフローを構築したい方
  • 手描きアニメーション風の映像を効率的に作りたいクリエイター

この記事で得られること

  • EbSynthの基本原理の理解: テクスチャ合成とPatchMatchアルゴリズムの仕組みがわかる
  • 3つの利用方法の習得: ブラウザ版(EbSynth 2)、Python版(ReEzSynth)、ComfyUI連携それぞれの使い方
  • 実制作で使えるワークフロー: Stable Diffusionと組み合わせたスタイル転送パイプラインの構築方法

この記事で扱わないこと

  • After EffectsやDaVinci Resolveの操作方法
  • Stable Diffusion自体の導入・学習方法(別記事で解説予定)
  • 3Dレンダリングとの連携(StyLit等の上級トピック)

1. EbSynthとの出会い

「この動画を油絵風にしたいんだけど、全フレーム手で塗るのは無理だよな......」

動画制作をやったことがある人なら、一度はこう思ったことがあるんじゃないだろうか。実写映像をアニメ風にしたい、CGのプラスチック感を手描きの温かみに変えたい。でも動画って1秒あたり24〜30フレーム。3分の動画なら5,400フレーム以上。全部手で塗るなんて気が遠くなる。

私がEbSynthを知ったのは、Netflixの「Wednesday」の一場面がきっかけだった。あの独特の手描きアニメーションシーケンスが、まさにEbSynthの技術で実現されていたのだ。リチャード・リンクレイター監督の長編アニメ映画「Apollo 10½: A Space Age Childhood」でも使われている。ハリウッドの現場で実際に使われているVFXツールが、なんと無料で使えるという事実に衝撃を受けた。

EbSynthを一言で表すなら、「1枚塗れば、全部塗れる」ツールだ。料理に例えると、1皿だけ完璧に盛り付けたら、残りの99皿も同じクオリティで自動的に盛り付けてくれるシェフのようなもの。しかも、生成AIとは違ってピクセルレベルで元のキーフレームを忠実に再現するので、「AIっぽいハルシネーション」が発生しない。これがEbSynthの最大の強みだ。

ここまでで、EbSynthがどんなものか、なんとなくイメージできたでしょうか。次は、この記事で使う用語を整理しておきましょう。


2. 前提知識の確認

本題に入る前に、この記事で登場する用語を確認します。

2.1 キーフレーム(Keyframe)とは

動画の中から選んだ「基準となる1枚の画像」のこと。EbSynthでは、このキーフレームに手描きやスタイル変更を加えて、その変更を他の全フレームに伝播させる。映画のアニメーション制作で「原画」と呼ばれるものに近い概念だ。

2.2 スタイル転送(Style Transfer)とは

ある画像のスタイル(画風・色調・テクスチャ)を、別の画像や動画に適用する技術のこと。ゴッホの「星月夜」風に写真を変換する、といった使い方が代表例。EbSynthはこのスタイル転送を動画に対して行うが、ニューラルネットワークではなくテクスチャ合成アルゴリズムを使うのが特徴だ。

2.3 オプティカルフロー(Optical Flow)とは

動画内で各ピクセルがフレーム間でどの方向にどれだけ動いたかを推定する技術のこと。EbSynthはこのオプティカルフローを使って、キーフレームのスタイルを「動きに沿って」他のフレームに伝播させる。人間の体が右に動いたなら、塗った色も右に追従するわけだ。

2.4 PatchMatchアルゴリズムとは

画像をパッチ(小さな矩形領域)に分割し、最も類似するパッチ同士を高速にマッチングするアルゴリズム。2009年にBarnesらによって提案され、Adobe Photoshopの「コンテンツに応じた塗りつぶし」にも使われている。EbSynthのコアエンジンそのものだ。

2.5 テクスチャ合成(Texture Synthesis)とは

既存の画像パッチを再配置・再合成して、新しい画像を生成する技術のこと。生成AIがランダムなノイズから画像を「生成」するのとは根本的に異なり、元画像のピクセルを1:1で保持するため、細部が鮮明に再現される。

これらの用語が押さえられたら、EbSynthの背景を見ていきましょう。


3. EbSynthが生まれた背景

3.1 開発チームと原点

EbSynthは、チェコ共和国プラハに拠点を置くVFXデュオ、Šárka SochorováとOndřej Jamriškaによって開発された。二人は「Secret Weapons」というスタートアップを設立し、もともとはリードアーティストのJakubが描いたコンセプトアートをアニメーションとして動かすための自動ロトスコープツールとして開発を始めた。

Ondřej Jamriškaは2018年にオリジナルのEbSynthをGitHubでオープンソースとして公開した。コアのアルゴリズムは、彼らのチームがACM SIGGRAPH等で発表してきた一連のテクスチャ合成研究に基づいている。

3.2 従来の課題とEbSynthが解決したこと

従来の動画スタイル変換には、大きく3つの壁があった。

課題 従来の方法 EbSynthの解決策
作業量の膨大さ 全フレーム手描き(例: 映画「Loving Vincent」は65,000枚の油絵を手描き) キーフレーム1枚を編集するだけで全フレームに伝播
時間的一貫性の欠如 フレームごとにAIで変換するとチラつき(フリッカー)が発生 オプティカルフローに基づく連続的な伝播で一貫性を維持
アーティストの制御力不足 生成AIは出力が予測不能で、意図しない変更が入る ピクセルレベルでキーフレームを忠実に再現、ハルシネーションなし

特に3番目のポイントは重要だ。Stable DiffusionやRunway Gen-1などの生成AIベースのスタイル転送ツールは、美しい結果を出すものの「AIが勝手に追加した要素」が混じることがある。EbSynthは生成モデルを一切使わないため、アーティストが塗ったものだけが忠実に反映される。この「アーティストファースト」の設計思想が、ハリウッドの制作現場で信頼を得た理由だ。

3.3 EbSynth 2への進化

2025年9月、Secret WeaponsはEbSynth V2をリリースした。主な変更点は以下の通り。

項目 EbSynth(旧版) EbSynth 2
動作環境 デスクトップアプリ(Windows/Mac) ブラウザベース(Chrome推奨)
UI 最小限(Open/Save/Exportのみ) タイムライン、レイヤー、ブラシ搭載
プレビュー バッチ処理後に確認 リアルタイムプレビュー
処理速度 標準 約10倍高速化
GPU対応 NVIDIA NVIDIA, AMD, Apple Silicon
料金 無料(ベータ) Free: 720p MP4 / Pro: $20/月(4K, PNG出力)

背景がわかったところで、基本的な仕組みを見ていきましょう。


4. 基本概念と仕組み

4.1 EbSynthの処理パイプライン

EbSynthの処理は、以下の3ステップで行われる。

ステップ1: キーフレーム編集
  ↓ アーティストが動画の1フレームを選び、スタイルを適用
ステップ2: ガイド分析
  ↓ オプティカルフローとエッジ検出で動きの情報を抽出
ステップ3: パッチマッチング合成
  ↓ キーフレームのパッチを動きに沿って再配置し、全フレームを生成

重要なのは、ステップ3で「新しいピクセルを生成する」のではなく、「キーフレームのピクセルを再配置する」だけだという点だ。だからこそハルシネーションが起きない。

4.2 PatchMatchの仕組み(コア技術)

EbSynthの心臓部であるPatchMatchアルゴリズムを、もう少し詳しく見てみよう。

【PatchMatchの処理フロー】

1. 初期化:
   ターゲット画像の各パッチに対して、
   ソース画像からランダムにパッチを割り当てる

2. 伝播(Propagation):
   隣接するパッチのマッチング結果を参照し、
   「隣がうまくいっているなら、自分もその近くが良いはず」
   という仮定でマッチングを改善する

3. ランダム探索(Random Search):
   現在のマッチング結果の周辺をランダムに探索し、
   より良いマッチングがあれば更新する

4. 反復:
   2と3を数回繰り返して収束させる

このアルゴリズムの天才的な点は、「完全に最適な解を求めない」ことだ。ランダム初期化と局所的な伝播を組み合わせることで、計算量を劇的に削減しつつ、実用上十分な品質を実現している。全探索なら数時間かかる処理が、PatchMatchなら数秒で終わる。

4.3 ガイドチャンネルによる合成制御

EbSynthでは、複数のガイドチャンネルを使って合成の方向性を制御できる。

ガイドの種類 役割 用途
カラーガイド 色の対応関係を指定 基本的なスタイル伝播
エッジガイド 輪郭線の対応関係を指定 構造を保持した合成
ポジショナルガイド 位置の対応関係を指定 動きの大きいシーンでの安定性向上
セグメンテーションガイド 領域の対応関係を指定 顔・体・背景などの部位別制御

各ガイドには重みを設定でき、例えばエッジガイドの重みを上げれば輪郭がより鮮明に保持される。デフォルトではスタイルの重みが1.0、ガイドの重みが0.66で、ガイド対スタイルの比率は2:1が推奨されている。

4.4 生成AIとの根本的な違い

ここで、EbSynthと生成AIベースのスタイル転送の違いを整理しておこう。

比較項目 EbSynth 生成AI(Stable Diffusion等)
技術基盤 ノンパラメトリックテクスチャ合成 ニューラルネットワーク(拡散モデル等)
ピクセルの由来 キーフレームの実ピクセルを再配置 ノイズから新たに生成
ハルシネーション なし あり得る
時間的一貫性 オプティカルフローで高い一貫性 フレームごとにばらつきやすい
創造性 アーティストの編集に依存 プロンプトで多様なスタイルを指定可能
処理速度 非常に高速 比較的遅い
苦手なもの キーフレームにないオブジェクトの出現 特になし(品質は別問題)

私の見解では、EbSynthと生成AIは「競合」ではなく「補完」の関係にある。Stable Diffusionでキーフレームのスタイルを作り、EbSynthでそれを動画全体に安定的に伝播させる——このハイブリッドワークフローが、2025年現在の最も実用的なアプローチだ。

基本概念が理解できたところで、実際にコードを書いて動かしてみましょう。


5. 実践:実際に使ってみよう

EbSynthを使う方法は3つある。それぞれの特徴を理解して、自分に合った方法を選ぼう。

方法 難易度 対象ユーザー メリット
EbSynth 2(ブラウザ版) デザイナー・アーティスト インストール不要、直感的UI
ReEzSynth(Python) 中〜高 開発者・研究者 スクリプト化可能、カスタマイズ自由
ComfyUI連携 AI画像生成ユーザー Stable Diffusionとのシームレス連携

この記事では、Python開発者向けにReEzSynthを中心に解説する。

5.1 環境構築

ReEzSynthはPyTorchベースのEbSynth再実装で、CUDAバックエンドによる高速処理が特徴だ。

# リポジトリのクローン
git clone https://github.com/FuouM/ReEzSynth.git
cd ReEzSynth

# 依存関係のインストールとCUDA拡張のビルド
pip install .

# ビルドが難しい場合はJITコンパイルも利用可能
# pip install . をスキップすれば、初回実行時に自動コンパイルされる

前提条件チェックリスト:

要件 最低バージョン 確認コマンド
Python 3.9以上 python --version
CUDA Toolkit 11.x または 12.x nvcc --version
PyTorch(CUDA版) 2.0以上 python -c "import torch; print(torch.__version__)"
NVIDIA GPU Compute Capability 3.0+ nvidia-smi
C++コンパイラ VS2019+(Windows)/ GCC(Linux) cl(Win) / gcc --version(Linux)

5.2 環境別の設定ファイル

ReEzSynthはYAMLベースのプロジェクト設定ファイルで管理する。以下の3種類を用意した。

開発環境用(config.dev.yaml)

# config.dev.yaml - 開発環境用(このままコピーして使える)
# 小さい解像度で素早くイテレーションするための設定

project:
  name: "ebsynth_dev_project"
  content_dir: "./input/frames"       # 元動画のフレーム連番
  style_dir: "./input/styles"         # キーフレーム(スタイル適用済み)
  output_dir: "./output/dev"
  
synthesis:
  edge_method: "Classic"              # PST / Classic / PAGE から選択
  flow_model: "raft"                  # raft / ef_raft / neuflow_v2
  patch_size: 5                       # パッチサイズ(奇数、デフォルト: 5)
  num_pyramid_levels: 6               # ピラミッドレベル数
  search_vote_iters: 12               # 探索投票の反復回数
  patchmatch_iters: 6                 # PatchMatchの反復回数
  
blending:
  use_poisson_blending: true          # ポアソンブレンディングで継ぎ目を滑らかに
  use_temporal_nnf_propagation: false  # 開発中はOFFで高速化
  use_sparse_feature_guide: false      # 開発中はOFFで高速化

processing:
  max_resolution: 544                 # 短辺の最大解像度(開発時は低解像度)
  device: "cuda"                      # cuda / cpu
  cache_flow: true                    # オプティカルフローをキャッシュ

本番環境用(config.prod.yaml)

# config.prod.yaml - 本番環境用(高品質出力向け)
# 最終レンダリング用の高品質設定

project:
  name: "ebsynth_prod_project"
  content_dir: "./input/frames"
  style_dir: "./input/styles"
  output_dir: "./output/production"

synthesis:
  edge_method: "PAGE"                 # 最高品質のエッジ検出(処理は遅い)
  flow_model: "raft"                  # 安定性重視でRAFTを使用
  patch_size: 7                       # 大きめのパッチで品質向上
  num_pyramid_levels: 8               # ピラミッドレベルを増やして精度向上
  search_vote_iters: 16               # 探索回数を増やして品質向上
  patchmatch_iters: 8                 # 反復回数を増やして品質向上

blending:
  use_poisson_blending: true
  use_temporal_nnf_propagation: true   # 本番ではONでフリッカー低減
  use_sparse_feature_guide: true       # 本番ではONでテクスチャ安定化

processing:
  max_resolution: 1080                # フルHD出力
  device: "cuda"
  cache_flow: true

テスト環境用(config.test.yaml)

# config.test.yaml - CI/CD・テスト用
# 最小限の設定で動作確認するための設定

project:
  name: "ebsynth_test"
  content_dir: "./tests/fixtures/frames"
  style_dir: "./tests/fixtures/styles"
  output_dir: "./tests/output"

synthesis:
  edge_method: "PST"                  # 最速のエッジ検出
  flow_model: "ef_raft"               # 軽量フローモデル
  patch_size: 5
  num_pyramid_levels: 4               # 最小限のピラミッド
  search_vote_iters: 6                # 最小限の反復
  patchmatch_iters: 4

blending:
  use_poisson_blending: false          # テスト時はOFFで高速化
  use_temporal_nnf_propagation: false
  use_sparse_feature_guide: false

processing:
  max_resolution: 256                 # テスト用の低解像度
  device: "cuda"                      # CUDAがなければ "cpu" に変更
  cache_flow: false                   # テスト時はキャッシュ不要

5.3 基本的な使い方(Python API)

ReEzSynthはコマンドライン版とPython API版の両方を提供している。ここではPython APIを使った基本的なワークフローを示す。

"""
EbSynth基本ワークフロー - ReEzSynth Python API
動画のフレーム連番にキーフレームのスタイルを伝播させるサンプル。
実行方法: python run_ebsynth.py
前提: pip install . でReEzSynthがインストール済みであること
"""
import os
from pathlib import Path

# ReEzSynthのメインクラスをインポート
from reezsynth import Ezsynth, ImageSynth


def prepare_frames(video_path: str, output_dir: str) -> list[str]:
    """動画をフレーム連番に分解する前処理"""
    import cv2
    
    os.makedirs(output_dir, exist_ok=True)
    cap = cv2.VideoCapture(video_path)
    frame_paths = []
    idx = 0
    
    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            break
        frame_path = os.path.join(output_dir, f"frame_{idx:05d}.png")
        cv2.imwrite(frame_path, frame)
        frame_paths.append(frame_path)
        idx += 1
    
    cap.release()
    print(f"抽出完了: {idx}フレーム -> {output_dir}")
    return frame_paths


def run_style_transfer(
    content_dir: str,
    style_paths: dict[int, str],
    output_dir: str,
    edge_method: str = "Classic",
    flow_model: str = "raft",
) -> None:
    """
    EbSynthによるスタイル転送を実行する。
    
    Parameters
    ----------
    content_dir : str
        元動画のフレーム連番が格納されたディレクトリ
    style_paths : dict[int, str]
        キーフレームのインデックスとスタイル画像パスの辞書
        例: {0: "./styles/frame_00000_styled.png",
             50: "./styles/frame_00050_styled.png"}
    output_dir : str
        出力先ディレクトリ
    edge_method : str
        エッジ検出手法("PST", "Classic", "PAGE")
    flow_model : str
        オプティカルフローモデル("raft", "ef_raft", "neuflow_v2""""
    os.makedirs(output_dir, exist_ok=True)
    
    # キーフレームのインデックスをソート
    style_indices = sorted(style_paths.keys())
    style_images = [style_paths[idx] for idx in style_indices]
    
    # インデックス文字列を生成(例: "0_50")
    style_idxes_str = "_".join(str(idx) for idx in style_indices)
    
    # Ezsynthインスタンスを作成して実行
    ez = Ezsynth(
        content_dir=content_dir,
        style_paths=style_images,
        style_idxes=style_idxes_str,
        output_dir=output_dir,
        edge_method=edge_method,
        flow_model=flow_model,
    )
    
    # 合成を実行
    results = ez.run()
    print(f"合成完了: {len(results)}フレーム -> {output_dir}")


def main():
    """メイン処理"""
    # === 設定 ===
    content_dir = "./input/frames"        # 元動画のフレーム
    output_dir = "./output/stylized"      # 出力先
    
    # キーフレームの定義(フレーム番号: スタイル画像パス)
    style_paths = {
        0: "./input/styles/frame_00000_styled.png",    # 先頭フレーム
        50: "./input/styles/frame_00050_styled.png",   # 中間フレーム
    }
    
    # === 実行 ===
    run_style_transfer(
        content_dir=content_dir,
        style_paths=style_paths,
        output_dir=output_dir,
        edge_method="Classic",   # 開発時はClassicがバランス良い
        flow_model="raft",       # 安定性重視
    )
    
    print("全処理完了!")


if __name__ == "__main__":
    main()

5.4 実行結果

上記のコードを実行すると、以下のような出力が得られる。

$ python run_ebsynth.py
[INFO] Loading content frames from ./input/frames ...
[INFO] Found 100 content frames.
[INFO] Loading style frames: [0, 50]
[INFO] Computing optical flow (RAFT) ...
[INFO] Computing edge maps (Classic) ...
[INFO] Running synthesis pass (forward) ...
[INFO] Running synthesis pass (reverse) ...
[INFO] Blending bidirectional results ...
合成完了: 100フレーム -> ./output/stylized
全処理完了!

出力ディレクトリには、スタイルが適用されたフレーム連番が保存される。あとはffmpeg等で動画に結合すれば完成だ。

# フレーム連番を動画に結合
ffmpeg -framerate 30 -i ./output/stylized/frame_%05d.png \
       -c:v libx264 -pix_fmt yuv420p output_stylized.mp4

5.5 よくあるエラーと対処法

エラー 原因 対処法
ebsynth_torch not found CUDA拡張のビルドに失敗 CUDA Toolkitとpip install .を再実行。または JITコンパイル(pip install .をスキップ)を利用
CUDA out of memory GPUメモリ不足(動画合成はメモリ集約型) max_resolutionを下げる(例: 1080 → 544)。バッチサイズの削減も有効
Visual Artifacts (Jitter/Flicker) フレーム間の一貫性不足 use_temporal_nnf_propagation: trueuse_sparse_feature_guide: trueを有効化。キーフレームを追加
Style/content resolution mismatch スタイル画像と元フレームの解像度が不一致 両者の解像度を完全に一致させる。リサイズにはPillowcv2.resizeを使用
NaN in edge detection 入力テンソルにゼロが多すぎる マスク済み入力は避けるか、pre_mask: falseに設定

5.6 環境診断スクリプト

問題が発生した場合は、以下のスクリプトで環境を診断できる。

#!/usr/bin/env python3
"""
EbSynth環境診断スクリプト
実行方法: python check_ebsynth_env.py
"""
import sys
import shutil


def check_environment():
    """EbSynth実行に必要な環境をチェックして問題を報告"""
    issues = []
    warnings = []
    
    # 1. Pythonバージョン確認
    if sys.version_info < (3, 9):
        issues.append(
            f"Python 3.9以上が必要です(現在: {sys.version}"
        )
    else:
        print(f"[OK] Python {sys.version}")
    
    # 2. PyTorch + CUDA確認
    try:
        import torch
        print(f"[OK] PyTorch {torch.__version__}")
        
        if torch.cuda.is_available():
            gpu_name = torch.cuda.get_device_name(0)
            vram = torch.cuda.get_device_properties(0).total_mem
            vram_gb = vram / (1024 ** 3)
            print(f"[OK] CUDA利用可能: {gpu_name} ({vram_gb:.1f}GB)")
            
            if vram_gb < 4:
                warnings.append(
                    f"VRAM {vram_gb:.1f}GB は少なめです。"
                    "低解像度での処理を推奨します"
                )
        else:
            issues.append(
                "CUDAが利用できません。CUDA版PyTorchを"
                "インストールしてください"
            )
    except ImportError:
        issues.append("PyTorchがインストールされていません")
    
    # 3. 必須パッケージ確認
    required_packages = {
        "cv2": "opencv-python",
        "numpy": "numpy",
        "PIL": "Pillow",
        "yaml": "PyYAML",
    }
    for module, package in required_packages.items():
        try:
            __import__(module)
            print(f"[OK] {package}")
        except ImportError:
            issues.append(
                f"{package}がインストールされていません: "
                f"pip install {package}"
            )
    
    # 4. ffmpeg確認(動画結合用)
    if shutil.which("ffmpeg"):
        print("[OK] ffmpeg が利用可能")
    else:
        warnings.append(
            "ffmpegが見つかりません。"
            "フレーム連番を動画に結合する際に必要です"
        )
    
    # 5. CUDA拡張確認
    try:
        import ebsynth_torch
        print("[OK] ebsynth_torch CUDA拡張がビルド済み")
    except ImportError:
        warnings.append(
            "ebsynth_torch CUDA拡張が未ビルドです。"
            "JITコンパイルで自動ビルドされますが、"
            "事前ビルド推奨: pip install ."
        )
    
    # 結果表示
    print("\n" + "=" * 50)
    if issues:
        print("--- 問題が見つかりました ---")
        for issue in issues:
            print(f"  [NG] {issue}")
    if warnings:
        print("--- 警告 ---")
        for warning in warnings:
            print(f"  [WARN] {warning}")
    if not issues and not warnings:
        print("環境は正常です。EbSynthを実行できます!")
    elif not issues:
        print("必須要件は満たしています。警告を確認してください。")


if __name__ == "__main__":
    check_environment()

5.7 Docker設定

チーム開発や再現性を重視する場合は、Dockerを活用しよう。

# Dockerfile
FROM nvidia/cuda:12.1.0-devel-ubuntu22.04

# 基本パッケージ
RUN apt-get update && apt-get install -y \
    python3.11 python3.11-dev python3-pip \
    git ffmpeg libgl1-mesa-glx libglib2.0-0 \
    && rm -rf /var/lib/apt/lists/*

# Python設定
RUN ln -s /usr/bin/python3.11 /usr/bin/python
WORKDIR /app

# ReEzSynthのインストール
RUN git clone https://github.com/FuouM/ReEzSynth.git /app/reezsynth
WORKDIR /app/reezsynth
RUN pip install --no-cache-dir .

# 作業ディレクトリ
WORKDIR /workspace
COPY . .

CMD ["python", "run_ebsynth.py"]
# docker-compose.yml
version: '3.8'
services:
  ebsynth:
    build: .
    runtime: nvidia
    environment:
      - NVIDIA_VISIBLE_DEVICES=all
    volumes:
      - ./input:/workspace/input      # 入力ファイル
      - ./output:/workspace/output    # 出力ファイル
      - ./configs:/workspace/configs  # 設定ファイル
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

実装方法がわかったので、次は具体的なユースケースを見ていきます。


6. ユースケース別ガイド

6.1 ユースケース1: 実写動画をアニメ風に変換

想定読者: YouTuberやショート動画クリエイターで、実写映像にアニメ風エフェクトを加えたい方

推奨ワークフロー: Stable Diffusionでキーフレームをアニメ風に変換 → EbSynthで動画全体に伝播

サンプルコード:

"""
ユースケース1: 実写→アニメ変換パイプライン
Stable Diffusion(img2img)でスタイル画像を作成し、
EbSynthで動画全体に伝播させるワークフロー。
"""
import os
import cv2
from pathlib import Path


def select_keyframes(
    frames_dir: str,
    interval: int = 30,
) -> list[int]:
    """
    キーフレームを等間隔で選択する。
    動きが激しいシーンではintervalを小さくする。
    
    Parameters
    ----------
    frames_dir : str
        フレーム連番ディレクトリ
    interval : int
        キーフレーム間隔(フレーム数)
    
    Returns
    -------
    list[int]
        選択されたキーフレームのインデックス
    """
    frame_files = sorted(Path(frames_dir).glob("*.png"))
    total = len(frame_files)
    
    # 先頭・末尾は必ず含め、間を等間隔で埋める
    keyframe_indices = list(range(0, total, interval))
    if keyframe_indices[-1] != total - 1:
        keyframe_indices.append(total - 1)
    
    print(f"キーフレーム選択: {len(keyframe_indices)}枚 / {total}")
    return keyframe_indices


def create_anime_style_keyframe(
    source_path: str,
    output_path: str,
    prompt: str = "anime style, vibrant colors, cel shading",
) -> str:
    """
    Stable Diffusion img2imgでアニメ風キーフレームを生成する。
    ※ 実際のSD APIコールは環境依存のため、ここでは概念を示す。
    """
    # ここにStable Diffusion APIのコールを実装
    # 例: AUTOMATIC1111 WebUI API, ComfyUI API, diffusers等
    #
    # from diffusers import StableDiffusionImg2ImgPipeline
    # pipe = StableDiffusionImg2ImgPipeline.from_pretrained(...)
    # result = pipe(prompt=prompt, image=source_image, strength=0.6)
    # result.images[0].save(output_path)
    
    print(f"スタイル適用: {source_path} -> {output_path}")
    return output_path


def run_anime_pipeline(
    video_path: str,
    output_dir: str = "./output/anime",
    keyframe_interval: int = 30,
) -> None:
    """実写→アニメ変換の全体パイプライン"""
    frames_dir = "./tmp/frames"
    styles_dir = "./tmp/styles"
    os.makedirs(frames_dir, exist_ok=True)
    os.makedirs(styles_dir, exist_ok=True)
    
    # Step 1: 動画をフレーム分解
    cap = cv2.VideoCapture(video_path)
    fps = cap.get(cv2.CAP_PROP_FPS)
    idx = 0
    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            break
        cv2.imwrite(f"{frames_dir}/frame_{idx:05d}.png", frame)
        idx += 1
    cap.release()
    
    # Step 2: キーフレーム選択
    keyframe_indices = select_keyframes(frames_dir, keyframe_interval)
    
    # Step 3: 各キーフレームにスタイル適用
    style_paths = {}
    for ki in keyframe_indices:
        src = f"{frames_dir}/frame_{ki:05d}.png"
        dst = f"{styles_dir}/frame_{ki:05d}_styled.png"
        create_anime_style_keyframe(src, dst)
        style_paths[ki] = dst
    
    # Step 4: EbSynthで伝播
    from reezsynth import Ezsynth
    
    style_idxes = "_".join(str(i) for i in sorted(style_paths))
    ez = Ezsynth(
        content_dir=frames_dir,
        style_paths=list(style_paths.values()),
        style_idxes=style_idxes,
        output_dir=output_dir,
        edge_method="Classic",
        flow_model="raft",
    )
    ez.run()
    
    # Step 5: 動画に再結合
    os.system(
        f"ffmpeg -framerate {fps} "
        f"-i {output_dir}/frame_%05d.png "
        f"-c:v libx264 -pix_fmt yuv420p "
        f"{output_dir}/anime_output.mp4"
    )
    print(f"完成: {output_dir}/anime_output.mp4")


if __name__ == "__main__":
    run_anime_pipeline("./input/source_video.mp4")

6.2 ユースケース2: VFXリタッチ(デジタルメイクアップ・傷消し)

想定読者: 映像制作者やVFXアーティストで、動画内の修正作業を効率化したい方

推奨ワークフロー: Photoshop/GIMPでキーフレーム上の修正を行い → EbSynthで動画全体に伝播

サンプルコード:

"""
ユースケース2: VFXリタッチ(傷消し・メイクアップ伝播)
1フレームだけPhotoshop等で修正し、
その修正を動画全体に自動伝播させる。
"""
from reezsynth import Ezsynth


def run_vfx_retouch(
    content_dir: str,
    retouched_frame_path: str,
    retouched_frame_index: int,
    output_dir: str,
) -> None:
    """
    VFXリタッチの伝播を実行する。
    
    Parameters
    ----------
    content_dir : str
        元動画のフレーム連番ディレクトリ
    retouched_frame_path : str
        修正済みキーフレームのパス
    retouched_frame_index : int
        修正したフレームのインデックス
    output_dir : str
        出力先ディレクトリ
    """
    ez = Ezsynth(
        content_dir=content_dir,
        style_paths=[retouched_frame_path],
        style_idxes=str(retouched_frame_index),
        output_dir=output_dir,
        # リタッチはエッジ保持が重要
        edge_method="PAGE",
        flow_model="raft",
    )
    ez.run()
    print(f"リタッチ伝播完了: {output_dir}")


if __name__ == "__main__":
    run_vfx_retouch(
        content_dir="./input/frames",
        retouched_frame_path="./input/retouched/frame_00030.png",
        retouched_frame_index=30,
        output_dir="./output/retouched",
    )

6.3 ユースケース3: ComfyUIとの連携ワークフロー

想定読者: ComfyUIでStable Diffusionを活用しており、動画スタイル転送をノードベースで行いたい方

推奨構成: ComfyUI-EbSynth拡張を導入し、ノードベースでワークフローを構築

セットアップ手順:

# ComfyUIのカスタムノードディレクトリに移動
cd /path/to/ComfyUI/custom_nodes

# ComfyUI-EbSynth拡張をクローン
git clone --recurse-submodules https://github.com/FuouM/ComfyUI-EbSynth.git

# 依存関係のインストール
cd ComfyUI-EbSynth
pip install -r requirements.txt

# ComfyUIを再起動

ノード設定のポイント:

パラメータ 推奨値 説明
style_idxes "0_50_99" キーフレームのインデックス(_区切り)
edge_method Classic バランス型。高品質ならPAGEに変更
edge_weight 1.0 エッジガイドの強さ。上げると輪郭が鮮明に
flow_model RAFT 安定性重視。高速化ならEF_RAFT

ユースケースを把握できたところで、この先の学習パスを確認しましょう。


7. 学習ロードマップ

この記事を読んだ後、次のステップとして以下をおすすめします。

初級者向け(まずはここから)

  1. EbSynth 2(ブラウザ版)を試すhttps://ebsynth.com/ にアクセスし、無料プランで短い動画(10秒程度)を変換してみる。インストール不要で最も手軽。
  2. キーフレーム作成の練習 — Photoshop、GIMP、またはProcreate等で1フレームを塗り替え、EbSynth 2に読み込ませて結果を確認する。
  3. 公式チュートリアル動画を視聴 — EbSynth公式サイトの10分チュートリアルで基本ワークフローを習得。

中級者向け(実践に進む)

  1. ReEzSynth(Python版)の導入https://github.com/FuouM/ReEzSynth をクローンし、コマンドラインとPython APIの両方を試す。
  2. Stable Diffusion × EbSynthのハイブリッドワークフロー構築 — img2imgでスタイルフレームを生成し、EbSynthで伝播するパイプラインを自動化する。
  3. オプティカルフローの理解を深める — RAFT論文(Teed & Deng, 2020)を読み、フローの可視化や評価手法を学ぶ。

上級者向け(さらに深く)

  1. オリジナルのEbSynth C++ソースコードを読むhttps://github.com/jamriska/ebsynth でPatchMatch実装の詳細を理解する。
  2. 学術論文を辿る — Jamriška et al. (2019) "Stylizing Video by Example" がEbSynthの理論的基盤。ACM SIGGRAPH論文として発表されている。
  3. カスタムガイドチャンネルの実装 — ReEzSynthのModulation Maps機能を使い、独自のガイドレイヤーを追加してみる。

8. まとめ

この記事では、EbSynthについて以下を解説しました。

  1. EbSynthの基本原理: PatchMatchアルゴリズムとテクスチャ合成による、ハルシネーションのないスタイル転送の仕組み
  2. 3つの利用方法: EbSynth 2(ブラウザ版)、ReEzSynth(Python API)、ComfyUI連携それぞれの環境構築と使い方
  3. 実制作ワークフロー: 実写→アニメ変換、VFXリタッチ、生成AIとのハイブリッドパイプライン

私の所感

EbSynthの面白さは、「AIではない」ことにある。2025年、あらゆるツールが「AI搭載」を謳う中で、EbSynthは頑なにノンパラメトリックなテクスチャ合成を守り続けている。そしてそれが、Netflixの「Wednesday」やリチャード・リンクレイターの映画で採用されるという結果に繋がっている。

私の見解では、これは「AIが万能ではない」ことの好例だ。ピクセルレベルの制御が必要な場面では、生成AIよりもEbSynthのような決定論的アルゴリズムの方が適している。もちろん、Stable Diffusionでキーフレームを作り、EbSynthで伝播させるという「いいとこ取り」のワークフローが最強なのは間違いない。

RTX 5090を手に入れた今、ReEzSynthのCUDAバックエンドがどれだけ速く回るか試すのが楽しみだ。4K動画のリアルタイム合成も夢じゃないかもしれない。

EbSynthは「アーティストの筆を動画全体に拡張する」ツールだ。AIに描かせるのではなく、人間が描いたものを広げる。この哲学に共感するなら、ぜひ一度触ってみてほしい。


参考文献


推奨タグ: Python, 画像処理, 初心者, 動画, AI

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?