要求を書いて、ユースケース書いて、分析を行ってドメインモデルを構築し
追加要求を出して、さらに分析まで行いました。
テクニカルアーキテクチャを詰めていきます。
言語はC#, さらに対象は Windows Store Application, と Desktop WPF Application とします。
PortableClassLibrary として作ります。
まずは調査ですね。
イメージを作成してビューに表示する訳ですが、そこら辺を具体的にはっきりさせます。
まずダミーで PortableClassLibrary から Byte 配列を提供するようにして
その Byte 配列をビューに表示するというものを作って動作確認までとってみます。
調べていて気になった点をメモ
Image の Source に渡す D3DImage を CopyBackBuffer で RenderTargetBitmap として取得して CopyPixels で byte 配列を取得…可能?
そんなことせずとも DrawingVisual を使って Canvas に D3DImage を DrawImage 出来ることを確認。
DrawingVisual にも DrawImage で Bitmap を渡せるか?
DrawingVisual から Byte 配列を取るには RenderTargetBitmap に Render して CopyPixels する?
まずはこれができないと始まりません。
そして出来ないはずはありません。
テスト手順を詳細に示します。
WPFアプリケーションを作成、そこにCanvasコントロールを配置したものを次に示します。
実行すると次のウィンドウが表示されます。
まず、Canvas に何か表示してみましょう。
名前空間 source を定義して、Canvas に source にある MyVisualHost という名前のコンテンツを配置しています。
051話で学んだWPFの表示ツリー(VisualTree)というやつです。
(よくわからない人はエッセンシャルWPFを読むと理解できると思います。)
実行すると次の結果が得られます。
矩形、テキスト、サークルをクリックするとその図形だけ不透明度が変化します。
肝心の MyVisualHost のコードは次のサンプルと全く同じです。
DrawingVisual を使用したヒット テストのサンプル
コードの詳細は次のリンク先で説明されています。
読みました?
DrawingVisual について基本を理解出来ました?
では先へ進めます。
画像ファイルからbyte配列を作ってみます。
一応動くみたいです。
画像ファイルからピクセルデータのbyte配列を取得出来ました。
ピクセルデータのbyte配列をキャンバスに描画してみます。
上記コードについて SetBytes は byte 配列から Bitmap を作成
WriteBytes は作成された Bitmap の領域に byte 配列の情報を上書き
GetDrawingVisual は Bitmap から DrawingVisual を取得
テストコードはこちら
ファイルからビットマップを作り、バイト配列を取り、バイト配列でビットマップを作り直し、バイト配列で上書きし、DrawingVisual を取得しています。
この DrawingVisual を Canvas に対して AddLayer しています。
AddLayer の詳細は以下のとおり
試しに実行した結果が次のとおり。
テストに使っている絵はしばらく前に描いたものです。
あまり気にしないでください。
表示されている領域をクリックすると、不透明度が変化し、後ろに配置しているレイヤー(表示要素)が透けて見えるようになります。
表示した領域以外を選択した場合はヒットテストに不合格になるので、不透明度が変化しないことも確認しました。
で、大事なことが DrawingVisual から byte 配列が取り出せるかということです。
調べてみます。
うーん、少しパフォーマンスが落ちそうですし、フォーマットが Pbgra32 に固定されてしまいますが
次のコードで Visual から byte 配列を取り出せました。
かゆい!
visual から bitmap を作るには一度 randerTargetBitmap に rander してから
再作成(低速)の工程を通らないとデータの同期が取れない。
かなりやっつけですが、バイト配列⇔ビジュアルを簡単にかつ考えられる限り高速にやりとりするビットマップを作ってみました。
バイト配列をビジュアルに表示し、ビジュアルの内容をバイト配列に変換
これが行えるようになりました。
これでキャンバスの情報を元に画像処理、画像処理の結果をキャンバスに反映
が具体的に実現できると言えるようになりました。
さて、Desktop WPF でのPixel操作はこれで良いと思います。
ShaprDX の WPF サンプルの描画先は D3DImage なので DrawingVisual に Rander 可能ですし、やりたいことはできるでしょう
不安要素は Store でもこのコードが流用できるのかどうかです。
さっそくPortableClassLibraryに組み込んで、調べてみましょう。
無理でした。
基本的に Desktop WPF, Store どちらも使えるようにするには
どちらにも依存しないようにプリミティブ型だけを利用したアセンブリが必要になります。
つまり上記のクラスは WPF 専用のファイルになります。
とりあえず同様のことが Store でできるのか調べてみましょう。
できそうですが、コードに大きな修正を入れなくてはなりません。
これはひどい!
ストアアプリを作るには .NET フレームワークすべてを切り離さなくてはなりません。
ここまで設計してきた部分は WPF 用として、デバッグやプラグイン開発はすべて Desktop で行なうこと
完成したプラグインを Store で利用することで同じ結果が得られること
これを開発スタイルとして考えていきましょう。
そうすることで、機能の切り分けがはっきり見えてきます。
正直なところ、Store アプリ開発って設計面で結構たいへん。
で、まだ Store アプリでバイト配列を使ったピクセル値操作ができていないので、調べていきます。
Store アプリで画像処理をする場合はデモ用として、基本的にカメラからの入力を処理します。
そんな時に使うのが CameraCaptureUI と MediaCapture です。
まずは簡単な CameraCaptureUI を試してみます。
XAML には次に示す通り、Image を名前付きで配置します。
Store アプリのサンプルで示した通りですが、次のコードでOSが良い感じにカメラをサポートして
UIを表示してくれます。
カメラの切り替え、カメラの設定、撮影など、これだけのコードで撮影に関してはすべてやってくれますが
今回目的とする画像処理を挟むフェーズが入り込む余地がなく、これではうまくいきません。
…と思ったけど、コード見ていると介入できそうですね。
Bitmap を WriteableBitmap にして、線でも引いてみますか。
結果、無理でした。
CameraCaptureUI は便利ですが、拡張不可。
つまり画像処理には使えない。
ちなみに試したコードがこちら↓
これで毎フレーム更新されること、Image コントロールはサイズ固定なのに勝手に
全画面になることなど、正直ドキュメントのどこを読めばこのへんの動きが理解できるのか不明
どこかのサイトに、拡張不可との記述もあったし、CameraCaptureUI に対する調査は打ち切ることにした。
残るは MediaCapture のみ、調べていきましょう。
XAML には次の通り
で、プレビュー表示するだけならば次の通り
確かにプレビュー表示されるんだけど、毎フレーム処理をはさむ場合は
どうやってそれをハンドリングすれば良いんでしょう?
細かいことは上記にすべて書かれていました。
デバイスを選んで、準備、プレビュー、グレースケールでプレビュー、録画(グレースケールで録画)
録画したファイルを再生、これが出来るようになります。
要約すると、私たちがやりたいフレームごとにイベントをハンドルして画像を操作しつつプレビュー表示するという使い方は
サポートされていないということです。
カメラ画像は単に無加工でプレビューするだけしか出来ません。
(画像処理を行おうとする人にとっては、なんだそれ使えないなと思うことでしょう。)
MediaCapture クラスには使えそうなインタフェースは用意されていませんでしたが
CaptureElement にはどうでしょうか?
…調べましたけど、ダメですね。
おや?次のトピックが良い感じに jpeg 形式で bitmap を取得していますね。
PrepareLowLagPhotoCaptureAsync
参考に動かしてみます。
撮影こそ成功しますが、カメラを起動して、撮影して、表示するまで 0.5 秒ほど要しています。
これはそのままは使えませんね。
プレビューと比べると大変低速ですが、メモリ上に変換したデータを写真として書き込む処理を何度も繰り返すことで
やりたかったことが出来るようになりそうです。
ちょっと調べてみます。
できました、FPS は 15 程度とかなりひどい状況ですが、リアルタイムでカメラの Byte 配列を取得できています。
ネット上には、もっと効率の良い方法はありませんかと書き込みが多数書かれていますが
これといった回答が記述されていませんでした。(2014.04現在)
そのうち解決する?
ひとまず、Store アプリでもカメラから毎フレーム?の画像データの Byte 配列を得ることが確認できました。
かなり処理に時間を要しますが…
まだ Desktop WPF でカメラから毎フレーム画像データの Byte 配列を得ることを確認していませんでした。
こちらも方法を示します。
情報は豊富ですが、何を選んだら良いのやら
Windows Multimedia Video Capture
C# and VB.NET Webcam Library and Sample
Webcamera, Multithreading and VFW
とかがネットで検索すると引っかかりますね。
最初に示した Windows Multimedia Video Capture のサンプルを触ってみます。
問題なさそう?
Windows 8.1 Pro のタブレットPCでも試してみます。
Microsoft .NET Framework 3.5 Service Pack 1 がビルド、実行に必要でした。
案内してもらえたので迷わずインストール完了
実行を確認
タブレットに備え付けのフロント、リアカメラを選択、それぞれの画像をリアルタイムで表示できました。
問題なさそうです。
では3日前に家に届いたIntel の CREATIVE Interactive Gesture Cameraで試してみます。
結果、真っ黒な画面が表示されるだけで、うまく表示されませんでした。
そんな時ひらめいたのですが、2014年1月の International CES で
Intel RealSense テクノロジーとしてお披露目された(βリリースは2013年から)
Intel Perceptual Computing SDK 2013 を使ってみてはどうでしょうか?
実にタイムリーですし、正攻法ですね。
というか世界の技術革新速度がすごすぎる!こんな小型で高精度な TOF 距離画像センサが 1万円台で手に入るなんて…
正直大学院にいた 24歳のころはこの時代が来るころには自分は 50歳くらいになると思っていたよ。(現在 28歳です…)
サンプルの capture_viewer で確認できました。
備え付けのカメラや USB で繋げた Web カメラ、CREATIVE カメラのカラーと深度画像など何でも取得できますね。
商用利用可能ライセンスでもありますので、組み込んで世に送り出しても問題なし。
色々と調べて勿体ないことしたと思いますが、これに決めました。
実装の詳細を見てみます。
ぱっと見 C++ で提供されているライブラリ群ですね。
これを CLR でラップするプロジェクト libpxcclr があり、C# のプロジェクトはこれを参照しています。
capture_viewer 自体は C++ のサンプルしか提供されていませんが、使い方さえ学べば C# で簡単に実現できるのでは?
インストールすると $(PCSDK_DIR) 環境変数が作成されます。
ディフォルトでは Program Files (x86)\Intel\PCSDK です。
サンプルプロジェクトはこれからの相対パスで include や lib を見ています。
doc フォルダにリファレンスが置いてありますので一読します。少々お待ちください。
読みました。
初見の感想と同じで C++ で全アクセスできて C# でもほとんど同じことが出来るとのこと
ドキュメントに PXC[M]Capture と [M] が付けば C# でも用意されているよと、そういうことらしいです。
まず PXCSession を作成して、エラーコードが PXC_STATUS_NO_ERROR でないことを確認後
そのセッションを介して I/O モジュールやアルゴリズムモジュールにアクセスします。
セッションは一つまたは複数インスタンス化できますが、セッション間は通信できないものだそうです。
(ふむふむ)
セッションを作って、バージョン番号を表示するコードは次のとおり
セッションという名前から分かる通り、まずはじめにセッションを作ること、
すべてのアクセスが終わってからセッションを開放すること
また、セッションを扱う上で、次の決まりを守ってほしいとのこと
DynamicCast を使うことと Dispose で解放するということです。
で、そのセッションを作ると同時にモジュールやアルゴリズムがロードされて
それらをすべて列挙するサンプルコードが次のとおり
ほかにも、自分で作ったモジュールを明示的にセッションにロードすることができるとのこと
モジュールはインスタンス化しないことには始まりません。
次のようにモジュールを識別する CUID を指定してインスタンスを手に入れるのが一般的な方法です。
今度はオーディオ入力(マイク)やビデオ入力(Webカメラなど)のデバイスを列挙、情報表示する方法を示します。
次のようにセンサ-ビデオキャプチャー用のデバイスモジュール情報を列挙、そこからモジュールインスタンスを作成して
さらにモジュールに対してデバイス情報を列挙しています。
どうやってデバイスを検出するかなどはこのライブラリが全部やってくれるので、使う側としてはうれしいですね。
数行書いて列挙するだけです。(簡単)
情報を得るだけではなく、デバイスのプロパティも操作できます。
下記の例はカラーと深度画像のFOV(画角)を横軸、縦軸で求めるもの
距離計測の一番低い信頼できる値と飽和する値を求めるもの(計測範囲で良いのかな?)
カラーカメラの自動露出を切って、手動で露出値を設定しているものです。
プロパティのIDを与えるだけで取得、設定が Query, Set Property で可能ということが示されています。
(プロパティ一覧がどこかにほしいな。)
デバイスからは複数の情報が流れてくる訳ですが、それら情報の流れを stream と呼びます。
どんな stream がデバイスから流れてきているか列挙する方法が次のとおり。
VideoStream に関しては ImageType でカラー画像なのか距離画像なのか判断できるとのこと。
device.CreateStream でその stream インスタンスを生成できて
QueryProfile でプロパティを取得できるとか
capture_viewer で解像度とフレームレートを列挙していたが、それを列挙取得するサンプルコードが
CHMドキュメントに示されています。
だんだんテクニカルアーキテクチャから、Intel Perceptual Computing SDK のドキュメントまとめになりつつある
話を切ります。
2014/03/27 初記。
2014/04/20 更新。