LoginSignup
3
5

More than 3 years have passed since last update.

OpenCV plus Unityで画像の背景を動的に削除する方法

Posted at

やりたいこと

らくがきARの様なアプリを作りたいと思ったので、今回は手始めにOpenCVを使ってUnityにインポートした画像の背景を削除(透明化)します。
ソースコードは以下にアップロードしてあります。
https://github.com/AzetaTakuya/MakeImageBackgroundTransparentUsingOpenCV

結果

こんな感じ
結果画像.png
左から【元画像 -> グレースケール化 -> 二値化 -> マスク化 -> 背景削除】といった工程から画像の背景を透明化することができました。

実装

環境

OpenCV plus Unity を試す

まずUnityプロジェクトを作成し、AssetStoreからOpenCV plus Unityをインポートします。
OpenCVplusUnityインポート.png
インポートが完了すると、エラー[ error CS0227: Unsafe code may only appear if compiling with /unsafe. Enable "Allow 'unsafe' code" in Player Settings to fix this error.]が出るかと思います。
OpenCVplusUnityエラー.png
unsafeコードを使用するので 【File -> BuildSettings -> PlayerSettings -> Player -> OtherSettings -> Allow 'unsafe' Code】 にチェックを入れます。
エラーがなくなったので、早速OpenCVplusUnityのデモを見てみます。
今回は画像の輪郭を抽出したりしたいので、それに近いデモである【Assets -> OpenCV+Unity -> Demo -> Identifiy_Contours_by_Shape -> ContoursByShapeScene.scene】を開きます。
実行結果はこんな感じ。
Demo実行結果.png
白黒画像に色とテキストが付きました。
デモ結果.png
このデモを元に画像の背景を透明化します。

画像の背景を削除

画像をインポート

Unityに画像ファイルをインポートします。そのままの状態では読み込めない為、Read/Write Enabledにチェックを入れます。
テクスチャインポート.png

コード

元画像(m_texture)から
【元画像 -> グレースケール化画像 -> 輪郭抽出画像 -> マスク化画像 -> 背景透明化画像】を表示します。

namespace OpenCvSharp.Demo
{
    using UnityEngine;
    using System.Collections;
    using OpenCvSharp;
    using UnityEngine.UI;
    using System.Threading.Tasks;
    using System.Collections.Generic;

    public class MakeImageBackgroundTransparentUsingOpenCV : MonoBehaviour
    {
        #region public members
        public Texture2D m_texture;

        public RawImage m_image_origin;
        public RawImage m_image_gray;
        public RawImage m_Image_binarization;
        public RawImage m_image_mask;
        public RawImage m_image_backgroundTransparent;

        public double v_thresh = 180;
        public double v_maxval = 255;
        #endregion

        private void Start()
        {
            #region load texture
            Mat origin = Unity.TextureToMat(this.m_texture);
            m_image_origin.texture = Unity.MatToTexture(origin);
            #endregion

            #region  Gray scale image
            Mat grayMat = new Mat();
            Cv2.CvtColor(origin, grayMat, ColorConversionCodes.BGR2GRAY);
            m_image_gray.texture = Unity.MatToTexture(grayMat);
            #endregion

            #region Find Edge
            Mat thresh = new Mat();
            Cv2.Threshold(grayMat, thresh, v_thresh, v_maxval, ThresholdTypes.BinaryInv);
            m_Image_binarization.texture = Unity.MatToTexture(thresh);
            #endregion

            #region Create Mask
            Mat Mask = Unity.TextureToMat(Unity.MatToTexture(grayMat));
            Point[][] contours; HierarchyIndex[] hierarchy;
            Cv2.FindContours(thresh, out contours, out hierarchy, RetrievalModes.Tree, ContourApproximationModes.ApproxNone, null);
            for(int i = 0; i < contours.Length; i++)
            {
                Cv2.DrawContours(Mask, new Point[][] { contours[i] }, 0, new Scalar(0, 0, 0), -1);
            }
            Mask = Mask.CvtColor(ColorConversionCodes.BGR2GRAY);
            Cv2.Threshold(Mask, Mask, v_thresh, v_maxval, ThresholdTypes.Binary);
            m_image_mask.texture = Unity.MatToTexture(Mask);
            #endregion

            #region TransparentBackground
            Mat transparent = origin.CvtColor(ColorConversionCodes.BGR2BGRA);
            unsafe
            {
                byte* b_transparent = transparent.DataPointer;
                byte* b_mask = Mask.DataPointer;
                float pixelCount = transparent.Height * transparent.Width;

                for (int i = 0; i < pixelCount; i++)
                {
                    if (b_mask[0] == 255)
                    {
                        b_transparent[0] = 0;
                        b_transparent[1] = 0;
                        b_transparent[2] = 0;
                        b_transparent[3] = 0;
                    }
                    b_transparent = b_transparent + 4;
                    b_mask = b_mask + 1;
                }
            }
            m_image_backgroundTransparent.texture = Unity.MatToTexture(transparent);
            #endregion
        }

    }
}

シーン

上のスクリプトをアタッチするGameObjectと5つのRawImageを用意します。
MakeImageBackgroundTransparentUsingOpenCVにインポートした画像とRawImageをアタッチします。
シーン.png
インスペクタ.png

実行

実行すると【元画像 -> グレースケール化画像 -> 輪郭抽出画像 -> マスク化画像 -> 背景透明化画像】が表示されます。
実行.png
※うまくいかなかった場合はv_threshの値を弄ってみてください。

まとめ

  • ARアプリにガンガン使っていけそう。
  • 白背景の画像以外も対応出来る様にする必要がある。

参考

3
5
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
3
5