写真をハート型に切り抜いて撮影するサンプル

2012年6月4日(月)
PROJECT KySS

今回紹介するのは、写真をハート型などの形に添って切り抜いたような処理をするサンプルです。カメラアプリや、写真をデコレーションするアプリなどの処理として使うことができます。

はじめに動作内容を説明します。

  1. プログラムを立ち上げると、ハート型のフレーム内にカメラが表示される。
  2. 「カメラ」アイコンをタップするとシャッターが切られ、被写体が写し取られる。
  3. 画像とXMLファイルを保存した旨のメッセージが表示される。

Marketplaceで公開中の「どこカメ」 に使用しています。

ドコかめ

この時点で「フォルダ」アイコンが使用可能になり、タップすると映した画像の一覧が表示されます。一覧から好きな画像を選んでタップすると、コンテキストメニューが表示され、「データの削除」をタップすると画像が削除されます(図1)。

注意して欲しいのはカメラの解像度を640×480ピクセルに設定することです。規定値のままでは解像度が大きい(4128×3096ピクセル)ため、ストレージを圧迫してしまいます。

(*)このサンプルはエミュレーターでも動作はしますが、実機での動作確認を原則とします。

 図1:ハート型のフレーム内にカメラが表示され、「カメラ」アイコンで写真を撮る。「フォルダ」アイコンで写した画像の一覧が表示される。任意の画像をタップするとコンテキストメニューが表示される。メニュー内の「データの削除」をタップすると、任意の画像が削除される(クリックで拡大)

サンプル一式は、会員限定特典としてダウンロードできます。記事末尾をご確認ください。

実機(IS12T)で動かした動画はこちらです。

プロジェクトの作成

VS 2010のメニューから[ファイル(F)/新規作成(N)/プロジェクト(P)]と選択します。

次に、「Windows Phone アプリケーション」を選択して、「名前(N)」に任意のプロジェクト名を指定します。ここでは「WP71_ImageInTheCameraFrame」という名前を付けています。Windows Phoneのバージョンは7.1を選択します。

ソリューションエクスプローラー内にImageというフォルダを作り、ハート型の画像を追加しておきます。またIconsというフォルダを作り、C:\Program Files\Microsoft SDKs\Windows Phone\v7.1\Icons\darkにある、appbar.feature.camera.rest.pngとappbar.folder.rest.pngを追加しておきます。

名前もcamera.pngとfolder.pngに変更しておきます。これら2つの画像は、プロパティから「ビルドアクション」にコンテンツを指定してください。デフォルトのResourceのままでは画像は表示されませんので、注意してください。

VS2010メニューの「プロジェクト(P)/参照の追加(R)」と選択して、Microsoft.Phone.Controls.ToolkitとSystem.Xml.Linqを追加しておいてください。

Windows Phone Toolkitは下記URLよりダウンロードできますので、インストールしておいてください。
→参照:Windows Phone Toolkit - Nov 2011 (7.1 SDK)

MainPage.xamlの編集とコントロールの追加

x:NameがPageTitleというTextBlockのTextプロパティに「ハート型フレームを配置してカメラで撮る」と指定します。Styleプロパティに指定されているPhoneTextTitle1Styleでは文字サイズが大きすぎるので、PhoneTextTitle3Styleを指定して小さく表示します。

ツールボックスからInkPresenterコントロールを配置し、その子要素としてImageコントロールを2個と、Rectangleコントロールを1個配置します。一番手前に配置したImageコントロールのSourceプロパティにImageフォルダ内のハート型の画像を指定します。書き出されるXAMLコードをリスト1のように編集します。

リスト1 編集されたXAMLコード(MainPage.xaml)

(1)要素を配置しています。

(2)プロパティ要素内に、 要素を配置します。CompositeTransformクラスは、1つのオブジェクトに複数の異なる変換を適用することができるクラスです。CenterXとCenterYプロパティに、0.5と指定します。CenterXプロパティでは、CompositeTransformで指定された、すべての変換の中心点のx座標を、CenterYプロパティでは、CompositeTransformで指定された、すべての変換の中心点の y座標を設定します。回転を表すRotationに90を指定します。

