ページ

2013年8月23日金曜日

InPortでのデータ受信イベントについて

OpenRTMで開発してる際、InPortにデータが到着したことを検知してある処理を行いたいと思い、データ受信イベントハンドラを記述する方法を探していました。

しかし、OpenRTMのInPortのリファレンスのところにあるイベントハンドラにはそれっぽいものがありません…
散々探し回ってついにやりたいことを実現することはできたのですが、調べていく中でデータポートまわりの自分の理解に大きな勘違いがあったので、それについてメモっておきたいと思います。

大きな勘違いは、Portによってデータのやり取りがなされているわけではない、ということです。
実はInPort/OutPortはポート間の接続を管理しているのみで、実際のデータの送受信はConnectorによって行われています。具体的に図で描くと、

のようになっています。

そのため、データが到着した時に実行するメソッドを登録する先はPortではなく、Connectorになります。この登録にはInPortにあるメソッド、addConnectorDataListenerを用いて行います。

m_DataInputIn.addConnectorDataListener(ConnectorDataListenerType::ON_RECEIVED, dataReceivedListener);

これを踏まえて、今回はデータ転送遅延を計測するコンポーネントを作ってみました。DLはこちらからどうぞ。
InPortからデータが到着した時点で自分のタイムスタンプとデータに付随しているタイムスタンプを比較し、差の時間を表示します。
また、Outportからはコンフィグで設定したサイズのデータが1sおきに送信されます。

2013年8月20日火曜日

Intel Perceltual Computing SDKのPXCMImage.ImageDataからWriteableBitmapに直接データをコピーする

WPFでPerC SDKを利用したアプリケーション開発を行なっているのですが、その際に画面への表示にはImageSourceとしてWriteableBitmapを利用するのが便利です。PXCMImage.ImageDataにはToWriteableBitmapという便利なメソッドが存在していて、これを使えば簡単に変換を行うことができます。

しかし、このメソッドでは1フレーム表示ごとにWriteableBitmapのインスタンスが作られ、しばらく溜まるとGCによって開放されるということが繰り返されるようで、メモリ使用量がのこぎりの刃のようになってしまいます。
実際のところそれでも良いのかもしれないのですが、メモリは明示的に開放しないと気が済まないたちなので、なんとかして解決したいところです…

そこで、ToWriteableBitmapの使用を諦め、PerC SDKのPXCMImage内のメモリを直接触って、WriteableBitmapにコピーすることにしました。

--

手順は2段階に分かれていて、まずPXCMImageからbyte配列にデータをコピーします。
PXCMImage.ImageInfo info;
image.QueryInfo(out info);

PXCMImage.ImageData data;
image.AcquireAccess(PXCMImage.Access.ACCESS_READ, PXCMImage.ColorFormat.COLOR_FORMAT_RGB32, out data);

byte* p = (byte*)data.buffer.planes[0];
for (int i = 0; i < info.width * info.height * 4; i++)
    _buffer[i] = p[i];

image.ReleaseAccess(ref data);
この時、画像データにAcquireAccessするときにフォーマットを指定するのがポイントです。ここでRGB32を指定することで、1ピクセルあたり4バイトとして画像データにアクセスすることができます。
また、画像の先頭番地はplanes[0]にあるので、byte*に変換してアクセスします(unsafeを付ける必要があります)。

そして次に、コピーしたデータをUIスレッドの持つWriteableBitmapに書き込みます。
PXCMImage.ImageInfo info;
image.QueryInfo(out info);

var pw = (int)info.width;
var ph = (int)info.height;
var st = (int)info.width * 4;

var gs = _writeableBitmap;
var bf = _buffer;

//copy PXC image to image source
DispatcherHelper.BeginInvoke(new Action(() =>
    {
        gs.WritePixels(new Int32Rect(0, 0, pw, ph), bf, st, 0);
        RaisePropertyChanged("GestureImageSource");
    }));
一度バッファーに読み込んで、それをコピーするといった2段階に分けて処理を行っているのは、ImageSourceとなるWriteableBitmapはUIスレッドに存在していて、画像の更新を行うスレッドからはFreezeしない限りアクセス出来ないためです。

このようにすることで、WriteableBitmapのインスタンスを無駄に生成せずに画面表示を行うことが出来ました。

2013年8月12日月曜日

OpenRTMでの2台のPC間の遅延計測

OpenRTMを使って2台のPCにコンポーネントを配置し、データをやり取りするシステムを作ろうと思ってます。それに先立ち、データのやり取りにどれぐらいの遅延が発生するかを調べてみました。
結果から言うと、

1MB10MB100MB
同一プロセス内通信1ms17ms130ms
同一PC内通信6ms45ms320ms
PC間通信168ms265ms1150ms
※C++コンポーネントをVisualStudioでReleaseビルド
※PC間通信はWindows PC 2台をGigabitEthernetで直接接続
※RTMホスト側にNTPサービスを立ち上げ、時刻同期

のようになりました。
この際、ハマったのが、OpenRTMのタイムスタンプがシステム時刻に依存しているので、2台のPCの時刻を同期させなくては正しい結果が得られない点です。(当たり前ですが…)

