manim×Claude3で動画素材を大量制作する

  • このエントリーをはてなブックマークに追加
  • LINEで送る

 大規模言語モデルである「Claude3」のリリース日(2024年3月4日)から、はや1ヶ月以上が経ちました。GPT-5も2024夏くらいにリリースとのことで、生成AIの流行の波はまだまだこれから加速していきそうです!GPT-4にも負けず劣らずといった精度を誇るClaude3、業務では文章校正や分からないコードを教えてくれるメンターとして、プライベートでも話し相手代わりによく使っています。というところで、nヶ月振りにブログ執筆中の証拠です。書き出しを考え校正するのに何日使っているのだか……(´・ω・`)

 今回は、そのClaude3と数学アニメーションツールであるmanimというライブラリを使ってエフェクト素材や動画素材を沢山作ってみよう!という趣旨で進んでいきます。というのも、近頃Twitter……XでClaude3やGPT-4を使って動画生成をしてみた!というポストが流れてきていたのを見て、しっかり影響されました。映像がある分、生成AIの使い道として皆興味持ってくれたらいいな……!!!

本題

実行環境

本記事では、以下のような環境で行います。
 ・google colaboratory
 ・python3
 ・manim(バージョン:0.18.0.post0)
 ・Claude3

ここからColaboratoryで作業

google colaboratoryにアクセスすると、以下のような画面が表示されるので、左下の「+ノートブックを新規作成」から新しいノートブックを作成してみましょう!折角notebookを作ったのに社用アカウントから全体公開できなかったのでこの記事で説明していきます…!(´・ω・`)

画面右上の「+コード」を押下して、新しいコードブロックを出して環境構築から始めてみましょう!

manimと必要なパッケージのインストール(以下のコードブロックは右上に一括コピーのボタンがありますので、便利にコピーが出来ます)

!apt update
!apt install -qq build-essential python3-dev libcairo2-dev libpango1.0-dev ffmpeg
!apt install -qq texlive texlive-fonts-extra texlive-latex-extra texlive-science

!pip install manim==0.18.0.post0

コードブロックを左上の再生ボタンから実行した後、以下のようにセッションの再起動を求められますので、指示に従って再起動しましょう。

manimで使われているフォントは偶に漢字が崩れることがあるので、(本記事では使いませんが備忘録的に)日本語フォントも一緒にダウンロードしておきましょう。

!apt install fonts-noto-cjk

ここでmanimを使うよ~とimportしておいて…

from manim import *

このコードブロックを実行して、以下の表示が無事に出たら環境構築はおしまいです!

公式サンプルを動かしてみる

環境が出来たということで、早速公式サンプルを動かしてみましょう!以下のコードをコピーして、【+コード】からコードブロックを作ってペーストし、コードブロック左上の実行ボタンから動画を再生してみてください。manimで作られた動画がぬるぬると動いてることが分かります!

%%manim -qm -v WARNING SquareToCircle

class SquareToCircle(Scene):
    def construct(self):
        circle = Circle()  # create a circle
        circle.set_fill(PINK, opacity=0.5)  # set color and transparency

        square = Square()  # create a square
        square.rotate(PI / 4)  # rotate a certain amount

        self.play(Create(square))  # animate the creation of the square
        self.play(Transform(square, circle))  # interpolate the square into the circle
        self.play(FadeOut(square))  # fade out animation

↓こんな感じになります

動画テーマについて

”動画を大量制作する”というブログタイトルですが、お題が無いとプロンプトを投げにくい…ということで、AIと言えばニューラルネットワークの図!映えると言えば星が降るようなキラキラエフェクト!(?)というテンションで少し前の私が決めてくれたので、それを使っていきましょう。以下にイメージ図を添付しておきます。これをアニメーションにしていきましょう!(絵心が無い)

ここから生成AIを使ってみる

では早速、生成AIを使ってコードを雑にポン出ししていきましょう。ちなみに、今記事のこの部分を書いている私は、プロンプトエンジニアリングすることによって出力される動画の精度がぐんと上がると期待しています。後でプロンプトエンジニアリングする未来の私、頼んだぞ!