(3)プロパティ要素内に、要素を配置します。CompositeTransformクラスは、1つのオブジェクトに複数の異なる変換を適用することができるクラスです。CenterXとCenterYプロパティに0.5と指定します。ImageコントロールのWidthとHeightの半分の値を指定しても構いません。その場合、コントロールの位置が変わりますので、調整してください。回転を表すRotationに90を指定します。

プロパティ要素内に要素を配置し、x:NameにmyVideoBrushと指定しておきます。VideoBrushは、ビデオ コンテンツで領域を塗りつぶす要素です。

(4)最前面に表示される要素のSourceプロパティにImageフォルダのハート型の画像を指定します。このハート型のPNG画像はハートの部分が透明化されている画像です。

(5) 要素のIconUriプロパティにIconsフォルダの画像を指定し、Clickイベントにイベントハンドラを指定します。

<phone:PhoneApplicationPage 
    x:Class="WP71_ImageInTheCameraFrame.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="696"
    FontFamily="{StaticResource PhoneFontFamilyNormal}"
    FontSize="{StaticResource PhoneFontSizeNormal}"
    Foreground="{StaticResource PhoneForegroundBrush}"
    SupportedOrientations="Portrait" Orientation="Portrait"
    shell:SystemTray.IsVisible="True">
 
    <!--LayoutRoot は、すべてのページ コンテンツが配置されるルート グリッドです-->
    <Grid x:Name="LayoutRoot" Background="White">
      <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="PageTitle" Text="?型フレームを配置してカメラで撮る" Foreground="Navy" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle3Style}"/>
      </StackPanel>
 
      <!--ContentPanel - 追加コンテンツをここに入力します-->
      <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,12">
        <InkPresenter x:Name="InkPresenter1" Margin="0,67,0,122"  Width="388" Background="White"> ■(1)
          <Image  Height="386" Name="ellipseImage" Stretch="Fill" Width="434" Canvas.Left="383" Canvas.Top="-7"> ■(2)
            <Image.RenderTransform> ■(2)
              <CompositeTransform Rotation="90" CenterX="0.5" CenterY="0.5"/> ■(2)
            </Image.RenderTransform> ■(2)
          </Image> ■(2)
          <Rectangle Height="386" Name="Rectangle1" Stroke="Black" StrokeThickness="1" Width="436" UseLayoutRounding="False" d:LayoutRounding="Auto" Canvas.Left="-128.61" Canvas.Top="68.592"> ■(3)
          <Rectangle.RenderTransform> ■(3)
            <CompositeTransform Rotation="90" CenterX="0.5" CenterY="0.5" /> ■(3)
          </Rectangle.RenderTransform> ■(3)
          <Rectangle.Fill> ■(3)
            <VideoBrush x:Name="myVideoBrush" /> ■(3)
          </Rectangle.Fill> ■(3)
        </Rectangle> ■(3)
        <Image Name="Image1" Stretch="Fill" Width="390" Canvas.Left="-3" Canvas.Top="-11" Height="438" Source="/WP71_ImageInTheCameraFrame;component/Image/heart_pink.png" />
      </InkPresenter> ■(4)
    </Grid>
  </Grid>
  
  <!--ApplicationBar の使用法を示すサンプル コード-->
  <phone:PhoneApplicationPage.ApplicationBar>
    <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
      <shell:ApplicationBarIconButton IconUri="/Icons/camera.png" Text="シャッターを切る" Click="GoShutter"/> ■(5)
      <shell:ApplicationBarIconButton IconUri="/Icons/folder.png" Text="データ一覧" Click="GoIchiran"/> ■(5)
    </shell:ApplicationBar>
  </phone:PhoneApplicationPage.ApplicationBar>

</phone:PhoneApplicationPage>

レイアウト図は図2のようになります。

 図2:各コントロールを配置した(クリックで拡大)

次に、MainPage.xamlを展開して表示される、MainPage.xaml.vbをダブルクリックしてリスト2のコードを記述します。

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

リスト2 (MainPage.xaml.vb)

Option Strict On
Imports Microsoft.Devices

仮想ファイルシステムの作成および、使用するための型が含まれている、System.IO.IsolatedStorage名前空間をインポートします。分離ストレージによって、安全なクライアント側のストレージが提供されます。

