OpenCV Plus Unityでヘッドトラッキング+オブジェクト追従

UnityでOpenCV系の検索するとだいたい有料アセットの「OpenCV for Unity」が引っかかる。

今回は無料アセットのOpenCV Plus Unityを使って顔を認識させて、顔の位置にオブジェクトを配置して追従させる。

 

Unity 2017.4

手順

アセットをインポート

unsafeエラーが出るのでBuild Settings>Player Settings>Other Settings>Allow ‘unsafe’ Codeにチェックをつける。

Assets>OpenCV+Unity>Demo>FaceDetectorSceneシーンを開く

 

初めて実行するとこのメッセージが出てくるけど×で閉じる。

目とか輪郭を追加情報DLしたら表示できるだけなので今回はパス。

邪魔なのでスクリプトから消す

FaceDetectorScene.csから以下をコメント化

#if UNITY_EDITOR
				// query user to download the proper shape predictor
				if (UnityEditor.EditorUtility.DisplayDialog("Shape predictor data missing", errorMessage, "Download", "OK, process with face rects only"))
					Application.OpenURL("http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2");
#else
             UnityEngine.Debug.Log(errorMessage);
#endif

 

 

 

WEBカメラが問題なければ普通にひょじされて顔の周りに赤枠がつく。

エラーが出る場合は

FaceRigとかunityCaputureとかwindowsMRのUSBがWEBカメラとして認識したり

カメラ関係が問題あるのでなんとかする。

 

シーン内のCanvas>RawImageのFaceDetectorSceneスクリプトが赤枠つけてる処理なので調べる

ProcessTexture内のprocessor.MarkDetectedが赤枠を付けた情報を返しているので中身を見る

必要なのはココだけ(余計なモノ省いた状態)

        public void MarkDetected()
        {
            foreach (DetectedFace face in Faces)
            {
                //顔の長方形
                // Cv2.Rectangle(画像,左上と右下の角の座標,線)
                Cv2.Rectangle((InputOutputArray)Image, face.Region, Scalar.FromRgb(255, 0, 0), 2);
                
            }
        }

この中のface.Regionが顔の位置を持っているので、顔の位置情報とかを返すように修正する

 public void MarkDetected(ref int x,ref int y,ref int width,ref int height)
        {
            foreach (DetectedFace face in Faces)
            {
                //顔の長方形
                // Cv2.Rectangle(画像,左上と右下の角の座標,線)
                Cv2.Rectangle((InputOutputArray)Image, face.Region, Scalar.FromRgb(255, 0, 0), 2);
                
                x = face.Region.Center.X;//中心点のX
                y = face.Region.Center.Y;//中心点のY
                width = face.Region.Width;
                height = face.Region.Height;

            }
        }

 

あとはもらった情報をワールド座標に変換するためなんか色々調べる。ここが分かりやすかった。↓

【Unity】永久保存版! 座標を変換して狙った位置に表示する方法

 

FaceDetectorSceneスクリプトを修正する。

色々理解できてなくて絶対に全然違うところあるので誰か教えて欲しい・・・

public class FaceDetectorScene : WebCamera
	{
	public TextAsset faces;
	public TextAsset eyes;
	public TextAsset shapes;

	private FaceProcessorLive<WebCamTexture> processor;

        //良く分からんが追加
        [SerializeField] private RectTransform targetRect;//WEBカメラのテクスチャを貼っているターゲットを指定
        private Canvas _canvas;
        public Canvas Canvas { get { return this._canvas ? this._canvas : this._canvas = targetRect.GetComponent<Graphic> ().canvas; } }
        
        public GameObject cubeobj;//追従させるCubeオブジェクト
        
        Vector3 worldPos = Vector3.zero;//ワールド座標

        //OpenCVから帰ってきた情報を格納するヤツ
        private int testX;
        private int testY;
        private int testW;
        private int testH;

        //追加ここまで

     //途中省略~




     /// <summary>
	/// Per-frame video capture processor フレームごとのビデオキャプチャプロセッサ
	/// </summary>
	protected override bool ProcessTexture(WebCamTexture input, ref Texture2D output)//テクスチャを返す
	{
	// detect everything we're interested in  興味のあるすべてを検出する
	processor.ProcessTexture(input, TextureParameters);

	  // mark detected objects 検出されたオブジェクトにマークを付ける
	  processor.MarkDetected(ref testX,ref testY,ref testW,ref testH);

           Vector2 testvc2;
           testvc2.x=testX;
           testvc2.y=testY;

           GameObject mainCamObj = Camera.main.gameObject;//メインカメラ取得

            //スクリーン座標→ワールド座標に変換
            //RectTransformUtility.ScreenPointToWorldPointInRectangle:変換
            RectTransformUtility.ScreenPointToWorldPointInRectangle (targetRect, testvc2, Canvas.worldCamera, out worldPos);

            if(testvc2 != Vector2.zero){//顔認識した時だけオブジェクトの位置を更新
                
               worldPos.z=0;
               worldPos.y=-worldPos.y;
               cubeobj.transform.position =  worldPos;
               Debug.Log("x:"+worldPos.x+" "+"y:" + worldPos.y +" " +"z:"+worldPos.z);
            }

	    // processor.Image now holds data we'd like to visualize 
            //processor.Imageは、視覚化するデータを保持するようになりました
	    output = Unity.MatToTexture(processor.Image, output);   // if output is valid texture it's buffer will be re-used, otherwise it will be re-created
            //出力が有効なテクスチャである場合、そのバッファは再利用され、そうでない場合は再作成されます

	   return true;
	}

 

CanvasオブジェクトのRenderModeはWorldSpaceにする。

アタッチする情報とかはとりあえず以下を参照

 

赤枠の中心点からズレるのはCanvasカメラとかScreenPointToWorldPointInRectangleあたりが何か間違ってるんだろうけど

わからん・・・

 

とりあえずいったん動くところまで。

 

このページちゃんとした処理が分かるまで検索エンジンに引っかからないようにしておこう・・・。

 

関連記事




コメントを残す

※コメントは承認後に表示されます。
 コメントを公開されたくない場合、名前の後に「:非公開」とつけてください。