Arc's blog

競プロなど

C++&Siv3Dでリアルタイム動画加工ソフト作ったった

Arcです。突然ですが、謎ソフト作ってみました。

機能追加しやすい設計にはしましたが、機能が足りてないです。動作も重いです。クオリティには期待するな。

名付けてRealtimeMovieHacker厨ニっぽい

製作経緯

  • CombNaf3のネタを何か持ってみたかった
  • Siv3Dを使ってみたかった
  • 高校生のうちにプログラミングで何か「形あるモノ」を作ってみたかった

こんな感じですね。

ソフト概要

キーボード操作によって、Webカメラで取得した映像をリアルタイム加工して出力します。

このように動作します。

動作環境はWindows 7 SP1,8,8.1,10です。CPUはできるだけ高性能なものが望ましい。

macOSLinuxには対応していません……が、いつの日か対応するかもしれません(後述)。

ダウンロード

ソフトウェア本体はGithubのReleasesからどうぞ。(無保証です) 一番上が最新版です。"Assets"の下の"RtMovieHacker_vX.Y.Z.zip"をダウンロード、展開してください。

ソースコードGithubで公開しています。

使い方

  1. RtMovieHacker.exeを起動する
  2. Webカメラを起動する
  3. キー操作などで映像にエフェクトを掛ける(詳細は付属のREADME.txt)

実装

ここからは技術的な話。

使ってる言語とかライブラリとか

全てC++14を利用しています。

また、外部ライブラリとしてSiv3Dを利用しています。

Siv3DはWindowsのみ対応。macOS/Linuxに対応するOpenSiv3Dが開発されていますが、現状だと機能が足りなかったので使いませんでした。機能が増えてきたらOpenSiv3Dを利用して移植するかも。

開発はVisual Studio 2015で行いました。

ルーチン

最初に、使用可能なWebカメラを全て起動します。

また、全てのエフェクタクラスをインスタンス化します。

後は以下の処理をずっと回すだけ。

カメラ切り替えキーが押されたらカメラを切り替える(ここの動作は不安定です)
カメラで新しいフレームを取得する
エフェクタ起動条件を満たしていたらエフェクタを起動する
エフェクタ停止条件を満たしていたらエフェクタを停止する
フレームにエフェクトを掛ける
フレームを出力する

エフェクタ管理の実装

std::list<std::reference_wrapper<Effecter>> effecter_disabled(停止しているエフェクタへの参照のリスト)、std::list<std::reference_wrapper<<Effecter>> effecter_enabled(起動しているエフェクタへの参照のリスト)という2つのリンクリストが核になっています。任意位置での削除操作を高速化したいのでstd::listを使用しました。

また、全てのエフェクタは、抽象クラスclass Effecterをpublic継承して実装しています。こうすることで、全てのエフェクタに必須な関数を一元管理できます。

再実装する必要のある関数は以下の通り。

  • virtual bool needs_enable()(起動条件を満たしていたらtrue)
  • virtual bool needs_disable()(停止条件を満たしていたらtrue)
  • virtual void effect()(エフェクトを実行する)

また、全てのエフェクタにはcore_dataという構造体への参照が呼ばれます。core_data.imageを書き換えることで出力フレームが加工されます。

初期化処理として、全てのエフェクタクラスをインスタンス化します。次に、全てのエフェクタクラスへの参照をeffecter_disabledに入れます。

次に、effecter_disabled内の全てのインスタンスに対して、needs_enable()を呼び出します。trueならそのインスタンスeffecter_enabledpush_back()して、effecter_disabled側のインスタンスerase()します。ここの削除操作の時短のためにstd::listを使用しました。ここの処理でつまづきました……。

次に、effecter_enabled内のすべてのインスタンスに対して、needs_disable()を呼び出します。以下、察してください。

で、effecter_enabled内のすべてのインスタンスに対して、effect()を呼び出します。これによってエフェクトが掛かります。

なんか操作が重いのでチューンアップする必要がありそうです(思い当たる節があったので、そのうち更新します)。v0.1.0で修正できました

なんでこんなことをしたのか

こう実装することによって、effecter_enabledの中身が「昔に起動したエフェクタ」を先頭にして並びます。effect()もこの順番に呼び出されるので、あるエフェクトに別のエフェクトを重ねる事ができます。決まった順番で「起動しているか判定→エフェクト掛ける」という方法だと、命令の並び順によってエフェクトを掛ける順番が決まってしまうので……。

エフェクタの作り方

"Effecter.h"にこんな感じで追記。

class <ClassName> :public Effecter{

public:
    <ClassName>(CoreData&);
    bool needs_enable();
    bool needs_disable();
    void effect();
    //その他エフェクタ内で使う関数
}

適当なcppファイルに関数を実装する。

#include<Siv3D.hpp>
#include"CoreData.h"
#include"Effecter.h"

<ClassName>::<ClassName>(CoreData& input_data) :Effecter(input_data){};

bool <ClassName>::needs_enable(){
    //起動条件を満たしたらtrueをreturnするように実装
}

//その他もろもろの関数を実装

"Main.cpp"void Main()内にこんな感じで追記。

<ClassName> <InstanceName>(core_data);
effect_ctrl.push_to_disabled(<InstanceName>);

割と面倒くさい

感想

初めてプログラミングに触れた(プチコンっていう、DSiで動くBASIC環境でした)小学生の頃は、ちょっとした入力や条件分岐はできても「このサンプルゲームどうやって動いてるの……」っていう感じでした。

で、それから5年ぐらい。なんかそれっぽい(これは罠で、実用性皆無)ものができました。素直に嬉しい。

どんなに複雑なプログラムでも「入力」「処理」「出力」しか無いんですよね。ここらへんの「できるだけシンプルに捉える」考え方を持てたのは競プロ効果かもしれないです。

また、実装を通してc++の機能をたくさん学習しました。c++機能多すぎ

エフェクタは割と簡単に追加できるように実装してみました(そのつもり)。ちょっとずつ機能追加していけたら良いなあ。

最後に、このソフトの開発を実現してくれたSiv3D開発チームの方々に感謝申し上げます。

では。

更新履歴

20180325 動作映像をv0.1.1仕様に変更