Windows Phoneアプリケーション開発入門

第36回Mangoで追加されたカメラ機能を使ってみよう!(2)

はじめに

先日、世界初となるWindows Phone 7.5端末のIS12Tが発売されました。現時点(2011年9月12日)ではWindows Phone 7.5が搭載された端末はこの1台だけです。

IS12Tには、13.2Mの高画質カメラが搭載されています。前回カメラプレビューの表示に使ったプロジェクトを拡張して、フラッシュモードの設定、撮影解像度の取得と設定、静止画撮影を行うまでをご紹介させていただきます。

前回から機能を追加したサンプルプロジェクトを用意しましたので、是非ソースコードと一緒に記事を読み進めて頂ければと思います。

フラッシュモードを設定する

静止画撮影を行う上で必要なフラッシュモードの設定と撮影解像度の設定についてご紹介したいと思います。

標準カメラはデフォルトで、被写体の周りが暗い場合にカメラ撮影時にフラッシュを炊く設定になっています。フラッシュを発光させるかどうかの設定値はenumで定義されています。

Windows Phone OS 7.1で定義されているフラッシュモードは下記の通りです。

フラッシュモード 説明
FlashMode.On フラッシュは常にON
FlashMode.Auto 自動発光モード
FlashMode.Off フラッシュは常にOFF
FlashMode.RedEyeReduction 赤目補正モード

フラッシュモードがFlashMode.On、FlashMode.RedEyeReductionの場合、必ずフラッシュが発光します。FlashMode.Autoの場合にWindows Phone OSがフラッシュをさせるかどうかを判定し、必要であればカメラ撮影時にフラッシュが発光します。

FlashMode.Offの場合フラッシュは発光しません。

フラッシュモードを設定する

上記の表の通り、定義としてはフラッシュモードが4つありますが、それぞれのWindows Phone端末でハードウェア的にサポートしていない可能性があります。

そこでまずPhotoCamera.IsFlashModeSupportedメソッドを使用して、現在のカメラでそのフラッシュモードが使用できるのかを判定し、使用可能であればフラッシュモードの設定を変更するようにします。

// フラッシュモードの設定を行う
// カメラが赤目補正モードに対応しているか判定する
if (camera.IsFlashModeSupported(FlashMode.RedEyeReduction)) {
    camera.FlashMode = FlashMode.RedEyeReduction;
}

余談ですが、カメラのフラッシュを懐中電灯代わりに使用しているアプリケーションが公開されているのをMarketplaceにて発見しました。VideoSourceを設定せずプレビューを非表示の状態でフラッシュモードを常にONに設定することで実現しているようです。

// フラッシュを常に発光させる
camera.FlashMode = FlashMode.On;

フラッシュは静止画撮影時に一瞬発光させるものであるため、発光パーツの消耗や消費電力的にフラッシュを付けっぱなしにするのはあまりおススメできませんが、面白いアイディアですね。

静止画撮影を行う

静止画撮影を行うトリガーとしてApplicationBarに下図のように撮影用のメニューアイテムを追加しました。

画像

MainPage.xamlのApplicationBar.MenuItemsにApplicationBarMenuItemを追加しています。追加したメニューアイテムには、静止画撮影のトリガーとなるClickイベントのハンドラを設定しています。

<!-- アプリケーションバー -->
<phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True" 
        IsMenuEnabled="True" Mode="Minimized">

        <shell:ApplicationBar.MenuItems>
            <shell:ApplicationBarMenuItem 
                x:Name="menuItemTakePicture" 
                Text="Take Picture" 
                Click="menuItemTakePicture_Click" />
        </shell:ApplicationBar.MenuItems>

    </shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>

ここでトリガーとして用意したmenuItemTakePicture_Clickメソッドがコールされた時、静止画撮影を行うように実装してみましょう。

静止画撮影の解像度の設定

Windows Phone OSを搭載するためのハードウェア要件として、5Mピクセル以上のカメラを搭載しなければいけないことになっています。

