ASUSのXtionPRO LiveからRGB画像とDepth画像を取得し,
OpenCVでちょこっと処理し,結果をC#で表示するプログラム.
今回はC++で基本的な作業を行なうDLLを作成し,C#でGUI部分を作成する.
- 基本DLL.OpenCVやOpenNIなどの外部ライブラリを使用する.
- プロジェクト名:XCapBase
- ソースファイル:XtionCaptureBase.cpp
- ヘッダファイル:XtionCaptureBase.h
- ラッパークラスDLLその1.C++として,1のライブラリをくるみ,入出力のみ行う.
- プロジェクト名:XCapWrapper
- ソースファイル:XCaptureWrapper.cpp
- ヘッダファイル:XCaptureWrapper.h
- ラッパークラスDLLその2.C++/CLIとして,2のライブラリをくるむ.C#から参照可能.
- プロジェクト名:XCWrapCLI
- ソースファイル:XCWrapCLI.cpp
- ヘッダファイル:XCWrapCLI.h
- C#のWindows Formプログラム.
1~3はDLLとして作成する.(Win32コンソールアプリケーションを選択し,ウィザードの最後でDLLにチェックを入れる)
いろいろと無駄があるかもしれないけれど,とりあえず動くのでOK.
XCapBase : 基本処理クラス
このクラスの中であれば,OpenCVやOpenNIの関数を自由に使える.
ただし,public 関数には,独自型を使わないようにしておく.
XtionCaptureBase.h
#ifdef XCAPBASE_EXPORTS
#define XCB_DECLSPEC __declspec(dllexport) // DLLにexport
#else
#define XCB_DECLSPEC __declspec(dllimport) // DLLをimport
#endif
#include <XnCppWrapper.h>
#include <opencv2/opencv.hpp>
class XCB_DECLSPEC CXtionCaptureBase
{
public:
CXtionCaptureBase(void);
~CXtionCaptureBase(void);
void capture(int *w, int *h, char **rgb, char **dmap);
private:
xn::Context m_context;
xn::ImageGenerator m_image;
xn::DepthGenerator m_depth;
cv::Mat m_RGBmat;
cv::Mat m_Depthmat;
};
XtionCaptureBase.cpp
#include "XtionCaptureBase.h"
#pragma comment(lib,"openNI.lib")
#ifdef _DEBUG
#pragma comment (lib, "opencv_core242d.lib")
#pragma comment (lib, "opencv_highgui242d.lib")
#pragma comment (lib, "opencv_imgproc242d.lib")
#else
#pragma comment (lib, "opencv_core242.lib")
#pragma comment (lib, "opencv_highgui242.lib")
#pragma comment (lib, "opencv_imgproc242.lib")
#endif
using namespace xn;
using namespace cv;
CXtionCaptureBase::CXtionCaptureBase(void)
{
m_context.Init();
XnMapOutputMode mapMode;
mapMode.nXRes = 640;
mapMode.nYRes = 480;
mapMode.nFPS = 30;
m_image.Create(m_context);
m_image.SetMapOutputMode(mapMode);
m_depth.Create(m_context);
m_depth.SetMapOutputMode(mapMode);
m_context.StartGeneratingAll();
}
CXtionCaptureBase::~CXtionCaptureBase(void)
{
}
void CXtionCaptureBase::capture(int *w, int *h, char **rgb, char **dmap)
{
ImageMetaData imageMD;
DepthMetaData depthMD;
m_context.WaitAnyUpdateAll();//wait and error processing
m_image.GetMetaData(imageMD);
m_depth.GetMetaData(depthMD);
m_depth.GetAlternativeViewPointCap().SetViewPoint(m_image);
//Image
Mat imageR(480, 640, CV_8UC3, (unsigned char*)imageMD.RGB24Data());
cvtColor(imageR, m_RGBmat, CV_RGB2BGR);
//Depth
Mat depth16(480, 640, CV_16UC1, (unsigned short*)depthMD.Data());
convertScaleAbs(depth16, m_Depthmat, 0.7, -300); //16bit -> 8bit
*w = 640;
*h = 480;
*rgb = (char *)m_RGBmat.data;
*dmap = (char *)m_Depthmat.data;
}
XCapWrapper : C++のラッパークラス
プロジェクトの「参照」に,XCapBaseを追加する.
XCapWrapper.h
#ifdef XCAPWRAPPER_EXPORTS
#define XCW_DECLSPEC __declspec(dllexport) // DLLにexport
#else
#define XCW_DECLSPEC __declspec(dllimport) // DLLをimport
#endif
class XCW_DECLSPEC CXCaptureWrapper
{
public:
CXCaptureWrapper(void);
~CXCaptureWrapper(void);
void capture(int *w, int *h, char **rgb, char **dmap);
};
XCapWrapper.cpp
#include "XCaptureWrapper.h"
#include "../XCapBase/XtionCaptureBase.h"
CXtionCaptureBase *cxcb;
CXCaptureWrapper::CXCaptureWrapper(void)
{
cxcb = new CXtionCaptureBase();
}
CXCaptureWrapper::~CXCaptureWrapper(void)
{
delete cxcb;
}
void CXCaptureWrapper::capture(int *w, int *h, char **rgb, char **dmap)
{
cxcb->capture(w, h, rgb, dmap);
}
XCWrapCLI : C++/CLIのラッパークラス
「構成プロパティ」の「全般」で共通言語ランタイムサポート(/clr)を選択する.
プロジェクトの共通プロパティ「参照」に「System.Drawing」と「XCapWrapper」を追加.
キャプチャした画像をBitmapにして返す.
参考サイト:
C++/CLIで動かしているOpenCVのCvImageをC#.NETのBitmapオブジェクトにして読み込む
ここで取り出そうとするビットマップ画像の幅は,4の倍数じゃないとエラーが起こる.
XCWrapCLI.h
#pragma once
using namespace System::Drawing;
namespace XCWrapper {
public ref class XCWrapCLI
{
public:
XCWrapCLI(void);
void capture(void);
System::Drawing::Bitmap^ getBitmap(void);
System::Drawing::Bitmap^ getDepthmap(void);
private:
System::Drawing::Bitmap^ bitmap;
System::Drawing::Bitmap^ depthmap;
};
}
XCWrapCLI.cpp
#include "XCWrapCLI.h"
#include "../XCapWrapper/XCaptureWrapper.h"
namespace XCWrapper {
CXCaptureWrapper *xcap;
XCWrapCLI::XCWrapCLI(void)
{
xcap = new CXCaptureWrapper();
}
void XCWrapCLI::capture()
{
int w, h;
char *prgb, *pdmap;
xcap->capture(&w, &h, &prgb, &pdmap);
//RGB Image
this->bitmap = dynamic_cast<System::Drawing::Bitmap^>(
gcnew System::Drawing::Bitmap(w, h, w*3,
System::Drawing::Imaging::PixelFormat::Format24bppRgb,
static_cast<System::IntPtr>(prgb)));
//DepthMap
this->depthmap = dynamic_cast<System::Drawing::Bitmap^>(
gcnew System::Drawing::Bitmap(w, h, w,
System::Drawing::Imaging::PixelFormat::Format8bppIndexed,
static_cast<System::IntPtr>(pdmap)));
System::Drawing::Imaging::ColorPalette^ pal = this->depthmap->Palette;
for(int i=0; i<256; i++) pal->Entries[i] = Color::FromArgb(255,i,i,i);
this->depthmap->Palette = pal;
}
System::Drawing::Bitmap^ XCWrapCLI::getBitmap(void)
{
return this->bitmap;
}
System::Drawing::Bitmap^ XCWrapCLI::getDepthmap(void)
{
return this->depthmap;
}
}
XtionCapture : C#のプログラム
スタートボタンを押すと,キャプチャ画像を(スレッドを使って)表示する.
mainForm.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using XCWrapper;
namespace XtionCapture
{
public partial class mainForm : Form
{
private Thread trd;
private bool moveFlag;
XCWrapCLI xcap;
public mainForm()
{
InitializeComponent();
moveFlag = false;
xcap = new XCWrapCLI();
}
private void mainForm_Load(object sender, EventArgs e)
{
trd = new Thread(new ThreadStart(this.ThreadTask));
trd.IsBackground = true;
trd.Start();
}
private void ThreadTask()
{
while (true)
{
if (moveFlag)
{
xcap.capture();
pictureBox1.Image = xcap.getBitmap();
pictureBox2.Image = xcap.getDepthmap();
}
Thread.Sleep(30);
}
}
private void StartButton_Click(object sender, EventArgs e)
{
moveFlag = true;
}
private void StopButton_Click(object sender, EventArgs e)
{
moveFlag = false;
}
}
}