2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C言語でオーバーライドしてみた!!!

Posted at

はじめに

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言語ではできないはず、そう思われてこの記事を開かれたことかと思います。

確かにその通り、しかし実際にできるのです。ああ、ネット民も笑覧あれ!!

image.png
大川ぶくぶ『ポプテピピック』一巻より

どうやって?

最近テトの勢いすごくないですか?ライアーダンサーにメズマライザーに…オーバーライドなんか特に!

僕はニコ動の活動を応援しています。

茶番は以上です。ということで 吉田夜世「オーバーライド」をOpneCVでAAにし、C言語でアニメーションにしてみたい と思います。

手順

  1. 動画をローカルに落とす
  2. 一定時間で区切って画像にする
  3. AAに変換
  4. 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秒以上はかかったかな。

するとインスタンス化に際して指定した通り、framesdistフォルダができます。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なんでパッケージもできそうですね。

謝辞

吉田夜世さま、重音テトさまには深く感謝申し上げます。
ご助言いただいた諸先輩方同胞諸君にも御礼申し上げます。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?