Kinect Unityで人物のみ抽出する方法

Unityを利用してkinectから人物のみを切り出す方法をまとめます。ネットで調べたところXNA版とWPF版は多く存在しましたが、Unityでの方法は見つからなかったので。

kinect1

 

まず初めに開発環境です

Windows8.1 Enterprise 64bit

Unity Pro 4.6.1f

wrapper to Kinect SDK 1.7 

 

wrapperPackageをインストールするとAssets>Script>Kinect>KinectImgControllersの中にDisplayColor.csとDisplayDepth.csが作られます。

このスクリプトをオブジェクトにアタッチすると、DisplayColorからはRGB画像が、DisplayDepthからはDepth画像がテクスチャとしてオブジェクトに貼られます。

DisplayDepth.csの中を見てみると、Update()の中にコメントアウトされた

tex.SetPixels32(convertPlayersToCutout(dw.segmentations));

があります。これは人がいるところをテクスチャとして吐き出す関数です。それのコメントアウトを外し、tex.SetPixels32(convertDepthToColor(dw.depthImg));をコメントアウトをして無効にします。

またDisplayDepth.csとDisplayColor.csの両方にrenderer.material.mainTexture = tex;の記述があるのでコメントアウトしてください。これがあるとオブジェクトに画像が張られてしまうためです。

これでDisplayDepth.csをアタッチされたオブジェクトにプレイヤー部分のみを抽出するテクスチャが貼り付けられるようになりました。

convertPlayersToCutoutを見てみると人がいるところはアルファチャンネルが255になり、いないところは0になるようです。

今回引数として使用しているdw.segmentationsはDepthWrapper.csから確認するとpixelを一つ一つ確認してプレイヤーがいたらtrueを返すような仕組みになっていおり、playerは6名まで確認することができるようです。

 

さて、これでRGB画像と人物情報を持った画像を生成することができました。

次に適当な背景画像を用意します。

 

今回は一つシェーダーを用意し、背景画像、RGB画像、人物画像を三つ重ね、人がいる部分はRGB画像を、人がいない部分は背景画像をといった処理を書いていきたいと思います。

まず実装するために必要なシェーダーMaskingShader.shaderを書きます

 

MaskingShader.shader


_BackTex (“Back”, 2D) = “white” {}
_MaskTex (“Mask”, 2D) = “white” {}
}
SubShader {
Tags { “RenderType”=”Opaque” }
LOD 200

Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 3.0

//バーテックス用入力
struct vertexInput{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};

//フラグメント用入力/バーテックス用出力
struct fragmentInput{
float4 position : SV_POSITION;
float2 uv : TEXCOORD0;
};

sampler2D _MainTex;
sampler2D _BackTex;
sampler2D _MaskTex;

fragmentInput vert(vertexInput v){
fragmentInput o;
o.position = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = v.uv;
return o;
}

float4 frag(fragmentInput i) : COLOR{
float4 maintex = tex2D(_MainTex, i.uv);
float4 backtex = tex2D(_BackTex, i.uv);
float4 masktex = tex2D(_MaskTex, i.uv);
float4 output;

if(masktex.a > 0.2f){
output.rgb = maintex.rgb;
}else{
output.rgb = backtex.rgb;
}

return float4(output.r,output.g,output.b,1.0);
}

ENDCG
}
}
FallBack “Diffuse”
}


 

 

3つのテクスチャを格納し、masktexの値でmaintexを出すかbacktexを出すか判断しています。

またこのシェーダーのためのマテリアルMaskmatも用意しておいてください。

 

次に、このシェーダーにテクスチャを渡すためのスクリプトMaskScript.csを書きます

 

MaskScript.cs


using UnityEngine;
using System.Collections;

public class MaskScript : MonoBehaviour {
Texture2D color;
Texture2D depth;
public Texture2D back;
public Material Mat;
DisplayDepth ddepth;
DisplayColor dcolor;

void Awake(){
ddepth = this.GetComponent<DisplayDepth> ();
dcolor = this.gameObject.GetComponent<DisplayColor> ();
}
// Use this for initialization
void Start () {
Mat.SetTexture (“_MainTex”, color);
Mat.SetTexture (“_BackTex”, back);
Mat.SetTexture (“_MaskTex”, depth);

}

// Update is called once per frame
void Update () {
color = dcolor.tex;
depth = ddepth.tex;

Mat.SetTexture (“_MainTex”, color);
Mat.SetTexture (“_BackTex”, back);
Mat.SetTexture (“_MaskTex”, depth);
}

}


 

RGB画像、Depth画像両方が必要なので、双方のtexをpublicに直すことをお忘れなく。

最後にスクリーンになるオブジェクトに、

DisplayDepth.cs

DisplayColor.cs

MaskScript.cs

Maskmat

をすべてアタッチします。MaskScript.csのMatの部分には下にあるMaskmatをアサインしてください。

kinect3

 

そして、Maskmat上にあるBackの項目に背景にしたい画像を用意して格納し、実行すると人物画像だけ切り取ることができます。

kinect2

 

kinectのRGBカメラとDepthカメラの位置関係から少しずれが出ていますが、スクリプトで修正することはできると思います。