Pythonで西遊記（1978）のオープニングを再現してみようとした

# はじめに
初カキコ...ども...

DASです。獺祭（だっさい）と名乗ったりもしています。普段はで「心で工学」を胸に、勉学に励んでいます。

この度、私が所属している公認課外団体の[名古屋工業大学プログラミング部C0de](https://c0de-web.club.nitech.ac.jp/)においてピクシブ株式会社様の協賛、並びに諸先輩方のご協力により「第2回 C0deハッカソン with pixiv」が開催されました。
はじめに、このイベントを開催してくださった皆様にお礼申し上げます。

さて、この記事では私が今回にハッカソンで作ろうとしたものとそのきっかけ、技術的な裏話をしていこうと思います。他の参加者の方々も記事を出されていますが、それらほど技術的に高度なお話ではなく、日記みたいなものなのでゆったりと読んでいただければと思います。

# 成果物
さっそくですが、出来上がったもの（が、生成するもの）がこれです。
ざっくりとした概要についてもこの**ツイート**に記載されておりますので時間の無い方はどうぞ。

https://x.com/c0demattari/status/1871199388326089068

# 制作のきっかけと流れ

##出会い
このハッカソンが始まる少し前、こんな動画が流行りました

https://www.nicovideo.jp/watch/sm44340781

これを見た瞬間、私に電流が走りました。
―――そうだ、西遊記のOPを再現しよう。

そもそも、このドラマが結構好きで特にOPの登場人物を一人ひとり紹介していく演出がすごく洒落てるなあなどと思っていたのもあります。

二人チームで走る事が決定したその日、どのようにして実装するかを話し合いました。
ざっくりとした仕様としては、とにかくなんでもいいから写真と動画を入力すればそれを基に先述の演出を再現した動画を出力するものとしました。
ここで、経験豊富な相方がOpenCVとPythonを使えば動画の作成が簡単にできるよ～と教えてくれたので、インターフェース部分はPythonでGUIを作るときの定番であるTkinterで、動画の生成はOpenCV+Pythonで行う事にし、形式はWindows用アプリケーションにすることとしました。

よ～し、Python初心者だけど頑張って実装するぞ～～～～！！！！！！

## 急病
キックオフ翌日からハッカソン終了1日前まで大体10日間、謎の発熱が続きました。
こんな状況ではハッカソンどころではありません。何も作れないでいるまま時間は過ぎていき、終了4日ほど前にはリタイアを宣言しました。

## 悪あがき
ただ、このまま何もしないのはアレです。終了日前夜、ネットに転がる記事を参考にしながら軽くプログラムを書いてみました。動画さえ作れればいいやとインターフェースなんてものはありません。その結果それっぽいものが出来上がっちゃいました。ただ、あくまでも**プログラム**の域からは出ていません。

# 技術的なお話
さっさとこの話をせえやという方、大変お待たせしました。
といっても、ただのプログラムでしかないので、流れは以下の通りきわめてシンプルです。

1. 適当な箇所に置いた画像を読み込む
2. ``resize``関数で読み込んだ画像を圧縮する
3. 背景を描画できるような行列を用意して一部を読み込んだ画像に置き換える
4. 得られた行列を``imwrite``関数で画像に書き起こす
5. 1.~4.までを繰り返して**動画のフレーム分**の画像を作成する
6. ``VideoWriter``を用意して``write``関数から画像(=フレーム)を追加する
7. ``release``関数で``VideoWriter``から動画を作成

基本はこの流れで動画を2種類作ります。
こんなのと、
![ezgif-2-38f9e73841.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3978318/9bbe2797-f1e6-9c4f-6b20-7b65c1788009.gif)
こんなのですね
![ezgif-2-613c621114.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3978318/dcdd9505-4cd2-bfd4-d882-9cad6975faef.gif)
2つ目の方は画像が歪んじゃってますが、動画を作成するプロセスは分離していますので、ちゃんと16:9の画像を別個に用意すればいい感じになってくれます。
とはいえ、本当は横に何か適当なフィラーでも用意して歪まないようにするのが筋だと思います。

さらにもう一つ、動画から新たな動画を作ります。沙悟浄が水から飛び出したり孫悟空が暴れまわる映像のような、早送りされた動画も挟みたかったからですね。
これもまたOpenCV様や先人のおかげで簡単に作れちゃいます。手順としては以下の通りです。

1. ``VideoCapture``関数で動画を読み込む
2. ``get(cv2.CAP_PROP_FPS)``でフレーム数を、``get(cv2.CAP_PROP_FRAME_WIDTH)``とget(cv2.CAP_PROP_FRAME_HEIGHT)で縦横の画素数を取得
3. 2.で取得した数字を基に``VideoWriter``を作成(ここで出力する動画のfpsを設定できる)
4. ``read``関数で読み込んだ動画のフレームを1つずつ取得
5. 適当な数(ここでは10)だけ飛ばして``write``関数で書き込んでいく
6. ``release``関数で``VideoWriter``から動画を作成

この結果としてコマ数を元の動画の10%に減らし、かつ任意のfps数の動画が得られるというわけです。このfps数を調整してあげると、ちょっとカクついて、早回しになった動画が作れるのです。

さて、ここまでに3つの動画が出来たので、それらを結合していきます。
今度もまた、各動画からフレームを``get``関数で取得してそれを``VideoWriter``に書き込んでいくという処理で済みます。

済むはずでした。
動画というのは音声と画像で構成されることは皆さんよくご存じだと思います。
この音声がどうもバイナリレベルで邪魔をしてくるようで、音声を持つかどうかでうまく結合できるかどうかが変わってくるようです。

よって、元々音声データを保持していた3番目の動画と音声データを最初から持たない1,2番目の動画が結合できないという事態に陥ってしまいました。

すなわち、最終的に得られるのはGIFでお見せした二つの動画をくっつけたもの、ということになります。
以上がこのプログラムの説明になります。

# 反省・今後の展望
先述の動画くっつかない問題は音声データを一切持たない動画を出力できれば解消できるはずですが、それにはコーデックについての勉強をする必要がありそうです

一発屋枠として好評いただきましたが、やはりインターフェースが無いのは致命的です。ドラッグアンドドロップでファイルのやり取りができるインターフェースをTkinterで作りたいところです。

BGMにモンキーマジックがかかっていないと西遊記っぽさが出ませんよね。動画に音声データを結合する処理を説明してくれている記事はかなり多くあるので、それらを参考にすればどうにかなりそうです。

最初の構想段階で出ていた話にカメラを使うというのがありました。Pythonを使えばカメラからリアルタイムに顔認識が出来るのでこれを利用して、顔を認識したら画像として切り取って動画を作成しよう、というものです。出来たら面白いですよね。~~まだそこまでPythonを使いこなせていないのだけれど...~~

それから肝心なのは字幕を付ける事です。

> 名無しの権兵衛 演: 俺  

みたいなことをやって遊びたいですよね。Pythonは日本語の文字をそのまま扱えるようにはできていませんが、フォントを読み込むというひと手間を加えれば実装できそうです。

# 参考
https://qiita.com/moshi/items/cc7fd0b3c1b008c56a13

https://qiita.com/MuAuan/items/f63a6dcdd6794b71c072

https://monomonotech.jp/kurage/raspberrypi/opencv_japanese.html