Webカメラで撮影した写真をセピア調に演出するアプリを作る

2013年9月3日(火)
薬師寺 国安

戻る(←)アイコンがタップされた時の処理

ピクチャライブラリにアクセスし、ピクチャライブラリ内にsepiaという文字を含んだファイル名がある場合は、「Folder」アイコンの使用を可能にし、それ以外は不可とします。

myFrameを非表示、「Sepia調に変換」ボタンを使用可、「Attach Camera」アイコンを使用可、GridViewを表示状態にします。
Webカメラのデバイス名が表示されているComboBox内の、先頭のデバイスを選択状態にします。よって、リアカメラを選択していた場合でも、戻る(←)アイコンをタップして、元の画面に戻った際は、フロントカメラが選択されていることになります。これは、どのデバイスが先頭に来るかによって異なります。

非同期処理で行われるためメソッドの先頭にAsyncを追加します。

  Private Async Sub backButton_Click(sender As Object, e As RoutedEventArgs) Handles backButton.Click
    Dim myFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary
    Dim myPictureFiles = Await myFolder.GetFilesAsync()
    For Each myPhoto In myPictureFiles
      If myPhoto.Path.Contains("sepia") Then
        ichiranButton.IsEnabled = True
      Else
        ichiranButton.IsEnabled = False
      End If
    Next
    myFrame.Visibility = Xaml.Visibility.Collapsed
    changeSepiaButton.IsEnabled = True
    shutterButton.IsEnabled = True
    GridView1.Visibility = Xaml.Visibility.Visible
    cameraComboBox.SelectedIndex = 0
  End Sub

WriteableBitmapExtensions.FromStreamAsyncメソッドを使って、リソース内の画像を読み込む関数

ピクチャライブラリのSepiaSourceImageサブフォルダにアクセスし、GetFilesAsyncメソッドでファイルを取得して、コレクション変数_myPictureFilesに格納します。
メンバ変数fileNameListをクリアしておきます。

ファイル名を格納している_myPictureFilesコレクション変数内のファイルを、変数myPtotoFileに格納しながら、以下の処理を繰り返します。
fileNameListオブジェクトにAddメソッドで、フルパス付のファイル名をPathプロパティで取得し、Path.GetFileNameWithoutExtensionメソッドで、パスと拡張子を除いたファイル名に、文字列「.jpg」を連結して追加していきます。
fileNameListオブジェクト内に格納されたファイル名で、GridView1.SelectedIndexに該当するファイルを、GetFileAsyncメソッドで取得し、imageFileで参照します。
WriteableBitmapExtensions.FromStreamAsyncメソッドでリソース内の画像を読み込み、戻り値とします。非同期処理で行われるため関数の先頭にAsyncを追加します。

  Private Async Function GetTestImageAsync() As Task(Of WriteableBitmap)
    Dim myFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary
    Dim mySubFolder As StorageFolder = Await myFolder.CreateFolderAsync("SepiaSourceImage", CreationCollisionOption.OpenIfExists)
    Dim _myPictureFiles = Await mySubFolder.GetFilesAsync
    fileNameList.Clear()
    For Each myPhotoFile In _myPictureFiles
      fileNameList.Add(Path.GetFileNameWithoutExtension(myPhotoFile.Path) & ".jpg")
    Next
    Dim imageFile As StorageFile = Await mySubFolder.GetFileAsync(fileNameList(GridView1.SelectedIndex))
    Return Await WriteableBitmapExtensions.FromStreamAsync(Await imageFile.OpenReadAsync)
  End Function

「Sepia調に変換」ボタンがクリックされた時の処理

GetTestImageAsync関数を呼び出し、読み込んだ画像ファイルを、myBmpで参照します。

myBmp.EffectSepiaメソッドでSepia調に変換された画像を、メンバ変数mySourceで参照します。
Image1のSourceプロパティにmySourceを指定します。これで、Image1にセピア調に変換された画像が表示されます。「Save」アイコンの使用を可能にします。

  Private Async Sub changeSepiaButton_Click(sender As Object, e As RoutedEventArgs) Handles changeSepiaButton.Click
    Dim myBmp = Await GetTestImageAsync()
    mySource = myBmp.EffectSepia
    Image1.Source = mySource
    saveButton.IsEnabled = True
  End Sub

