Leap Motionで画像のトリミングと保存を行うためのサンプルプログラムを作る

2013年11月9日(土)
薬師寺 国安

次にLeap Motionのタッチ処理になります。タッチのイメージは次の図3のようなイメージです。

Updateメソッド(ホバー)の処理

手前側が「ホバー状態(hovering)」、奥側が「タッチ状態(touching)」を表わします。空間の範囲は前後「1」〜「-1」となっています。

図4:Leap Motionのタッチ検出イメージ(Leap Motion SDKのAPIドキュメントから引用)(クリックで拡大)

まずホバーの場合は、表示されている円がBlueの色になります。タッチ・ポイントの位置をメンバー変数「x」と「y」に格納します。

Win32 APIのSetCursorPos(touchPoint.X, touchPoint.Y)と指定します。タッチ・ポイントの位置とマウスカーソルの位置が同じ位置に表示されます。

apimouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)と指定して、マウスの左ボタンを外した状態にします。

画面に表示されている指の数をleap.Frame.Fingers.Countプロパティで取得して、メンバー変数「FingersCount」に格納しておきます(リスト9)。

リスト9 ホバー時の処理(MainWindow.xaml.vb)

For Each Pointable As Pointable In leap.Frame.Pointables
  ……コード略(前述)……
If Pointable.TouchDistance > 0 AndAlso Pointable.TouchZone <> Global.Leap.Pointable.Zone.ZONENONE Then
      touchIndicator.Color = Colors.Blue
      x = 0
      y = 0
      x = touchPoint.X
      y = touchPoint.Y
      SetCursorPos(touchPoint.X, touchPoint.Y)
      apimouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
 
      FingersCount = leap.Frame.Fingers.Count' 表示されている指の本数を取得して、メンバ変数FingersCountに格納しておく
    ……コード略(続きは後述)……
  End If
Next

次にタッチした処理になります。タッチした場合は、表示されている円が赤に変わります。

Updateメソッド(タッチ)の処理

指が1本認識されている場合は、SetCursorPos(touchPoint.X, touchPoint.Y)と指定して、タッチ・ポイントとカーソルの位置を同じ位置に表示します。

  apimouse_event(MOUSEEVENTF_MOVE, 0, 0, 0, 0)
  apimouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)

と指定して、マウスを動かした処理と、マウスの左ボタンを押した処理を行います。

タッチ以外の処理ではタッチ・ポイントの色がGoldになり、

  SetCursorPos(touchPoint.X, touchPoint.Y)
  apimouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)

と指定します。

具体的にはリスト10のコードになります。

リスト10 タッチとタッチ以外の処理

If Pointable.TouchDistance > 0 AndAlso Pointable.TouchZone <> Global.Leap.Pointable.Zone.ZONENONE Then
  ……コード略(前述)……
  ElseIf Pointable.TouchDistance <= 0 Then
    touchIndicator.Color = Colors.Red
        If FingersCount = 1 Then
          SetCursorPos(touchPoint.X, touchPoint.Y)  ' 指が1本認識されている場合は、タッチ・ポイントとカーソルの位置を同じ位置に表示する。
          apimouse_event(MOUSEEVENTF_MOVE, 0, 0, 0, 0)
          apimouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
        
        End If
        'タッチ対象外
      Else
        touchIndicator.Color = Colors.Gold
        SetCursorPos(touchPoint.X, touchPoint.Y) 
        apimouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
      End If
    Next
  End Sub

inkCanvas1_MouseDownメソッドの処理

次にinkCanvas1の上でマウスが押下された時の処理です。

一度inkCanvas1の内容をクリアしておきます。

inkCanvas1の編集モードがInkCanvasEdithingMode.Noneの時の処理です。

新しいRectangleクラスのインスタンスrectを作成します。Point型メンバー変数pにinkCanvas1に対する相対的なマウスポインターの位置をe.GetPosition(inkCanvas1)で格納します。

RectオブジェクトのSetValueメソッドでInkCanvasのLeftPropertyとTopPropertyにinkCanvas1に対するXとY座標を指定します。RectオブジェクトのWidthとHeightを設定します。rectオブジェクトのStrokeDashArray(破線パターンを設定するプロパティ)プロパティに変数doubles(2,2)の値を指定して、破線とします。StrokeThickness(アウトラインの幅)には3を指定します。アウトラインの色にはGoldを指定し、FillにはTransparent(透明色)を指定します。inkCanvas1にrectオブジェクトを追加します。

画像の上でタッチするとGoldの破線枠が表示されます。具体的なコードはリスト11になります。

リスト11 inkCanvas1_MouseDownメソッドの処理

  Private Sub inkCanvas1_MouseDown(sender As Object, e As MouseButtonEventArgs) Handles inkCanvas1.MouseDown
    messageTextBlock.Text = String.Empty
    inkCanvas1.Children.Clear()
    Button1.IsEnabled = True
    If inkCanvas1.EditingMode = InkCanvasEditingMode.None Then
      rect = New Rectangle
      p = e.GetPosition(inkCanvas1)
 
      rect.SetValue(InkCanvas.LeftProperty, p.X)
      rect.SetValue(InkCanvas.TopProperty, p.Y)
      rect.Width = p.X
      rect.Height = p.Y
      Dim doubles As New DoubleCollection
      doubles.Add(2)
      doubles.Add(2)
      rect.StrokeDashArray = doubles ' StrokeDashArray(2,2)と同じ
      rect.StrokeThickness = 3
      rect.Stroke = New SolidColorBrush(Colors.Gold)
      rect.Fill = New SolidColorBrush(Colors.Transparent)
      inkCanvas1.Children.Add(rect)
    End If
  End Sub

inkCanvas1_MouseMoveメソッドの処理

inkCanvas1上でマウスカーソルを移動させた時の処理です。

Point型変数p2にinkCanvas1に対する相対的なマウスポインターの位置をe.GetPosition(inkCanvas1)で格納します。

選択した移動距離の範囲を変数wとhに格納します。移動距離のwの値が0.1と同じか少なかった場合は、rectオブジェクトのSetValueメソッドでInkCanvas.LeftPropertyに移動した範囲のXの値を指定します。rectのWidthに移動した距離を指定します。

移動した距離のhの値が0.1かそれより少なかった場合は、rectオブジェクトのSetValueメソッドでInkCanvas.TopPropertyに移動した範囲のYの値を指定します。rectのHeightに移動した距離を指定します。

具体的なコードはリスト12になります。

リスト12  inkCanvas1_MouseMoveメソッドの処理

  Private Sub inkCanvas1_MouseMove(sender As Object, e As MouseEventArgs) Handles inkCanvas1.MouseMove
    If rect Is Nothing = False Then
      Dim p2 As Point = e.GetPosition(inkCanvas1)
      Dim w As Double = p2.X - p.X ' X座標の移動した距離
      Dim h As Double = p2.Y - p.Y ' Y座標の移動した距離
      If (w <= 0.1) Then
        rect.SetValue(InkCanvas.LeftProperty, p2.X)
        rect.Width = p.X - p2.X
      Else
        rect.Width = w
      End If
      If (h <= 0.1) Then
        rect.SetValue(InkCanvas.TopProperty, p2.Y)
        rect.Height = p.Y - p2.Y
      Else
        rect.Height = h
      End If
    Else
      Exit Sub
    End If
  End Sub

[切り取る(Button1)]がクリックされた時の処理(Button1_Clickメソッド)

File.OpenReadメソッドで、ソリューション・エクスプローラー内のImageフォルダ内の画像を読み取り、streamで参照します。

BitmapImageの新しいインスタンスbmpオブジェクトを作成します。BeginInitでBitmapImageの初期化を開始します。BitmapImageのストリームソースにstreamを指定します。EndInitで初期化を終了します。

bmpオブジェクトで初期化された新しいWriteableBitmapのインスタンスmyWriteableBitmapオブジェクトを作成します。BitmapFactory.ConvertToPbgra32Format(bmp)でbmpをPbgra32フォーマットに変換します。
この変換を行っていないとエラーが表示されますので、注意してください。

WriteableBitmapExのCropメソッドで、選択した範囲を切り取ります。切り取った画像をresultImageに表示します。[保存]ボタンの使用を可能にします。

具体的なコードはリスト13になります。

リスト13 Button1_Clickメソッド処理

Private Sub Button1_Click(sender As Object, e As RoutedEventArgs) Handles Button1.Click
    Using stream = File.OpenRead("Image/冬の桜.jpg")
      Dim bmp As BitmapImage = New BitmapImage
      bmp.BeginInit()
      bmp.StreamSource = stream
      bmp.EndInit()
 
      Dim myWriteableBitmap = New WriteableBitmap(bmp)
      myWriteableBitmap = BitmapFactory.ConvertToPbgra32Format(bmp)
      Dim myCrop = myWriteableBitmap.Crop(rect.GetValue(InkCanvas.LeftProperty), rect.GetValue(InkCanvas.TopProperty), rect.Width, rect.Height) ' 選択した範囲をCropメソッドで切り取る
      resultImage.Source = Nothing
      resultImage.Source = myCrop ' 切り取った画像を表示する
 
    End Using
    saveButton.IsEnabled = True
  End Sub

inkCanvas1_MouseUpメソッドの処理

ReleaseAllTouchCapturesメソッドで、キャプチャされている全てのタッチデバイスをこの要素から解放します。

コードはリスト14になります。

リスト14  inkCanvas1_MouseUpメソッドの処理

  Private Sub inkCanvas1_MouseUp(sender As Object, e As MouseButtonEventArgs) Handles inkCanvas1.MouseUp
    inkCanvas1.ReleaseAllTouchCaptures()
  End Sub

[保存(saveButton)]がクリックされた時の処理(saveButton_Clickメソッド)

切り出された画像の表示されている、resultImage.Sourceで初期化された、新しいWriteableBitmapのオブジェクトbmpを作成します。PngBitmapEncoderでPNG形式のイメージのエンコードに使用される新しいエンコーダーのインスタンス、myEncoderを作成します。

指定したBitmapSourceから新しいBitmapFrameを作成して、myEncoderに追加します。

File.Opneメソッドで保存するフォルダとファイル名(TrimmingTest.png)を連結して、フォルダにファイルを書きこみ、FileStream変数fsで参照します。

myEncoder.Save(fs)でビットマップイメージを指定したStream(fs)にエンコードします。

保存した旨のメッセージを表示し、[切り取る]と[保存]ボタンの使用を不可とします。

具体的なコードはリスト15になります。

リスト15 saveButton_Clickメソッド

Private Sub saveButton_Click(sender As Object, e As RoutedEventArgs) Handles saveButton.Click
    Dim bmp As New WriteableBitmap(resultImage.Source)
    Dim myEncoder = New PngBitmapEncoder
    myEncoder.Frames.Add(BitmapFrame.Create(bmp))
    Using fs As FileStream = File.Open(System.IO.Path.Combine(savePath, "TrimmingTest.png"), FileMode.Create)
      myEncoder.Save(fs) ' 指定したフォルダにTrimmingTest.pngとして保存する
  
    End Using
    messageTextBlock.Text = savePath & "フォルダにTrimmingTest.pngとして保存しました。"
    Button1.IsEnabled = False
    saveButton.IsEnabled = False
  End Sub

※注意
今回紹介したサンプルコードを動かす際には、「LeapCSharp.NET4.0.dll」や「LeapCSharp.dll」、「Leap.dll」を読者の皆さん自身のフォルダ内にあるDLLファイルに指定し直さなければ動かない可能性があるので、動かない場合は再指定して下さい。

  • Leap Motionで画像のトリミングと保存を行うためのサンプルプログラム

    『新世代モーションコントローラー Leap Motion -Visual Basicによる実践プログラミング-』 第4回のサンプルプログラムです。
薬師寺国安事務所

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

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