ブログ@kaorun55

HoloLensやKinectなどのDepthセンサーを中心に書いています。

Kinect for Windows SDK v2.0 で赤外線画像を取得する

Kinect for Windows SDK v2.0入門 目次

赤外線画像を取得して表示する方法について解説します。Kinect for Windows v1でも赤外線画像の取得はできましたが、PrimeSense系センサーの仕様で、カラー画像と赤外線画像はどちらかしか使えませんでした。v2ではカラー画像と赤外線画像は同時に取得できるようになり、有効に活用できそうです。

赤外線画像の使いどころとして、最初に思いつくのが暗所での利用でしょう。暗いところでも画像が取得できるようになるので、赤外線画像を利用した画像処理が可能になるかと思います。

それを発展させた例として昨年のKinect for Windows Contestで技術賞を受賞した「非接触バイタル・センシング」が挙げられます。Kinect v2でも実現可能なこの技術をいち早く実装しており、これに赤外線画像を利用しています(下記画像はKinect for Windows Contestのサイトより引用)。

ここでは基本となる赤外線画像データの取得と表示について解説します。

なお、現在のSDKでは赤外線画像のInfraredFrameと長時間露光した赤外線画像のLongExposureInfraredFrameの2種類がありますが、違いが内容に見受けられます。試したい場合にはこのサンプルをInfraredXxxxxからLongExposureInfraredXxxxxに変えてみてください。

環境

筆者の環境は次の通りです。

実行結果

赤外線画像を表示します。

キャプチャ

解説

赤外線画像はほぼカラー画像と同じ方法で取得します。InfraredFrameSource、InfraredFrameReader、InfraredFrameを使用します。

赤外線画像の解像度(幅、高さなど)はFrameDescriptionで取得できます。解像度は512x424のDepthデータと同じサイズ。データフォーマットはushort(16bit)の配列です。

初期化

初期化の手順は次の通りです

  1. Kinectを開く
  2. 赤外線画像リーダーを開く

まずKinectを開きます。続いてInfraredFrameSourceを使ってFrameDescriptionを取得します。最後に赤外線画像リーダーを作成し、データの読み込み準備を行います。データの読み込みは、C++ではポーリング、C#ではイベントハンドラの登録を行います。

C++

void initialize()

{

// デフォルトのKinectを取得する

ERROR_CHECK( ::GetDefaultKinectSensor( &kinect ) );

// Kinectを開く

ERROR_CHECK( kinect->Open() );

BOOLEAN isOpen = false;

ERROR_CHECK( kinect->get_IsOpen( &isOpen ) );

if ( !isOpen ){

throw std::runtime_error("Kinectが開けません");

}

// 赤外線画像リーダーを取得する

ComPtr<IInfraredFrameSource> infraredFrameSource;

ERROR_CHECK( kinect->get_InfraredFrameSource( &infraredFrameSource ) );

ERROR_CHECK( infraredFrameSource->OpenReader( &infraredFrameReader ) );

// 赤外線画像のサイズを取得する

ComPtr<IFrameDescription> infraredFrameDescription;

ERROR_CHECK( infraredFrameSource->get_FrameDescription( &infraredFrameDescription ) );

ERROR_CHECK( infraredFrameDescription->get_Width( &infraredWidth ) );

ERROR_CHECK( infraredFrameDescription->get_Height( &infraredHeight ) );

// バッファーを作成する

infraredBuffer.resize( infraredWidth * infraredHeight );

}

C#(デスクトップ)

private void Window_Loaded( object sender, RoutedEventArgs e )

{

try {

// Kinectを開く

kinect = KinectSensor.GetDefault();

if ( kinect == null ) {

throw new Exception("Kinectを開けません");

}

kinect.Open();

// 赤外線画像の情報を取得する

infraredFrameDesc = kinect.InfraredFrameSource.FrameDescription;

// 赤外線リーダーを開く

infraredFrameReader = kinect.InfraredFrameSource.OpenReader();

infraredFrameReader.FrameArrived += infraredFrameReader_FrameArrived;

}

catch ( Exception ex ) {

MessageBox.Show( ex.Message );

Close();

}

}

C#(Windows ストアアプリ)

protected override void OnNavigatedTo( NavigationEventArgs e )

{

base.OnNavigatedTo( e );

try {

// Kinectを開く

kinect = KinectSensor.GetDefault();

if ( kinect == null ) {

throw new Exception( "Kinectを開けません" );

}

kinect.Open();

// 赤外線画像の情報を取得する

infraredFrameDesc = kinect.InfraredFrameSource.FrameDescription;

// 画像化のためのバッファを作成する

infraredBitmapBuffer = new byte[infraredFrameDesc.LengthInPixels * 4];

infraredBitmap = new WriteableBitmap( infraredFrameDesc.Width, infraredFrameDesc.Height );

ImageInfrared.Source = infraredBitmap;

infraredBuffer = new ushort[infraredFrameDesc.LengthInPixels];

// 赤外線画像リーダーを開く

infraredFrameReader = kinect.InfraredFrameSource.OpenReader();

infraredFrameReader.FrameArrived += infraredFrameReader_FrameArrived;

}

catch ( Exception ex ) {

MessageDialog dlg = new MessageDialog( ex.Message );

dlg.ShowAsync();

}

}

データの取得

続いてデータを取得し表示します。C++ではポーリング、C#ではイベントハンドラになりますが、大きな流れは同じです。

  1. 赤外線画像フレームを取得する
  2. 赤外線画像データを取得する
  3. 赤外線画像データを表示する

イベントが発生またはフレームが取得できたら、フレームから赤外線画像データをコピーします。赤外線画像データは1ピクセルあたり16bitのデータが512×424個ならんでいます。C++およびC#(デスクトップ)ではこれをそのまま画像化して表示します。C#(Windows ストアアプリ)ではBGRAデータに変換して表示しています。

C++

void updateInfrared()

{

// フレームを取得する

ComPtr<IInfraredFrame> infraredFrame;

auto ret = infraredFrameReader->AcquireLatestFrame( &infraredFrame );

if ( ret == S_OK ){

// BGRAの形式でデータを取得する

ERROR_CHECK( infraredFrame->CopyFrameDataToArray( infraredBuffer.size(), &infraredBuffer[0] ) );

// フレームを解放する

// infraredFrame->Release();

}

}

void draw()

{

// カラーデータを表示する

cv::Mat colorImage( infraredHeight, infraredWidth, CV_16UC1, &infraredBuffer[0] );

cv::imshow( "Infrared Image", colorImage );

}

C#(デスクトップ)

void infraredFrameReader_FrameArrived( object sender, InfraredFrameArrivedEventArgs e )

{

// カラーフレームを取得する

using ( var colorFrame = e.FrameReference.AcquireFrame() ) {

if ( colorFrame == null ) {

return;

}

// 赤外線画像データを取得する

var infraredBuffer = new ushort[infraredFrameDesc.Width * infraredFrameDesc.Height];

colorFrame.CopyFrameDataToArray( infraredBuffer );

// ビットマップにする

ImageColor.Source = BitmapSource.Create( infraredFrameDesc.Width, infraredFrameDesc.Height, 96, 96,

PixelFormats.Gray16, null, infraredBuffer, infraredFrameDesc.Width * (int)infraredFrameDesc.BytesPerPixel );

}

}

C#(Windows ストアアプリ)

void infraredFrameReader_FrameArrived( InfraredFrameReader sender, InfraredFrameArrivedEventArgs args )

{

UpdateInfraredFrame( args );

DrawInfraredFrame();

}

private void UpdateInfraredFrame( InfraredFrameArrivedEventArgs args )

{

// 赤外線画像フレームを取得する

using ( var infraredFrame = args.FrameReference.AcquireFrame() ) {

if ( infraredFrame == null ) {

return;

}

// 赤外線画像データを取得する

infraredFrame.CopyFrameDataToArray( infraredBuffer );

}

}

private void DrawInfraredFrame()

{

// 赤外線画像データをBGRAデータに変換する

for ( int i = 0; i < infraredBuffer.Length; i++ ) {

// 0-65535を0-255に変換する

byte value = (byte)(infraredBuffer[i] * 255 / 65535);

int colorindex = i * 4;

infraredBitmapBuffer[colorindex + 0] = value;

infraredBitmapBuffer[colorindex + 1] = value;

infraredBitmapBuffer[colorindex + 2] = value;

infraredBitmapBuffer[colorindex + 3] = 255;

}

// ビットマップにする

var stream = infraredBitmap.PixelBuffer.AsStream();

stream.Write( infraredBitmapBuffer, 0, infraredBitmapBuffer.Length );

infraredBitmap.Invalidate();

}

まとめ

赤外線画像自体は簡単に取得できます。これを使って何をするかというところがアイデアになります。赤外線画像に対する画像処理は、観測範囲ではあまり見かけないので、試してみるとよいかと思います。

Kinect for Windows SDK v2.0入門 目次