「Save」アイコンがタップされた時の処理

Image1.Sourceの値をDirectCastでWriteableBitmapにキャストして、myBmpオブジェクトで参照します。

SaveAsyncメソッドで、保存する場所をピクチャライブラリ内、保存する画像形式をPngに指定し、保存するファイル名を、文字列「sepia」を先頭に追加した「yyyy年MM月dd日HH時mm分ss秒」形式とし、Widthを640、Heightを480として保存します。

「sepia2013年01月01日10時10分10秒.png」といった形式の画像ファイルが保存されます。

「ピクチャフォルダー内に保存しました。」のメッセージを表示し、「Folder」アイコンの使用を可能に、「Save」アイコンの使用を不可とします。非同期処理で行われるためメソッドの先頭にAsyncを追加します。

  Private Async Sub saveButton_Click(sender As Object, e As RoutedEventArgs) Handles saveButton.Click
    Dim myBmp = DirectCast(Image1.Source, WriteableBitmap)
    Await myBmp.SaveAsync(ImageDirectories.PicturesLibrary, ImageFormat.Png, "sepia" & DateTime.Now.ToString("yyyy年MM月dd日HH時mm分ss秒"), 640, 480)
    Dim message As New MessageDialog("ピクチャフォルダ内に保存しました。")
    Await message.ShowAsync
    ichiranButton.IsEnabled = True
    saveButton.IsEnabled = False
  End Sub

「Folder」アイコンがタップされた時の処理

myFrameを表示状態にします。「Sepia調に変換」ボタンの使用を不可、「AttachCamera」アイコンの使用を不可、GridView1を非表示状態にします。

NavigateメソッドでIchiranShowPageに遷移します。値は何も渡さないのでNothingを指定します。

  Private Sub ichiranButton_Click(sender As Object, e As RoutedEventArgs) Handles ichiranButton.Click
    myFrame.Visibility = Xaml.Visibility.Visible
    changeSepiaButton.IsEnabled = False
    shutterButton.IsEnabled = False
    GridView1.Visibility = Xaml.Visibility.Collapsed
    myFrame.Navigate(GetType(IchiranShowPage), Nothing)
  End Sub
End Class

次に、ソリューションエクスプローラー内のIchiranShowPage.xamlを展開して表示される、IchiranShowPage.xaml.vbをダブルクリックしてリスト4のコードを記述します。

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

リスト4 (IchiranShowPage.xaml.vb)

Option Strict On

Imports Windows.Storage
Imports Windows.UI
Imports Windows.UI.Popups
Public NotInheritable Class IchiranShowPage
  Inherits Page

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

「ファイル名」と「削除」ボタンの付いたセピア調に変換された画像を一覧表示する、DataShowプロシージャを実行します。

  Protected Overrides Async Sub OnNavigatedTo(e As Navigation.NavigationEventArgs)
    DataShow()
  End Sub

「ファイル名」と「削除」ボタンの付いたセピア調に変換された画像を一覧表示する処理

ピクチャライブラリにアクセスします。GetFilesAsyncメソッドでピクチャライブラリ内の画像を取得して、コレクション変数myPictureFilesに格納します。

ファイルを格納しているmyPictureFilesコレクション内のファイルを、変数myPhotoに格納しながら、以下の処理を繰り返します。ファイル名に「sepia」という文字列が含まれている場合の処理です。

新しいBitmapImageのインスタンスbmpオブジェクトを作成します。SetSourceメソッドに、

Await myPhoto.OpenReadAsync

と指定し、OpenReadAsyncメソッドで、現在のファイルのランダムアクセスストリームを開きBitmapSourceのソースイメージに設定します。
新しいImageのインスタンスmyImageを作成します。Widthに640、Heightに480、StretchにNone、Sourceプロパティにbmpオブジェクトを指定します。

