Webカメラを使ったアプリを作ろう

2014年5月20日(火)
薬師寺 国安

では、ロジックコードについて解説しましょう。今回のコードは約90行程度のコードです。タイトル通り100行に収まって一安心というところです。

ロジックコード(MainPage.xaml.vb)

名前空間をインポートします(リスト2)。

リスト2 各種名前空間のインポート

写真、オーディオ録音、ビデオなんどのメディを作成したり、連携させたりするためのクラスを提供するWindows.Media.Capture名前空間を読み込みます。
Imports Windows.Media.Capture

ファイル、フォルダーおよびアプリケーションの設定を管理するクラスの含まれる、Windows.Storage名前空間をインポートします。
Imports Windows.Storage

メディア形式のプロパティが作成される時に必要となるクラスおよび他のプログラミング要素を提供するクラスの含まれる、Windows.Media.MediaProperties名前空間をインポートします。
Imports Windows.Media.MediaProperties

コンテキストメニューおよびメッセージダイアログのサポートを提供するクラスの含まれる、Windows.UI.Popups名前空間をインポートします。
Imports Windows.UI.Popups

デバイスを列挙するためのクラスを提供するクラスの含まれる、Windows.Devices.Enumeration名前空間をインポートします。
Imports Windows.Devices.Enumeration

コアシステム機能へのアクセスおよびUIのランタイム情報をアプリケーションに提供するクラスの含まれるWindows.UI.Core名前空間をインポートします。
Imports Windows.UI.Core

コンテキストメニューおよびメッセージダイアログのサポートを提供するWindows.UI.Popups名前空間をインポートします。
Imports Windows.UI.Popups

ここで皆さんは「どのクラスがどの名前空間に含まれているのかいちいち覚えてなんかいられないよ」と思わないでしょうか? そこはご心配無用です。ちゃんとVS2013ではそこまで考えて、プログラマーの負担を軽減してくれています。図3を見てください。MessageDialogと入力したのですが、波線が引かれてエラーになっています。波線の個所に赤の短いアンダーラインが見えるはずです。そこにマウスカーソルを持っていくと[赤丸に×]のアイコンが表示されます。この[赤丸に×]のアイコンの横の[▼]をクリックすると図3のようなメッセージが表示され、このクラスを使うためには●●●●の名前空間が必要なのでインポートしますと表示され、クリックすると、その名前空間が自動的に追加されます。他のクラスに関しても、不足している名前空間をちゃんと表示して取り込んでくれます。

不足している名前空間の取り込みを自動的に行ってくれる
図3 不足している名前空間の取り込みを自動的に行ってくれる

では、次にメンバー変数を定義しておきましょう(リスト3)。

リスト3 メンバー変数の定義

Public NotInheritable Class MainPage
    Inherits Page

インデックスによってアクセスできる要素の読み取り専用コレクションを表すクラスのIReadOnlyList(Of IStorageFile)クラス型のメンバー変数myPictureFilesを宣言します。
    Private myPictureFiles As IReadOnlyList(Of IStorageFile)

Webカメラなどのキャプチャデバイスからフォト、オーディオ、ビデオをキャプチャする機能を提供するMediaCaptureクラス型のメンバー変数myMediaCaptureを宣言します。
    Private myMediaCapture As MediaCapture

コレクションメンバー変数myPictureFilesの中のインデックス番号を指定するInteger型のメンバー変数Indexを宣言します。
    Private Index As Integer

BitmapImageクラス型のメンバー変数saveBmpを宣言します。
    Private saveBmp As BitmapImage

DeviceInfomationオブジェクトのコレクションを表すDeviceInformationCollectionクラス型のメンバー変数myCameraを宣言します。
    Private myCamera As DeviceInformationCollection

保存する画像ファイル名を格納する文字列型のメンバー変数saveImageFileNamewを宣言します。
    Private saveImageFileName As String

