Supershipの名畑です。台湾に行った際に伊藤潤二先生のプリクラを見かけたのですが、台湾で3ヶ月に渡って原画展を開催されていたのですね。さすがです。
はじめに
これまで、Gen、Veo、AnimeGenなどなど、静止画を動画生成AIで動画にする記事をいくつか書いてきました。
ただ、静止画を動かすというのは、シンプルなものであれば、動画に特化したAIでなくても可能ではあります。
ということで今回は、Claude Codeに日本語で指示を出すだけ、という前提で静止画に動きをつけてみようと思います。
凝ったことはやらないです。
どこまでなら意図通りにやれるだろうということを試した備忘となります。
今回試す段階
今回は以下の5段階を試してみました。
| 段階 | 内容 |
|---|---|
| Lv1 | カメラワーク(ズームと手ぶれ) |
| Lv2 | 光と色で雰囲気を出す |
| Lv3 | 人物と背景を別々に動かす(奥行き、呼吸、窓の外の雨) |
| Lv4 | 髪だけを揺らす |
| Lv5 | 瞬きをさせる |
環境
- MacBook
- Claude Code
- Python
- ffmpeg:動画編集
- NumPy:数値計算
- opencv-python-headless:画像処理
インストールされていない方は、それぞれの公式ページをご覧になってインストールください。
バージョン確認
私の場合、Numpyとopencv-python-headlessはpipでインストールしました。
% claude --version
2.1.310 (Claude Code)
% python --version
Python 3.14.3
% ffmpeg -version | head -1
ffmpeg version 9.0.2 Copyright (c) 2000-2026 the FFmpeg developers
% pip list | grep -i -E "numpy|opencv"
numpy 2.5.4
opencv-python-headless 5.0.0.93
元画像
過去記事「Stable DiffusionのWeb APIを用いて画像上の人物はそのままに背景だけをAI生成で入れ替えてみた」で作成した画像を使うことにします。
作業用フォルダを作り、本画像をsource.pngとして置きます。
Claudeの起動
作業用フォルダでClaude Codeを起動します。
% claude
今回は Opus 5.5を使いました。
> /model
Select model
Switch between Claude models. Your pick becomes the default for new sessions. For other/previous model names, specify with --model.
1. Default (recommended) Opus 5.5 · Best for everyday, complex tasks
❯ 2. Opus 5.5 ✔ For complex work and everyday tasks
3. Fable 5.1 For your toughest challenges
以下略
Lv1〜Lv5は、それぞれ独立した指示です。
どれも source.png から1本の動画を作ります。
また、どの指示にも共通で以下の条件を入れています。
- ライブラリを固定する:インストール済みのものを伝え、追加しないよう指示する
- 学習済みモデルを使わせない:AIモデルを使われると、今回のテーマから外れてしまうため
- 出力の形式をそろえる:見比べやすいよう、サイズ・フレームレート・長さを同じにする
- やり取りを日本語にする:日本語で指示しても、途中経過や結果の報告が英語で返ってくることがあるため
Lv1:カメラワーク
指示
source.png から、カメラが動いているように見える動画を作ってください。
・顔に向かってゆっくりズームインする
・手持ちカメラのような揺れを加える。ガタガタ震えるのではなく、ゆったりとした揺れにする
条件:
・NumPy・OpenCVはインストール済みで、ffmpegも使える。他のライブラリは追加しない
・画像を生成するAIモデルや、背景除去などの学習済みモデルは使わない
・出力は400×400、15fps、5秒のMP4で、ファイル名は lv1.mp4
・あわせて、同じ動画をffmpegでGIFに変換した lv1.gif も作る
・やり取りは日本語でお願いします
結果抜粋
確認した結果
- ffprobe で確認:400×400、15fps、75フレーム、5.0秒
- 0秒・1秒・2秒・3秒・4秒のコマを並べて見たところ、全身から始まって顔のアップへ寄っていきます。最後まで顔が画面の中央付近に収まっていて、端に黒い余白も出ていません
しくみ
- ズームイン:画像を切り抜く範囲を
1.1倍から2.0倍まで寄せていき、切り抜く中心を画像の中心から顔へ移動させています。動き始めと終わりをゆっくりにしているので、急に動き出したり止まったりしません。
- 手持ち風の揺れ:0.23〜0.67Hz のゆっくりした波を3つ重ねて、上下・左右の位置と傾き(最大で約1度)を揺らしています。速い波は入れていないので、ガタガタ震えず、ゆったりと揺れます。
揺れの幅は切り抜く範囲に合わせて変えているので、寄るにつれて揺れが目立ちすぎることもありません。
- 画質:一度2倍の大きさで描いてから縮小し、輪郭のギザギザを抑えています。
- 使っていないもの:学習済みのモデルは使っていません。OpenCV に付いている顔検出(Haar cascade)も学習済みモデルにあたるため、顔の位置は画像を目で見て決めた座標 (520, 290)
を書き込んでいます。別の画像に使うときは、スクリプト冒頭の FACE を書き換えてください。
Lv2:光と色で雰囲気を出す
指示
source.png に、次の3つのエフェクトをかけた動画を作ってください。
カメラは動かさず、画像の位置と大きさは固定のままにしてください。
・時間とともに少しずつ暖色寄りにする(夕方に向かうイメージ)
・斜めに横切る光の帯(ライトリーク)
・周辺減光(ビネット)とフィルムグレイン(粒子感)
条件:
・NumPy・OpenCVはインストール済みで、ffmpegも使える。他のライブラリは追加しない
・画像を生成するAIモデルや、背景除去などの学習済みモデルは使わない
・出力は400×400、15fps、5秒のMP4で、ファイル名は lv2.mp4
・あわせて、同じ動画をffmpegでGIFに変換した lv2.gif も作る
・やり取りは日本語でお願いします
結果抜粋
出力の確認結果(ffprobe)
- lv2.mp4:400×400、15fps、75フレーム、5.0秒(約1.3MB)
- lv2.gif:同じMP4をffmpegでGIFに変換したもの。パレットを生成してから変換しています(約8.1MB)
各エフェクトの作り方
- カメラ固定:最初に画像を400×400に縮小し、その後は全フレームで同じ位置と大きさのまま色だけを加工しています。
- 暖色化:時間とともに赤を強め、青を弱めます。暗い部分にも少しオレンジをのせ、変化は始めと終わりがゆるやかになるようにしました。最終フレームでは、夕方らしい色合いになります。
- ライトリーク:オレンジ色の斜めの帯が、左上の画面外から右下の画面外へ横切ります。明るい部分が白く飛びにくい「スクリーン合成」で重ねています。帯は中盤で最も強く、帯の途中でも強さ
を少しずつ変えて、ただの棒に見えないようにしました。
- ビネット:画面の中心から外側に向かって、なめらかに暗くなります(四隅で約55%暗くなる)。
- フィルムグレイン:フレームごとに違うノイズを少しぼかして、粒に見せています。中間の明るさで強め、暗い部分と明るい部分では弱めにかかります。
最初は粒子が強すぎたので、弱めに直しました。あわせてライトリークの帯を細くし、少し強くしています。
Lv3:人物と背景を分けて動かす
指示
source.png の人物と背景を別々のレイヤーに分けて、動画を作ってください。
・人物を動かしたときに見える、人物の後ろの部分も自然に埋める
・カメラが横に動いたとき、遠くの背景は大きく、手前の人物は小さくずらして奥行きを出す
・人物の胸から上を、呼吸のようにわずかに上下させる
・背景レイヤーにだけ雨を降らせる(窓の外の雨)
条件:
・NumPy・OpenCVはインストール済みで、ffmpegも使える。他のライブラリは追加しない
・画像を生成するAIモデルや、背景除去などの学習済みモデルは使わない
・出力は400×400、15fps、5秒のMP4で、ファイル名は lv3.mp4
・あわせて、同じ動画をffmpegでGIFに変換した lv3.gif も作る
・やり取りは日本語でお願いします
結果抜粋
処理の内容
1. 人物と背景の分離:OpenCVのGrabCut(色の分布から前景と背景を分ける従来の手法)を使いました。人物の大まかな輪郭、確実に人物の部分(顔・胴体)、確実に背景の部分(画面の端など)を
手で指定しています。そのあと一番大きな塊だけ残し、縁を少しぼかして背景になじませました。
2. 人物の後ろの穴埋め:人物より少し広い範囲を消し、行ごとに左右の背景の色を直線でつないで埋めました。横縞が出ないよう縦方向にならし、細かいノイズを足してのっぺり感を抑えています
。髪の毛先は切り抜きから漏れやすいので、頭まわりだけ消す範囲を広げました。
3. 奥行き:背景は左右に最大16px、人物は最大4pxずらしています。端が欠けないよう、元画像を440pxに縮小してから中央の400pxを切り出しています。
4. 呼吸:元画像のy=600あたり(胸)より上を、上下に最大約1.8px動かしています。その下は少しずつ動きを小さくし、腰から下は止めています。
5. 雨:手で指定した窓ガラスの範囲にだけ、背景レイヤーで斜めの雨筋を260本降らせました。手前・奥の3段階ではなく、本ごとに奥行きを乱数で決め、速さ・長さ・明るさを変えています。人物
はその上に重ねるので、雨は人物の後ろにだけ見えます。雨の日らしくするため、窓の外を少し暗く青寄りにもしています。
注意点
- 人物の輪郭と窓ガラスの範囲は、この画像に合わせて座標を手で決めています。別の画像に使うときは座標を直す必要があります。
- 穴埋めは左右の色を補間しているだけなので、人物の後ろの窓枠やビルの形までは再現できません。画面に見えるのは人物がずれた数pxの帯だけなので、動画ではほぼ目立ちません。
- 動きの大きさは lv3.py 先頭の BG_SHIFT(背景のずれ)、FG_SHIFT(人物のずれ)、BREATH(呼吸の幅)で変えられます。
Lv4:髪だけを揺らす
指示
source.png の人物の髪の毛先だけを、ふわっと揺らした動画を作ってください。
カメラは動かさず、髪以外の部分は静止したままにしてください。
・顔・あご・首は揺らさない
・付け根(つむじ付近)は動かさず、毛先ほど大きく揺らす
・揺れる部分と揺れない部分の境目が裂けないよう、なめらかにつなぐ
条件:
・NumPy・OpenCVはインストール済みで、ffmpegも使える。他のライブラリは追加しない
・画像を生成するAIモデルや、背景除去などの学習済みモデルは使わない
・出力は400×400、15fps、5秒のMP4で、ファイル名は lv4.mp4
・あわせて、同じ動画をffmpegでGIFに変換した lv4.gif も作る
・やり取りは日本語でお願いします
結果抜粋
作り方
1. 髪の切り抜き:左の柱と髪のハイライトがほぼ同じ明るさで、しきい値では分けられませんでした。そこで、手で大まかな範囲を指定して OpenCV の GrabCut で髪だけを切り抜きました。
2. 髪の後ろの背景:髪を消した部分を cv2.inpaint で周りの色から塗りつぶしました。毛先が動いたときに見える数ピクセルの隙間は、この背景で埋まります。
3. 揺れの大きさ:
- つむじ(元画像の座標で 520, 85 付近)からの距離に応じて、付け根は 0、毛先ほど大きく揺らします。
- 顔・あご・首・コートは動かさない範囲として指定しました。そこから離れるにつれて揺れが徐々に大きくなるので、境目に段差は出ません。
- 揺れの大きさの分布を全体にぼかしてあるので、動く部分と動かない部分の境目も裂けません。
4. 揺れ方:ゆっくりした左右の揺れに、毛先へ伝わる波と小さなゆらぎを足しました。あわせて毛先が少し浮き上がり、外側へ広がるようにしています。5秒でちょうど元に戻るので、つなぎ目なく
ループします。
5. 揺れた髪を、手順2で作った止まったままの背景に重ねて合成しています。
確認したこと
- フレーム同士の差分を取ると、変化があるのは左右の毛先だけでした。顔・首・背景の差は最大でも 4〜5 程度で、これは MP4 圧縮によるノイズの範囲です。
- 抜き出したフレームを目で見た限り、裂けやずれは見つかりませんでした。
Lv5:瞬きをさせる
指示
source.png の人物に、瞬きをさせた動画を作ってください。
カメラは動かさず、目以外の部分は静止したままにしてください。
・5秒の間に2〜3回、自然な間隔で瞬きする
・まぶたが上から下りて、また上がる動きにする(1回あたり0.2〜0.3秒ほど)
・目を閉じたときの見た目が不自然にならないようにする
条件:
・NumPy・OpenCVはインストール済みで、ffmpegも使える。他のライブラリは追加しない
・画像を生成するAIモデルや、背景除去などの学習済みモデルは使わない
・出力は400×400、15fps、5秒のMP4で、ファイル名は lv5.mp4
・あわせて、同じ動画をffmpegでGIFに変換した lv5.gif も作る
・やり取りは日本語でお願いします
結果抜粋
作り方
- 目の位置:画像を拡大して、左右の目について上まぶたと下まぶたのふちの座標を目視で読み取り、曲線にしました。
- まぶたの動き:上まぶたを、各列で下まぶたのところまで引き下ろします。
- まつ毛の帯(太さ4px)は形を変えずにそのまま下へ移動します。
- その上にあるまぶたの皮膚(14px分)を縦に伸ばして、眼球を覆います。
- 黒目や白目は描き直さず、下りてきたまぶたで隠れるだけなので、閉じたときに作り物っぽく見えにくくなります。
- 閉じた見た目:まぶたが眼球を覆った部分を7%だけ暗くして、薄い影をつけました。閉じきったときは下まぶたより1〜2px下まで下ろし、白目のすき間が残らないようにしています。
- タイミング:瞬きは0.8秒・2.6秒・3.9秒の3回で、間隔をわざと不揃いにしています。1回は4フレーム(約0.27秒)で、閉じる動きより開く動きを少しゆっくりにしました。
- 静止部分:変形するのは両目のまわりの小さな範囲だけです。カメラは動かさず、それ以外の画素は元画像のままです。
確認したこと:開いた状態・半分閉じた状態・閉じた状態を元の解像度で拡大して見比べました。最初は画面右の目の目頭に白目がわずかに残っていたので、その部分のまぶたの座標を直して、残ら
ないことを確認しています。
残っている点:閉じたときのまぶたは元の皮膚を縦に伸ばしているので、拡大するとかすかに縦すじが見えます。出力の400pxではほとんど目立ちません。
最後に
試しでやってみただけだったのですが、思ったより意図通りにスムーズに編集してくれるものですね。
もっと踏み込んでやってみたいと感じました。
宣伝
SupershipのQiita Organizationを合わせてご覧いただけますと嬉しいです。他のメンバーの記事も多数あります。
Supershipではプロダクト開発やサービス開発に関わる方を絶賛募集しております。
興味がある方はSupership株式会社 採用サイトよりご確認ください。





