OpenCvSharp3 + Kinect v2

Windows フォームアプリ で OpenCvSharp 3 + Kinect v2 を使ってみる簡単な例です。たぶんこれが早いと思います。
Microsoft のサンプルはWPFのものが多いので、Windowsフォームを使う例としても残しておきます。

ソリューション一式はGitHubにも置いてあります。

環境

ここでは下記の環境での解説となっています。
それぞれ事前にインストールしておく必要があります。

1. 準備

1.1. プロジェクトの作成

Visual Studio を起動して 「ファイル」→「新規作成」→「プロジェクト」 を選びます。

出てきたウィンドウでは下記のように選び、適当な場所に適当な名前で作成します。

  • 上部で「.NET Framework 4.5」
  • 左のテンプレートは「Visual C#」
  • 中央は「Windows フォームアプリケーション」

OpenCvKinect_001

 

1.2. Microsoft.Kinect への参照の追加

新規プロジェクト作成できたら、ソリューションエクスプローラーでプロジェクトの下の「参照」(下図で青くなっている行)を右クリックして、「参照の追加」を選びます。

OpenCvKinect_005

出てきたウィンドウで左は「アセンブリ」が選ばれた状態とし、右上の検索欄に「Kinect」と入れて「Microsoft.Kinect」を探します。

OpenCvKinect_006

見つかった「Microsoft.Kinect」にマウスカーソルを持っていき、チェックを付けてOKを押します。

Microsoft.Kinect.Face など他のものもありますが、それは必要に応じてチェックします。(普通にカラー画像、深度画像、骨格を扱う範囲ではMicrosft.KinectだけでOKです。)

参照に追加できたら次へ進みます。

1.3. OpenCvSharp3 の追加

またソリューションエクスプローラーで、今度はプロジェクト(下図の青くなっている行)を右クリックし、「NuGet パッケージの管理」を選びます。

OpenCvKinect_003

出てきたウィンドウの上部では「参照」を選んだ状態とし、検索欄に「OpenCvSharp3」と入れて探します。

OpenCvKinect_004

「OpenCvSharp3-AnyCPU」を選んで「インストール」をします。
(マンドリルのアイコンです。)

インストールできれば、準備完了です。

2. フォームの編集

2.1. デザイン

Form1.cs を開いて、メインになるフォームに PictureBox を一つ追加しておきます。
その PictureBox を選択すると右上に小さい三角が出ますが、そこからサイズモードを Zoom にして、親コンテナーにドッキングしておくと良いでしょう。

OpenCvKinect_007

2.2. コード

ソリューションエクスプローラーの Form1.cs を右クリックして「コードの表示」を選ぶとコード編集ができます。

Form1.cs のコードはこんな感じになります。
(最初に決めたプロジェクト名が違う場合など、下のすべてをコピペしてもうまく動くとは限りません。念のため。)

using System;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.Kinect;
using OpenCvSharp;
using OpenCvSharp.Extensions;

namespace OpenCvKinectV2
{
    public partial class Form1 : Form
    {
        /// <summary>
        /// KinectSensorのインスタンス
        /// </summary>
        KinectSensor kinect;

        /// <summary>
        /// 最新のカラー画像
        /// </summary>
        Mat colorImage;

        /// <summary>
        /// 出力用に処理後の画像
        /// </summary>
        Mat colorOutputImage;

        /// <summary>
        /// PictureBoxで表示するBitmap
        /// </summary>
        Bitmap colorBitmap;


        public Form1()
        {
            InitializeComponent();
        }

        /// <summary>
        /// ウィンドウが開いたときの処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_Load(object sender, EventArgs e)
        {
            // KinectSensorのインスタンスを取得
            this.kinect = KinectSensor.GetDefault();

            // カラー、深度等をまとめて取得するイベントハンドラを設定
            MultiSourceFrameReader multiSourceFrameReader = this.kinect.OpenMultiSourceFrameReader(
                FrameSourceTypes.Color      // 今回はカラーのみ
                );
            multiSourceFrameReader.MultiSourceFrameArrived += FrameArrived;

            // 画像の準備
            InitializeImage();

            // Kinect利用開始
            this.kinect.Open();
        }

        /// <summary>
        /// 画像を新規作成
        /// </summary>
        private void InitializeImage()
        {
            // カラー画像の準備
            FrameDescription colorFrameDescription = this.kinect.ColorFrameSource.FrameDescription;
            this.colorImage = new Mat(
                colorFrameDescription.Height,
                colorFrameDescription.Width,
                MatType.CV_8UC4                     // 8ビット×4チャンネル分
                );
            this.colorImage.SetTo(Scalar.All(255)); // 最初は画像全体を白色に塗りつぶし

            // 同じサイズ・深度で出力用画像を作成
            this.colorOutputImage = this.colorImage.Clone();

            // カラー画像Bitmapの作成
            this.colorBitmap = this.colorOutputImage.ToBitmap();

            // PictureBoxへBitmapを割り当て
            this.pictureBox1.Image = this.colorBitmap;
        }

        /// <summary>
        /// Kinectのデータ取得時に呼ばれる処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void FrameArrived(object sender, MultiSourceFrameArrivedEventArgs e)
        {
            MultiSourceFrame multiSourceFrame = e.FrameReference.AcquireFrame();

            // カラー画像取得時の処理
            using (ColorFrame colorFrame = multiSourceFrame.ColorFrameReference.AcquireFrame())
            {
                if (colorFrame != null)
                {
                    // OpenCVの画像にKinectのカラー画像を複製
                    colorFrame.CopyConvertedFrameDataToIntPtr(
                        this.colorImage.Data,   // カラー画像Matのデータ部分ポインタ
                        (uint)(this.colorImage.Total() * this.colorImage.ElemSize()),   // 全画素数 × 一画素のバイト数
                        ColorImageFormat.Bgra   // RGBでなくBGRAの順番
                        );

                    // これでOpenCVでの画像処理ができる
                    // (例としてRGBごとの閾値処理)
                    Cv2.Threshold(this.colorImage, this.colorOutputImage, 127.0, 255.0, ThresholdTypes.Binary);

                    // PictureBoxへ描画
                    UpdatePictureBox(this.colorOutputImage);
                }
            }
        }

        /// <summary>
        /// PictureBoxの表示を更新
        /// <param name="image">表示画像。colorBitmapと同じサイズ・色深度でなければならない</param>
        /// </summary>
        private void UpdatePictureBox(Mat image)
        {
            // 指定された画像でBitmapを更新
            image.ToBitmap(this.colorBitmap);

            // PictureBoxの描画を要求
            this.pictureBox1.Invalidate();
        }

        /// <summary>
        /// ウィンドウが閉じられたときの処理
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            // Kinect利用終了
            this.kinect.Close();
        }
    }
}

3. 実行

以上で Form1.cs のデザインとコードの編集ができたら、Kinect v2 をつないでデバッグの開始をしてみます。
上手く動けばこんな感じになります。

OpenCvKinect_008

なお今回のサンプルでは上のコードでは、カラー画像そのままではなく R,G,B それぞれについて2値化する処理を入れてあります。

UpdatePictureBox(this.colorOutputImage);
の行を
UpdatePictureBox(this.colorImage);
と書き換えると、処理していない画像を表示できます。


投稿日

カテゴリー:

,

タグ: