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

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

メンバー変数の宣言

次にメンバー変数を宣言します。

今回はWin32 APIを使用するためにWin32 APIの宣言も行います。下記のリスト3の部分がWin32 APIに関する宣言か所になります。

リスト3 (Win32 APIに関する宣言)

  Private Declare Function SetCursorPos Lib "user32" (x As Integer, y As Integer) As Boolean
  Private Declare Function apimouse_event Lib "user32" Alias "mouse_event" (ByVal dwFlags As Int32, ByVal dx As Int32, ByVal dy As Int32, ByVal cButtons As Int32, ByVal dwExtraInfo As Int32) As Boolean
  Private Const MOUSEEVENTF_MOVE = &H1
  Private Const MOUSEEVENTF_LEFTDOWN = &H2
  Private Const MOUSEEVENTF_LEFTUP = &H4

新しいControllerクラスのインスタンスであるleapメンバー変数を宣言します。次に、インク・ストローク(=System.Windows.Ink名前空間のStrokeクラスで表現される、WPF上でのインクの線)の外観を指定する新しいDrawingAttributesクラス(System.Windows.Ink名前空間)のインスタンスである「touchIndicatorメンバー変数」を宣言します。デジタイザーとスタイラスから収集された単一のデータ・ポイントを表すStylusPoint構造体の「touchPointメンバー変数」を宣言します。

次にRectangleクラス型の「rectメンバー変数」を宣言します。また、System.Wiondows.Pointクラスからの「pメンバー変数」を宣言します。切り出した画像を保存するフォルダ名を格納する「savePathメンバー変数」を宣言します。

そのほかの必要なメンバー変数も宣言しています(リスト4)。

リスト4 メンバー変数の宣言(MainWindow.xaml.vb)

Private leap As New Controller
  Private touchIndicator As New DrawingAttributes
  Public touchPoint As StylusPoint
  Private rect As Rectangle
  Private p As System.Windows.Point

  Private savePath As String
  Private windowWidth As Double = 1920
  Private windowHeight As Double = 1080
  Private x As Integer
  Private y As Integer
  Private tx As Double
  Private ty As Double
  Private FingersCount As Integer

MainWindow_Loadedメソッドの処理

MainWindow_Loadedメソッド(=メイン・ウィンドウのLoadedイベントのハンドラー)では、MainWindowが読み込まれたときの処理を実装します。

メンバー変数savePathに切り出した画像を保存するフォルダを指定します。

指定したフォルダが存在しなかった場合は、CreateDirectoryメソッドでフォルダを作成します。

inkCanvas1のEditingMode(ユーザー編集モードの設定)プロパティに、InkCanvasEditingMode.None(ペンがInkCanvasにデータを送信する時に、アクションが実行されないようにする)を指定します。

AddHandlerステートメントを使って、構成ツリーのオブジェクトがレンダリングされる直前に発生する「CompositionTarget.Renderingイベント」に対するイベント・ハンドラーとしてUpdateメソッドを指定します(※Updateメソッドの実装内容は後述)。

インク・ストロークの外観を表す、DrawingAttributesオブジェクトのインスタンス「touchIndicator」のWidthプロパティとHeightプロパティに、それぞれ「20」を指定します。スタイラスの形状を指定するStylusTipプロパティに「StylusTip.Ellipse」を指定して円形とします。Leap Motionの上で指をかざすと、かざした指の本数に応じて20px(px=ピクセル)の円が表示されるようになります(リスト5)。

リスト5  MainWindowが読み込まれたときの処理を行うMainWindow_Loadedメソッドの実装内容(MainWindow.xaml.vb)

  Private Sub MainWindow_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded
    savePath = "C:\LeapmotionTrimmingImage" ' 切り出した画像を保存するフォルダ
    If Directory.Exists(savePath) = False Then
      Directory.CreateDirectory(savePath)
    End If
    inkCanvas1.EditingMode = InkCanvasEditingMode.None
    AddHandler CompositionTarget.Rendering, AddressOf Update ' Updateイベント・ハンドラ—を実行する
    touchIndicator.Width = 20
    touchIndicator.Height = 20
 
    touchIndicator.StylusTip = StylusTip.Ellipse ' 20pxの円が表示される
  End Sub

Updateメソッドの処理

次は、CompositionTarget.Renderingイベント・ハンドラーであるUpdateメソッドの処理です。

まず、上記のコードを実装した段階で、「Update」の位置に[エラー修正のオプション]というスマート・タグが表示されるので、そのタグのメニューから['ChangeBackgroundLeapMotion.MainWindow' に 'Update' のメソッド スタブを生成]をクリックすると、Updateメソッドのひな型が追加されます。次に、そのメソッド内を実装していきます。

「paintCanvas」という名前のInkPresenter内をクリアします。この処理を行っていないと、Updateメソッドは常に呼び出されているため、円を画面上で動かすと、円の軌跡が残ったまま表示されてしまいます。そのため、InkPresenter内をクリアする必要があります。

Leap Motionのフレームを表すFrame型(Leap名前空間)の変数「frame」を宣言します。InteractionBox型(Leap名前空間)の変数「interactionBox」を宣言し、そこにleap.Frame.InteractionBoxプロパティからInteractionBoxオブジェクトを取得します。InteractionBoxオブジェクトは、Leap Motionで認識できる稼働範囲となります。InteractionBoxオブジェクトを使用することで、指やツール(=ペンなど)の位置を実際のディスプレイの座標系に変換できます(リスト6)。
詳細な図については、「Leap Motionでのタッチ操作はどう開発するのか?」を参照してください。

リスト6 指やツールの位置を実際のディスプレイの座標系に変換する(MainWindow.xaml.vb)

Private Sub Update(sender As Object, e As EventArgs)
    paintCanvas.Strokes.Clear()
    windowWidth = Me.Width
    windowHeight = Me.Height

    Dim frame As Leap.Frame = leap.Frame
    Dim interactionBox As InteractionBox = leap.Frame.InteractionBox
End Sub

Leap.Frame.Pointablesプロパティで得られるPointableListオブジェクト内を変数「Pointable」で反復処理しながら、1つずつPointableオブジェクトを処理し、それぞれのタッチ位置を取得していきます。

interactionBoxオブジェクトのNormalizePointメソッドに引数としてPointable.StabilizedTipPositionプロパティ値を渡し、ポインター上の位置を取得します。

変数「windowWidth」と「windowHeight」で表されたクライアント領域のウィンドウがある場合、以下のリスト7で示す計算式を使用してこのウィンドウ内のタッチ・ポイントの2D座標を得ることができます。

リスト7 ウィンドウ内のタッチ・ポイントの2D座標を得るコード(MainWindow.xaml.vb)

Private Sub Update(sender As Object, e As EventArgs)

……コード略……

  For Each Pointable As Pointable In leap.Frame.Pointables
    Dim normalizedPosition As Leap.Vector = interactionBox.NormalizePoint(Pointable.StabilizedTipPosition)
    tx = normalizedPosition.x * windowWidth
    ty = windowHeight - normalizedPosition.y * windowHeight
    touchPoint = New StylusPoint(tx, ty)  ' ウィンドウ内のタッチ・ポイントの位置を取得する
 
  ……コード略(後述)……
  Next
End Sub

さらに、上記のFor Eachステートメントの一番下に次のリスト8を追記して、ディスプレイ上に円形のタッチ・ポイントを表示します。

リスト8 ディスプレイ上にタッチ・ポイントを表示する処理(MainWindow.xaml.vb)

For Each Pointable As Pointable In leap.Frame.Pointables
  ……コード略(前述)……
  
  Dim tips As New StylusPointCollection(New StylusPoint() {touchPoint})
  Dim touchStroke As New Stroke(tips, touchIndicator)
  paintCanvas.Strokes.Add(touchStroke)
Next
  • 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メルマガ会員のサービス内容を見る

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