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

:::note warn
この記事は2024年5月2日に弊社ブログにて公開された記事の移植記事となります。
:::

　大規模言語モデルである「Claude3」のリリース日（2024年3月4日）から、はや数ヶ月が経ちました。GPT-4にも負けず劣らずといった精度を誇るClaude3、業務では文章校正や分からないコードを教えてくれるメンターとして、プライベートでも話し相手代わりによく使っています。

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

 # 本題
 ## 実行環境
 - google colaboratory
 - python3
 - manim (バージョン：0.18.0.post0)
 - Claude3

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

 https://colab.research.google.com/?hl=ja

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/ed1b042a-f6d2-ba10-5234-ea0feda26bd2.png)
 
画面右上の「＋コード」を押下して、新しいコードブロックを出して環境構築から始めてみましょう！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/fcb86552-a272-f733-004d-4b4dd4a7dbad.png)

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/8b43dd95-0292-f365-d70e-fb21d971dc3c.png)

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

```python
!apt install fonts-noto-cjk
```

ここでmanimを使うよ～とimportしておいて…
```python
from manim import *
```
このコードブロックを実行して、以下の表示が無事に出たら環境構築はおしまいです！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/7e5113be-e0ef-e37b-2f13-60049519727a.png)

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

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

https://docs.manim.community/en/stable/tutorials/quickstart.html#transforming-a-square-into-a-circle

```python
%%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
```

↓こんな感じになります

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/c04654c8-a70d-1833-ca62-010bdd9764d6.png)

## 動画テーマについて

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/76a8168e-f4a2-bd83-761b-96f0b69132a8.png)


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

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

## Claude3をノー勉で使ってみる
### ニューラルネットワークのアニメーション

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

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/4084e0ee-43db-f1cf-86a9-4675ed7bb823.png)

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

```python
%%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)
```

![1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/d9cc9938-8c69-7d5d-f646-c7d59a3340c0.gif)

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

### キラキラエフェクト

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

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/c2d065e7-5243-f01d-395f-ebfdef259d60.png)

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/bd7286b4-ccf8-21ee-8229-e0d10930a8b2.png)

賢い～

```python

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)
```

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/a5178297-0638-d8ac-ebb3-4dc982a7f10b.png)

うん？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/4461c615-ded2-13ef-5119-5ac8af3d72de.png)

おや？

![2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/b8ba295f-7a89-5a91-5075-7441726ebf0f.gif)

花粉！花粉みが凄い…！


## ChatGPTをノー勉で使ってみる

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

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/51e2eaf4-e63d-5635-6021-764b36d6aaac.png)

ChatGPTの方が、ほんの少し回答が丁寧ですね。

```python
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が無いけど大丈夫かな…？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/45abf84c-2c88-e2fc-6898-85bf1d60e586.png)

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


### キラキラエフェクト

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/75c0497e-8b64-c867-033e-6ebbe1ef13f2.png)

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

```python

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)  # 終了後の待機時間

```

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/6762904f-b616-2a5d-56ec-21ec83d940bd.png)

おっ？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/5c16b2cd-5acb-5d5c-004c-cbac0363f812.png)

星が！

![4.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/c275e327-d1d0-1d83-d3e8-f03431f532c8.gif)

降ってる！青いけど！

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

記事上部**ここから生成AIを使ってみる**を書いた私から３～４日経った私が今このトピックを書いていますが、精度上がらんかったらどうするんや！という不満でいっぱいです。しかしながら、昔の自分に文句を言っても仕方ないので、早速はじめて行きましょう！

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

https://docs.anthropic.com/en/prompt-library/library

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/bca85549-7d9c-d9d6-c7fd-ad022eec0db7.png)

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/8c533afb-86e4-6c3a-2f5f-7d2d26f80209.png)

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

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

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

```text

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

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/125717c2-733e-e712-9406-84d386d6a982.png)

どうだ…！？

```python
%%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にコピー＆ペーストして…

![5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/0e2d78e5-b1f6-c216-5459-3a28f70e5ca9.gif)

それっぽいアニメーションだ！

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/5a906fe5-88fc-3d33-d939-660c228b7e97.png)

### キラキラエフェクト

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

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

```text

====
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に設定します。
```

頼むぞ…！（何十回目の試行）

```python
%%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に持ってきまして…若干時間が掛かって…

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/8a51c84c-7867-5c7b-a95d-92de4f0f0891.png)

お！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/88db5c0f-899d-ce7b-0a77-67d5c8341782.png)

（ChatGPTで作ったやつの色違いだな）

![6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/597915/2504b3c5-8a9d-7539-3daa-c3de56a7c285.gif)

これを求めてるんだよ！（ChatGPTで作ったものの色違いだな…）


# まとめ

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