写真から顔を自動認識して、簡単に目隠し加工する(後編)
次に、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に位置する
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に位置する
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を、XElement.Parseメソッドで読み込みます。
Descendantsメソッドで、子孫要素であるすべての
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