連載
» 2012年12月20日 14時30分 UPDATE

WinRT/Metro TIPS:Webカメラで撮影するには?[Win 8]

タブレットPCやノートPCの多くには「Webカメラ」が搭載されている。これを使ってWindowsストア・アプリで写真や動画を撮影する方法を説明する。

[山本康彦,BluewaterSoft]
WinRT/Metro TIPS
業務アプリInsider/Insider.NET

powered by Insider.NET

「WinRT/Metro TIPS」のインデックス

連載目次

 タブレットPCやノートPCの多くには、「Webカメラ」と呼ばれるカメラが搭載されている。これを使ってWindowsストア・アプリで写真や動画を撮影するには、どうしたらよいだろうか? 本稿では、Webカメラで写真を撮る方法を説明する。本稿のサンプルは「Windows Store app samples:MetroTips #18」からダウンロードできる。

事前準備

 Windows 8(以降、Win 8)向けのWindowsストア・アプリを開発するには、Win 8とVisual Studio 2012(以降、VS 2012)が必要である。これらを準備するには、第1回のTIPSを参考にしてほしい。本稿では64bit版Win 8 ProとVS 2012 Express for Windows 8を使用している。

Webカメラを使うWindowsストア・アプリを作るには?

 Webカメラを使うには、まずそのための「機能」(capability)をアプリに与えねばならない*1。それにはアプリケーション・マニフェストで宣言する。プロジェクトのPackage.appxmanifestファイルをVS 2012で開き、[機能]タブで[マイク]と[Webカメラ]にチェックを入れる(次の画像)。

ap-winrttips_0018_01.gif VS 2012でアプリケーション・マニフェストを編集する(部分)

 なお、Webカメラを使うためには、必ず[マイク]の「機能」も与えなければならない。Webカメラで動画を撮影する際には、自動的にマイクも使われるからだ。

*1 原語は「capability」となっており、「(何かをする)能力がある状態」を意味する。「function」とは意味が違うので、文脈で読み取ってほしい。例えば「Webカメラのcapabilityをアプリに与える」とは、「アプリを、Webカメラを使える(という能力がある)状態にする」という意味であって、「アプリにWebカメラの機能を持たせる」ということではない。capabilityを与えるとアプリはその能力を使えるようになるが、コードを実装しなければ機能しないのだ。


簡単に写真を撮るには?

 CameraCaptureUIクラス(Windows.Media.Capture名前空間)を使うと、簡単に写真や動画を撮影できる。撮影用のUI(ユーザー・インターフェイス)が用意されているので、次のようなコードを書くだけでよい。

// ダイアログ生成
var dialog = new Windows.Media.Capture.CameraCaptureUI();

// 写真のフォーマットを指定
dialog.PhotoSettings.Format
    = Windows.Media.Capture.CameraCaptureUIPhotoFormat.Png;

// 写真撮影モードでダイアログを起動
var file = await dialog.CaptureFileAsync(
                   Windows.Media.Capture.CameraCaptureUIMode.Photo);

// 撮影できると、保存されたファイルのStorageFileが返ってくる。
// 保存先はApplicationData.Current.TemporaryFolder固定
if (file != null)
{
  var msgbox = new Windows.UI.Popups.MessageDialog(file.Path, "撮影結果");
  await msgbox.ShowAsync();
}


' ダイアログ生成
Dim dialog = New Windows.Media.Capture.CameraCaptureUI()

' 写真のフォーマットを指定
dialog.PhotoSettings.Format _
      = Windows.Media.Capture.CameraCaptureUIPhotoFormat.Png

' 写真撮影モードでダイアログを起動
Dim file = Await dialog.CaptureFileAsync(
                    Windows.Media.Capture.CameraCaptureUIMode.Photo)

' 撮影できると、保存されたファイルのStorageFileが返ってくる。
' 保存先はApplicationData.Current.TemporaryFolder固定
If (file IsNot Nothing) Then
  Dim msgbox = New Windows.UI.Popups.MessageDialog(file.Path, "撮影結果")
  Await msgbox.ShowAsync()
End If