そこで、RTMホスト側のPCにNTPサービスを立ち上げ

net start w32time

ファイアウォールのUDP123番に受信許可設定を行った上で、クライアント側からNTPで時刻の同期を行いました。

2013年8月6日火曜日

Eclipse CDT で git を使う

Eclipse CDT で git を使う機会があったのでメモしておきます

Help > Install new software から、work with に以下を Add

Name: egit
Location: http://download.eclipse.org/egit/updates

そして、出てきたリストからEclipse Git Team Provider を Intall
このプラグインはVisualStudioにも存在していて、そちらの方で使い方を学んだのでこれを選択しました。

しばらくしてインストール完了すれば、あとは Project Explorer からプロジェクトを右クリックして、Team以下のメニューからまるっと操作できます。

--

[追記]

Indigo(3.7.2)でインストールしようとしたときにGit Team Providerが見つからなかった問題に遭遇しました…
なので、明示的にIndigoに対応したバージョンのディレクトリ http://download.eclipse.org/egit/updates-1.3 を追加してやったところ、うまくできました。

2013年8月4日日曜日

Intel Perceptual Computing SDK を C# から使う

Intel Perceptual Computing SDK (PerC SDK)を使い機会があったので、備忘録的に使い方を纏めておきたいと思います。
ここに書いてあることは大体SDKのドキュメントのProgramming Guideを読めば書いてあります。じっくり学びたい方はこちらをどうぞ。
また、この記事に書いてあることは私個人の理解に基づいているため、大いに間違っている可能性があります。もし間違いを発見した方は、そっとコメントして頂けると非常に助かります…(╹◡╹)

SDKの構成


PerC SDKを使うにあたって、モジュール、デバイス、セッションといった聞きなれない言葉が出てきます。
まずはこれらについて説明しておきたいと思います。

PerC SDKの中には顔認識、ジェスチャー認識などの機能を持った複数のモジュールと、ジェスチャーカメラなどのハードウェア抽象化して管理する複数のデバイスが存在しています。
PerC SDKでは、すべてのデバイスとモジュールはSDKの中に隠蔽されており、ユーザーアプリケーションはそれらを直接扱うことはできず、全ての操作はSDK Interfaceを通して行われます。この時、デバイスやアルゴリズムを複数のアプリケーションで共有するため、アプリケーションとSDK Interfaceの間の処理を管理をセッションによって行なっています。


PerC SDKを使う際には、

  1. まず対話のためのセッションを取得し
  2. モジュールの実体化とデバイスの取得を行い
  3. それらを接続
してから処理を開始する必要があります。

C#から使う

最近はC#にどっぷり浸かりきっていて、出来ればC#の世界から一歩も外に出たくありません。
幸い、PerC SDKにはC#のポートが存在するので、ありがたくこれを利用させていただきましょう。


C#のポートはlibpxcclr.dllという名前のマネージドDLLで、PerC SDKのインストールディレクトリのframwork/CSharpの中にソースが入っています。環境に合わせてビルドしましょう。

libpxcclr.dllがビルドできたらプロジェクトの参照に加えて、以下の手順で処理を行います。

1.セッションの作成

PXCMSession session;
PXCMSession.CreateInstance(out session);

2.モジュールとI/Oデバイスの作成

PXCMGesture gesture;
session.CreateImpl<PXCMGesture>(PXCMGesture.CUID, out gesture);

UtilMCapture capture = new UtilMCapture(session);

3.設定と接続

PXCMGesture.ProfileInfo pi;
gesture.queryProfile(0, out pi);
capture.LocateStreams(ref pi.inputs);
gesture.setProfile(ref pi);

4.実行

PXCMImage[] images = new  PXCMImage[PXCMCapture.VideoStream.STREAM_LIMIT];
PXCMScheduler.SyncPoint[] syncPoints = new PXCMScheduler.SyncPoint[2];

while(true)
{
    PXCMImage.Dispose(images);
    PXCMScheduler.SyncPoint.Dispose(sps);

    capture.ReadStreamAsync(images, out sps[0]);
    gesture.ProcessImageAsync(images, out sps[1]);
    PXCMScheduler.SyncPoint.SynchronizeEx(sps);

    //ここで表示などの処理
}
PXCMImage.Dispose(images);
PXCMScheduler.SyncPoint.Dispose(sps);
またこの際、セッション、モジュール、デバイスはそれぞれPerC SDK内のネイティブコードとつながっているので、使い終わったら必ずDisposeする必要があります。
可能な限りusingステートメント内で使用するようにしましょう。

--

[追記]
PerC SDKには、これらの処理を簡潔に行うための、パイプラインが用意されているそうです…
単純に深度画像とジェスチャーを取得するのみなら、
_pipeline = new UtilMPipeline();
_pipeline.QueryCapture().SetFilter(deviceName)
_pipeline.EnableGesture(moduleName);
_pipeline.Init();
で初期化して、
_pipeline.AcquireFrame(true)

var gesture = _pipeline.QueryGesture();
var depthImage = _pipeline.QueryImage(PXCMImage.ImageType.IMAGE_TYPE_DEPTH);

//何らかの処理

_pipeline.ReleaseFrame();
で画像とジェスチャーを取得することができます。