写真から顔を自動認識して、簡単に目隠し加工する(後編)

2012年3月2日(金)
PROJECT KySS

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

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

リスト2 (FacialrecognitionPage.xaml.vb)

Option Strict On
Imports System.Xml.Linq
Imports System.Windows.Media.Imaging

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

  Imports System.IO.IsolatedStorage

曲、アルバム、再生リスト、およびピクチャを列挙、再生、および表示するためのクラスの含まれる、Microsoft.Xna.Framework.Media名前空間をインポートします。MediaLibrayクラスを使って画像をPicturesに保存するため、この名前空間が必要です。

Imports Microsoft.Xna.Framework.Media
Imports System.IO

Partial Public Class FacialrecognitionPage
  Inherits PhoneApplicationPage
 
  Public Sub New()
    InitializeComponent()
  End Sub

「顔認識WebAPI」を使用するのに必要な「認証キー」を文字列型の定数変数として宣言します。

  Const AppId As String = "認証キー"
  
  Dim uriString As String

2点間の直線を描画するLineクラス型のメンバ変数myLineを宣言します。

  Dim myLine As Line

XMLの要素を表すXElementクラス型のメンバ変数myDocを宣言します。

  Dim myDoc As XElement
  
  Dim myIndex As Integer

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

ここで、前編で作成した、ImageShowPage.xamlから渡された文字データを受け取ります。文字データはNavigationContextのQueryStringにDictionary として提供されます。送信時のキーワード(この場合Index)を基に渡された文字列情報の値(myParam(“Index”))を取得します。ListBoxから選択された画像のインデックスを取得します。この値を数値に変換してmyIndexに格納します。
新しいWebClientのインスタンスmyWebClientオブジェクトを作成します。WebClientクラスは、データの送受信用のメソッドを提供するクラスです。
String またはUriとして指定したリソースをダウンロードする、DownloadStringAsyncメソッドで、サーバ上のImageInfo.xmlをダウンロードします。キャッシュから読み込まないように、常に新しいデータを読み込むよう、引数に現在の時間、分、秒を指定しています。
AddHandlerステートメントで、非同期のリソース ダウンロード操作の完了時に発生する、DwonloadStringCompletedイベントに、イベントハンドラを追加します。イベントハンドラ内では以下の処理を実行します。
ダウンロードが成功しなかった場合は、警告メッセージを出して、処理を抜けます。成功した場合は、XElement.Parseメソッドでダウンロードした結果(resultArgs.Result)を読み込みます。Image1のSourceプロパティに、ユーザーの専用サーバでImageInfo.xmlを保存しているフォルダ名と、変数myIndexに位置する要素の値を連結して、BitmapImageを初期化して指定します。Image1にImageShowPage.xamlで選択された画像が表示されます。

  Protected Overrides Sub OnNavigatedTo(e As System.Windows.Navigation.NavigationEventArgs)
    Dim myParam As IDictionary(Of String, String) = NavigationContext.QueryString
    myIndex = Integer.Parse(myParam("Index"))
 
    Dim myWebClient As New WebClient
 
    AddHandler myWebClient.DownloadStringCompleted, Sub(resultSender As Object, resultArgs As DownloadStringCompletedEventArgs)
               If resultArgs.Error Is Nothing = False Then
                  MessageBox.Show("XMLファイルが見つかりません")
                  Exit Sub
               Else
                   If myIndex < 0 Then
                       eyeMaskButton.IsEnabled = False
                       Exit Sub
                   Else
                       myDoc = XElement.Parse(resultArgs.Result)
                       Image1.Source = New BitmapImage(New Uri("ユーザの専用サーバURL/ImageFileUpload/ImageData/" & myDoc.Descendants("fileName")(myIndex).Value, UriKind.Absolute))
                   End If
                End If
           End Sub
  
        myWebClient.DownloadStringAsync(New Uri(String.Format("ユーザの専用サーバURL/ImageFileUpload/ImageData/ImageInfo.xml?myTime={0}", DateTime.Now.ToLongTimeString), UriKind.Absolute))
 
        MyBase.OnNavigatedTo(e)
    End Sub

[目隠し]ボタンがタップされた時の処理

人物が2人写っている場合は、図3のようなXMLが返されます。

図3:人物が2人の場合に返されるXML(XML宣言は省略)

