【Unity】OpenCVを用いて残像を描画する(背景切り抜き)

ご無沙汰しております。 Unityの内容かつ、あまり記事が見当たらないものをブログにと思っていたので、期間が開いてしまいました。UnityとXcode半々ぐらいなので空きがちです。

今回は、定点カメラの動画から、スポーツの動き(スイング)を残像表示する必要があったのでその内容を記事にします。 また今回なんですが、OpenCVForUnityのアセットが必須になります。アセットがなくてもOpenCVは使用できた気もするのですが、知らないのでこのアセットでということにします。

概要

OpenCVの背景差分で実装します。この手法で実現できるのは背景切り抜きになります。イメージでいうと、Web会議の映像の背景を宇宙とかにして人物だけ映していると思うのですが、その人物切り出しみたいなものです。 この方法では背景画像と動体画像から動体だけの画像が作成可能で、スイング残像とかだとこれを動画だと毎フレーム切り出して画像に張り付けを繰り返すことで残像を1枚の画像に表示させることが可能です。

実装方法

OpenCVUnityのBackgroundSubtractorMOG2のクラスを使います。 今回実装した関数の引数となる入力画像は、

  1. 背景となる画像

  2. 動体が映っている画像

  3. 切り取り画像の貼り付け先画像 の3つです。

    public Mat MaskDraw(Mat currentMat, Mat preMat, Mat backMat)
    {
        using (Mat fgMaskMat = new Mat(backMat.rows(), backMat.cols(), CvType.CV_8UC1))
        using (Mat bgMaskMat = new Mat(backMat.rows(), backMat.cols(), CvType.CV_8UC1))
        // 背景差分法
        using (BackgroundSubtractorMOG2 mog2 = Video.createBackgroundSubtractorMOG2())
        {
            // 背景とする画像
            mog2.apply(backMat, fgMaskMat);
            // マスクさせるものが移っている画像、ここでは過去画像でマスク画像を作成
            mog2.apply(currentMat, fgMaskMat);

            // マスク画像を閾値を設定して2値化、閾値は調整可能
            // これをしないとジャギジャギのマスクになる            
            Imgproc.threshold(fgMaskMat, fgMaskMat, 80, 255, Imgproc.THRESH_BINARY);
            // マスク画像をつかって画像を反転させる(現状だと人部分が切り抜かれたMatデータ)
            Core.bitwise_not(fgMaskMat, bgMaskMat);

            // 結果画像のベースを現在画像に設定
            var result = new Mat(preMat.rows(), preMat.cols(), CvType.CV_8UC1);
            preMat.copyTo(result);

            // 切り抜いた画像を貼り付け先の画像に加算描画
            currentMat.copyTo(result, fgMaskMat);

            return result;
        }
    }

参考記事

http://blog.livedoor.jp/maikka_gogo/archives/1051597401.html

背景差分 — OpenCV-Python Tutorials 1 documentation

OpenCVとUnityで背景差分によるChromakeyをやってみた - Qiita

マスク処理で余分な場所を隠す | Let's Computer Vision