Windows Phone PhotoCameraの画像取得と編集

UX-TVに対する質問(On Twitter)を見ていて、そういえばMSCのセッションのフォローが中途半端に終わっていたなと思い出し、急遽ポスト。PhotoCameraの画像データを保存したり、画像を編集したりする辺りの基本コードを紹介します。

PhotoCameraを使う場合には、以下のコードで先ず初期化しておきます。

using Microsoft.Devices;

・・・

// クラスメンバー変数としてカメラのインスタンスを宣言
private PhotoCamera photoCamera;

・・・

      // カメラインスタンス作成
      photoCamera = new PhotoCamera(CameraType.Primary);
      // カメラ初期化完了時にコールされるメソッドを登録
      photoCamera.Initialized += new EventHandler<CameraOperationCompletedEventArgs>(photoCamera_Initialized);
      // イメージキャプチャ時にコールされるメソッドを登録
      photoCamera.CaptureImageAvailable += new EventHandler<ContentReadyEventArgs>(photoCamera_CaptureImageAvailable);

他にサムネイルイメージキャプチャ完了を知らせるイベントもありますが、ここでは省略します。それからフラッシュ(というかライトというか)の点灯は、Initializedイベントが発火した後(つまり初期化完了以降)でしか制御できないというTipsをはさみながら、次にイメージキャプチャ時の説明に移ります。

ボタンのクリックイベントハンドラやスクリーンのタップイベントハンドラなどで、

      photoCamera.CaptureImage();

とCaptureImage()メソッドをコールすると、画像キャプチャー機能が働き、終わったら、photoCamera_CaptureImageAvailableがコールされます。

void photoCamera_CaptureImageAvailable(object sender, ContentReadyEventArgs e)
{
    // このハンドラをコールするスレッドは外部スレッドなので、UIを操作する場合は、以下の様にDispatcherにデレゲート
    Deployment.Current.Dispatcher.BeginInvoke(delegate()
    {   
        BitmapImage photoImage = new BitmapImage();
        photoImage.SetSource(e.ImageStream);

上の図のように、BitmapImageインスタンスを一つ作り、SetSourceメソッドでイベント引数eで渡されたImageStreamをソースとして登録します。
これで、プログラム上で、PhotoCameraが撮った画像を、BitmapImageとして扱うことが出来るようになります。

更に、画像にテキストや図形を重ねたい場合は、

        Image image = new Image();
        image.Source = photoImage;
        Canvas videoCanvas = new Canvas();
        videoCanvas.Children.Add(image);

と、今度はImageクラスのインスタンスを一つ作って、photoImageを渡し、Canvasを一つ作って、作成したimageをChildrenに加えれば、Canvasに書き込まれたことになります。後は、このCanvasにテキストや図形を貼り付けていけば、写真と図を組み合わせた画像が出来上がります。例えば、

        // デバイスの姿勢を記録
        TextBlock tbDir = new TextBlock();
        tbDir.Text = String.Format("Direction:Yaw={0:0.000},Pitch={1:0.000},Row={2:0.000}", motion.CurrentValue.Attitude.Yaw, motion.CurrentValue.Attitude.Pitch, motion.CurrentValue.Attitude.Roll);
        tbDir.FontSize = 20;
        Canvas.SetTop(tbDir, 360);
        Canvas.SetLeft(tbDir, 10);
        videoCanvas.Children.Add(tbDir);

と書けば、Motionセンサーから取得した姿勢情報を、写真画像に重ねられます。普通にCanvasに描き込む要領で、RectangleやPolygonを描いていけば図も重ねることが出来ます。このコードでは、videoCanvasは、コードの中で作成していますが、XAMLで、UIの一部として宣言していれば、写真画像とテキストや図形を重ねた絵を表示可能です(ま、あたりまえですね)

最後に、作成した画像をファイルに保存する方法を説明します。

        WriteableBitmap photoBitmap = new WriteableBitmap(videoCanvas,null);
        photoBitmap.Invalidate();

写真画像、テキスト、図形、その他諸々を重ねたキャンバスをソースとして、WriteableBitmapインスタンスを作成します。
ここで、Invalidate()メソッドをコールするのがミソ。このメソッドをコールして初めてキャンバスのChildrenに追加したエレメントが描画されるようなので、これをやらないと、何も画像は出来上がりません。絶対に忘れてはいけないメソッドコールです。

後は、

        // テンポラリにJPEGファイルを作成して、IsolatedStorageに保存
        string fileName = String.Format("arsample{0}.jpg", DateTime.Now.ToString("yyyyMMdd_HHmmss"));
        IsolatedStorageFile myStorage = System.IO.IsolatedStorage.IsolatedStorageFile.GetUserStoreForApplication();
        IsolatedStorageFileStream tmpFileStream = myStorage.OpenFile(fileName, FileMode.Create, FileAccess.Write);
        photoBitmap.SaveJpeg(tmpFileStream, photoBitmap.PixelWidth, photoBitmap.PixelHeight, 0, 100);

の流れで処理をすれば、ファイルの出来上がり。ここではIsolatedStorageに蓄積していますが、別の場所でも全然構いません。

AR動画を表示しながら、画像キャプチャを行い、位置情報やその他の情報を書き込むサンプルは、https://mangorealarcamera.codeplex.com/ で公開しているので、参考にしてくださいね。