カメラを使ってみよう


カメラは昨今の携帯デバイスにはかかせない機能となっています。

カメラは写真や動画を撮影するだけでなく、ARアプリなど現実とバーチャルをつなぐインターフェイスとして非常に有効なハードウェアとなります。

今回はこのカメラ機能を使って写真を撮影する方法を紹介します。

詳細は以下から。

画面の向き

今までの記事ではデフォルトで縦横両対応の画面でサンプルを作成してきましたが、カメラで縦の状態を撮影するのは少し手間が必要なので
今回はシンプルに横固定の画面にしましょう。

アプリを横固定にするためにはphone:PhoneApplicationPage要素のSupportedOrientations属性とOrientation属性Landscapeを設定します。

MainPage.xaml

<phone:PhoneApplicationPage 
    ~省略~
    SupportedOrientations="Landscape" Orientation="Landscape"
    shell:SystemTray.IsVisible="True">

VideBrushをレイアウトに配置する

カメラで撮影を行う場合はどのように撮れているかを確認するためのプレビュー画面が必要になってきます。

カメラのキャプチャ映像を描画するためのクラスとしてVideBrushクラスというものがあります。

VideBrushクラスは配置された領域を指定されたソースのビデオ映像で塗りつぶすクラスです。
このVideBrushのソースをカメラのキャプチャに設定することでプレビュー画面を作成することが可能になります。

VideBrushクラスは様々なコントロールの中に配置することが可能ですが、今回は描画が高速なCanvasの中に配置することにします。

また今回は分離ストレージに撮影した画像を保存するのですが、エミュレータでは撮影した画像を確認することができないため、撮影が完了したことを知らせるためのメッセージ出力用TextBlockを画面下に設置します。

MainPage.xaml

<Canvas x:Name="viewfinderCanvas" Width="640" Height="480" Tap="viewfinder_Tapped">
        <Canvas.Background>
            <VideoBrush x:Name="viewfinderBrush">
                <VideoBrush.RelativeTransform>
                    <CompositeTransform x:Name="viewfinderTransform"
                                        CenterX="0.5"
                                        CenterY="0.5"
                                        />
                </VideoBrush.RelativeTransform>
                
            </VideoBrush>

        </Canvas.Background>
        <TextBlock Canvas.Left="12" Canvas.Top="417" Height="51" Name="txtMessage" Text="TextBlock" Width="413" Foreground="Black" />
    </Canvas>

参照設定を追加する

カメラを使用するにはMicrosoft.Xna.Framework.Mediaをusingディレクティブに追加する必要があります。
Microsoft.Xna.Framework.Mediaはデフォルトの参照設定では使用できないため参照設定に追加します。

usingディレクティブへの参照の追加

カメラを使用するためにはusingディレクティブに下記の参照を追加する必要があります。

MainPage.xaml.cs

using Microsoft.Devices;
using Microsoft.Xna.Framework.Media;

PhotoCameraクラスをインスタンス化する

PhotoCameraクラスはカメラの機能を提供するクラスです。
PhotoCameraクラスのインスタンス化するためには数秒の時間を要しますのでOnNavigatedToをオーバーライドし、その中にインスタンス化の処理を記述します。

またインスタンス化後に、撮影の完了時にコールされるCaptureImageAvailableイベントに撮影後の処理を実行するためのメソッドを設定します。

MainPage.xaml.cs

protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
    if (PhotoCamera.IsCameraTypeSupported(CameraType.Primary) == true)
    {
       cam = new PhotoCamera(CameraType.Primary);
       cam.CaptureImageAvailable +=
           new EventHandler<Microsoft.Devices.ContentReadyEventArgs>
               (cam_CaptureImageAvailable);
       viewfinderBrush.SetSource(cam);
    }
    else
    {
        txtMessage.Text = "このデバイスではカメラを使用できません。";
    }
}

5行目でPhotoCameraをインスタンス化しています。
引数に渡している値はメインカメラを使用することを表しています。
メインカメラの他にサブカメラ(ディスプレイ側に設置されているカメラ)を使用することもでき、その場合はCameraType.FrontFacingを引数として渡します。
なお変数camはクラス変数として下記のように宣言されています。

PhotoCamera cam;

6~8行目でCaptureImageAvailableイベントにメソッドを関連付けています。

9行目ではVideoBrushにカメラのキャプチャをソースとして指定しています。

撮影するためのメソッドをコールする

撮影はPhotoCameraクラスのCaptureImageメソッドを使用することで実行されます。

下記はCanvasのタップ時にコールされるイベントと関連付けられているメソッドです。
関連付け自体はxaml内で行なっています。

MainPage.xaml.cs

void viewfinder_Tapped(object sender, GestureEventArgs e)
{
    if(cam != null)
    {
        try
        {
            cam.CaptureImage();
        }catch(Exception ex){
            txtMessage.Text = ex.Message;
        }
                
    }
}

保存処理を実装する

CaptureImageAvailableイベントに関連付けたメソッドに保存処理を実装します。

今回はCameraRollに保存することにします。

MainPage.xaml.cs

void cam_CaptureImageAvailable(object sender, Microsoft.Devices.ContentReadyEventArgs e)
{
    photoCounter++;
    string fileName = photoCounter + ".jpg";
    Deployment.Current.Dispatcher.BeginInvoke(delegate()
    {
        txtMessage.Text = "保存を開始します";
    });
    library.SavePictureToCameraRoll(fileName, e.ImageStream);
    Deployment.Current.Dispatcher.BeginInvoke(delegate()
    {
        txtMessage.Text = "camera rollに保存されました。";
    });
}

3行目のphotoCounter変数はクラス変数として下記のように宣言されています。

private int photoCounter = 0;

この変数はファイル名の命名のために使用しているだけで特別な意味はありません。

5行目と10行目で使用しているDeployment.Current.Dispatcher.BeginInvokeはUIスレッドへ通知を行うためのメソッドです。
CaptureImageAvailableイベントはUIスレッド(画面描画を行うスレッド)とは別のスレッドで実行されますので、UIスレッドにアタッチするためにDeployment.Current.Dispatcher.BeginInvokeメソッドを使用する必要があります。

9行目で使用されているlibrary変数はクラス変数として下記のように宣言されています。

MediaLibrary library = new MediaLibrary();

ここではMediaLibraryクラスのSavePictureToCameraRollメソッドを使用してCameraRollに撮影した画像をjpeg形式で保存しています。

画面を抜けた場合にPhotoCameraインスタンスを解放する

撮影画面から別の画面に繊維する、またはロック状態になった場合はPhotoCameraインスタンスを解放してあげるべきです。
PhotoCameraインスタンスは明示的に解放を行わないとカメラが動作し続けてしまうため、解放を行わない場合は予期せぬ不具合や電池の浪費につながります。

PhotoCameraインスタンスの解放はDisposeメソッドをコールするだけで完了します。

MainPage.xaml.cs

protected override void OnNavigatingFrom
        (System.Windows.Navigation.NavigatingCancelEventArgs e)
{
    if (cam != null)
    {
        cam.Dispose();
    }
}

ここまでで基本的なカメラの実装は完了となります。

関連する記事: