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

2012年2月27日(月)
PROJECT KySS

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

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

リスト3 (ImageShowPage.xaml.vb)

Option Strict On
Imports System.Xml.Linq

ImageInfoクラスに文字列型の「画像名」プロパティを定義しておきます。

Public Class ImageInfo
  Property 画像名 As String
End Class

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

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

新しいWebClientのインスタンスmyWebClientオブジェクトを作成します。WebClientクラスは、データの送受信用のメソッドを提供するクラスです。
String またはUriとして指定したリソースをダウンロードする、DownloadStringAsyncメソッドで、サーバー上のImageInfo.xmlをダウンロードします。キャッシュから読み込まないように、常に新しいデータを読み込むよう、引数に現在の時間、分、秒を指定しています。
AddHandlerステートメントで、非同期のリソース ダウンロード操作の完了時に発生するDwonloadStringCompletedイベントに、イベントハンドラを追加します。イベントハンドラ内では以下の処理を実行します。
ダウンロードが成功しなかった場合は、警告メッセージを出して、処理を抜けます。成功した場合は、XElement.Parseメソッドでダウンロードした結果(resultArgs.Result)を読み込みます。読み込んだ結果はmyDoc変数に格納されます。ImageInfoクラスの新しいリストであるimageListを作成します。Descendantsメソッドで、子孫要素であるすべての要素のコレクションに対して、各要素を変数resultに格納しながら、ImageInfo クラスの「画像名」プロパティに、「ユーザーの専用サーバーの画像を保存するフォルダ」と要素の値を連結して指定し、AddメソッドでimageListオブジェクトに追加します。ListBoxのItemsSourceプロパティにimageListオブジェクトを指定します。これで、ListBoxにサーバーに保存されている画像の一覧が表示されます。

  Protected Overrides Sub OnNavigatedTo(e As System.Windows.Navigation.NavigationEventArgs)
    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
          Dim myDoc As XElement = XElement.Parse(resultArgs.Result)
          Dim imageList As New List(Of ImageInfo)
          For Each result In From c In myDoc.Descendants("fileName") Select c
              imageList.Add(New ImageInfo With {.画像名 = "ユーザーの専用サーバーのURL/ImageFileUpload/ImageData/" & result.Value})
          Next
          ListBox1.ItemsSource = imageList
      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

ListBoxから任意の画像が選択された時の処理

選択された画像のインデックスを引数に、後編で作成するFacialrecognitionPage.xamlページに遷移します。

  Private Sub ListBox1_SelectionChanged(sender As Object, e As System.Windows.Controls.SelectionChangedEventArgs) Handles ListBox1.SelectionChanged
    NavigationService.Navigate(New Uri(String.Format("/FacialrecognitionPage.xaml?Index={0}", ListBox1.SelectedIndex.ToString), UriKind.Relative))
  End Sub
End Class

ASP.NETページの作成(ImageFileUpload)

VS2010のメニューから、[ファイル(F)/新規作成(N)/Webサイト(W)]と選択し、表示される画面から「ASP.NET Webサイト」を選択します。「webの場所(L)」に今回は「フォルダ名\ImageFileUpload」と指定し[OK]ボタンをクリックします。

ソリューションエクスプローラー内にImageDataというフォルダを作成し、というルート要素だけのImageInfo.xmlを作成しておきます。

VS2010メニューの[Webサイト(S)/参照の追加(R)]と選択して、System.Xml.Linqを追加しておいてください。

ソリューションエクスプローラー内のDefault.aspxを展開して表示される、Default.aspx.vbにリスト3のコードを記述します。

※このコードをサーバーに配置した際の、アクセス権の設定やIISの設定は各自が行ってください。

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

リスト3 (Default.aspx.vb)

Option Strict On
Imports System.IO
Imports System.Xml

Partial Class _Default
  Inherits System.Web.UI.Page

ページが読み込まれた時の処理

