#概要
小ネタです。
AzureKinectから得られた赤外線画像をUnityで見れるようにしようとしたけど、
UnityのTextureに変換する所に一瞬つまづいたので、その解決策の忘備録も含めています。
#結果
以下のようにUnityで赤外線画像が見れるようになります。
#unity #AzureKinect
— unagi (@UnagiHuman) December 24, 2019
UnityでAzureKinectの赤外線画像を表示してみた。 pic.twitter.com/BD1X4PVpOV
#AzureKinectから赤外線画像を取得してUnityで見れる形にする
https://github.com/microsoft/Azure-Kinect-Sensor-SDK/issues/490
IRデータの取り扱い方は上のリンク先の投稿を参考にしました。
IR画像のサイズはdepthのwidth,heightと同じで、データ的には16bitでIRの輝度が書かれています。
このデータをUnityのRenderTextureに変換するにはデータを変換する必要があります。
この変換はshader内でやってしまうのが一番速度が速いです。
以下に、Unity上でKinectからIRデータを取得してUnityのRenderTextureに変換するコードを乗っけます。
また、あくまでサンプルなので簡潔に書いているため実用的なソースではありません。本来はAzureKinect側からデータを受け取る側を別スレッドにすべきだし、またKinectは最高30FPSなのでUpdateでデータの処理をするのは無駄なのでそこら辺の考慮もすべきです。
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using Device = Microsoft.Azure.Kinect.Sensor.Device;
/// <summary>
/// IR画像取得サンプル
/// </summary>
public class TestIR : MonoBehaviour
{
/// <summary>
/// ColorFormatをR8_UNormに設定したRenderTexture(width,heightはDepth画像に合わせる)
/// </summary>
[SerializeField] private RenderTexture _irTexture = null;
[SerializeField] private Shader _unprojectIRshader = null;
[SerializeField] private float _maxIRValue = 10000;
[SerializeField] private float _minIRVaue = 0;
private Material _material;
private Texture2D _tempIR;
private Device _kinectDevice;
// Start is called before the first frame update
void Start()
{
//GraphicsFormat.R8_UNorm
_material = new Material(_unprojectIRshader);
_tempIR = new Texture2D(_irTexture.width, _irTexture.height, GraphicsFormat.R16_UNorm, 0);
_kinectDevice = Device.Open();
}
// Update is called once per frame
void Update()
{
//irのデータ配列を取得
var capture = _kinectDevice.GetCapture();
var irData = capture.IR.Memory;
//シェーダーに放り込むソース用のテクスチャにロード
_tempIR.LoadRawTextureData(irData.ToArray());
_tempIR.Apply();
capture.Dispose();
//変換元のソースのテクスチャをセット
_material.SetTexture("_IrTexture", _tempIR);
//IR輝度の最大値(画像の見え方の調整)
_material.SetFloat("_MaxValue", _maxIRValue);
//IR輝度の最小値(画像の見え方の調整)
_material.SetFloat("_MinValue", _minIRVaue);
var prevRT = RenderTexture.active;
//変換先のテクスチャをセット(ColorFormatをR8_UNorm)
Graphics.SetRenderTarget(new RenderBuffer[1]{ _irTexture.colorBuffer }, _irTexture.depthBuffer);
Graphics.Blit(null, _material, 0);
RenderTexture.active = prevRT;
}
private void OnDestroy()
{
if (_material != null) Destroy(_material);
if (_tempIR != null) Destroy(_tempIR);
}
}
以下はshader側の処理です。
Shader "UnprojectIR"
{
CGINCLUDE
#include "UnityCG.cginc"
texture2D _IrTexture;
float _MaxValue;
float _MinValue;
//16bitをR8_Normフォーマットに変換(0~1.0)
inline float GetIR(texture2D source, int tx, int ty, int vmax, int vmin)
{
int v = source[uint2(tx, ty)] * 65535;
float colorValue = 0.0f;
if (v <= vmin)
{
colorValue = 0.0f;
}
else if (v >= vmax)
{
colorValue = 1.0f;
}
else
{
colorValue = (float)(v - vmin) / (float)(vmax - vmin);
}
return colorValue;
}
void Vertex(
float4 position : POSITION,
out float4 positionOut : SV_Position,
inout float2 texCoord : TEXCOORD0
)
{
positionOut = UnityObjectToClipPos(position);
}
void Fragment(
float4 position : SV_Position,
float2 texCoord : TEXCOORD0,
out float colorOut : SV_Target0
)
{
uint w, h;
_IrTexture.GetDimensions(w, h);
// Texture index
uint tx = texCoord.x * w;
uint ty = texCoord.y * h;
float ir = GetIR(_IrTexture, tx, ty, _MaxValue, _MinValue);
//output write
colorOut = ir;
}
ENDCG
SubShader
{
Cull Off ZWrite Off ZTest Always
Pass
{
CGPROGRAM
#pragma vertex Vertex
#pragma fragment Fragment
ENDCG
}
}
}
#まとめ
UnityでAzureKinectの赤外線を見る方法を調べた。これを利用して以前の別記事でやったような再帰性反射材をつかったトラッキングをやってみたい。