私の持っているHTC Mozartは最大8Mピクセルまでしか撮影できませんが、IS12Tは13Mピクセルまで撮影することが可能です。機種によって搭載されているカメラセンサーは異なり、当然撮影可能な解像度も違ってきます。

実行中のWindows Phone端末で撮影可能な解像度を得るためには、PhotoCamera.AvailableResolutionsプロパティからSizeの配列を取得します。

// 静止画撮影可能な解像度を取得する
var sizes = camera.AvailableResolutions;
// 撮影可能な解像度のうち一番大きなものを設定
camera.Resolution = sizes.Last();

デバッグ実行中のスクリーンショットを取りました。下図の通り解像度が取得できているのが確認できます。

画像

Windows Phoneエミュレータでの撮影可能な解像度を取得したところです。取得できた解像度は、640×360、640×480(VGA⁠⁠、2592×1944(5M⁠⁠、3264×2448(8M)の4つ。通常Windows Phoneの標準カメラアプリでは存在しない撮影サイズが含まれていることに注目してください。

静止画撮影シーケンスを開始する

さて、本題の静止画撮影処理を実装していきましょう。先にフラッシュモードと撮影解像度の設定を行います。可能性としては無視できると思いますが、将来端末間で実装に差が出た場合を考え、明確に任意の値を設定するようにしておきましょう。

画像

アプリケーションバーのメニューの「take picture」がタップされると、撮影シーケンス中に発行されるイベントハンドラの設定を行い、PhotoCamera.CaptureImageメソッドにて静止画撮影のシーケンスを開始します。

private void menuItemTakePicture_Click(object sender, EventArgs e) {
    // 静止画撮影可能な解像度を取得する
    var sizes = camera.AvailableResolutions;
    // 撮影可能な解像度のうち一番大きなものを設定
    camera.Resolution = sizes.Last();

    // 撮影シーケンス時に発行されるイベントにハンドラを設定
    camera.CaptureStarted += new EventHandler(camera_CaptureStarted);
    camera.CaptureImageAvailable += new EventHandler<ContentReadyEventArgs>(camera_CaptureImageAvailable);
    camera.CaptureThumbnailAvailable += new EventHandler<ContentReadyEventArgs>(camera_CaptureThumbnailAvailable);
    camera.CaptureCompleted += new EventHandler<CameraOperationCompletedEventArgs>(camera_CaptureCompleted);

    // 静止画撮影の開始要求
    camera.CaptureImage();
}

静止画撮影の要求を投げると、まずCaptureStartedイベントが発行されます。連続撮影しないのであればこの時点でプレビューを止めてしまって良いかもしれません。

// 静止画撮影の開始
void camera_CaptureStarted(object sender, EventArgs e) {
    Debug.WriteLine("camera_CaptureStarted");
}

撮影した画像は最終的にJPEGデータにエンコードされます。PhotoCamera.Resolutionプロパティに指定した大きな解像度の画像のエンコードが完了すると、CaptureImageAvailableイベントが通知されます。

撮影とエンコードが正しくできているかを調べるため、エンコード済みの静止画画像のストリームが引数のContentReadyEventArgs.ImageStreamプロパティから取得できますので、これを分離ストレージへ保存します。

// カメラからの静止画取り込み終了し画像が使用可能になった
void camera_CaptureImageAvailable(object sender, ContentReadyEventArgs e) {
    Debug.WriteLine("camera_CaptureImageAvailable");

    try {
        // 分離ストレージへキャプチャーデータへ保存する
        using (var store = IsolatedStorageFile.GetUserStoreForApplication())
        using (var strm = store.CreateFile("capture_image.jpg")) {
            var bytes = new byte[256 * 1024];
            while (true) {
                int read = e.ImageStream.Read(bytes, 0, bytes.Length);
                if (read <= 0) break;
                strm.Write(bytes, 0, read);
            }
        }
    } catch (Exception ex) {
        // ここでエラー処理
    } finally {
        e.ImageStream.Close();
    }
}

サムネイル画像のJPEGエンコードが完了するとCaptureThumbnailAvailableイベントが通知されます。エンコードの完成したほうが先に通知されます。

サムネイル画像もCaptureImageAvailableイベントの処理と同様に分離ストレージへ保存してしまいましょう。

// カメラからの静止画取り込み終了しサムネイル画像が使用可能になった
void camera_CaptureThumbnailAvailable(object sender, ContentReadyEventArgs e) {
    Debug.WriteLine("camera_CaptureThumbnailAvailable");

    try {
        // 分離ストレージへサムネイルデータへ保存する
        using (var store = IsolatedStorageFile.GetUserStoreForApplication())
        using (var strm = store.CreateFile("thumbnail.jpg")) {
            var bytes = new byte[256 * 1024];
            while (true) {
                int read = e.ImageStream.Read(bytes, 0, bytes.Length);
                if (read <= 0) break;
                strm.Write(bytes, 0, read);
            }
        }
    } catch (Exception ex) {
        // ここでエラー処理
    } finally {
        e.ImageStream.Close();
    }
}

最後にCaptureCompletedイベントが通知されて、静止画撮影シーケンスは完了です。引数のCameraOperationCompletedEventArgsのSucceededプロパティを参照することで、静止画撮影が成功したのか失敗したのかを判定することができます。

// 静止画撮影の完了
void camera_CaptureCompleted(object sender, CameraOperationCompletedEventArgs e) {
    Debug.WriteLine("camera_CaptureCompleted");

    if (e.Succeeded) {
        // 静止画撮影が成功した時の処理
    } else {
        // 静止画撮影が失敗した時の処理 
    }
}

以上、静止画撮影の開始までの設定と静止画撮影シーケンス中に発生するイベントについて説明いたしました。今回のTipsでは、分離ストレージにJPEGデータを保存しました。

Isolated Storage Explorer Toolの使い方

Windows Phoneの分離ストレージはセキュアな反面、格納したデータをPC側のエクスプローラー等のファイルシステムからアクセスすることができず、開発効率が落ちてしまう原因となっていました。

Windows Phone SDK 7.1から分離ストレージに対してアクセスする方法として「Isolated Storage Explorer Tool」というツールが追加されました。コマンドラインベースであまり使いやすいとは言えませんが、今まで分離ストレージにアクセスできなかったことを考えると、格段にデバッグ効率が上がったと言えます。

このツールが追加されたのはWindows Phone SDK 7.1からですが、対象となるアプリケーションはWindows Phone OS 7.0向けのものでも問題ありません。

Isolated Storage Explorer Toolでできることは以下の3つです。

  • Windows Phone内の分離ストレージからPCへデータを転送
  • PCからデータをWindows Phone内の分離ストレージへ転送
  • Windows Phone内の分離ストレージのディレクトリの一覧表示

Isolated Storage Explorer Toolのコマンドのパラメータ

開発環境であるWindowsが32bit版か64bit版かで、インストールされているディレクトリが異なります。自分の環境に合ったものを選択してください。

Windowsが32bit版の場合
"C:\Program Files\Microsoft SDKs\Windows Phone\v7.1\Tools\IsolatedStorageExplorerTool\ISETool.exe"
Windowsが64bit版の場合
"C:\Program Files (x86)\Microsoft SDKs\Windows Phone\v7.1\Tools\IsolatedStorageExplorerTool\ISETool.exe"

ISETool.exeに指定する書式は以下の通りです。

ISETool.exe <ts|rs|dir[:device-folder]> <xd|de> <Product GUID> [<desktop-path>]

コマンドの引数を前から順に説明していきます。

パラメータ 説明
ts (Take snapshot)
Windows Phone内の分離ストレージからPCへデータを転送
rs (Restore snapshot)
PCからデータをWindows Phone内の分離ストレージへ転送
dir Windows Phone内の分離ストレージのディレクトリの一覧表示
特定のディレクトリの一覧をみたい場合は「dir:"/Shared"」のように指定する。ディレクトリを指定しなかった場合はルートディレクトリの一覧が表示される。
xd Windows Phoneエミュレータを対象とする
de Windows Phone端末(実機)を対象とする
Product GUID データをダンプしたい対象となるアプリのWPAppManifest.xmlに書かれているProduct GUID
desktop-path PC側の転送先または転送元パス

これらのパラメータを使いわけることで、最初に紹介した3つの機能別にISETool.exeを使い分けることができます。Visual Studioのソリューションエクスプローラーから、テストを行いたいプロジェクトのPropertiesディレクトリにあるWMAppManifest.xmlを開いてください。

画像

App要素にProductID属性がありますのでメモしておいてください。例として使用しているCameraPreviewTestのプロダクトIDは「d529745c-470a-4fc0-aab0-50b76d24bfe3」で、このIDを使用して、分離ストレージからのデータの取り込みやデータの格納を行います。

画像

Windows Phone内の分離ストレージからPCへデータを転送

プロダクトIDが「d529745c-470a-4fc0-aab0-50b76d24bfe3」のアプリケーションの分離ストレージの内容を、Windows Phone端末(実機)からPCの「c:\test」ディレクトリへ転送しています。

> ISETool.exe ts de d529745c-470a-4fc0-aab0-50b76d24bfe3 c:\test

コマンドを実行し、問題なく終了するとDownload Successfulと表示されます。特に進捗が表示されることも無いので、サイズの大きなファイルを転送している場合は、コマンドプロント上でのフィードバックがありませんので注意してください。

> ISETool.exe ts de d529745c-470a-4fc0-aab0-50b76d24bfe3 c:\test
Download Started ... Into Folder: c:\test
Download Successful Into Folder: c:\test

PCからデータをWindows Phone内の分離ストレージへ転送

プロダクトIDが「d529745c-470a-4fc0-aab0-50b76d24bfe3」のアプリケーションの分離ストレージに対して、PCの「c:\test」ディレクトリのデータをWindows Phone端末(実機)へ転送しています。

> ISETool.exe ts de d529745c-470a-4fc0-aab0-50b76d24bfe3 c:\test

Windows Phone内の分離ストレージのディレクトリの一覧表示

プロダクトIDが「d529745c-470a-4fc0-aab0-50b76d24bfe3」のアプリケーションの分離ストレージのルートディレクトリ一覧を参照します。

> ISETool.exe ts dir d529745c-470a-4fc0-aab0-50b76d24bfe3

dirに続いて「:device-folderを指定すると特定のディレクトリ一覧を参照することが可能です。

以下の例では、プロダクトIDが「d529745c-470a-4fc0-aab0-50b76d24bfe3」のアプリケーションの分離ストレージのルート直下のSharedディレクトリ一覧を参照しています。

> ISETool.exe dir:"/Shared" de d529745c-470a-4fc0-aab0-50b76d24bfe3
<DIR>          Transfers
<DIR>          ShellContent
<DIR>          Media

撮影した画像がきちんと撮れているかを確認するために分離ストレージに保存されたデータを確認してみましょう。Isolated Storage Explorer Toolを使って、分離ストレージに保存したJPEGデータをPCへ転送してみました。

画像

問題なくPCへ転送が行われ、エクスプローラーで表示できていることを確認できました。

さいごに

プレビュー表示に使用する画像のことを、プレビューフレームやプレビューバッファーと呼びます。

次回は、プレビューフレームを取得し、カメラからインプットを表示させるだけではなく、画像を加工した上でプレビュー表示を行うTipsと、バーコードライブラリを使用してバーコードの読み取りを行う方法をご紹介したいと思います。

今回は以上で終わりです。ありがとうございました。

おすすめ記事

記事・ニュース一覧