カメラの向きを取得し、縦横の位置を合わせて表示させる

2012年1月13日(金)
PROJECT KySS

ロジックコードを記述する

リスト2 (MainPage.xaml.vb)

Option Strict On

ランチャーやチューザーに関するクラスの含まれる、Microsoft.Phone.Tasks名前空間をインポートします。
Imports Microsoft.Phone.Tasks

CameraCaptureTask を使って取得した写真や画像を、ExifLib を使って正しい方向に回転して表示させるため、ExifLibをインポートしておきます。
Imports ExifLib

JPEG ファイルを WriteableBitmap オブジェクトにデコードするPictureDecorderクラスの含まれる、Microsoft.Phone名前空間をインポートします。
Imports Microsoft.Phone 

Imports System.IO

曲、アルバム、再生リスト、およびピクチャを列挙、再生、および表示するためのクラスの含まれる、Microsoft.Xna.Framework.Media名前空間をインポートします。ピクチャへのアクセスを提供するMediaLibrayクラスを使用するため、この名前空間のインポートが必要です。
Imports Microsoft.Xna.Framework.Media

Imports System.Windows.Media.Imaging

Partial Public Class MainPage
  Inherits PhoneApplicationPage

  ' コンストラクター
  Public Sub New()
    InitializeComponent()
  End Sub

CameraCaptureTaskクラス型のメンバ変数myCameraCaptureTaskを宣言します。
  Dim myCameraCaptureTask As CameraCaptureTask

Exifの情報を元にして画像を回転するExifLib.ExifOrientaion型のメンバ変数myOrientationを宣言します。
  Dim myOrientation As ExifLib.ExifOrientation

バイト型の配列、imageByteをメンバ変数として宣言します。
  Dim imageBytes As Byte()

ストリーム型のmyCaptureImageメンバ変数を宣言します。
  Dim myCaptureImage As Stream

ページがアクティブになった時呼び出されるメソッド

AddHandlerステートメントで、チューザータスクが完了した時に発生するCompletedイベントにイベントハンドラを追加します。イベントハンドラ内では以下の処理が実行されます。
カメラアプリケーションが起動して、写真がきちんと撮れた場合の処理です。
ストリーム型のmyCaptureImageメンバ変数に、写真のデータ(ChosenPhoto)を含むストリームを格納します。ChosenPhoto.Positionプロパティに0を指定し、写真データの、現在のストリーム内の位置を設定します。
ExifLibに属するJpegInfo型のinfo変数を宣言し、ExifReader.ReadJpegメソッドで、写真のEXIF データから向きを取得します。Exifの情報を元にして画像を回転するExifLib.ExifOrientaion型のメンバ変数myOrientationに、カメラの向きを格納します。カメラの向きがTopLeft、つまり横撮影であった場合は360画像を回転させます。カメラの向きがToRight、つまり縦撮影であった場合は90度画像を回転させます。
写真のデータを含む、ストリームのバイトの長さを取得し、バイト配列を作成します。Readメソッドで、現在のストリームからバイトシーケンスを読み取ります。
ChosenPhoto.Readの書式は以下の通りです。

ChosenPhoto.Read(バイト配列,0ベースのバイトオフセット(現在のストリームから読み取ったデータの格納を開始します), 現在のストリームから読み取るバイトの最大数)

Seekメソッドで、現在のストリーム内の位置を設定します。書式は以下の通りです。

ChosePhoto.Seek(基点のパラメーターのバイト オフセット, 参照ポイントを示すSeekOrigin型の値) 

SeekOriginにはBeginを指定し、ストリームの先頭を指定しています。

WriteableBitmap型の変数imageSourceを宣言し、PictureDecoder.DecodeJpeg(resultArgs.ChosenPhoto)で、撮った写真をJPEGファイルとしてWriteableBitmapオブジェクトにデコードします。PictureDecoder.DecodeJpegメソッドはMicrosof.Phone名前空間に属しています。WriteableBitmapクラスは書き込み更新することのできるBitmapSourceを提供するクラスです。