Claude3をノー勉で使ってみる

ニューラルネットワークのアニメーション

ではとりあえず、ニューラルネットワークのアニメーションをClaude3に作ってみてもらいましょう。プロンプトエンジニアリングとか何も考えずに、適当にプロンプトを作って…

ニューラルネットワークの説明をアニメーションを用いて説明してください。manimを使ってください。

プロンプトをClaude3に投げてみましょう。

ちゃんとそれっぽいものが返ってきました!このコードをGoogle colabに張り付けて実行してみましょう。

%%manim -qm -v WARNING NeuralNetworkAnimation
# ※↑ここはClaude3で出してくれないので自分でclass名に設定する

from manim import *

class NeuralNetworkAnimation(Scene):
    def construct(self):
        # 入力層のニューロンを表現
        input_neurons = [Dot([-3 + i, 2, 0], color=BLUE) for i in range(3)]
        input_labels = [Text(f"x{i+1}").next_to(neuron, UP) for i, neuron in enumerate(input_neurons)]

        # 隠れ層のニューロンを表現
        hidden_neurons = [Dot([-1 + i, 0, 0], color=GREEN) for i in range(5)]
        hidden_labels = [Text(f"h{i+1}").next_to(neuron, UP) for i, neuron in enumerate(hidden_neurons)]

        # 出力層のニューロンを表現
        output_neurons = [Dot([3 + i, -2, 0], color=RED) for i in range(2)]
        output_labels = [Text(f"y{i+1}").next_to(neuron, UP) for i, neuron in enumerate(output_neurons)]

        # 層間の接続を表現
        connections = []
        for i in range(len(input_neurons)):
            for j in range(len(hidden_neurons)):
                connection = Line(input_neurons[i], hidden_neurons[j], stroke_width=0.5)
                connections.append(connection)
        for i in range(len(hidden_neurons)):
            for j in range(len(output_neurons)):
                connection = Line(hidden_neurons[i], output_neurons[j], stroke_width=0.5)
                connections.append(connection)

        # アニメーションを作成
        self.play(
            *[Create(neuron) for neuron in input_neurons + hidden_neurons + output_neurons],
            *[Write(label) for label in input_labels + hidden_labels + output_labels],
            *[Create(connection) for connection in connections],
            run_time=3
        )

        self.wait(2)

一分ほど待つと…

当たらずとも遠からず。割と思った通りのものが出力されました。アニメーションはついていますが、ちょっとした”コレジャナイ感”がありますね。

キラキラエフェクト

次に、星が降るようなキラキラエフェクトを作ってもらいましょう。こっちもプロンプトエンジニアリングとか何も考えずに、適当にプロンプトを作って…

空から星が降ってくるようなアニメーションを作ってください。manimを使います。

プロンプトをClaude3に投げてみましょう。

FallingStars!?めっちゃそれっぽい!このコードをGoogle colabに張り付けて実行してみ……FRAME_WIDTHとFRAME_HEIGHTが定義されてないよ~というエラーが出ました。じゃあその辺のエラーもClaude3にお任せして…

便利~

from manim import *

class FallingStars(Scene):
    def construct(self):
        # 背景を真っ暗にする
        self.camera.background_color = BLACK

        # 星の描画関数
        def make_star():
            star = Dot(color=YELLOW, radius=0.1)
            star.move_to(np.array([random.uniform(-config.frame_width / 2, config.frame_width / 2),
                                  random.uniform(config.frame_height / 2, 2 * config.frame_height),
                                  0]))
            return star

        # 星の配列を作成
        stars = [make_star() for _ in range(50)]

        # アニメーションを作成
        self.add(*stars)
        self.play(
            *[star.animate.move_to(np.array([random.uniform(-config.frame_width / 2, config.frame_width / 2),
                                            random.uniform(-config.frame_height / 2, -config.frame_height),
                                            0])) for star in stars],
            run_time=5,
            rate_func=linear
        )

        self.wait(2)

動画が出力されると思います!さっそく実行してみましょう!

うん?

おや…?

