文字列で動画を表現する
動機
文字列でらき☆すたOPを再現してみた
これをやりたい
小学生の頃にこの動画を見てすごく感激した記憶がある。今、こうしてある程度コンピュータが使えるようになって、どういうライブラリを使ったりどのような実装をすればよいかがなんとなくわかるようになったので、思い立って作ってみることにした。
できたもの
$ python3 v2t.py <input> <output> [-s size] [-m mode]
size っていうのは、動画を画素の集まりとして見たときにどれぐらいの画素数をまとめて一つの文字として表現するかのパラメータとなっている。
例: size が 16 なら、縦 16 px、横 16 / 2 = 8 px が一つの文字として表現される。(デフォルト)
動画の明るさに応じて自動的に二種類の文字色と背景色に変更される。(白黒が入れ替わる)
-m で WHITE か BLACK を指定すれば固定される。
$ python3 tshow.py <file>
v2t.py で生成したファイルを指定すると、ターミナル上で再生される。
元の動画の FPS とはちょっとズレていて、ほんの少しだけ遅い。元動画が2分ぐらいだと5秒ぐらい遅れて再生終了する修正済み
ターミナルで再生するとき、ターミナルの大きさが足りていないとリターンコード1を返して終了する。
$ python3 t2v.py [-h] [-W WIDTH] [-H HEIGHT] [-f FPS] [-A AUDIO] input output
v2t.py で生成したファイルを input とし、出力としたい動画のファイル名を output に指定する。
$ python3 image2textimage.py [-h] [-s SIZE] [-m MODE] [-t THRESHOLD] [-c SCALE][input] [output]
与えられた画像を、テキスト化した画像に変換する。
stdin や stdout に生で吐けるようになっている。
requirements
code:requirements.txt
opencv-python
opencv-contrib-python
numpy
tqdm
pymediainfo
ffmpeg-python
pydub
もっとやりたいこと
その画面の平均的な暗さに応じて、フレームごとに黒を基調とするのか、白を基調とするのかを選択するようにしたい
だん!/icons/check.icon
どうにかして元の動画と等倍の速度で再生したい (tshow.py)
pymediainfo で FPS を取得したらオリジナルのものが取れたっぽい
けどもしかしたら OpenCV で正常に FPS は取得できていたかも……(勘違い)
再生処理がおかしくてズレてただけだった。修正済み
文字列の再生動画を作れるようにしたい
FFmpeg をうまく利用して元動画の音声を付与できないか?
できた/icons/check.icon
文字列ファイルさえあればブラウザ上で動くようにしたい
やりたいね~
より高速に文字列に変換できるようにしたい
最初にすべての frame を取得しておいてから後で文字列化させてみると、そこが結構重いということがわかった
↑これはマジでメモリ消費量がアタマだったのでやめた、 imap でフレームへのファイルパスと必要なオプションを渡してテキスト化までさせるようにして並列化したら改善した
だん/icons/check.icon
なんか libcaca っていうのがあってぇ……
高機能、しかも高速!つよすぎ
や、でもおれのやつのほうが軽いぜ……✌️(色付けてないからね)
色付きのやつも実装してみたいね
なんかもうちょっと自分のやつを高速化してリアルタイムに変換しながら再生とかできないものだろうか?
特に何もしなくてもできた。さらに高速化するためには、事前に OpenCV 側でフレームを文字の縦横の数 * 1px に変換してからグレースケールにして一つの画素ごとに文字を割り振るなどが有効かもしれないと考えている
バグ
OpenCV 側が入力の動画の FPS を虚偽報告してくる
FFmpeg を依存関係に入れればなんかいい感じに取得できるのでは?
直した/icons/check.icon
pymediainfo 使った
OpenCV があるコーデックだと全てのフレームを読んでくれない
FFmpeg に一旦全部吐かせてから OpenCV でそれらすべてを読むようにした/icons/check.icon
opencv-python-contrib を入れても WSL だとそもそも manylinux 用の OpenCV がパッケージマネージャーによって入っている場合があって、この場合は自前で OpenCV をビルドする必要が出てくる?
cv2.cv2 に truetype モジュールがないと怒られた
opencv-contrib と共にビルドしたら動いた
オリジナルの動画と同時に再生してみるとだんだんオリジナル側に追いつけなくなったり逆に早く再生されてしまったりする
各フレームごとに描画開始時刻を取得するのではなく、動画の再生が開始された時点での時刻のみを保持しておくことによって修正された(commit へのリンク)。 /icons/hr.icon
何を内部でしているか
v2t.py
v2t.py は、動画を読み込んで文字列の羅列を出力するスクリプトです。
動画とは、そもそも画像を高速に切り替えることで成り立っているものです。これら一枚一枚のことをフレームと呼びます。
OpenCV(でもできるが) FFmpeg を利用して動画からフレームを一つずつ順番に抽出することができます。ここで抽出したフレーム一枚のことを F と呼びます。
まず、F をグレースケールに変換します。すると、黒を0、白を255として、合計256段階で各画素が表現されます。
次に、そのフレームをある程度の大きさごとに分割します。このスクリプトでは、分割後の画素の行列の縦横比が 2:1 になるように分割しています。(表示される文字が縦長なので)ここで、その行列が最後には文字となるので、行列が大きければ大きいほど粗く変換されます。
使用している文字については、だんだん薄くなるように並べています。
CHARS = ['W', '#', 'R', 'E', '8', 'x', 's', 'i', ';', ',', '.', ' ']
だいたいこれが v2t.py のやっていることですが、各フレームについて、そのフレームのグレースケールの各画素の値の平均が閾値を越えていなければ、背景色黒、文字色白となり、逆に越えていれば、背景色白、文字色黒となる機能が入っています。
tshow.py
tshow.py は、動画から文字列の羅列に変換されたファイルを読み取り、それをターミナル上で再生するスクリプトです。
標準で含まれている curses パッケージを利用し、ターミナルの文字を削除しては出力し、…… を繰り返しているだけです。
curses-windows を入れれば Windows でも動きますが、背景色と文字色が変動してくれないので体験が良くない可能性がある
t2v.py
OpenCV で文字列の羅列を描画し、動画化します。FFmpeg も利用して、オリジナルの動画の音声を合成させるオプションも用意しました。
image2textimage.py
v2t.py, t2v.py の関数を呼び出して、与えられた画像をテキスト化された画像に変換します。