Imports System.IO.IsolatedStorage

Imports System.Windows.Media.Imaging
Imports System.IO
Imports System.Xml.Linq
Imports Microsoft.Phone
Partial Public Class MainPage
  Inherits PhoneApplicationPage
 
  ' コンストラクター
  Public Sub New()
    InitializeComponent()
  End Sub
  Dim bufferValue As Integer = 5119

myPhotoCameraをPhotoCameraクラスのメンバ変数として宣言します。PhotoCameraクラスには、カメラアプリケーションの基本カメラ機能を提供し、イメージ キャプチャ、フォーカス、解像度、フラッシュ モードなどの機能を有効にして構成するためのメンバが含まれています。

  Dim myPhotoCamera As PhotoCamera
  
  Dim imageFileName As String
  Dim recordDate As String

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

変数storageを、ファイルとディレクトリを格納している分離ストレージ領域を表すIsolateStorageFileクラスとして宣言します。DirectoryExistsメソッドでImageInFrameというフォルダが存在しているかどうかをチェックし、存在していない場合は、CreateDirectoryメソッドでImageInFrameというフォルダを作成します。

PhotoCameraクラスの新しいインスタンスを作成し、myVideoBrushという名前のVideoBrushにSetSourceメソッドでmyPhotoCameraオブジェクトを指定します。

AddHandlerステートメントで、カメラ オブジェクトが初期化されたときに発生する、Initializedイベントにイベントハンドラを追加します。Succeededプロパティで、カメラ操作が失敗した時は処理を抜けます。

それ以外は変数myResolutionを使って、AvailableResolutionsプロパティで、使用できるカメラの解像度を参照します。カメラでキャプチャしたイメージの解像度を設定できるResolutionプロパティに、先に取得したmyResolutionの一番先頭の解像度を指定します。一番先頭の解像度は640×480ピクセルです。

Try~Catch~End Tryで例外処理を行います。例外が発生した場合は、メッセージを表示します。別スレッドからの表示になります。

Path.CombineでImageInFrameというフォルダとImageList.xmlというファイルを連結し、変数xmlFilePathに格納します。ImageList.xmlファイルがImageInFrameフォルダに存在する場合は、「データ一覧」のフォルダアイコンの使用を可能にし、それ以外は使用不可としておきます。

AddHandlerステートメントで、イメージが利用可能な場合に発生する、CaptureImageAvailableイベントに、myPhotoCamera_CaptureImageAvailableイベントハンドラを追加します。

  Protected Overrides Sub OnNavigatedTo(e As System.Windows.Navigation.NavigationEventArgs)
      Dim storage As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication
      If storage.DirectoryExists("ImageInFrame") = False Then
        storage.CreateDirectory("ImageInFrame")
      End If
 
      myPhotoCamera = New PhotoCamera
      myVideoBrush.SetSource(myPhotoCamera)
 
      AddHandler myPhotoCamera.Initialized, Sub(cameraSender As Object, cameraArgs As CameraOperationCompletedEventArgs)
        Try
          If cameraArgs.Succeeded = False Then
            Exit Sub
          Else
            Dim myResolution As IEnumerable(Of Size) = myPhotoCamera.AvailableResolutions
            myPhotoCamera.Resolution = myResolution.First
          End If
        Catch
            Deployment.Current.Dispatcher.BeginInvoke(Sub()
                       MessageBox.Show("カメラが起動するまでお待ちください。")
                                                   End Sub)
        End Try
      End Sub
 
      Dim xmlFilePath As String = Path.Combine("ImageInFrame", "ImageList.xml")
      If storage.FileExists(xmlFilePath) = True Then
          TryCast(ApplicationBar.Buttons(1), ApplicationBarIconButton).IsEnabled = True
      Else
          TryCast(ApplicationBar.Buttons(1), ApplicationBarIconButton).IsEnabled = False
      End If
      
      AddHandler myPhotoCamera.CaptureImageAvailable, AddressOf myPhotoCamera_CaptureImageAvailable
    MyBase.OnNavigatedTo(e)
  End Sub
  • フレームで切り抜いて撮影するサンプル

四国の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メルマガ会員のサービス内容を見る

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