Windows Phoneのプログラムから送られたfileNameを受け取って、変数fileNameに格納します。
サーバーの物理パスとImageDataフォルダとfileNameを連結して、変数filePathに格納しておきます。
FileStreamクラスの変数streamを用意し、File.Openメソッドで、filePathに指定したファイルを、Createモードで開きます。Createでは、新しいファイルが作成されます。既にファイルが存在する場合は上書きされます。4096バイトで初期化されたByte型の配列変数bufferを宣言します。POSTされたデータ(Request.InputStream)を取得し、Readメソッドで、リソースに格納されているストリームを読み取りFileStreamにWriteメソッドで書き込みます。これで、Windows PhoneのプログラムからPOSTされた、現在の「年月日時間分秒.jpg」ファイルが作成されます。InlineAssignHelperヘルパー関数(後述)を使っています。
次に、保存されたjpg画像の、ファイル名の一覧を記録するXMLファイル(ImageInfo.xml)に、jpg画像のファイル名を追加する処理を行います。
XElement.LoadメソッドでImageDataフォルダ内のImageInfo.xmlを読み込みます。Visual Basic の埋め込み式を用いて、要素を作成し、内容テキストとして、埋め込み式の構文である を用いて変数fileNameの値を指定します。これは ASP.NET で使用される構文と同じです。生成したXMLを読み込んだImageInfo.xmlに追加します。Saveメソッドで保存します。このImageInfo.xmlファイルは保存された.jpgファイル名の一覧を記録したファイルです。

  Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
    Dim fileName As String = Context.Request.QueryString("fileName").ToString
    Dim filePath As String = Server.MapPath("./") & "ImageData/" & fileName
 
    Using stream As FileStream = File.Open(filePath, FileMode.Create)
      Dim buffer As Byte() = New Byte(4096) {}
      Dim myByteRead As Integer
 
      While (InlineAssignHelper(myByteRead, Context.Request.InputStream.Read(buffer, 0, buffer.Length))) <> 0
        stream.Write(buffer, 0, myByteRead)
      End While
      stream.Close()
      Response.Flush()
    End Using
 
    Dim xmldoc As XElement = XElement.Load(Server.MapPath("./") & "ImageData/ImageInfo.xml")
    Dim addXml As XElement = <fileName><%= fileName %></fileName>
    xmldoc.Add(addXml)
    xmldoc.Save(Server.MapPath("./") & "ImageData/ImageInfo.xml")
 
  End Sub

MSDNのドキュメントで下記のように定義されているInlineAssignHelperヘルパー関数

  Private Shared Function InlineAssignHelper(Of T)(ByRef target As T, ByVal value As T) As T
    
    target = value
    Return value
  End Function
End Class

次にVS2010メニューの[Webサイト(S)/新しい項目の追加(W)]と選択して、「Webフォーム」を作成します。「名前(N)」はDefault2.aspxのままにしておきます。ソリューションエクスプローラー内のDefault2.aspxを展開して表示される、Default2.aspx.vbにリスト4のコードを記述します。

このDefault2.aspx.vbは、Windows Phoneで選択されたjpgファイルを削除する処理です。

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

リスト4 (Default2.aspx.vb)

Option Strict On
Imports System.IO
Imports System.Xml.Linq
Partial Class Default2
  Inherits System.Web.UI.Page

ページが読み込まれた時の処理

StreamReaderクラスでPOSTされたデータ(Request.InputStream)を取得し、reader変数で参照します。StreamReaderクラスは、特定のエンコーディングのバイトストリームを読み込むTextReader を実装するクラスです。
取得したデータの内容をReadToEndメソッドで読み取り、変数readStrに格納しておきます。readStrの値を数値に変換してmyIndex変数に格納します。readStrにはWindows Phoneから送られた、選択されたjpgファイルのインデックスが格納されています。
インデックスに該当する.jpg画像ファイルと、ImageInfo.xml内で該当する.jpgファイルの記録されている、要素と内容を削除します。削除したImageInfo.xmlをSaveメソッドで保存します。

  Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
    Dim reader As New StreamReader(Me.Request.InputStream(), System.Text.Encoding.UTF8)
    Dim readStr As String = reader.ReadToEnd
    Dim myIndex As Integer = Integer.Parse(readStr)
 
    Dim doc As XElement = XElement.Load(Server.MapPath("./") & "ImageData/ImageInfo.xml")
    Dim deleteData = doc.Descendants("fileName")(myIndex)
    Dim delImage As String = doc.Descendants("fileName")(myIndex).Value
    File.Delete(Server.MapPath("./") & "ImageData/" & delImage)
    deleteData.Remove()
    doc.Save(Server.MapPath("./") & "ImageData/ImageInfo.xml")
  End Sub
End Class

前編は以上で終わりです。次回は後編として、サーバーから読み込んだJPG画像に、FUJIFILMが提供している「顔検出WebAPI」を使用して、「目隠し」の処理を行う方法を紹介します。

  • 「写真から顔を自動認識して、簡単に目隠し加工する」サンプルプログラム

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

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