ファイルを表すクラスであるStorageFileクラス型のメンバー変数myFileを宣言します。
    Private myFile As StorageFile

メンバー変数の宣言ができたところで、第1回目で解説した方法でリスト4のコードを生成します。ページがアクティブになった時のコードです。

AddHandlerステートメントで、現在アクティブになって入るウインドウのVisibleプロパティの値が変更された時に、Current_Changedイベントハンドラを追加します。実装されているカメラのデバイスを取得してComboBoxに表示するDataShowタスクを実行します。非同期処理で行なわれるためメソッドの先頭にAsyncを付けます。

リスト4 ページがアクティブになった時の処理

    Protected Overrides Async Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
        AddHandler Window.Current.VisibilityChanged, AddressOf Current_Changed
        Await DataShow()
        MyBase.OnNavigatedTo(e)
    End Sub

次は、ページがアクティブになった時に実行されるCurrent_Changedイベントの処理です(リスト5)。

ウインドウが表示されている場合は、CameraRestartタスクを実行します。非同期処理で行われるため、メソッドの先頭にAsyncを追加します。

リスト5 現在アクティブになって入るウインドウのVisibleプロパティの値が変更された時の処理

    Private Async Sub Current_Changed(sender As Object, e As VisibilityChangedEventArgs)
        If e.Visible = True Then
            Await CameraRestart()
        End If
    End Sub

リスト5でウインドウが表示されている場合と書いていましたが、そのウインドウが表示されているときの処理がリスト6です。

新しいMediaCaptureのインスタンスを作成します。MediaCaptureオブジェクトの初期化設定を含む、新しいMediaCaptureInitializationSettingsクラスのインスタンスを作成し、settings変数で参照します。カメラのデバイス情報を持っている、myCameraから、非表示となっているコンボボックスのインデックスが0の値のデバイスIDを表す文字列を取得して、VideoDeviceIdプロパティに指定します。この場合は、例えばタブレットPCでリアカメラやフロントカメラが実装されていても、常にフロントカメラを選択して、その画像を表示します。InitializeAsyncメソッドで、MediaCaptureオブジェクトをsettingオブジェクトで初期化します。CaptureElementのSourceプロパティにmyMediaCaptureオブジェクトを指定します。StartPreviewAsyncメソッドでプレビューを開始します。

※注意:この処理を行っていないと、カメラがバックグラウンドに回って復帰した時に、カメラがハングアップしてしまうので、この処理は必ず必要です。非同期処理で行われるため、メソッドの先頭にAsyncを追加します。

リスト6 ウインドウが表示されているときの処理

   Private Async Function CameraRestart() As Task
       myMediaCapture = New MediaCapture
        Try
            Dim settings = New MediaCaptureInitializationSettings()
            settings.VideoDeviceId = myCamera(0).Id
            Await myMediaCapture.InitializeAsync(settings)
            CaptureElement1.Source = myMediaCapture
            Await myMediaCapture.StartPreviewAsync
        Catch
            Exit Function
        End Try
    End Function

次は、非表示となっているComboBoxにPCが実装しているデバイスの一覧を表示する処理です(リスト7)。

ピクチャライブラリにアクセスし、CreateFolderAsyncメソッドで「CameraSample」というサブフォルダーを作成します。CreationCollisionOption.OpenIfExistsを指定することで、既にフォルダーが存在する場合は、そのフォルダー名を返してくれます。フォルダーが存在しない場合は新規に作成してくれます。CreationCollisionOption列挙体については下記URLを参照してください。

http://msdn.microsoft.com/en-us/library/windows/apps/windows.storage.creationcollisionoption

FindAllAsyncメソッドで全てのビデオキャプチャデバイスを列挙して、DeviceInfomationオブジェクトのコレクションである、myCameraコレクション変数で参照します。