花粉!!!花粉みが凄い……!!これは流石に映えない……(´・ω・`)

ChatGPTをノー勉で使ってみる

Claude3と同じく、プロンプトエンジニアリングせずにプロンプトをChatGPT(GPT-4)に投げてみます。Claude3と同じプロンプトを入力しちゃいましょう!

ニューラルネットワークのアニメーション

ニューラルネットワークの説明をアニメーションを用いて説明してください。manimを使ってください。

近頃ちょっとChatGPTの返信、遅くなりましたよね…回線の問題なのかな…?

ChatGPTの方がほんの少し回答が丁寧な気がします。

from manim import *

class NeuralNetworkScene(Scene):
    def construct(self):
        # ニューロンの位置を定義
        positions = {
            "input": [UP*3 + LEFT*4, UP*2 + LEFT*4, UP*1 + LEFT*4, DOWN*0 + LEFT*4, DOWN*1 + LEFT*4],
            "hidden": [UP*2.5 + LEFT*1, UP*1.5 + LEFT*1, DOWN*0.5 + LEFT*1, DOWN*1.5 + LEFT*1],
            "output": [UP*2 + RIGHT*2, DOWN*1 + RIGHT*2]
        }
        layers = {key: VGroup(*[Circle(radius=0.15, color=BLUE).move_to(pos) for pos in positions[key]]) for key in positions}

        # 層間の接続を描画
        for i, start_layer in enumerate(["input", "hidden"]):
            end_layer = ["hidden", "output"][i]
            for start in layers[start_layer]:
                for end in layers[end_layer]:
                    self.add(Line(start.get_center(), end.get_center(), buff=0.15).set_stroke(WHITE, 2))

        # 各層のニューロンをシーンに追加
        for layer in layers.values():
            self.add(layer)

self.playが無いけど大丈夫かな…?

おー!割とちゃんとニューラルネットワークの図って分かる!ここからアニメーションして……って、左下の再生ボタンが見つからない……。と調べてみたら、画像ですね。アニメーションを出して~っていう目的とは一致しません。(´・ω・`)

キラキラエフェクト

空から星が降ってくるようなアニメーションを作ってください。manimを使います。

こっちもプロンプトを投げて…

なんとなく良さそうなものが出来ましたね。

from manim import *
import random

class FallingStars(Scene):
    def construct(self):
        self.camera.frame_width = config.frame_width  # 全画面設定
        self.camera.frame_height = config.frame_height  # 全画面設定

        stars = VGroup()

        # 星を生成
        for _ in range(50):  # 星の数を調整可能
            star = Star(fill_opacity=1).scale(random.uniform(0.05, 0.2))
            star.move_to([random.uniform(-config.frame_width / 2, config.frame_width / 2),
                          config.frame_height / 2 + 1, 0])
            stars.add(star)

        # 星が落ちるアニメーション
        self.play(
            LaggedStart(*[
                star.animate.move_to([star.get_x(),
                                      -config.frame_height / 2 - 1, 0])
                for star in stars
            ], lag_ratio=0.1),
            run_time=4,  # 落ちる速度の調整
            rate_func=linear
        )

        self.wait(1)  # 終了後の待機時間

ちょっとの時間待機して実行してみると…

おっ?

星が!!!

降ってる!!!!!青いけど!!!

プロンプトエンジニアリングしてみる

そもそもプロンプトエンジニアリングとは?という方は、私が以前に書いたこの記事を是非ご覧ください。記事上部ここから生成AIを使ってみるを書いた私から3~4日経った私が今このトピックを書いていますが、精度上がらんかったらどうするんや!!!という不満でいっぱいです。しかしながら、昔の自分に文句を言ってても仕方ないので、早速はじめて行きましょう!また、前回の記事でChatGPTのプロンプトエンジニアリングのデモをしているので、本記事では省略させていただきます。m(_ _)m

Claude3をプロンプトエンジニアリングしてみる

Claudeは公式からプロンプトライブラリが提供されているので、この中から使えそうなものをお借りして試してみましょう…( ..)φ

アニメーションのテーマが決まってるなら、画像中央にあるコードコンサルタントがパッと見た印象的に、この中では合致してそうですね!見てみましょう。

同じ機能を維持…よりは改善してもらいたいので、そのあたりはオリジナルの編集を加えてやってみましょうか!( ..)φ

ニューラルネットワークのアニメーション

ちょっとだけ工夫したものがこちらです。Claude3 に自身の役割を説明して、環境の説明を追記して…


貴方のタスクは、pythonのコードを最適化して書くことです。三層パーセプトロンの図をmanimを使って描画してください。この時、ノード同士が重ならないように工夫してください。また、ノードは縦並びにして一層ごとに色分けしてください。
manimのバージョンはv0.18.0に設定します。

それでは実行してみましょう!

どうだ…!?

%%manim -qm -v WARNING ThreeLayerPerceptron

from manim import *

class ThreeLayerPerceptron(Scene):
    def construct(self):
        # 層の設定
        num_inputs = 3
        num_hidden = 4
        num_outputs = 2
        node_radius = 0.3
        layer_spacing = 2
        node_spacing = 1

        # 入力層
        input_layer = VGroup(*[Circle(radius=node_radius, fill_color=BLUE, fill_opacity=1) for _ in range(num_inputs)])
        input_layer.arrange(DOWN, buff=node_spacing)
        input_layer.shift(layer_spacing * LEFT)

        # 隠れ層
        hidden_layer = VGroup(*[Circle(radius=node_radius, fill_color=GREEN, fill_opacity=1) for _ in range(num_hidden)])
        hidden_layer.arrange(DOWN, buff=node_spacing)

        # 出力層
        output_layer = VGroup(*[Circle(radius=node_radius, fill_color=RED, fill_opacity=1) for _ in range(num_outputs)])
        output_layer.arrange(DOWN, buff=node_spacing)
        output_layer.shift(layer_spacing * RIGHT)

        # 線の描画
        lines = VGroup()
        for i in input_layer:
            for j in hidden_layer:
                lines.add(Line(i.get_center(), j.get_center(), buff=0.1))
        for i in hidden_layer:
            for j in output_layer:
                lines.add(Line(i.get_center(), j.get_center(), buff=0.1))

        # アニメーション
        self.play(LaggedStart(*[GrowFromCenter(node) for node in input_layer], lag_ratio=0.2))
        self.play(LaggedStart(*[GrowFromCenter(node) for node in hidden_layer], lag_ratio=0.2))
        self.play(LaggedStart(*[GrowFromCenter(node) for node in output_layer], lag_ratio=0.2))
        self.play(LaggedStart(*[Create(line) for line in lines], lag_ratio=0.05))
        self.wait(3)

google colabにコピー&ペーストして…

お!?それっぽい!?

線が伸びてきた!!

(説明ラベルは無くなったけど)それっぽいアニメーションだ!!!!

もったりした動作が気になる方は、以下のようにアニメーションウィンドウ右下の三点リーダーをクリックして、ダウンロードしてから動画を見てみてください。手元のプレイヤーでサクサク動画が再生されると思います。

キラキラエフェクト

こいつがなかなか曲者で全ッッ然思った通りのものが出来ない…

折角!ChatGPTで良さげなコードが生成されたので!これを参考にして作ってみてもらいましょう!!(苦肉の策)

```
from manim import *
import random

class FallingStars(Scene):
    def construct(self):
        self.camera.frame_width = config.frame_width  # 全画面設定
        self.camera.frame_height = config.frame_height  # 全画面設定

        stars = VGroup()

        # 星を生成
        for _ in range(50):  # 星の数を調整可能
            star = Star(fill_opacity=1).scale(random.uniform(0.05, 0.2))
            star.move_to([random.uniform(-config.frame_width / 2, config.frame_width / 2),
                          config.frame_height / 2 + 1, 0])
            stars.add(star)

        # 星が落ちるアニメーション
        self.play(
            LaggedStart(*[
                star.animate.move_to([star.get_x(),
                                      -config.frame_height / 2 - 1, 0])
                for star in stars
            ], lag_ratio=0.1),
            run_time=4,  # 落ちる速度の調整
            rate_func=linear
        )

        self.wait(1)  # 終了後の待機時間
```

以上のコードを参考にして、黄色の星が画面上端からランダムにゆっくり落下していくようなアニメーションのためのコードを作成してください。

manimのバージョンはv0.18.0に設定します。

頼むぞ…!(何十回目の試行)

%%manim -qm -v WARNING FallingStars

from manim import *
import random

class FallingStars(Scene):
    def construct(self):
        self.camera.frame_width = config.frame_width  # 全画面設定
        self.camera.frame_height = config.frame_height  # 全画面設定
        
        stars = VGroup()

        # 星を生成
        for _ in range(50):  # 星の数を調整可能
            star = Star(fill_opacity=1, color=YELLOW).scale(random.uniform(0.05, 0.2))
            star.move_to([random.uniform(-config.frame_width / 2, config.frame_width / 2),
                          config.frame_height / 2 + 1, 0])
            stars.add(star)

        # 星が落ちるアニメーション
        self.play(
            LaggedStart(*[
                star.animate.move_to([star.get_x(),
                                      -config.frame_height / 2 - 1, 0])
                for star in stars
            ], lag_ratio=0.1),
            run_time=10,  # 落ちる速度の調整
            rate_func=linear
        )
        self.wait(1)  # 終了後の待機時間

これをgoogle colabに持ってきまして…若干時間が掛かって…

お!!

(ChatGPTで作ったやつの色違いだな)

これを求めてるんだよ!!(ChatGPTで作ったものの色違いだな……)

副産物

%%manim -qm -v WARNING FallingStars
from manim import *
import random

class FallingStars(Scene):
    def construct(self):
        self.camera.frame_width = config.frame_width  # 全画面設定
        self.camera.frame_height = config.frame_height  # 全画面設定
        
        stars = VGroup()
        hollow_stars = VGroup()

        # 星を生成
        for _ in range(50):  # 星の数を調整可能
            star = Star(fill_opacity=1, color=YELLOW).scale(random.uniform(0.05, 0.2))
            hollow_star = Star(fill_opacity=0, color=YELLOW).scale(random.uniform(0.05, 0.2))
            star.move_to([random.uniform(-config.frame_width / 2, config.frame_width / 2),
                          config.frame_height / 2 + 1, 0])
            hollow_star.move_to([random.uniform(-config.frame_width / 2, config.frame_width / 2),
                                 config.frame_height / 2 + 1, 0])
            stars.add(star)
            hollow_stars.add(hollow_star)

        # 星が落ちるアニメーション
        self.play(
            LaggedStart(*[
                star.animate.move_to([star.get_x(),
                                      -config.frame_height / 2 - 1, 0]).rotate(PI * 2 * random.random(), about_point=star.get_center())
                for star in stars
            ], lag_ratio=0.1),
            LaggedStart(*[
                hollow_star.animate.move_to([hollow_star.get_x(),
                                             -config.frame_height / 2 - 1, 0]).rotate(PI * 2 * random.random(), about_point=hollow_star.get_center())
                for hollow_star in hollow_stars
            ], lag_ratio=0.1),
            run_time=7,  # 落ちる速度の調整
            rate_func=linear
        )
        self.wait(1)  # 終了後の待機時間
        

星にバリエーションがあったら嬉しいな……

動かしてみたら綺麗だったのでおまけとして……良かったら実際に見てみてください!

まとめ

 ここからは、所感も交えてこれまでの話をまとめていきます。はじめに、アイデアを思いつく力が動画作成に第一に大事であることが分かりました。まさか、動画テーマが全然思いつかないとは……。(愚痴)さらに、プロンプトを投げる段階でもコツがわからないとドツボにはまったり……本記事は結構な時間が掛けられて作成されております。python+manimの勉強もしたら、細かい修正やニュアンスの変更などの部分は生成AIに頼りきりにならずにお手軽にできるかもしれませんね!理想の動画を追い求めるとかなりの時間が必要ですが、「それっぽい動画を何個も生成したい」という場合にはpython+manim+生成AIの動画生成、おすすめです!

  • このエントリーをはてなブックマークに追加
  • LINEで送る

SNSでもご購読できます。