toruのブログ

月1くらいで記事書きたい。

Pythonスクリプトを使って DaVinci Resolve の Fusion ページでアニメーションの制御を行う

背景と目的

DaVinci Resolve の FusionページではSpline Editor を使ってアニメーションを細かく制御できる(図1参照)。 これは Pythonスクリプトからでも実行可能だが、やり方が分かりづらかったので備忘録として記事を残しておく。

図1. Spline Editor を使ってアニメーションに緩急を付けている様子

Pythonスクリプトを使って Spline Editor と同等の操作を行う方法

Pythonスクリプトを使って fusion composition や各種ノードを作成した上で、以下の手順を実行すれば良い。

  1. keyframe と Spline Editor の設定を辞書型の変数として定義しておく
  2. BezierSpline インスタンスを作成する
  3. BezierSpline インスタンスに対してSetKeyFrames()を使い 手順1. で作成した辞書型の変数を代入する
  4. ノードのパラメータ(今回はWidth)に対して BezierSpline インスタンスを代入する

冒頭の図1 に相当する Python の実装例は以下の通り。

# RectangleMask ノードは rect_mask という変数名で作成済みとする

key_frames = {
    0: { 1: 0.0, 'LH': {1: 0.0, 2: 0.0}, 'RH': {1: 20.0, 2: 0.0} },
    23: { 1: 1.0, 'LH': {1: -20.0, 2: 0.0}, 'RH': {1: 0.0, 2: 0.0} }
}
bezier_spline = fusion_comp.BezierSpline()
bezier_spline.SetKeyFrames(key_frames)
rect_mask["Width"] = bezier_spline

ここで、上記のコードで作成した変数key_framesについて軽く解説を行っておく*1

まず023の数字は keyframe を意味する。上記のコードでは 0フレームと 23フレームに keyframe を打つことを意味する。

keyframe内部の 1: 0.01: 1.0 はターゲットとなるパラメータの値を意味する。 上記のコードでは 0フレームに Width=0.0 を、23フレームに Width=1.0 を設定している。

最後に 'LH': {1: 0.0, 2: 0.0}, 'RH': {1: 20.0, 2: 0.0}'LH': {1: -20.0, 2: 0.0}, 'RH': {1: 0.0, 2: 0.0} は Spline Editor で設定するハンドルの相対位置を意味している。 例えば'LH': {1: -20.0, 2: 0.0}はレフトハンドルの x座標を -20だけ移動した場所に、y座標を 0だけ移動した場所に設定することを意味する。

以上がkey_framesの説明である。

サンプルコード

プロジェクトの作成から図1の設定までを一通り実行するサンプルコードを以下に添付しておく。

# -*- coding: utf-8 -*-
import sys
import os

if sys.platform == "darwin":  # macOS
    resolve_script_api = (
        "/Library/Application Support/Blackmagic Design/DaVinci Resolve/"
        "Developer/Scripting"
    )
    sys.path.append(os.path.join(resolve_script_api, "Modules"))
    resolve_lut_path = \
        "/Library/Application Support/Blackmagic Design/DaVinci Resolve/LUT/"
elif sys.platform == "win32":  # Windows
    resolve_script_api = os.path.expandvars(
        r"%PROGRAMDATA%\Blackmagic Design\DaVinci Resolve\Support"
        r"\Developer\Scripting"
    )
    sys.path.append(os.path.join(resolve_script_api, "Modules"))
    resolve_lut_path = os.path.expandvars(
        r"%PROGRAMDATA%\Blackmagic Design\DaVinci Resolve\Support\LUT"
    )
elif sys.platform == "linux":  # Linux
    resolve_script_api = "/opt/resolve/Developer/Scripting"
    sys.path.append(os.path.join(resolve_script_api, "Modules"))
    resolve_lut_path = "/home/resolve/LUT"
    ValueError("Linux platform is not supported")

import DaVinciResolveScript as dvr_script

resolve = dvr_script.scriptapp("Resolve")
if resolve is None:
    raise ConnectionError("The DaVinci Resolve app is not running.")

project_name = "Apply Spline Editor Result 20250215"
project_manager = resolve.GetProjectManager()
project = project_manager.GetCurrentProject()
project_manager.CloseProject(project)
project_manager.DeleteProject(project_name)
project = project_manager.CreateProject(project_name)

# add fusion composition
resolve.OpenPage("edit")
media_pool = project.GetMediaPool()
timeline = media_pool.CreateEmptyTimeline("Timeline1")
timeline_item = timeline.InsertFusionCompositionIntoTimeline()
fusion_comp = timeline_item.AddFusionComp()

fusion_comp.Lock()

# add background node (tool)
x_pos = 1
y_pos = 1
base_bg = fusion_comp.AddTool("Background", x_pos, y_pos)
base_bg.SetInput("TopLeftRed", 0.5)
base_bg.SetInput("TopLeftGreen", 0.5)
base_bg.SetInput("TopLeftBlue", 0.5)

# add merge node (tool)
x_pos = 2
y_pos = 1
merge = fusion_comp.AddTool("Merge", x_pos, y_pos)

# add rectangle mask (tool)
x_pos = 2
y_pos = 3
rect_mask = fusion_comp.AddTool("RectangleMask", x_pos, y_pos)
rect_mask.SetInput("Width", 0.3)
rect_mask.SetInput("Height", 0.3)

# rectangle background (tool)
x_pos = 2
y_pos = 2
rect_bg = fusion_comp.AddTool("Background", x_pos, y_pos)
rect_bg.SetInput("TopLeftRed", 0.6666666)
rect_bg.SetInput("TopLeftGreen", 1.0)
rect_bg.SetInput("TopLeftBlue", 0.0)
rect_bg.SetInput("EffectMask", rect_mask)

# get MediaOut1
tool_name = "MediaOut1"
media_out = next(
    (vv for vv in fusion_comp.GetToolList().values() if vv.Name == tool_name), None
)

# connect tools
merge.ConnectInput("Background", base_bg)
merge.ConnectInput("Foreground", rect_bg)
media_out.ConnectInput("Input", merge)

# set keyframes with bezier spline
key_frames = {
    0: {
        1: 0.0, 'LH': {1: 0.0, 2: 0.0}, 'RH': {1: 20.0, 2: 0.0}
    },
    23: {
        1: 1.0, 'LH': {1: -20.0, 2: 0.0}, 'RH': {1: 0.0, 2: 0.0}
    },
}
bezier_spline = fusion_comp.BezierSpline()
bezier_spline.SetKeyFrames(key_frames)
rect_mask["Width"] = bezier_spline

fusion_comp.Unlock()

resolve.OpenPage("fusion")

感想

Spline Editor の横軸の単位がフレームになっているのが個人的には分かりやすくて助かった。 スクリプトを工夫すれば 24 fps / 30 fps / 60 fps の各種フレームレートで同じアニメーションを作成可能であり、テストパターン動画の作成で大いに役立った。

*1:分かりにくい、というか筆者は最初ちんぷんかんぷんだった