Xtion2を使ってみた

# Xtion2を動かしてみる
- Xtion2が発売されて、NiTEが使えるのものが出るのを待とうと思っていたら、いつの間にかドライバをインストールすることでNiTEが使えるということで、早速購入しました。
- ただ、今回この記事を書いたのは、NiTE2（骨格認識）を動かすまでに苦労したのでそのメモとして
![DSC_2414.JPG](https://qiita-image-store.s3.amazonaws.com/0/214668/8a04a579-c82b-bd75-b78d-4af16b1c6029.jpeg)
![DSC_2415.JPG](https://qiita-image-store.s3.amazonaws.com/0/214668/b96f6e90-2303-2bfc-e0bf-fdb4c28e56a2.jpeg)
![DSC_2416.JPG](https://qiita-image-store.s3.amazonaws.com/0/214668/0050f301-a228-d425-cb68-74b48747ba54.jpeg)

- Xtionと比べても小さいですね。ついでにカメラの三脚などのみ簡単に取り付けられるようになっているので、Xtionよりもいいと思います。

# 使用環境
- Windows10 Home
- CPU: Intel Core i7-8700K @ 3.70GHz
- RAM: 32GB
- NDIVIA Geforce GTX1080Ti(11GB)　（今回は関係なし）
- Visual Studio 2013
- C++
- OpenCV3.1（画像描画など）
- OpenNI2
- NiTE2.2

# インストール（OpenNI2）
- 早速ですが、OpenNI2は付属のCDにインストールプログラムがあるのでインストールしておきます。
    - 古いOpenNI2がインストールされている場合は、一度アンインストールしてもいいかと思います。
- インストール後、デスクトップに「AUSU_NiViewer2.2(x64)」というプログラムを起動したら動くかと思います。

![2018-05-06 (1).png](https://qiita-image-store.s3.amazonaws.com/0/214668/4afacb52-8bb2-95ee-0e7c-12fabced8d3b.png)

- きちんととれている感じですね。サンプルプログラムなので、後で実際にコードを書いていきます。

# インストール（NiTE2）
- NiTE2のインストールして骨格認識するまですごく悩まされました。
- Xtion2用にOpenNI2を[→ここ←](https://www.asus.com/jp/3D-Sensor/Xtion-2/HelpDesk_Download/)からインストール
- NiTE2のインストールは簡単で[→ここ←](https://social.msdn.microsoft.com/Forums/ja-JP/bae80fb0-fce5-468e-a292-fe46381af3e5/how-to-install-openni-22-nite-22-kinect-sdk-18-windows-7-3264-bit?forum=kinectsdk)にまとまれられているので、各自で必要なものをダウンロードする。
- 私は64Bit版をインストールしたので64Bit版での説明になりますが、32Bit版の方は「Program Files」の部分を「Program File (x86)」と置き換えていただければいいかと思います。
- NiTE2が入ったか確認するために、「C:\Program Files\PrimeSense\NiTE2\Sample\Bin」まで移動して、適当なプログラムを起動してみる（HandViewer.exeなど）
    - 一瞬起動して消えてしまう・・（ここで詰まっていた）
    - OpenNIは起動しているから、ドライバのインストールはできているはず。。
- ここで、Binフォルダの中のOpenNI2というフォルダの中の「Drivers」フォルダを変更することにしました。（ここに至るまで2w以上費やした。。。orz）
- 先ほどの公式サイトから「ASUS-Xtion2-nuitrack-win64.zip」もダウンロードしておきます。
- ZIPファイルを解凍したら、「nuitrack/bin」と移動します。この中にある「OpenNI2」というフォルダを先ほどのPrimesenseのフォルダに移動します。
    - 上書き保存してもらって結構です。
- その後、Driversフォルダに入って「SenDuck.ini」を開きます。開いたら3行目のセミコロンを消して「NiteMode = 1」とします。
- 再び、HandViewer.exeを起動すると起動するかと思います。
![2018-05-06 (2).png](https://qiita-image-store.s3.amazonaws.com/0/214668/f39fc16c-699e-303f-48bd-323903a44e81.png)

# OpenNI2を使ったプログラム
- OpenNI2を使ったプログラムは[この本](http://www.shuwasystem.co.jp/products/7980html/3916.html)を参考にしています。
- はじめに、OpenCVとOpenNI／NiTEで使用するヘッダファイルを定義しておきます。
    - ライブラリファイルなどの定義となります。

```c++:opencv.h
/***
* Revised 2016/08/07 for OpenCV-3
***/

#pragma once

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/core/core.hpp>              // (C++用)
#include <opencv2/imgproc/imgproc.hpp>        // (C++用)
#include <opencv2/highgui/highgui.hpp>        // (C++用)
#include <opencv2/ml/ml.hpp>                  // サポートベクタマシン、ブースティングなどの機械学習
#include <opencv2/features2d/features2d.hpp>  // SURF、FASTなどの特徴抽出
#include <opencv2/xfeatures2d.hpp>
#include <opencv2/xfeatures2d/nonfree.hpp>
#include <opencv2/video/background_segm.hpp>  // 前景/背景分離
#include <opencv2/video/tracking.hpp>         // トラッキング
#include <opencv2/objdetect/objdetect.hpp>    // Haar、LBP、HOGなどのオブジェクト検出器
#include <opencv2/calib3d/calib3d.hpp>        // カメラキャリブレーション、ステレオカメラなど
#include <opencv2/flann/flann.hpp>            // 高速最近傍処理(FLANN)など
#include <opencv2/superres/optical_flow.hpp>  //オプティカルフロー
//#include <opencv2/tracking/feature.hpp>

#define CV_VERSION_STR CVAUX_STR(CV_MAJOR_VERSION) CVAUX_STR(CV_MINOR_VERSION) CVAUX_STR(CV_SUBMINOR_VERSION)
#ifdef _DEBUG
#define CV_EXT_STR "d.lib"
#else
#define CV_EXT_STR ".lib"
#endif

#define _X64
//#undef _X64

#ifdef _X64
#pragma comment(lib, "C:\\OpenCV3.1.0\\vs2013\\x64\\lib\\opencv_calib3d"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "C:\\OpenCV3.1.0\\vs2013\\x64\\lib\\opencv_core"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "C:\\OpenCV3.1.0\\vs2013\\x64\\lib\\opencv_features2d"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "C:\\OpenCV3.1.0\\vs2013\\x64\\lib\\opencv_flann"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "C:\\OpenCV3.1.0\\vs2013\\x64\\lib\\opencv_highgui"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "C:\\OpenCV3.1.0\\vs2013\\x64\\lib\\opencv_imgcodecs"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "C:\\OpenCV3.1.0\\vs2013\\x64\\lib\\opencv_imgproc"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "C:\\OpenCV3.1.0\\vs2013\\x64\\lib\\opencv_ml"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "C:\\OpenCV3.1.0\\vs2013\\x64\\lib\\opencv_objdetect"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "C:\\OpenCV3.1.0\\vs2013\\x64\\lib\\opencv_photo"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "C:\\OpenCV3.1.0\\vs2013\\x64\\lib\\opencv_shape"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "C:\\OpenCV3.1.0\\vs2013\\x64\\lib\\opencv_stitching"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "C:\\OpenCV3.1.0\\vs2013\\x64\\lib\\opencv_superres"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "C:\\OpenCV3.1.0\\vs2013\\x64\\lib\\opencv_ts"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "C:\\OpenCV3.1.0\\vs2013\\x64\\lib\\opencv_video"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "C:\\OpenCV3.1.0\\vs2013\\x64\\lib\\opencv_videoio"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "C:\\OpenCV3.1.0\\vs2013\\x64\\lib\\opencv_videostab"	CV_VERSION_STR	CV_EXT_STR)
#else
#pragma comment(lib, "I:\\OpenCV3.1.0\\vs2013\\x86\\lib\\opencv_calib3d"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "I:\\OpenCV3.1.0\\vs2013\\x86\\lib\\opencv_core"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "I:\\OpenCV3.1.0\\vs2013\\x86\\lib\\opencv_features2d"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "I:\\OpenCV3.1.0\\vs2013\\x86\\lib\\opencv_flann"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "I:\\OpenCV3.1.0\\vs2013\\x86\\lib\\opencv_highgui"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "I:\\OpenCV3.1.0\\vs2013\\x86\\lib\\opencv_imgcodecs"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "I:\\OpenCV3.1.0\\vs2013\\x86\\lib\\opencv_imgproc"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "I:\\OpenCV3.1.0\\vs2013\\x86\\lib\\opencv_ml"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "I:\\OpenCV3.1.0\\vs2013\\x86\\lib\\opencv_objdetect"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "I:\\OpenCV3.1.0\\vs2013\\x86\\lib\\opencv_photo"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "I:\\OpenCV3.1.0\\vs2013\\x86\\lib\\opencv_shape"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "I:\\OpenCV3.1.0\\vs2013\\x86\\lib\\opencv_stitching"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "I:\\OpenCV3.1.0\\vs2013\\x86\\lib\\opencv_superres"	CV_VERSION_STR	CV_EXT_STR)
//#pragma comment(lib, "I:\\OpenCV3.1.0\\vs2013\\x86\\lib\\opencv_ts"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "I:\\OpenCV3.1.0\\vs2013\\x86\\lib\\opencv_video"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "I:\\OpenCV3.1.0\\vs2013\\x86\\lib\\opencv_videoio"	CV_VERSION_STR	CV_EXT_STR)
#pragma comment(lib, "I:\\OpenCV3.1.0\\vs2013\\x86\\lib\\opencv_videostab"	CV_VERSION_STR	CV_EXT_STR)
#endif

/***** 画像のピクセルの直接操作 OpenCV-2.x (MAT* ) *****/

// 汎用マクロ
// IMG: IplImage*, TYPE: 型, X,Y: 座標, ID: インデックス
// 型
// IPL_DEPTH_8U: unsigned char
// IPL_DEPTH_16S: short
// IPL_DEPTH_

#define M_VALUE(MAT, TYPE, X, Y, ID) ((TYPE*)((MAT)->data + (MAT)->step*(Y)))[(X)*(MAT)->channels()+(ID)]
// 汎用マクロ
// IMG: IplImage*, TYPE: 型, X,Y: 座標, ID: インデックス
// M_IMG:Mat ,M_X,M_Y:座標,ID:インデックス
// 型
// IPL_DEPTH_8U: unsigned char
// IPL_DEPTH_16S: short
// IPL_DEPTH_
#define I_VALUE(IMG, TYPE, X, Y, ID) ((TYPE*)((IMG)->imageData + (IMG)->widthStep*(Y)))[(X)*(IMG)->nChannels+(ID)]
#define I_VALUE_FOR_MAT(M_IMG, TYPE,M_X,M_Y,ID) (TYPE)M_IMG.data[(M_X) * M_IMG.channels() + (ID) +  M_IMG.step * (M_Y)]	
// よく使う画像用マクロ
// 8ビット1チャンネル画像 (CV_8UC1)
#define MVC(MAT, X, Y) M_VALUE((MAT), unsigned char, X, Y, 0)

// 8ビット3チャンネル画像 (CV_8UC3)
#define MBC(MAT, X, Y) M_VALUE((MAT), unsigned char, X, Y, 0)
#define MGC(MAT, X, Y) M_VALUE((MAT), unsigned char, X, Y, 1)
#define MRC(MAT, X, Y) M_VALUE((MAT), unsigned char, X, Y, 2)

// 16ビット整数1チャンネル画像 (CV_16U) 符号あり16ビット整数(short) 
#define MVS(IMG, X, Y) M_VALUE(IMG, short, X, Y, 0)

// 16ビット3チャンネル画像 (CV_16SC3) 符号あり16ビット整数(short) 
#define MBS(MAT, X, Y) M_VALUE((MAT), short, X, Y, 0)
#define MGS(MAT, X, Y) M_VALUE((MAT), short, X, Y, 1)
#define MRS(MAT, X, Y) M_VALUE((MAT), short, X, Y, 2)

// 32ビット浮動小数点1チャンネル画像 (CV_32FC1)
#define MVF(MAT, X, Y) M_VALUE((MAT), float, X, Y, 0)

// 32ビット浮動小数点3チャンネル画像 (CV_32FC3)
#define MBF(MAT, X, Y) M_VALUE((MAT), float, X, Y, 0)
#define MGF(MAT, X, Y) M_VALUE((MAT), float, X, Y, 1)
#define MRF(MAT, X, Y) M_VALUE((MAT), float, X, Y, 2)

/***** 画像のピクセルの直接操作 OpenCV-1.x (IplImage* ) *****/

// 汎用マクロ
// IMG: IplImage*, TYPE: 型, X,Y: 座標, ID: インデックス
// 型
// IPL_DEPTH_8U: unsigned char
// IPL_DEPTH_16S: short
// IPL_DEPTH_
#define I_VALUE(IMG, TYPE, X, Y, ID) ((TYPE*)((IMG)->imageData + (IMG)->widthStep*(Y)))[(X)*(IMG)->nChannels+(ID)]

// よく使う画像用マクロ
// 8ビット1チャンネル画像 (IPL_DEPTH_8U, 1)
#define IVC(IMG, X, Y) I_VALUE(IMG, unsigned char, X, Y, 0)

// 8ビット3チャンネル画像 (IPL_DEPTH_8U, 3)
#define IBC(IMG, X, Y) I_VALUE(IMG, unsigned char, X, Y, 0)
#define IGC(IMG, X, Y) I_VALUE(IMG, unsigned char, X, Y, 1)
#define IRC(IMG, X, Y) I_VALUE(IMG, unsigned char, X, Y, 2)

// 16ビット整数1チャンネル画像 (IPL_DEPTH_16S, 1) 符号あり16ビット整数(short) 
#define IVS(IMG, X, Y) I_VALUE(IMG, short, X, Y, 0)

// 16ビット3チャンネル画像 (IPL_DEPTH_16S, 3) 符号あり16ビット整数(short) 
#define IBS(IMG, X, Y) I_VALUE(IMG, short, X, Y, 0)
#define IGS(IMG, X, Y) I_VALUE(IMG, short, X, Y, 1)
#define IRS(IMG, X, Y) I_VALUE(IMG, short, X, Y, 2)

// 32ビット浮動小数点1チャンネル画像 (IPL_DEPTH_32F, 1)
#define IVF(IMG, X, Y) I_VALUE(IMG, float, X, Y, 0)

// 32ビット浮動小数点3チャンネル画像 (IPL_DEPTH_32F, 3)
#define IBF(IMG, X, Y) I_VALUE(IMG, float, X, Y, 0)
#define IGF(IMG, X, Y) I_VALUE(IMG, float, X, Y, 1)
#define IRF(IMG, X, Y) I_VALUE(IMG, float, X, Y, 2)

/***** 行列の要素の直接操作 *****/
// 行:ROW, 列: COL
// 32ビット浮動小数点1チャンネル行列 (CV_32FC1)
#define M1F1(MAT, ROW, COL) ((float*)((MAT)->data.ptr + (MAT)->step*(ROW)))[(COL)]

// 32ビット浮動小数点2チャンネル行列 (CV_32FC2)
#define M2F1(MAT, ROW, COL) ((float*)((MAT)->data.ptr + (MAT)->step*(ROW)))[(COL)*2+0]
#define M2F2(MAT, ROW, COL) ((float*)((MAT)->data.ptr + (MAT)->step*(ROW)))[(COL)*2+1] 

// 32ビット浮動小数点3チャンネル行列 (CV_32FC3)
#define M3F1(MAT, ROW, COL) ((float*)(((MAT)->data).ptr + (MAT)->step*(ROW)))[(COL)*3+0]
#define M3F2(MAT, ROW, COL) ((float*)(((MAT)->data).ptr + (MAT)->step*(ROW)))[(COL)*3+1]
#define M3F3(MAT, ROW, COL) ((float*)(((MAT)->data).ptr + (MAT)->step*(ROW)))[(COL)*3+2]

// 64ビット浮動小数点1チャンネル行列 (CV_32FC1)
#define M1D1(MAT, ROW, COL) ((double*)((MAT)->data.ptr + (MAT)->step*(ROW)))[(COL)]

// 64ビット浮動小数点2チャンネル行列 (CV_64FC2)
#define M2D1(MAT, ROW, COL) ((double*)((MAT)->data.ptr + (MAT)->step*(ROW)))[(COL)*2+0]
#define M2D2(MAT, ROW, COL) ((double*)((MAT)->data.ptr + (MAT)->step*(ROW)))[(COL)*2+1] 

// 64ビット浮動小数点3チャンネル行列 (CV_64FC3)
#define M3D1(MAT, ROW, COL) ((double*)((MAT)->data.ptr + (MAT)->step*(ROW)))[(COL)*3+0]
#define M3D2(MAT, ROW, COL) ((double*)((MAT)->data.ptr + (MAT)->step*(ROW)))[(COL)*3+1]
#define M3D3(MAT, ROW, COL) ((double*)((MAT)->data.ptr + (MAT)->step*(ROW)))[(COL)*3+2]
```


```c++:openni2.h
#pragma once

#include <OpenNI.h> //OpenNI2を使用する(RGB-D)
#include <NiTE.h> //NITE2を使用する(Skeleton)
#include "opencv.h"

#pragma comment(lib, "C:\\Program Files\\OpenNI2\\Lib\\OpenNI2.lib")
#pragma comment(lib, "C:\\Program Files\\PrimeSense\\NiTE2\\Lib\\NiTE2.lib")


/* -------------------------------------------
* カラーストリームデータをOpenCV型へ変換する
* [in] カラーストリーム
* [out]　cv::Mat
* ------------------------------------------*/
void ChangeColorStream(const openni::VideoFrameRef &videoFrame,
	cv::Mat &image)
{
	image = cv::Mat(
		videoFrame.getHeight(),
		videoFrame.getWidth(),
		CV_8UC3,
		(unsigned char*)videoFrame.getData());

	cv::cvtColor(image, image, CV_BGR2RGB);
}

/* -------------------------------------------
* 深度ストリームデータをOpenCV型へ変換する
* 0～10000ミリメートルを8ビットに変換する
* [in] 深度ストリーム
* [out]　cv::Mat
* ------------------------------------------*/
void ChangeDepthStream(const openni::VideoFrameRef &videoFrame,
	cv::Mat &image)
{
	image = cv::Mat(
		videoFrame.getHeight(),
		videoFrame.getWidth(),
		CV_16UC1,
		(unsigned char*)videoFrame.getData());

	image.convertTo(image, CV_8U, 255.0 / 10000);
}

/* -------------------------------------------
* 深度情報を取得する
* [in] 深度ストリーム
* [out]　深度距離
* ------------------------------------------*/
template<typename T>
void GetDepthData(const openni::VideoFrameRef &videoFrame,
	const T x, const T y, T &depthDist)
{
	openni::VideoMode videMode = videoFrame.getVideoMode();
	int position = (y * videMode.getResolutionX()) + x;

	unsigned short* depth = (unsigned short*)videoFrame.getData();

	depthDist = depth[position];
}
void GetDepthData(const openni::VideoFrameRef &videoFrame,
	const cv::Point2i &point, int &depthDist)
{
	GetDepthData(videoFrame, point.x, point.y, depthDist);
}


//NiTEの処理
/* -------------------------------------------
* 深度ストリームデータをOpenCV型へ変換する
* 0～10000ミリメートルを8ビットに変換する
* [in] 深度ストリーム
* [out]　cv::Mat
* ------------------------------------------*/
void ChangeUserTrackerStream(nite::UserTrackerFrameRef userTracke,
	cv::Mat &image)
{
	openni::VideoFrameRef depthFrame = userTracke.getDepthFrame();

	if (depthFrame.isValid())
	{
		ChangeDepthStream(depthFrame, image);
	}
}

/* -------------------------------------------
* ユーザの骨格情報を取得する
* [in] ユーザ情報
* [out]　ユーザの座標
* ------------------------------------------*/
void GetUserSkeletionPosition(
	const openni::VideoFrameRef &videoFrame,
	nite::UserTracker &userTracker,
	const nite::UserData user,
	nite::Point3f *userPosition)
{
	//スケルトンを取得し、追跡状態を確認
	const nite::Skeleton &skeleton = user.getSkeleton();
	if (skeleton.getState() != nite::SKELETON_TRACKED)
	{
		return;
	}

	//すべての関節を描画
	for (int j = 0; j < 15; ++j)
	{
		//信頼値（70%）以上なら処理する
		const nite::SkeletonJoint& joint = skeleton.getJoint((nite::JointType)j);
		if (joint.getPositionConfidence() < 0.7f)
		{
			continue;
		}

		//3次元を2次元に変換
		float fx = 0.0f, fy = 0.0f, fz = 0.0f;
		const nite::Point3f& position = joint.getPosition();
		userTracker.convertJointCoordinatesToDepth(
			position.x,
			position.y,
			position.z,
			&fx,
			&fy);

		userPosition[j].x = fx;
		userPosition[j].y = fy;
		GetDepthData(videoFrame, fx, fy, userPosition[j].z);
	}
}

/* -------------------------------------------
* カラーストリームデータをOpenCV型へ変換する
* [in] カラーストリーム
* [out]　cv::Mat
* ------------------------------------------*/
void DrawSkeletonPoint(
	cv::Mat &image,
	nite::UserTracker &userTracker,
	const nite::UserData user)
{
	//スケルトンを取得し、追跡状態を確認
	const nite::Skeleton &skeleton = user.getSkeleton();
	if (skeleton.getState() != nite::SKELETON_TRACKED)
	{
		return;
	}

	//すべての関節を描画
	for (int j = 0; j < 15; ++j)
	{
		//信頼値（70%）以上なら処理する
		const nite::SkeletonJoint& joint = skeleton.getJoint((nite::JointType)j);
		if (joint.getPositionConfidence() < 0.7f)
		{
			continue;
		}

		//3次元を2次元に変換
		float fx = 0.0f, fy = 0.0f;
		const nite::Point3f& position = joint.getPosition();
		userTracker.convertJointCoordinatesToDepth(
			position.x,
			position.y,
			position.z,
			&fx,
			&fy);

		cv::circle(image, cv::Point(fx, fy), 5, cv::Scalar(0, 0, 255), -1);
	}
}

void DrawLine(const nite::SkeletonJoint& joint1, const nite::SkeletonJoint& joint2,
	const nite::UserTracker &userTracker, cv::Mat &image)
{
	if (joint1.getPositionConfidence() < 0.7f ||
		joint2.getPositionConfidence() < 0.7f)
	{
		return;
	}

	//3次元を2次元に変換
	cv::Point2f f1, f2;
	const nite::Point3f& position1 = joint1.getPosition();
	userTracker.convertJointCoordinatesToDepth(
		position1.x, position1.y, position1.z,
		&f1.x, &f1.y);

	const nite::Point3f& position2 = joint2.getPosition();
	userTracker.convertJointCoordinatesToDepth(
		position2.x, position2.y, position2.z,
		&f2.x, &f2.y);

	cv::line(image, f1, f2, cv::Scalar(0, 255, 0), 2, CV_AA, 0);
}


void DrawSkeleton(
	cv::Mat &image,
	nite::UserTracker &userTracker,
	const nite::UserData user)
{
	//スケルトンを取得し、追跡状態を確認
	const nite::Skeleton &skeleton = user.getSkeleton();
	if (skeleton.getState() != nite::SKELETON_TRACKED)
	{
		return;
	}

	DrawLine(skeleton.getJoint(nite::JOINT_HEAD), skeleton.getJoint(nite::JOINT_NECK), userTracker, image);

	DrawLine(skeleton.getJoint(nite::JOINT_NECK), skeleton.getJoint(nite::JOINT_LEFT_SHOULDER), userTracker, image);
	DrawLine(skeleton.getJoint(nite::JOINT_NECK), skeleton.getJoint(nite::JOINT_RIGHT_SHOULDER), userTracker, image);
	DrawLine(skeleton.getJoint(nite::JOINT_RIGHT_SHOULDER), skeleton.getJoint(nite::JOINT_TORSO), userTracker, image);
	DrawLine(skeleton.getJoint(nite::JOINT_LEFT_SHOULDER), skeleton.getJoint(nite::JOINT_TORSO), userTracker, image);

	DrawLine(skeleton.getJoint(nite::JOINT_LEFT_SHOULDER), skeleton.getJoint(nite::JOINT_LEFT_ELBOW), userTracker, image);
	DrawLine(skeleton.getJoint(nite::JOINT_LEFT_ELBOW), skeleton.getJoint(nite::JOINT_LEFT_HAND), userTracker, image);

	DrawLine(skeleton.getJoint(nite::JOINT_RIGHT_SHOULDER), skeleton.getJoint(nite::JOINT_RIGHT_ELBOW), userTracker, image);
	DrawLine(skeleton.getJoint(nite::JOINT_RIGHT_ELBOW), skeleton.getJoint(nite::JOINT_RIGHT_HAND), userTracker, image);

	DrawLine(skeleton.getJoint(nite::JOINT_TORSO), skeleton.getJoint(nite::JOINT_LEFT_HIP), userTracker, image);
	DrawLine(skeleton.getJoint(nite::JOINT_TORSO), skeleton.getJoint(nite::JOINT_RIGHT_HIP), userTracker, image);

	DrawLine(skeleton.getJoint(nite::JOINT_LEFT_HIP), skeleton.getJoint(nite::JOINT_LEFT_KNEE), userTracker, image);
	DrawLine(skeleton.getJoint(nite::JOINT_LEFT_KNEE), skeleton.getJoint(nite::JOINT_LEFT_FOOT), userTracker, image);

	DrawLine(skeleton.getJoint(nite::JOINT_RIGHT_HIP), skeleton.getJoint(nite::JOINT_RIGHT_KNEE), userTracker, image);
	DrawLine(skeleton.getJoint(nite::JOINT_RIGHT_KNEE), skeleton.getJoint(nite::JOINT_RIGHT_FOOT), userTracker, image);
}
```
- それでは、OpenNI2を使ったプログラムコードになります。

```c++:main.cpp
// Windowsの場合はReleaseコンパイルすると
// 現実的な速度で動作します。

using namespace std;

#include "openni2.h"
#include "opencv.h"
#include <iostream>

void PleaseBottom()
{
	system("pause");
}

int main(int argc, char **argv)
{
	try
	{
		//ドライバの状態
		openni::Status rc = openni::STATUS_OK;

		//デバイス情報
		openni::Device device;

		//センサから得られたRGB-Dデータ
		openni::VideoStream depthStream, colorStream;

		//ANY_DEVICEならPCに接続されているデバイスを読み込む
		const char* deviceURI = openni::ANY_DEVICE;

		//OpenNIの初期化を行う
		rc = openni::OpenNI::initialize();
		printf("After initialization:\n%s\n", openni::OpenNI::getExtendedError());

		//デバイスを読み込めたかどうか
		rc = device.open(deviceURI);
		if (rc != openni::DEVICE_STATE_OK)
		{
			printf("Viewer: Device open failed:\n%s\n", openni::OpenNI::getExtendedError());
			openni::OpenNI::shutdown();
			throw std::runtime_error("openni::Device::open() failed");
		}

		//カラー画像の初期化を行う
		rc = colorStream.create(device, openni::SENSOR_COLOR);
		if (rc == openni::STATUS_OK)
		{
			rc = colorStream.start();
			if (rc != openni::STATUS_OK)
			{
				printf("Viewer: Couldn't start color stream:\n%s\n", openni::OpenNI::getExtendedError());
				colorStream.destroy();
				throw std::runtime_error("Couldn't start color stream failed");
			}
		}
		else
		{
			printf("SimpleViewer: Couldn't find color stream:\n%s\n", openni::OpenNI::getExtendedError());
			throw std::runtime_error("Couldn't find color stream failed");
		}

		//深度情報の初期化を行う
		rc = depthStream.create(device, openni::SENSOR_DEPTH);
		if (rc == openni::STATUS_OK)
		{
			rc = depthStream.start();
			if (rc != openni::STATUS_OK)
			{
				printf("Viewer: Couldn't start depth stream:\n%s\n", openni::OpenNI::getExtendedError());
				depthStream.destroy();
				throw std::runtime_error("Couldn't find depth stream failed");
			}
		}
		else
		{
			printf("Viewer: Couldn't find depth stream:\n%s\n", openni::OpenNI::getExtendedError());
			throw std::runtime_error("Couldn't find depth stream failed");
		}

		//表示用変数
		cv::Mat colorImage, depthImage;

		//本処理
		while (1)
		{
			//フレームの更新処理
			openni::VideoFrameRef colorFrame, depthFrame;

			//更新されたフレームの取得
			colorStream.readFrame(&colorFrame);
			depthStream.readFrame(&depthFrame);

			//フレームデータから画像データへ変換する
			ChangeColorStream(colorFrame, colorImage);
			ChangeDepthStream(depthFrame, depthImage);

			if (!colorImage.empty()) cv::imshow("COLOR", colorImage);
			if (!depthImage.empty()) cv::imshow("DEPTH", depthImage);

			int key = cv::waitKey(1);
			if (key == 'Q' || key == 'q')
			{
				break;
			}
		}


		return 0;
	}
	catch (std::exception& e)
	{
		cerr << openni::OpenNI::getExtendedError() << "\n";
		PleaseBottom();
	}
}
```

- 起動する前に、先ほどPrimeSenseにコピーした「OpenNI2（Driversが入っていたフォルダ）」をプログラムがあるところにコピーします（main.cppのある場所）。
- 起動すると、カラー画像と深度画像が表示されているかと思います。

# NiTE2を使ったプログラム
- NiTE2を使ってユーザートラッキング
- こちらもプログラムのあるフォルダに「PrimeSenseのSample/Bin」にあった「OpenNI2」と「NiTE2」というフォルダをコピーしてきます。

```c++:main.cpp
// Windowsの場合はReleaseコンパイルすると
// 現実的な速度で動作します。

using namespace std;

#include "openni2.h"
#include "opencv.h"
#include <iostream>

void PleaseBottom()
{
	system("pause");
}

int main(int argc, char **argv)
{
	try
	{
		//NiTEを初期化する
		nite::Status rcn = nite::NiTE::initialize();

		//ドライバの状態
		openni::Status rc = openni::STATUS_OK;

		//デバイス情報
		openni::Device device;

		//センサから得られたRGB-Dデータ
		openni::VideoStream depthStream, colorStream;

		//ANY_DEVICEならPCに接続されているデバイスを読み込む
		const char* deviceURI = openni::ANY_DEVICE;

		//OpenNIの初期化を行う
		rc = openni::OpenNI::initialize();
		printf("After initialization:\n%s\n", openni::OpenNI::getExtendedError());

		//デバイスを読み込めたかどうか
		rc = device.open(deviceURI);
		if (rc != openni::DEVICE_STATE_OK)
		{
			printf("Viewer: Device open failed:\n%s\n", openni::OpenNI::getExtendedError());
			openni::OpenNI::shutdown();
			throw std::runtime_error("openni::Device::open() failed");
		}

		//カラー画像の初期化を行う
		rc = colorStream.create(device, openni::SENSOR_COLOR);
		if (rc == openni::STATUS_OK)
		{
			rc = colorStream.start();
			if (rc != openni::STATUS_OK)
			{
				printf("Viewer: Couldn't start color stream:\n%s\n", openni::OpenNI::getExtendedError());
				colorStream.destroy();
				throw std::runtime_error("Couldn't start color stream failed");
			}
		}
		else
		{
			printf("SimpleViewer: Couldn't find color stream:\n%s\n", openni::OpenNI::getExtendedError());
			throw std::runtime_error("Couldn't find color stream failed");
		}

		//ユーザートラッカー変数
		nite::UserTracker userTracker;
		rcn = userTracker.create();
		if (rcn != nite::Status::STATUS_OK)
		{
			printf("Viewer: Device open failed:\n\n");
			throw std::runtime_error("userTracker.create(&device) failed");
		}

		//表示用変数
		cv::Mat colorImage, depthImage;

		//ユーザの座標データ
		nite::Point3f userPositions[15][15];

		//本処理
		while (1)
		{
			//フレームの更新処理
			openni::VideoFrameRef colorFrame, depthFrame;

			//更新されたフレームの取得
			colorStream.readFrame(&colorFrame);
			//depthStream.readFrame(&depthFrame);

			//フレームデータから画像データへ変換する
			ChangeColorStream(colorFrame, colorImage);
			//ChangeDepthStream(depthFrame, depthImage);

			nite::UserTrackerFrameRef userFrame;
			userTracker.readFrame(&userFrame);

			//深度画像を表示できるようにする
			ChangeUserTrackerStream(userFrame, depthImage);

			const nite::Array<nite::UserData> &users = userFrame.getUsers();

			for (int i = 0; i < users.getSize(); ++i)
			{
				const nite::UserData& user = users[i];

				//新しく検出したユーザなら、トラッキング開始
				if (user.isNew())
				{
					userTracker.startSkeletonTracking(user.getId());
				}

				//すでに検出したユーザの処理
				//消失していないならスケルトン表示
				if (!user.isLost())
				{
					DrawSkeleton(colorImage, userTracker, user);
					DrawSkeletonPoint(colorImage, userTracker, user);
				}

				GetUserSkeletionPosition(userFrame.getDepthFrame(), userTracker,
					user, userPositions[i]);

			}

			cerr << "Head = " << userPositions[0][0].x << "," << userPositions[0][0].y << "," << userPositions[0][0].z << endl;
			cerr << "Head = " << userPositions[1][0].x << "," << userPositions[1][0].y << "," << userPositions[1][0].z << endl;

			if (userPositions[0][nite::JOINT_LEFT_HAND].y < userPositions[0][nite::JOINT_HEAD].y)
				cv::putText(colorImage, "Hand Up", cv::Point(0, 5), cv::FONT_HERSHEY_COMPLEX, 0.7, cv::Scalar(0, 255, 255), 2);

			openni::VideoFrameRef vfr = userFrame.getDepthFrame();
			int depthDist;
			cv::Point2i cent(colorImage.cols / 2, colorImage.rows / 2);
			GetDepthData(vfr, cent, depthDist);
			cv::circle(depthImage, cent, 2, cv::Scalar(255), -1);
			cerr << depthDist << "\n";
			if (!colorImage.empty()) cv::imshow("COLOR", colorImage);
			if (!depthImage.empty()) cv::imshow("DEPTH", depthImage);

			int key = cv::waitKey(1);
			if (key == 'Q' || key == 'q')
			{
				break;
			}
		}


		return 0;
	}
	catch (std::exception& e)
	{
		cerr << openni::OpenNI::getExtendedError() << "\n";
		PleaseBottom();
	}
}
```

- 起動すると、骨格トラッキングのプログラムが走ります。
- これで、起動終わり！！

# サンプルコード
- 最後に、GitHubに今回のサンプルコードを載せておきます。
- 使用する際は、各自の責任でお願いします。
- [Sample code](https://github.com/chimamedia/Xtion2Sample)