Image2のSourceプロパティにデコードされたJPEGファイルを格納しているimageSourceオブジェクトを指定します。これでImage内に、撮った写真が表示されます。縦撮影の場合は縦向きで、横撮影の場合は横向きで表示されます。
カメラを起動している状態で撮影を中止した場合は、saveButton(フロッピーのアイコン)は使用不可のままで処理を抜けます。

  Protected Overrides Sub OnNavigatedTo(e As System.Windows.Navigation.NavigationEventArgs)
    myCameraCaptureTask = New CameraCaptureTask
    AddHandler myCameraCaptureTask.Completed, Sub(resultSender As Object, resultArgs As PhotoResult)
          If resultArgs.TaskResult = TaskResult.OK Then
              myCaptureImage = resultArgs.ChosenPhoto
              resultArgs.ChosenPhoto.Position = 0
              Dim info As JpegInfo = ExifReader.ReadJpeg(resultArgs.ChosenPhoto, resultArgs.OriginalFileName)
              myOrientation = info.Orientation
              Dim myAngle As Integer = 0
              Select Case info.Orientation
                     Case ExifOrientation.TopLeft 'カメラ横向き
                           myAngle = 0
                           Exit Select
                     Case ExifOrientation.TopRight 'カメラ縦向き
                           myAngle = 90
                           Exit Select
                     End Select
                     myComposite.Rotation = myAngle
                     imageBytes = New Byte(CInt(resultArgs.ChosenPhoto.Length)) {}
                     resultArgs.ChosenPhoto.Read(imageBytes, 0, imageBytes.Length)
                     resultArgs.ChosenPhoto.Seek(0, SeekOrigin.Begin)
                     Dim imageSource As WriteableBitmap = PictureDecoder.DecodeJpeg(resultArgs.ChosenPhoto)
                     Image2.Source = imageSource
              Else
                     saveButton.IsEnabled = False
                     Exit Sub
              End If
          End Sub
    MyBase.OnNavigatedTo(e)
  End Sub

カメラボタンがタップされた時の処理

カメラ機能を起動し、保存ボタン(フロッピーのアイコン)の使用を可能にします。
  Private Sub cameraButton_Click(sender As Object, e As System.Windows.RoutedEventArgs) Handles cameraButton.Click
    MessageBox.Show("カメラの解像度を低く設定し、設定の保存を行って、写真を撮ってください。")
    myCameraCaptureTask.Show()
    saveButton.IsEnabled = True
  End Sub

保存(フロッピーのアイコン)ボタンがタップされた時の処理

保存する画像のファイル名を作成します。現在の日付と時間(秒を含む)を連結したファイル名にします。日付の/を-に、時間の:を-にReplace関数で書き換えます。このファイル名を変数imageFileNameに格納しておきます。
Stream.Seek メソッドで、写真データの現在のストリーム内の位置を0に設定します。
ピクチャへのアクセスを提供する新しいMediaLibrayクラスのインスタンス、myLibrayを作成します。
MediaLibrary.SavePictureメソッドで、ストリームオブジェクトに含まれる画像を、指定したファイル名で、メディアライブラリーに保存し、その保存した画像をピクチャオブジェクトとして返します。保存した旨のメッセージを表示します。
  Private Sub saveButton_Click(sender As Object, e As System.Windows.RoutedEventArgs) Handles saveButton.Click
    Dim imageFileName As String = DateTime.Now.ToString("yyyyMMddHHmmss") &".jpg"
    
    myCaptureImage.Seek(0, 0)
    Dim myLibray As New MediaLibrary
    myLibray.SavePicture(imageFileName, myCaptureImage)
    MessageBox.Show("PicturesHubに保存しました。")
  End Sub
End Class

今回でWindows Phone Tips集第2弾は終了です。いかがですか?「基本編」、「応用編」、「Tips」、「Tips第2弾」と、いろいろなサンプルを見てきましたが、Windows Phoneでのサンプルの作成方法は理解できましたでしょうか?とにかく自分で手を動かして作ってみることが重要です。今までのサンプルをヒントに自分なりのWindows Phoneアプリを作って、ぜひMarketplaceに申請していただきたいと思います。また機会があれば、Marketplaceへのアプリの申請方法についても紹介したいと思います。

この連載に引き続き、次回からはTips集の第3弾を公開しますのでご期待ください。では、どうもありがとうございました。

  • 「撮った写真をカメラの向きに応じて表示させる」のサンプルファイル

四国のSOHO。薬師寺国安(VBプログラマ)と、薬師寺聖(デザイナ、エンジニア)によるコラボレーション・ユニット。1997年6月、Dynamic HTMLとDirectAnimationの普及を目的として結成。共同開発やユニット名義での執筆活動を行う。XMLおよび.NETに関する著書や連載多数。最新刊は「Silverlight実践プログラミング」両名とも、Microsoft MVP for Development Platforms - Client App Dev (Oct 2003-Sep 2012)。http://www.PROJECTKySS.NET/

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています