はじめに
Method overriding, in object-oriented programming, is a language feature that allows a subclass or child class to provide a specific implementation of a method that is already provided by one of its superclasses or parent classes. ―Method Overrideing, Wikipedia
(抄訳)OOPにおいて関数のオーバーライドとは、ある親クラス配下の関数の実装を子クラスが提供すること、またその機能を指す。
オーバーライド(英:Override)といえばオブジェクト指向の概念じゃないか、C言語ではできないはず、そう思われてこの記事を開かれたことかと思います。
確かにその通り、しかし実際にできるのです。ああ、ネット民も笑覧あれ!!
どうやって?
最近テトの勢いすごくないですか?ライアーダンサーにメズマライザーに…オーバーライドなんか特に!
僕はニコ動の活動を応援しています。
茶番は以上です。ということで 吉田夜世「オーバーライド」をOpneCVでAAにし、C言語でアニメーションにしてみたい と思います。
手順
動画をローカルに落とす- 一定時間で区切って画像にする
- AAに変換
- C言語で再生
Youtube、ニコ動から動画を落とす行為は、利用規約で禁じられています。
著作権的にもだいぶグレーです。営利なら特に。
その辺は十分注意してくださいね。
ちなみに以下のようなファイル構造になっています。ソースを読みたい読者は適宜参照ください。
.
|-- asciiart.py
|-- runner.py
|-- pirnt.c
|-- dist/*
|-- frames/*
`-- video.mp4
2 directories, 7 files
asciiart.py(このファイルの該当部分を適宜切り抜いて話します。)
import math
from pathlib import Path
import cv2
class AsciiArt:
def __init__(self, video_path: str, frames_folder: str, AAs_folder: str):
self.__video_path = Path(video_path)
self.__frames_folder = Path(frames_folder)
self.__AAs_folder = Path(AAs_folder)
def split_into_frame(self, fps=1):
cap = cv2.VideoCapture(str(self.__video_path))
frame_rate = int(cap.get(cv2.CAP_PROP_FPS) / fps)
frame_cnt = 0
while True:
ret, frame = cap.read()
if not ret:
break
if frame_cnt % frame_rate == 0:
idx = str(math.floor(frame_cnt / frame_rate)).zfill(3)
filename = f"frame_{idx}.jpg"
cv2.imwrite(f"{self.__frames_folder}/{filename}", frame)
frame_cnt += 1
cap.release()
def to_ascii(self, img_path: Path):
img = cv2.resize(cv2.imread(str(img_path)), (100, 60))
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ascii_chars = [
"@",
"#",
"S",
"%",
"?",
"*",
"+",
";",
":",
",",
".",
]
ascii_art: list[str] = []
for row in gray:
row_chars = [
ascii_chars[int(px / 255 * (len(ascii_chars) - 1))] for px in row
]
ascii_art.append("".join(row_chars))
self.__output_AA(img_path, ascii_art)
def __output_AA(self, img_path: Path, ascii_art: list[str]):
aa_path = Path(f"{self.__AAs_folder}/{img_path.stem}.txt")
with open(aa_path, mode="w") as f:
for line in ascii_art:
f.write(f"{line}\n")
動画の編集…というか加工は勉強もかねてOpenCVでやってみます。
言語はC++とかPythonとかJavaとかが使えるみたいですが、今回はPythonで行きます。
PythonもOpenCVも初めて使うのでおかしな箇所があればご指摘願います。
一定時間で区切って画像に切り出そう
def split_into_frame(self, fps=1):
# Read the video:
cap = cv2.VideoCapture(str(self.__video_path))
frame_rate = int(cap.get(cv2.CAP_PROP_FPS) / fps)
# Loop through the video and save frames:
frame_cnt = 0
while True:
ret, frame = cap.read()
if not ret:
break
if frame_cnt % frame_rate == 0:
idx = str(math.floor(frame_cnt / frame_rate)).zfill(3)
filename = f"frame_{idx}.jpg"
cv2.imwrite(f"{self.__frames_folder}/{filename}", frame)
frame_cnt += 1
cap.release()
大まかな流れはコメントの通りです。
変数fps
は任意の引数値により動的に決定されますが、指定されなかった場合には規定値1が使用されます。
また、while loop
内で決定されている出力ファイル名frame_[num].jpg
について、numは3桁の0埋めがなされており、後でファイルを参照するときに参照しやすいようにしています。
フォーマットは今回JPEGを使っていますが、ほかにも以下の形式が使えるようです。
画像をAAに変換しよう
def to_ascii(self, img_path: Path):
img = cv2.resize(cv2.imread(str(img_path), cv2.IMREAD_GRAYSCALE), (100, 60))
ascii_chars = [使いたい文字]
ascii_art: list[str] = []
for row in img:
row_chars = [
ascii_chars[int(px / 255 * (len(ascii_chars) - 1))] for px in row
]
ascii_art.append("".join(row_chars))
self.__output_AA(img_path, ascii_art)
def __output_AA(self, img_path: Path, ascii_art: list[str]):
aa_path = Path(f"{self.__AAs_folder}/{img_path.stem}.txt")
with open(aa_path, mode="w") as f:
for line in ascii_art:
f.write(f"{line}\n")
画像を読み込むとき、cv2.imread()
関数の第二引数にcv2.IMREAD_GRAYSCALE
を指定するとモノクロにしてくれるようです。
画像を読み込んだとき帰ってくる値は、vscodeによればMatLike
なるオブジェクトなんですが、実態は多次元のndarray
なため、ループで回せます。
このプログラムでは、一段目のforが行を取ってきて、二つ目の内包表現がpxに分解、これを任意の文字に変換します。
ファイルへの出力については専用の関数に切り出しています。
実行するスクリプトを書いて、動かしてみます。
from convert import AsciiArt
import pathlib
aa = AsciiArt("video.mp4", "frames", "dist")
aa.split_into_frame(fps=3)
for f in pathlib.Path("frames").glob("*.jpg"):
aa.to_ascii(f)
結構時間がかかりました。動画の長さにもよりますが、筆者環境では10秒以上はかかったかな。
するとインスタンス化に際して指定した通り、frames
とdist
フォルダができます。frames
は画像が入っている、一時的につくったフォルダなので、もう不要です。
ほしいのはdist
フォルダのtxtファイル。一つ開いてみると
長いので省略
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,:,:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,??????:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,??%%%%%?::*SS%S?:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,:%,,,,,+S%%%%%%%%%%%%%,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,:%%%%%%%%%%%%%%%%%%*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,S%%%%%%%%%%%%%%%%%%%%S,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,%%SSSSSSSSSSSSS%%%%%%%%%,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,SSSSSSSSSSSSSSS##S%S%%%%%%,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,+SSSSSSSSSSSSSSSSSSSSS#S%%%%%,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,SSSSSSSSS#S%%%%%%#SSSSSSS#%%%%S,,,,:??+,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,%SSSSSSSSS%%%%%%%%%%%%%S#SSS#%%%%,,??;+??????,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,SSSSSSSSS#%%%%%%%%%%%%%%%%%%##S%%%%?%???????%?,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,+*+:,,:#SSSSSSSSS#%%%%%%%%%%%%%%%%%%%%S%%%?SSS##SS%???,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,???%%?#SSSSSSSS#%%%%???????????%%%%%%%S#S%%S%S%SS%??%?,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,?*%SS%SSSSSSSSS%%%???????????%?????????%%%S%#SS%??????,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,S??%SS%SSSSSSS%??????????????,??????????S%%S%????%???%,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,:???%S#S%#SS#????????????????,:????????????????%??????,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,*??%???SS%%%??????:??????????:;+%??%?????%?%%?????????;,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,*????????????????+,??????????,,,;????????????????????,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,;?%??????????????,,,?????????,,:?SS#@??????????????#S??,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,S???????%%??????,,:*????????;,S#%S?SS#???%?%????S%%??S,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,?SSS??????%??????,:,,%???????:%#S**#,%#??????#S%SSS????,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,*?%%%S#%??%??????,,;;,*??????,,?#SSS*,S#S??*%%SSS??%???,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,%????SSS%S%??????SS+%SS??????,,?#S%?,,??;,,?%%???%????,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,???%?????%??????#S,SSS*;,S?%?,,#??%;+*??+,:????????S%S,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,%%?????S???????SS,,?SSS#,,,*,:,,,,:+*+??*,,+S????%%???,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,+?%SSSSS%%?????S.,S%#?S,,,,,,,,,,::%????:,,#???%S??%%:,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,+??S??SS%%??????S,,S?+,,,,,,,,,,,,,S????%:,,**?%??%??*,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,+?S%%%S#???%??S??%:;:,,,,,,,,,,,,,:?;???%,,,,???????%:S:,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,:???S%SS%S*;;:,??*++*,,,,,,;,,,,,,,,????*,,,?:%??%SS???:,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,?%???????%,,,,???*?::,,,,,,,,,,,,:?*:??;:,,,;;::;S?%%%?%,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,:???????%%,,,%S?????;,,,,,,,,%?,,,,,??:,,,,,,,,%??%%?%???:,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,?*%S?????;,,,,,,????:,,,,,;????+,,,:,,,,,,,,,,,,:???%S?%+%;,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,???%????,,,,,,,:%?%?%+,,,?????:,,,,,;:*%,,,,,,,,,??*:?%?,?:,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,+??????%:,,,,,,,;??,,,,,,,,,,,,,,,,?,,::,,,,,,,,,,,,,,:,,%,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,*????S??:,,,,,,,??:;,,,,,,,,,,,,%,;;:,:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,??????,,,,,,,,,??S,,+;,,,,,,,,?,;,,,,,:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,?SS%:,,,,,,,,,,%??,,,:S#,,,,,?,,,,,,:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,?%%;,,,,,,,,,,,,,?,:S%%%+**?++,?,,,,,,,,:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,?:,,,,,,,,,,,,,,,:*,,,,,++++++,,,,:,,,,:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,?%,,,,,,,,,,,,,,,,,,,,,,++++++,?,,,,,,,:,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,++++++*,,,,,,,+,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,++++++:,,,,,,,*,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,*S,,,,,,,,,,,,,,,??%*++++++:,,,,,,?*%?;,,,,:,,,,,+,:S,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,:,:+#%,,,,,,,:+%%??*?SS%*%?++%,,,,+*%%?S***??#%S%,,%+:###,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,S%?++;#,,,,%*****S?*##S??S***,,,,,,,SS*******++***:%;:?S,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,#;?,,,+;+***S**;;**%**?SSS#SS#,,SS%+******%+*****?+*:,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,;*+*,,,%*++++#**+#*S;**%?*++;+,,+%?*;?****#+*******;*++,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,+S++,,,:?**??**#+**S+#******,,,,,,**+S***?+#+****;*+:#+,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,*%?+:,,,:*******+S****#******,,,,,,,#;****S?S+***;;**:,:,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,++,,,,;*******?************?,,,,,,;?******%+**;;;++%,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,;;+*******************,,,,,,,:*********;;;;;;;,,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,?;;;;?*%***************?,,,,,,,********;;;;;;;;S,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,,;;;;;++?***??****?******:+,,:,+,S*****;;;;;;;;;;,,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,:;;;;;;;;**?******????**?**,,:,?:,?***;;+;;;;;;;;*,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,,+;;;;;;;;;;*??****?*?*****%,;;,;,,,**+;;;;;;;;;;;;,,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,;;;;;;;;;;;;;+**************,,,,,,,,,+;;;;;;;;;;*;;#,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,,*;+;;;;;;;;;;;;**************,,,,,,,,,;;;;;;;;;;;;;;,,,,,,,,,,,,,,,,,,,,,
,,,,,,,,,,,,,,,,,,,,,,,,,,?;;;;;;;;;;;;;;;;*************+,,,,,,,,,;;;;;;;;;;;;;,,,,,,,,,,,,,,,,,,,,,
いい感じですね。
C言語で再生
Q. Pythonでよくね?
A. C使わないとタイトル詐欺なんですよね
#include <stdio.h>
#include <unistd.h>
#define FILES 415
#define fps 3
int main(void)
{
for (int i = 0; i <= FILES; i++)
{
// clear screen
printf("%c[2J", 27);
FILE *fp;
char fname[21];
sprintf(fname, "./dist/frame_%03d.txt", i);
fp = fopen(fname, "r");
if (fp == NULL)
{
printf("FAILED: %s can't open!\n", fname);
return -1;
}
int chr;
while ((chr = fgetc(fp)) != EOF)
{
putchar(chr);
}
printf("%s open\n", fname);
// 1s = 1000000
usleep(1000000 / fps);
fclose(fp);
}
return 0;
}
しくみは割と単純で、ファイルを読み込み、表示して、一定時間止め、ファイルを閉じて、画面をクリアする、というのをファイルの枚数分繰り返しているだけです。
実際にやってみた
チカチカする…何というか改良の余地がありそうな感じですね。
後記
画像の読み込みからAA生成までほぼPythonなんでパッケージもできそうですね。
謝辞
吉田夜世さま、重音テトさまには深く感謝申し上げます。
ご助言いただいた諸先輩方同胞諸君にも御礼申し上げます。