カメラで写した写真をカメラロールに保存する
今回はPhotoCameraを使ったサンプルを紹介します。PhotoCameraクラスは、Windows Phoneの静止したカメラ・アプリケーションの基本的なカメラ機能を提供するクラスです。イメージのキャプチャ、フォーカス、解像度、およびフラッシュ・モードなどの機能を構成するためのメンバが含まれています。また、カメラの焦点、イメージのキャプチャ、イメージの可用性のイベントを提供します。
まずカメラで被写体を撮る場合は、第1回を参照の上、解像度を設定してください。
→参照:「第1回:カメラで写した写真を分離ストレージに保存し、写真の一覧を表示する」
解像度の変更について、今回のPhotoCameraを使ったサンプルでは、プログラムから変更することもできます。[解像度を低に設定]ボタンをクリックすると、デフォルトの4128×3096から、一番低い解像度である、640×480に設定されます。ただしプログラムが終了すると、640×480で設定の保存をしていない場合、デフォルトの4128×3096に戻ってしまいます。この解像度で写真の保存を続けていると内臓ストレージを圧迫することになりますので、640×480に設定して、設定の保存をしておくことをお勧めします。
まずは、このプログラムで実装する機能の動作を、下記に解説しておきます。
※このサンプルは実機(現時点ではauのIS12T)でしか動作しませんのでご注意下さい。
実機にプログラムをデプロイすると、矩形内に周りの風景が映し出されます。まず、[解像度を低に設定]ボタンをタップします。次に被写体にカメラを合わせ、[Auto Focus]ボタンをタップすると、自動的にフォーカスを合わすことができます。写真を撮るには、実機に付いているカメラスイッチを押すのではなく、[Shutter]ボタンをタップします。シャッター音がして風景が写しとられます。その後「保存しました」のメッセージが表示されます。ここで撮った写真はカメラロールに保存されます。アプリの一覧画面からPicturesをタップし、表示される項目からカメラロールを選択すると、今、撮った写真が保存されています。削除する場合は、右隅下の[・・・]をタップして表示されるメニューから「削除」を選択します(図1)。
図1:PhotoCameraで撮った写真がカメラロールに保存されている(クリックで拡大) |
サンプル一式は、会員限定特典としてダウンロードできます。記事末尾をご確認ください。
プロジェクトの作成
VS 2010のメニューから[ファイル(F)/新規作成(N)/プロジェクト(P)]を選択します。次に、「Windows Phone アプリケーション」を選択して、「名前(N)」に任意のプロジェクト名を指定します。ここでは「WP71_PhotoCamera」という名前を付けています。Windows Phoneのバージョンは7.1を選択します。
また、ピクチャへのアクセスを提供するMediaLibrayクラスを使用するため、VS2010メニューの「プロジェクト(P)/参照の追加(R)」から、Microsoft.Xna.Frameworkを追加しておいてください(図2)。
図2:Microsoft.Xna.Frameworkを追加する(クリックで拡大) |
MainPage.xamlの編集とコントロールの追加
x:NameがPageTitleというTextBlockのTextプロパティにPhotoCameraと指定します。
ツールボックスからRectangleコントロールを1個と、Buttonコントロールを3個配置します。
書き出されるXAMLコードをリスト1のように編集します。
リスト1 編集されたXAMLコード(MainPage.xaml)
(1)<Rectangle.RenderTransform>プロパティ要素内に<CompositeTransform>要素を配置します。この要素は、1 つのオブジェクトに複数の異なる変換を適用することができる要素です。CenterXとCenterYプロパティにはRectangleコントロールのWidthとHeightの約半分のサイズを指定します。CenterXプロパティでは、CompositeTransform で指定された、全ての変換の中心点の x 座標を設定します。CenterYプロパティでは、CompositeTransform で指定された、全ての変換の中心点の y 座標を設定します。回転を表すRotationに90を指定します。カメラを縦向きで撮った画像もデフォルトでは横向きに表示されるため、90度回転して縦向きに表示されるようにしています。 (2)<Rectangle.Fill>プロパティ要素内に<VideoBrush>要素を配置し、x:NameにmyVideoBrushと指定します。この<VideoBrush>要素内に被写体が表示されます。<VideoBrush>要素は、ビデオ コンテンツで領域を塗りつぶす要素です。 <phone:PhoneApplicationPage x:Class="WP71_PhotoCamera.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768" FontFamily="{StaticResource PhoneFontFamilyNormal}" FontSize="{StaticResource PhoneFontSizeNormal}" Foreground="{StaticResource PhoneForegroundBrush}" SupportedOrientations="Portrait" Orientation="Portrait" shell:SystemTray.IsVisible="True"> <!--LayoutRoot は、全てのページ コンテンツが配置されるルート グリッドです--> <Grid x:Name="LayoutRoot" Background="Transparent"> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <!--TitlePanel は、アプリケーション名とページ タイトルを格納します--> <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28"> <TextBlock x:Name="ApplicationTitle" Text="マイ アプリケーション" Style="{StaticResource PhoneTextNormalStyle}"/> <TextBlock x:Name="PageTitle" Text="PhotoCamera" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/> </StackPanel> <!--ContentPanel - 追加コンテンツをここに入力します--> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Rectangle Height="437" HorizontalAlignment="Left" Margin="13,15,0,0" Name="Rectangle1" Stroke="Black" StrokeThickness="1" VerticalAlignment="Top" Width="423"> <Rectangle.RenderTransform> ■(1) <CompositeTransform Rotation="90" CenterX="211" CenterY="218"/> ■(1) </Rectangle.RenderTransform> <Rectangle.Fill> ■(2) <VideoBrush x:Name="myVideoBrush"/> ■(2) </Rectangle.Fill> </Rectangle> <Button Content="Auto Focus" Height="76" HorizontalAlignment="Left" Margin="39,449,0,0" Name="focusButton" VerticalAlignment="Top" Width="186" /> <Button Content="Shutter" Height="72" HorizontalAlignment="Left" Margin="218,453,0,0" Name="shutterButton" VerticalAlignment="Top" Width="191" /> <Button Content="解像度を低に設定" Height="82" HorizontalAlignment="Left" Margin="39,519,0,0" Name="resolutionButton" VerticalAlignment="Top" Width="379" /> </Grid> </Grid> <!--ApplicationBar の使用法を示すサンプル コード--> ~コード略~ </phone:PhoneApplicationPage>
配置図は図3のようになります。
図3:各コントロールを配置した(クリックで拡大) |
次に、MainPage.xamlを展開して表示される、MainPage.xaml.vbをダブルクリックしてリスト2のコードを記述します。
ロジックコードを記述する
リスト2 (MainPage.xaml.vb)
Option Strict On Windows Phoneのデバイスのハードウェアに関連する情報が含まれているMicrosoft.Devices名前空間をインポートします。今回使用するPhotoCameraクラスは、この名前空間に属しています。 Imports Microsoft.Devices 仮想ファイルシステムを作成および使用するための型が含まれている、System.IO.IsolatedStorage名前空間をインポートします。分離ストレージによって、安全なクライアント側のストレージが提供されます。 Imports System.IO.IsolatedStorage Imports System.Windows.Media.Imaging 曲、アルバム、再生リスト、およびピクチャを列挙、再生、および表示するためのクラスの含まれる、Microsoft.Xna.Framework.Media名前空間をインポートします。ピクチャへのアクセスを提供するMediaLibrayクラスを使用するため、この名前空間のインポートが必要です。 Imports Microsoft.Xna.Framework.Media Imports System.IO Partial Public Class MainPage Inherits PhoneApplicationPage ' コンストラクター Public Sub New() InitializeComponent() End Sub PhotoCameraクラス用メンバ変数myPhotoCameraを宣言します。PhotoCameraクラスは、Windows Phoneの静止したカメラ・アプリケーションの基本的なカメラ機能を提供するクラスです。イメージのキャプチャ、フォーカス、解像度、およびフラッシュ・モードなどの機能を構成するためのメンバが含まれています。また、カメラの焦点、イメージのキャプチャ、イメージの可用性のイベントを提供します。 Dim myPhotoCamera As PhotoCamera Sharedキーワードで数値型のメンバ変数counterを宣言します。このcounterの値をJPEGファイル名とします。1.jpg、2.jpg・・・・といった具合になります。 Shared counter As Integer = 0
ページがアクティブになった時呼び出されるメソッド
新しいPhotoCameraのインスタンスmyPhotoCameraオブジェクトを生成します。 AddHandlerステートメントで、カメラ オブジェクトが初期化された時に発生する、Initializedイベントにイベントハンドラを追加します。Succeededプロパティで、カメラ操作が失敗した時は処理を抜けます。 キャプチャ シーケンスが完了すると発生するCaptureCompletedイベントで、myPhotoCamera_CaptureCompletedを実行します。また、イメージが利用可能な場合に発生する、CaptureImageAvailableイベントで、myPhotoCamera_CaptureImageAvailableを実行します。 SetSourceメソッドで、PhotoCameraのキャプチャソースを使用して、VideoBrushのソースを設定します。 Protected Overrides Sub OnNavigatedTo(e As System.Windows.Navigation.NavigationEventArgs) myPhotoCamera = New PhotoCamera AddHandler myPhotoCamera.Initialized, Sub(cameraSender As Object, cameraArgs As CameraOperationCompletedEventArgs) If cameraArgs.Succeeded = False Then Exit Sub End Sub AddHandler myPhotoCamera.CaptureCompleted, AddressOf myPhotoCamera_CaptureCompleted AddHandler myPhotoCamera.CaptureImageAvailable, AddressOf myPhotoCamera_CaptureImageAvailable myVideoBrush.SetSource(myPhotoCamera) MyBase.OnNavigatedTo(e) End Sub
ページが非アクティブになった時呼び出されるメソッド
イメージソース ストリームを破棄し、カメラのオブジェクトによって使用されているリソースを解放します。 全てのオブジェクトの関連付けを破棄します。 RemoveHandlerステートメントで、イベントとイベント ハンドラの関連付けを解除します。 Protected Overrides Sub OnNavigatedFrom(e As System.Windows.Navigation.NavigationEventArgs) If myPhotoCamera Is Nothing = False Then myPhotoCamera.Dispose() myPhotoCamera = Nothing Try RemoveHandler myPhotoCamera.CaptureCompleted, AddressOf myPhotoCamera_CaptureCompleted RemoveHandler myPhotoCamera.CaptureImageAvailable, AddressOf myPhotoCamera_CaptureImageAvailable Catch Exit Sub End Try End If MyBase.OnNavigatedFrom(e) End Sub
[Auto Focus]ボタンがクリックされた時の処理
AddHandlerステートメントで、フォーカス操作が完了した後に発生するAutoFocusCompletedイベントに、myPhotoCamera_AutoFocusCompletedイベントハンドラを追加します。 Focusメソッドで、カメラオートフォーカス操作を開始します。 Private Sub focusButton_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles focusButton.Click If myPhotoCamera Is Nothing = True Then Exit Sub Else AddHandler myPhotoCamera.AutoFocusCompleted, AddressOf myPhotoCamera_AutoFocusCompleted myPhotoCamera.Focus() End If End Sub
フォーカス操作が完了した後に発生するイベント
RemoveHandlerステートメントで、イベントとイベント ハンドラの関連付けを解除します。 「フォーカス完了」のメッセージを表示します。Me.Dispatcher.BeginInvokeメソッドで、別スレッドからメッセージを呼び出します。 Private Sub myPhotoCamera_AutoFocusCompleted(sender As Object, e As CameraOperationCompletedEventArgs) RemoveHandler myPhotoCamera.AutoFocusCompleted, AddressOf myPhotoCamera_AutoFocusCompleted Me.Dispatcher.BeginInvoke(Sub() MessageBox.Show("オートフォーカス完了")) End Sub
イメージが利用可能な場合に発生する処理
新しいMediaLibrayのインスタンスmyLibrayオブジェクトを作成します。 カウントされる変数counterと拡張子.jpgを連結した文字列をfilename変数に格納しておきます。 MediaLibrayクラスのSavePictureToCameraRollメソッドで、Xnaメディアライブラリを使用して画像を、カメラロールに保存します。 変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。FileExistsメソッドでfilenameに格納しているのと同じファイルが存在する場合は、DeleteFileメソッドで同名ファイルを削除します。 IsolatedStorageFileクラスのOpenFileメソッドでfilename変数内のファイルを、指定したファイルアクセスを使用して指定したモードでファイルを開きます。バイト型として4095の領域を確保します。e.ImageStream.Readメソッドで、イメージストリームからバイト数を読み取り、変数myByteReadに格納します。この変数が、0より大きい間、IsolatedStorageFileStream.Writeメソッドで、読み込んだイメージストリームのバイト数をバッファーに書き込んでいきます。 写真の撮影とエンコードが正しくできているかは、ContentReadyEventArgs.ImageStreamプロパティから取得できますので、これを分離ストレージへ保存します。e.ImageStreamを閉じ、保存した旨のメッセージを表示します。 e.ImageStream.Readの書式は下記の通りです。 e.ImageStream.Read(バイト配列, データの格納を開始するバッファー内のバイト オフセット(通常0を指定), 現在のストリームから読み取るバイトの最大数。) IsolatedStorageFileStream.Writeメソッドの書式は下記の通りです。 IsolatedStorageFileStream.Write(書き込むバッファー, 開始位置を示すバッファー内のバイト オフセット(通常0を指定) ,書き込む最大バイト数,) Private Sub myPhotoCamera_CaptureImageAvailable(sender As Object, e As ContentReadyEventArgs) Dim myLibray As New MediaLibrary Dim filename As String = counter & ".jpg" myLibray.SavePictureToCameraRoll(filename, e.ImageStream) Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication If storage.FileExists(filename) = True Then storage.DeleteFile(filename) End If Using myStream As IsolatedStorageFileStream = storage.OpenFile(filename, FileMode.Create, FileAccess.Write) Dim myBuffer(4095) As Byte Dim myByteRead As Integer = -1 Do myByteRead = e.ImageStream.Read(myBuffer, 0, myBuffer.Length) myStream.Write(myBuffer, 0, myByteRead) Loop While (myByteRead > 0) e.ImageStream.Close() Me.Dispatcher.BeginInvoke(Sub() MessageBox.Show("保存しました。")) End Using End Sub
[Shutter]ボタンがクリックされた時の処理
PhotoCameraにファインダーに表示されるイメージがある場合は、CaptureImageメソッドで、ファインダーに表示される現在のイメージのフル解像度キャプチャを開始します。例外が発生した場合は「エラーです。」と表示します。 Private Sub shutterButton_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles shutterButton.Click If myPhotoCamera Is Nothing = False Then Try myPhotoCamera.CaptureImage() Catch Me.Dispatcher.BeginInvoke(Sub() MessageBox.Show("エラーです。")) End Try End If End Sub
キャプチャ シーケンスが完了すると発生するイベント
Sharedステートメントで宣言したcounterの値を1ずつ増加させます。 Private Sub myPhotoCamera_CaptureCompleted(sender As Object, e As CameraOperationCompletedEventArgs) counter = counter + 1 End Sub
[解像度を低に設定]ボタンがクリックされた時の処理
AvailableResolutionsプロパティで、使用できるカメラの解像度を照会します。 カメラでキャプチャしたイメージの解像度を設定できる、Resolutionプロパティに、先に取得したmyResolutionの一番先頭の解像度を指定します。一番先頭の解像度は640×480の解像度です。 解像度を設定した旨のメッセージを表示します。 Private Sub resolutionButton_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles resolutionButton.Click Dim myResolution As IEnumerable(Of Size) = myPhotoCamera.AvailableResolutions myPhotoCamera.Resolution = myResolution.First MessageBox.Show("撮影可能な解像度を" & myPhotoCamera.Resolution.ToString & "に設定しました。") End Sub End Class
「カメラで写した写真をカメラロールに保存する」サンプルプログラム