CameraCaptureUIを使って写真を撮るコード(上:C#、下:VB)


 なお、FormatプロパティCaptureFileAsyncメソッドの引数を変えるだけで、動画の撮影も可能である。また、CameraCaptureUIクラスはフルスクリーンでしか機能しない。スナップ状態ではCaptureFileAsyncメソッド呼び出しで例外が出てしまうので注意してほしい。

初めて実行すると……?

 上のコードを試してみると、最初に実行したときに次の画像のようなメッセージ・ダイアログが出る。

ap-winrttips_0018_02.gif 初回実行時に出るダイアログ(部分)

 [許可]をタップすれば撮影できる。[ブロック]を選ぶと元の画面に戻ってしまい、撮影できなくなる。Webカメラの「機能」はマニフェストで付与しただけでは有効にならず、ユーザーの許可が必要なのだ。これはアプリの設定チャームの[アクセス許可]で、ユーザーがいつでも変更できる。実行中にWebカメラの[アクセス許可]を取り消されても問題を起こさないコードを書かねばならない。

独自のUIを構築するには?

 CameraCaptureUIクラスが提供するUIはお仕着せのものだった。CaptureElementコントロール(Windows.UI.Xaml.Controls名前空間)と、MediaCaptureクラス(Windows.Media.Capture名前空間)を使えば、独自のUIを作ることもできる。

 次の画像のようなUIを作ってみよう。画面の左半分にCaptureElementコントロールを置き、プレビューを表示する。右半分にはImageコントロールを置いて、撮影した画像を表示する。また、下部にはボタンを3つ配置し、左の2つはプレビュー開始、右端は撮影とした。

ap-winrttips_0018_03.gif CaptureElementコントロールを使う画面(XAMLデザイナ)

 この部分のXAMLコードは次のようになる。

<Grid Grid.Row="1" Background="#002244">
  <Grid.RowDefinitions>
    <RowDefinition Height="*" />
    <RowDefinition Height="Auto" />
  </Grid.RowDefinitions>

  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="1*" />
      <ColumnDefinition Width="1*" />
    </Grid.ColumnDefinitions>
    <CaptureElement x:Name="capturePreview" Grid.Column="0" />
    <Image x:Name="captureImage" Grid.Column="1" />
  </Grid>

  <StackPanel Grid.Row="1" Orientation="Horizontal"
      HorizontalAlignment="Center" Margin="0,10">
    <Button  Content="補正無し" HorizontalAlignment="Center" FontSize="40"
      Click="buttonPreviewClick" />
    <Button  Content="手ブレ補正" HorizontalAlignment="Center" FontSize="40"
      Click="buttonPreviewWithVideoStabilizationClick" />
    <Button  Content="《 撮影 》" HorizontalAlignment="Center" FontSize="40"
      Click="buttonTakePhotoClick" />
  </StackPanel>

</Grid>


CaptureElementコントロールを使う画面のXAMLコード


プレビューを表示するには?

 次のコードで、Webカメラの画像がCaptureElementコントロールに映し出されるようになる。MediaCaptureクラスのインスタンスを作り、初期化を行ってからCaptureElement コントロールに結び付け、StartPreviewAsyncメソッドを呼び出せばよい。

private async void buttonPreviewClick(object sender, RoutedEventArgs e)
{
  // 静止画撮影、動画録画を行うキャプチャ・オブジェクト
  var _capture = new Windows.Media.Capture.MediaCapture();

  try
  {
    // Webカメラの初期化
    await _capture.InitializeAsync();
  }
  catch (UnauthorizedAccessException ex)
  {
    // Webカメラがユーザーに許可されていないと、
    // UnauthorizedAccessException が出る
    return;
  }

  // Effectの追加はここで行う …… (1)

  // MediaCaptureインスタンスをCaptureElementコントロールに設定する
  capturePreview.Source = _capture;

  // プレビューを開始する
  await _capture.StartPreviewAsync();
}


Private Async Sub buttonPreviewClick(sender As Object, e As RoutedEventArgs)
  ' 静止画撮影、動画録画を行うキャプチャ・オブジェクト
  Dim _capture = New Windows.Media.Capture.MediaCapture()

  Try
    ' Webカメラの初期化
    Await _capture.InitializeAsync()

  Catch ex As UnauthorizedAccessException
    ' Webカメラがユーザーに許可されていないと、
    ' UnauthorizedAccessExceptionが出る
    Return
  End Try

  ' Effectの追加はここで行う …… (1)

  ' MediaCaptureインスタンスをCaptureElementコントロールに設定する
  capturePreview.Source = _capture

  ' プレビューを開始する
  Await _capture.StartPreviewAsync()
End Sub


CaptureElementコントロールにプレビューを表示するコード(上:C#、下:VB)


 さらに、 Windows.Media.VideoEffects名前空間にあるビデオエフェクトを掛けることもできる。現在は、手振れ補正エフェクトだけが用意されている。手振れ補正効果を使うには、上のコードの(1)の場所に次のコードを追加する。

  // キャプチャしたビデオに手ブレ補正効果を追加する
  await _capture.AddEffectAsync(
      Windows.Media.Capture.MediaStreamType.VideoRecord,
      Windows.Media.VideoEffects.VideoStabilization,
      null);


  ' キャプチャしたビデオに手ブレ補正効果を追加する
  Await _capture.AddEffectAsync(
      Windows.Media.Capture.MediaStreamType.VideoRecord,
      Windows.Media.VideoEffects.VideoStabilization,
      Nothing)


手振れ補正エフェクトを追加するコード(上:C#、下:VB)


 なお、同様の処理を、サスペンドからのリジューム時と、バックグラウンドからフォアグラウンドに戻ってきたときにも行う必要がある。

プレビューした画像を撮影するには?

 プレビューに表示されている映像から写真を撮影するには、次のようなコードを記述する。実際にプレビューを表示しているCaptureElementコントロールに結び付けられているMediaCaptureオブジェクトだけが撮影できる。MediaCaptureオブジェクトだけでは撮影できないので注意してほしい。

private async void buttonTakePhotoClick(object sender, RoutedEventArgs e)
{
  if (capturePreview.Source == null)
    return;

  // プレビューに付けた MediaCapture オブジェクトを取り出す
  Windows.Media.Capture.MediaCapture capture = capturePreview.Source;
     
  // 保存する画像ファイルのフォーマット(.pngファイルを指定)
  var imageProperties
    = Windows.Media.MediaProperties.ImageEncodingProperties.CreatePng();

  // 撮影する。一度ファイルに保存する
  StorageFile file
    = await ApplicationData.Current.TemporaryFolder.CreateFileAsync(
      "test.png", CreationCollisionOption.GenerateUniqueName);
  try
  {
    await capture.CapturePhotoToStorageFileAsync(imageProperties, file);
    // キャプチャ開始から撮影までの間に許可を取り消されると、ここで例外が出る
  }
  catch (Exception ex)
  {
    return;
  }

  // 撮影したファイルを読み込んで右側のImageコントロールに表示する
  using (var stream = await file.OpenReadAsync())
  {
    var bi = new BitmapImage();
    bi.SetSource(stream);
    captureImage.Source = bi;
  }
}


Private Async Sub buttonTakePhotoClick(sender As Object, e As RoutedEventArgs)
  If (capturePreview.Source Is Nothing) Then
    Return
  End If

  ' プレビューに付けたMediaCaptureオブジェクトを取り出す
  Dim capture As Windows.Media.Capture.MediaCapture = capturePreview.Source

  ' 保存する画像ファイルのフォーマット(.pngファイルを指定)
  Dim imageProperties = Windows.Media.MediaProperties.ImageEncodingProperties.CreatePng()

  ' 撮影する。一度ファイルに保存する
  Dim file As StorageFile = Await
    ApplicationData.Current.TemporaryFolder.CreateFileAsync("test.png",
    CreationCollisionOption.GenerateUniqueName)
  Try
    Await capture.CapturePhotoToStorageFileAsync(imageProperties, file)
    ' キャプチャ開始から撮影までの間に許可を取り消されると、ここで例外が出る
  Catch ex As Exception
    Return
  End Try

  '撮影したファイルを読み込んで右側のImageコントロールに表示する
  Using stream = Await file.OpenReadAsync()
    Dim bi = New BitmapImage()
    bi.SetSource(stream)
    captureImage.Source = bi
  End Using
End Sub


プレビューの画像を撮影してImageコントロールに表示するコード(上:C#、下:VB)


 なお、ここでは撮影した画像がファイルに保存されるCapturePhotoToStorageFileAsyncメソッドを使った。それとは別に、任意のストリームに書き出してくれるメソッドもある。そのCapturePhotoToStreamAsyncメソッドを使えば、撮影した画像をメモリに書き出すこともできるのだが、それをビットマップに変換するAPIは用意されていない(変換するコードを書くことは可能である)。

実行結果

 以上を実装して動かしてみたのが次の画像だ。

ap-winrttips_0018_04.gif 実行している様子

 筆者宅の近くにあるイチョウの木を撮影してみた。手振れ補正は、静止画では効果があるのかどうかが分からなかった。

まとめ

 Webカメラで撮影するには、簡便な方法と自前でUIを構築する方法とがある。UIを構築する場合に本稿で説明しきれなかったこととして、バックグラウンドに回された後のプレビュー再開や、カメラが複数搭載されているPCにおけるカメラ選択などの課題がある。

 詳しくは、次のドキュメントを参考にしてほしい。

「WinRT/Metro TIPS」のインデックス

WinRT/Metro TIPS

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

Focus

- PR -

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。