新しいTextBlockのインスタンスmyTextBlockオブジェクトを作成します。
文字サイズに20、文字色にBeige、Textプロパティに、パスと拡張子を除いたファイル名から、SubStringメソッドで6文字目から20文字取り出し、指定します。

「sepia2013年01月01日10時10分10秒.png」形式から、sepiaの文字と拡張子を除いたファイル名になります。中央揃えとしておきます。

新しいButtonのいインスタンスmyButtonを作成します。Contentに「削除」、文字サイズに18、Tagプロパティには、パスと拡張子を除いたファイル名に、文字列「.png」を追加した値を指定しておきます。中央揃えとしておきます。

新しいStackPanelのインスタンスmyStackPanelオブジェクトを作成します。
Marginに5を指定して余白を設けます。
Addメソッドで、myStackPanelオブジェクトに、myImage、myTextBlock、myButtonオブジェクトを追加します。

GridView1にAddメソッドでmyStackPanelオブジェクトを追加します。
これで、セピア調の画像にファイル名と削除ボタンの付いた画像の一覧がGridView内に表示されます。

AddHandlerステートメントで「削除」ボタンがクリックされた時のイベントハンドラを追加します。
イベントハンドラ内では以下の処理を行います。

Buttonの情報を格納しているdeSenderオブジェクトをDirectCastでButtonにキャストして、そのTagプロパティの値を取得して、変数myFileNameに格納しておきます。
GetFileAsyncメソッドで、変数myFileNameに格納されている画像ファイルを取得し変数delImageNameで参照します。
DeleteAsyncメソッドで参照していたファイルを削除します。

「削除しました。」のメッセージを表示し、GridView内をクリアします。
画面を再描画するために、DataShowプロシージャを実行します。

  Private Async Sub DataShow()
    Dim myFolder As StorageFolder = Windows.Storage.KnownFolders.PicturesLibrary
    Dim myPictureFiles = Await myFolder.GetFilesAsync()
 
    For Each myPhoto In myPictureFiles
      If myPhoto.Path.Contains("sepia") Then
        Dim bmp As New BitmapImage
        bmp.SetSource(Await myPhoto.OpenReadAsync)
        Dim myImage As New Image
        myImage.Width = 640
        myImage.Height = 480
        myImage.Stretch = Stretch.None
        myImage.Source = bmp
 
        Dim myTextBlock As New TextBlock
        With myTextBlock
          .FontSize = 20
          .Foreground = New SolidColorBrush(Colors.Beige)
          .Text = Path.GetFileNameWithoutExtension(myPhoto.Path).Substring(5, 20)
          .HorizontalAlignment = Xaml.HorizontalAlignment.Center
        End With
 
        Dim myButton As New Button
        With myButton
          .Content = "削除"
          .FontSize = 18
          .Tag = Path.GetFileNameWithoutExtension(myPhoto.Path) & ".png"
          .HorizontalAlignment = Xaml.HorizontalAlignment.Center
        End With
        Dim myStackPanel As New StackPanel
        myStackPanel.Margin = New Thickness(5)
        myStackPanel.Children.Add(myImage)
        myStackPanel.Children.Add(myTextBlock)
        myStackPanel.Children.Add(myButton)
        GridView1.Items.Add(myStackPanel)
 
        AddHandler myButton.Click, Async Sub(delSender As Object, delArgs As RoutedEventArgs)
          Dim myFileName As String = CStr(DirectCast(delSender, Button).Tag)
          Dim delImageName = Await myFolder.GetFileAsync(myFileName)
          Await delImageName.DeleteAsync
          Dim myMessage As New MessageDialog("削除しました。")
          Await myMessage.ShowAsync
          GridView1.Items.Clear()
          DataShow()
          End Sub
          End If
        Next
    End Sub
  End Class

アイコンの作成

詳細については、「自分の現在位置を取得して表示するサンプルプログラム」の記事を参照してください。

今回はここまでです。ありがとうございました。

薬師寺国安事務所

薬師寺国安事務所代表。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メルマガ会員のサービス内容を見る

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