Webカメラが実装されている場合は、コレクション変数myCameraが格納しているデバイスの個数分、繰り返し変数iで反復処理を行います。DeviceInfomationの列挙体である、変数cameraInfoで、コレクション変数myCameraが格納しているデバイスを参照します。cameraComboBoxにAddメソッドで取得したデバイス名を追加していきます。フロントカメラやリアカメラを実装しているタブレットPCでは、二つのデバイス名が追加されます。最初のデバイスを選択します(リアとフロントカメラがある場合フロントカメラを表示します)。新しいMediaCaptureのインスタンスを作成します。InitializeAsyncメソッドで、MediaCaptureオブジェクトを初期化します。CaptureElementのSourceプロパティにmyMediaCaptureオブジェクトを指定します。StartPreviewAsyncメソッドでプレビューを開始します。これでフロントカメラの画像が表示されます。

エラーが発生した場合はErrorShowタスクを実行します。非同期処理で行われるため、メソッドの先頭にAsyncを追加します。

リスト7 実装されているデバイスの一覧を取得して、非表示になっているComboBoxに表示する処理。非表示であるためユーザーからは見えません。

    Private Async Function DataShow() As Task
        Dim myFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary
        Dim SubFolder = Await myFolder.CreateFolderAsync("CameraSample", CreationCollisionOption.OpenIfExists)
        Dim myPictureFile = Await SubFolder.GetFilesAsync
        Try
            cameraComboBox.Items.Clear()
            myCamera = Await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture)
            For i As Integer = 0 To myCamera.Count - 1
                Dim cameraInfo = myCamera(i)
                cameraComboBox.Items.Add(cameraInfo.Name)
            Next
            cameraComboBox.SelectedIndex = 0
                  myMediaCapture = New MediaCapture
            Await myMediaCapture.InitializeAsync()
            CaptureElement1.Source = myMediaCapture
            Await myMediaCapture.StartPreviewAsync
        Catch
            ErrorShow()
        End Try
    End Function

PCにWebカメラ等のデバイスが実装されていない場合はエラーが発生します。その場合のエラー処理です(リスト8)。

リスト8 Webカメラが実装されていない場合のエラー処理

    Private Async Sub ErrorShow()
        Dim message As New MessageDialog("カメラが装備されておりません。")
        Await message.ShowAsync
        shutterButton.IsEnabled = False
        Exit Sub
    End Sub

次に、[写真を撮る]アイコンがタップされた時の処理です。このアイコンがタップされると同時にWebカメラからの画像も保存されます(リスト9)。保存される場所はピクチャライブラリ内のCameraSampleというサブフォルダーに保存されます。削除機能はつけていませんので、不要になった画像はこのフォルダーに入って手動で削除して下さい。

  • Webカメラからの画像を保存するためのプログラム

    『Windows ストア アプリ 100行プログラミング』 第4回のサンプルプログラムです。
薬師寺国安事務所

薬師寺国安事務所代表。Visual Basic プログラミングと、マイクロソフト系の技術をテーマとした、書籍や記事の執筆を行う。
1950年生まれ。事務系のサラリーマンだった40歳から趣味でプログラミングを始め、1996年より独学でActiveXに取り組む。1997年に薬師寺聖とコラボレーション・ユニット PROJECT KySS を結成。2003年よりフリーになり、PROJECT KySS の活動に本格的に参加、.NETやRIAに関する書籍や記事を多数執筆する傍ら、受託案件のプログラミングも手掛ける。Windows Phoneアプリ開発を経て、現在はWindows ストア アプリを多数公開中

Microsoft MVP for Development Platforms - Client App Dev (Oct 2003-Sep 2012)。Microsoft MVP for Development Platforms - Windows Phone Development(Oct 2012-Sep 2013)。Microsoft MVP for Development Platforms - Client Development(Oct 2013-Sep 2014)。Microsoft MVP for Development Platforms-Windows Platform Development (Oct 2014-Sep 2015)。

連載バックナンバー

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

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

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

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