Kinect for Windows SDK で画像などの取得時に呼び出される
KinectSensor.○○FrameReady系のイベントですが、
(ColorFrameReady, DepthFrameReady, SkeletonFrameReady,AllFramesReady)
通常だとUIスレッドで実行されます。
取得した値をそのまま表示する場合はそれでいいと思うのですが、
返された値をOpenCVなどのライブラリで処理してから、表示したい場合もあると思います。
その場合、UIスレッドを長時間の処理を行うことになってしまい、
入力に対するレスポンスも悪くなるため、
UIスレッド以外のスレッドで処理を行い、
画像を更新するときのみUIスレッドを用いるようにしたいところです。
そこで、FrameReady系のイベントの実行スレッドについて調べてみたのですが、
イベントへの関数の登録を、UIスレッド以外のスレッドで実行すれば、
FrameReadyイベントもUIスレッド以外で実行されるようです。
また、そのスレッドはイベントが実行される際に生きている必要はありません。
KinectSensor.Start(); が実行されたスレッドが実行されたスレッドが終了していると
なぜかイベントが実行されなかったので
KinectSensor.Start() の実行はUIスレッドで行う必要があります。
// ---- 別スレッドでイベントを設定 ----
Microsoft.Kinect.KinectSensor kinect_ = KinectSensor.KinectSensors[0];
~~~
var t= new System.Threading.Thread(
() =>
{
kinect_.ColorFrameReady += ColorImageReady;
}
);
t.Start();
t.Join();
kinect_.Start();
// ---------
スレッド(System.Threading.Thread)ではなくタスク(System.Threading.Tasks.Task)で
イベントの登録とKinectSensor.Start()を実行した場合、タスクが終了していても、
正常に非UIスレッドでイベントが実行されました。
一回限りの処理はスレッドよりタスクで実行したほうがいいと思うので、こちらをお勧めします。
// ---- タスクでイベントを設定 ----
Microsoft.Kinect.KinectSensor kinect_ = KinectSensor.KinectSensors[0];
~~~
var t = System.Threading.Tasks.Task.Factory.StartNew(
() =>
{
kinect_.ColorFrameReady += ColorImageReady;
kinect_.Start();
}
);
// ---------
また、非UIスレッドから、画像の更新はできないので、
画像の更新はDispather等を用いて、UIスレッドで実行する必要があります。
// --- 別スレッドで実行したイベントからUIスレッドを呼びだし画像を更新 ---
void ColorImageReady(object sender, ColorImageFrameReadyEventArgs args)
{
ColorImageFrame imgframe = args.OpenColorImageFrame();
if (imgframe != null)
{
byte[] pixelData = new byte[imgframe.PixelDataLength];
imgframe.CopyPixelDataTo(pixelData);
// 必要ならこのへんで画像処理
this.Dispatcher.BeginInvoke(
new Action<ColorImageFrame>(
(img) =>
{
ImageSource src =
BitmapImage.Create (img.Width, img.Height, 96, 96,
PixelFormats.Bgr32, null, pixelData,
img.Width * img.BytesPerPixel);
this.Image = src;
}
),imgframe
);
}
else { }
}
// ---------