顔の高さ、左目のX座標、左目のY座標、右目のX座標、右目のY座標を格納する変数を宣言します。ユーザの専用サーバのImageDataフォルダ内にある、変数myIndexに位置する要素の値で初期された、新しいUriクラスのインスタンスmyUriを作成します。
API へのリクエストは、apikeyや画像のurlなどの条件を下記のような URL で指定します。(REST)
apikeyには「認証キー」を指定します。urlには作成したmyUriオブジェクトを指定します。
Dim imageUri = String.Format("https://kaolabo.com/api/detect?apikey={0}&url={1}", AppId, myUri)

新しいWebClientのインスタンスmyWebClientオブジェクトを作成します。WebClientクラスは、データの送受信用のメソッドを提供するクラスです。
String またはUriとして指定したリソースをダウンロードするDownloadStringAsyncメソッドで、APIへのリクエストを指定した、imageUriを絶対URIでダウンロードします。
AddHandlerステートメントで、非同期のリソース ダウンロード操作の完了時に発生する、DwonloadStringCompletedイベントに、イベントハンドラを追加します。イベントハンドラ内では以下の処理を実行します。
ダウンロードした結果XMLを変数readXmldocに格納します。このままだと、名前空間が付加されていて、うまくXMLの内容を取得できませんので、ルート要素とその内容だけのXMLに、Replaceメソッドで書き換えます。
書き換えた結果XMLを、XElement.Parseメソッドで読み込みます。
Descendantsメソッドで、子孫要素であるすべての要素(図3参照)のコレクションに対して、各要素を変数resultに格納しながら、顔の高さや左右の目の”x”、”y”属性の値を取得し、数値に変換して各変数に格納します。
Lineの新しいインスタンスmyLineを作成します。myLineのX1、Y1、X2、Y2プロパティに、取得した左右の目のXとY座標を指定します。指定している数値は「目隠し」で表示される位置を調整している数値です。Strokeプロパティに赤色を指定します。StrokeThicknessプロパティには、顔の高さを10で除算した値を指定します。顔の高さを10で除算した幅で、目のXとY座標の2点に向かって線が引かれます。これが「目隠し」になります。InkPresenter1に、このmyLineオブジェクトを追加します。[PicturesHUBに保存]ボタンの使用を可能にします。

  Private Sub eyeMaskButton_Click(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles eyeMaskButton.Click
    Dim faceHeight As Integer = 0
    Dim left_eyeX As Integer = 0
    Dim left_eyeY As Integer = 0
    Dim right_eyeX As Integer = 0
    Dim right_eyeY As Integer = 0
 
    Dim myUri As Uri = New Uri("ユーザの専用サーバのURL/ImageFileUpload/ImageData/" & myDoc.Descendants("fileName")(myIndex).Value, UriKind.Absolute)
    Dim imageUri = String.Format("https://kaolabo.com/api/detect?apikey={0}&url={1}", AppId, myUri)
 
    Dim myWebClient As New WebClient
    myWebClient.DownloadStringAsync(New Uri(imageUri, UriKind.Absolute))
    AddHandler myWebClient.DownloadStringCompleted, Sub(resultdSender As Object, resultArgs As DownloadStringCompletedEventArgs)
        Dim readXmldoc As String = resultArgs.Result
        readXmldoc = readXmldoc.Replace("<results version=" & ChrW(34) & "1.0" & ChrW(34) & " xmlns=" & ChrW(34) & "http://xmlns.kaolabo.com/detect" & ChrW(34) & ">", "<results>")
        Dim xmldoc As XElement = XElement.Parse(readXmldoc)
        For Each result In From c In xmldoc.Descendants("face") Select c
            faceHeight = CInt(result.Attribute("height").Value)
            left_eyeX = CInt(CInt(result.Element("left-eye").Attribute("x").Value) * 0.7)
            left_eyeY = CInt(CInt(result.Element("left-eye").Attribute("y").Value) * 0.7)
            right_eyeX = CInt(CInt(result.Element("right-eye").Attribute("x").Value) * 0.7)
            right_eyeY = CInt(CInt(result.Element("right-eye").Attribute("y").Value) * 0.7)
            myLine = New Line
            With myLine
                .X1 = left_eyeX + 15
                .Y1 = left_eyeY
                .X2 = right_eyeX - 15
                .Y2 = right_eyeY
                .Stroke = New SolidColorBrush(Colors.Red)
                .StrokeThickness = CDbl(faceHeight / 10)
              End With
                InkPresenter1.Children.Add(myLine)
            Next
          End Sub
      savePicturesHubButton.IsEnabled = True
      Exit Sub
  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メルマガ会員のサービス内容を見る

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