SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

現役エンジニア直伝! 「現場」で使えるコンポーネント活用術(LEADTOOLS)

「LEADTOOLS」を使ってKinectから取得した距離データを可視化しよう

  • このエントリーをはてなブックマークに追加

 Microsoft Kinect for Windowsは物体までの距離を計測できるセンサーで、その戻り値はグレースケール相当の2バイトの画像として取得できます。この距離データを可視化するときにデータを見やすくするのに画像処理する場合が多いのですが、1ドットずつ処理していると処理時間がかかりすぎるため、フレームレートの低下を招き、距離データの再生にタイムラグが生じてしまいます。この画像処理の処理速度を向上させるという問題をどのようにすれば解決できるのか悩んでいた時に思いついたのが、今回取り上げるLEADTOOLS Imaging Proの存在です。

  • このエントリーをはてなブックマークに追加

 LEADTOOLSは高速な多機能画像処理コンポーネントですが、それゆえに画像ファイルに対する処理にのみ適用するようなイメージがあります。しかし、Kinectからのデータは連続した画像データとして取得できます。つまり、動画のように見えていても、処理単位は画像データとなるため、LEADTOOLSが適用できるのではないかと考えました。

 結論から言えば、非常に良い結果を得ることができました。

Kinect距離データを画面に表示する

 (※サンプルファイルの「CZ1312HistogramVB」に対応)

 Kinectからの距離データを、WPFのImageコントロールに表示するサンプルを作成します。

図1 サンプルプロジェクトの構成
図1 サンプルプロジェクトの構成

KinectModel.vb

 KinectModelクラスには、Kinectからの距離データをImageSourceとして公開する処理を記述します。

 重要な部分を抜粋すると次のようになります。

リスト1 KinectModel.vb
        :
    (略)
        :
Private WithEvents Kinect As KinectSensor

Private _DepthImageElement As ImageSource
Public Property DepthImageElement As ImageSource
    Get
        Return Me._DepthImageElement
    End Get
    Set(value As ImageSource)
        Me._DepthImageElement = value
        OnPropertyChanged()
    End Set
End Property
        :
    (略)
        :
Private Sub DiscoverKinectSensor()
    Me.Kinect = KinectSensor.KinectSensors.FirstOrDefault(
        Function(x)
            Return x.Status = KinectStatus.Connected
        End Function)
    If Me.Kinect IsNot Nothing Then
        Me.Kinect.DepthStream.Enable()
        Me.Kinect.Start()
        Me.DepthImageBitmap = New WriteableBitmap(DepthDesc.Width,
                                                  DepthDesc.Height,
                                                  96.0,
                                                  96.0,
                                                  PixelFormats.Gray16,
                                                  Nothing)
        ReDim Me.DepthImageFrameData(depthDesc.Width * depthDesc.Height - 1)
        Me.DepthImageBitmapRect = New Int32Rect(0,
                                                0,
                                                DepthDesc.Width,
                                                DepthDesc.Height)
        Me.DepthImageStride = DepthDesc.Width * Me.BytesPerPixel
    End If
End Sub
 
Private Sub Kinect_DepthFrameReady(sender As Object,
                                   e As DepthImageFrameReadyEventArgs) _
                               Handles Kinect.DepthFrameReady
    Using frame = e.OpenDepthImageFrame
        If frame IsNot Nothing Then
            frame.CopyPixelDataTo(Me.DepthImageFrameData)
 
            Me.DepthImageBitmap.WritePixels(Me.DepthImageBitmapRect,
                                            Me.DepthImageFrameData,
                                            Me.DepthImageStride,
                                            0)
            Me.DepthImageElement = Me.DepthImageBitmap
        End If
    End Using
End Sub

 重要なポイントは、距離データがセンサーから送られてくるとイベント呼び出しされるKinect_DepthFrameReadyイベントプロシージャで、この中で次の処理を実施しています。

  1. OpenDepthImageFrameメソッドでフレーム形式の距離データを取得
  2. CopyPixelDataToメソッドで2バイト配列に格納
  3. WritePixelsメソッドで2バイト配列をグレースケールのWritebleBitmapに設定
  4. WritebleBitmapをImageSourceとして公開

MainViewModel.vb

 MainViewModelクラスでは、KinectModelが公開しているDepthImageElementプロパティとPropertyChangedイベントを公開しています。

MainWindow.xaml

リスト2 MainWindow.xaml
<Window
    x:Class="MainWindow"
    Title="CZ1312HistogramVB" 
    Height="300" Width="640"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:CZ1312HistogramVB"
    Loaded="WIndow_Loaded" Closing="Window_Closing">
    <Viewbox>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="1*" />
                <ColumnDefinition Width="1*" />
            </Grid.ColumnDefinitions>
            <Image Grid.Column="0" Grid.Row="0"
                        Source="{Binding DepthImageElement}"  Width="640" Height="480" />
            <Image Grid.Column="1" Grid.Row="0"
                        Source="{Binding DepthImageElement}"  Width="640" Height="480" />
        </Grid>
    </Viewbox>
</Window>

 WPFの画面定義体であるXAMLで画面を左右に2分割し、同じデータを表示するようにします。あとで一方をLEADTOOLSで見やすくします。

MainWindow.xaml.vb

リスト3 MainWindow.xaml.vb
Class MainWindow
    Private ViewModel As New MainViewModel
 
    Public Sub New()
        InitializeComponent()
        Me.DataContext = Me.ViewModel
    End Sub
 
    Private Sub WIndow_Loaded(sender As Object, e As RoutedEventArgs)
        Me.ViewModel.StartCommand()
    End Sub
 
    Private Sub Window_Closing(sender As Object, e As ComponentModel.CancelEventArgs)
        Me.ViewModel.StopCommand()
    End Sub
End Class

 データの表示などはXAMLのBindingを使うので、コードビハインド側ではDataContextの設定と、Kinectセンサーの開始と停止のメソッド起動の記述だけで済みます。

実行結果

図2 実行結果
図2 実行結果

 距離に応じて手前が濃く、奥が薄いグレースケールで表示できていると思います。いろいろ動いてその変化を確認してみましょう。

LEADTOOLSを適用する

 Kinectの距離データが画面に表示できたら、サンプルを拡張してみましょう。

 拡張方法はいろいろありますが、WPFらしくImageコントロールにBindingしているDepthImageElementをコンバーターで変換するところにLEADTOOLSを適用します。

 そのためには、まずLeadConveterクラスをプロジェクトに追加します。

図3 LeadConveterの追加
図3 LeadConveterの追加

 次に、このクラスの中にLEADTOOLSを使うコードを記述しますが、その前に参照設定を追加します。

参照設定の追加

 LEADTOOLSにどのようなフィルタがあってどのような効果があるかは、ヘルプの「LeadTools.ImageProcessing.Color名前空間」の項目を参照してください。代表的なフィルタには、次のようなものがあります。

表1 代表的なフィルタ
クラス 解説
AddCommand リスト内の画像を加算合成または平均化します。
AddWeightedCommand リスト内の画像を、重み係数に従って加算合成または平均化します。
AutoColorLevelCommand 画像に対して自動カラーレベル調整を適用します。
この関数は、バーコード認識の結果を向上させるために画像を前処理する場合に役立ちます。
ChangeContrastCommand 画像のコントラストを増減します。
ChangeHueCommand カラーホイールを回転して、画像に含まれる色の色相を変更します。
HistogramEqualizeCommand 指定したカラースペースに基づいて、画像のピクセル数を線形化します。
これは、画像の暗い部分の細部を明瞭にする場合に使用します。
この関数は、バーコード認識の結果を向上させるために画像を前処理する場合に役立ちます。
IntensityDetectCommand 特定の画像にフィルタを適用し、特定のカラーチャネルにある特定の輝度範囲の色を検出することによって、画像を二分化します。
InvertCommand 指定した画像の色調を反転し、写真のネガのような効果を生み出します。
SolarizeCommand 画像にソラリゼーション効果(写真のフィルムを誤って光に露出してしまったような効果)を加えます。
この効果は、指定したしきい値以上の明度を持つすべてのカラーデータを反転することによって作成されます。
StretchIntensityCommand 明度値の範囲を中央に設定して最大化し、比例配分することによって、画像のコントラストを向上させます。
SwapColorsCommand 指定したカラーチャネルを入れ替えます。
TemperatureCommand 画像が冷たい、または暖かい印象になるように、画像の見た目を変更します。

 今回のサンプルでは画像の暗い部分、つまり、より距離が近い部分について細部を明瞭にしたいのでHistogramEqualizeCommandを使います。

 HistogramEqualizeCommandを使うには、LEADTOOLSコンポーネントへの参照設定が必要です。

図4 参照設定の追加
図4 参照設定の追加

LeadConverter.vb

 参照設定が完了したら、コンバータを記述します。

リスト4  LeadConverter.vb
Public Class LeadConverter
    Implements IValueConverter


    Public Function Convert(value As Object,
                            targetType As Type,
                            parameter As Object,
                            culture As Globalization.CultureInfo) As Object _
                        Implements IValueConverter.Convert
        If TypeOf value Is ImageSource Then
            Dim orignal = CType(value, ImageSource)
            Dim command As Leadtools.ImageProcessing.RasterCommand =
                New Leadtools.ImageProcessing.Color.HistogramEqualizeCommand

            If Not command Is Nothing Then
                Using image As Leadtools.RasterImage =
                    Leadtools.Windows.Media.RasterImageConverter.ConvertFromSource(
                        orignal,
                        Leadtools.Windows.Media.ConvertFromSourceOptions.None)
                    command.Run(image)
                    Return Leadtools.Windows.Media.RasterImageConverter.ConvertToSource(
                                image,
                                Leadtools.Windows.Media.ConvertToSourceOptions.None)
                End Using
            Else
                Return orignal
            End If
        Else
            Return value
        End If
    End Function

    Public Function ConvertBack(value As Object,
                                targetType As Type,
                                parameter As Object,
                                culture As Globalization.CultureInfo) As Object _
                            Implements IValueConverter.ConvertBack
        Throw New NotImplementedException
    End Function
End Class
  1. RasterCommandとしてHistogramEqualizeCommandを定義
  2. ConvertFromSourceメソッドで元のImageSourceをRasterImageに変換
  3. RunメソッドによりRasterImageにRasterCommandを実行
  4. ConvertToSourceで、RasterImageをImageSourceに変換

MainWindow.xaml

 XAMLにコンバータの利用設定を追記します。

リスト5 MainWindow.xaml
<Window
    x:Class="MainWindow"
    Title="CZ1312HistogramVB" 
    Height="300" Width="640"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:CZ1312HistogramVB"
    Loaded="WIndow_Loaded" Closing="Window_Closing">
    <Window.Resources>
        <local:LeadConverter x:Key="LeadConverter"/>
    </Window.Resources>
    <Viewbox>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="1*" />
                <ColumnDefinition Width="1*" />
            </Grid.ColumnDefinitions>
            <Image Grid.Column="0" Grid.Row="0" 
                   Source="{Binding DepthImageElement}"  
                   Width="640" Height="480" />
            <Image Grid.Column="1" Grid.Row="0" 
                   Source="{Binding DepthImageElement, Converter={StaticResource LeadConverter}}"  
                   Width="640" Height="480" />
        </Grid>
    </Viewbox>
</Window>

実行結果

図5 LEADTOOLSでの処理結果
図5 LEADTOOLSでの処理結果

 左右を見比べてみると左手前の凹凸や人物の上半身の凹凸がより分かりやすくなりました。また再遠部分が白く強調されたために全体的にその手前の物体の外観が分かりやすくなっていて、期待通りのフィルター効果が効いています。

別の効果を試してみる(サンプル:CZ1312ColorizeGrayVB)

 LEADTOOLSにはグレースケールをカラー化するためのColorizeGrayCommand関数も用意されています。そのため、LeadConverterクラスを次のように変えるだけで新たな見せ方を実現できます。

リスト6 LeadConverter.vb
Public Class LeadConverter
    Implements IValueConverter
 
 
    Public Function Convert(value As Object,
                            targetType As Type,
                            parameter As Object,
                            culture As Globalization.CultureInfo) As Object _
                        Implements IValueConverter.Convert
        If TypeOf value Is ImageSource Then
            Dim orignal = CType(value, ImageSource)
            Dim command As New Leadtools.ImageProcessing.Core.ColorizeGrayCommand With {
                    .GrayColors = Nothing}
 
            If Not command Is Nothing Then
                Using image As Leadtools.RasterImage =
                    Leadtools.Windows.Media.RasterImageConverter.ConvertFromSource(
                        orignal,
                        Leadtools.Windows.Media.ConvertFromSourceOptions.None)
                    command.Run(image)
                    Return Leadtools.Windows.Media.RasterImageConverter.ConvertToSource(
                                command.DestinationImage,
                                Leadtools.Windows.Media.ConvertToSourceOptions.None)
                End Using
            Else
                Return orignal
            End If
        Else
            Return value
        End If
    End Function
 
    Public Function ConvertBack(value As Object,
                                targetType As Type,
                                parameter As Object,
                                culture As Globalization.CultureInfo) As Object _
                            Implements IValueConverter.ConvertBack
        Throw New NotImplementedException
    End Function
End Class
  1. RasterCommandとしてColorizeGrayCommandを定義
  2. ConvertFromSourceメソッドで元のImageSourceをRasterImageに変換
  3. RunメソッドによりRasterImageにRasterCommandを実行
  4. ConvertToSourceで、RasterImageをImageSourceに変換

実行結果

図6 LEADTOOLSでの処理結果
図6 LEADTOOLSでの処理結果

 左右を見比べてみると右側はかなりサイケデリックな色合いになってしまいましたが、距離の違いや細かな凸凹がかなり明確になっています。もし、このような処理をLEADTOOLSを使わないで実現したとしたら全ドットに対して1ドットずつ値を判定して対応するカラー値に変換していかなければならずフレームレートの低下が発生しますが、LEADTOOLSであれば手軽で実行時のフレームレートも低下せずに実装できます。

 今回は自動変換を行いましたが値に応じて細かく色指定もできますので、必要な部分だけ色付けをするなどの用途にも使用できます。

まとめ

 動画が連続した静止画であることは、アニメがどのように作られているかを知っているとすぐに理解できます。そして静止画1枚に対しての処理時間が短ければ、静止画に対するフィルターをかけながらでも動画として見えるように処理し続けることも可能なのです。LEADTOOLSであれば、その高速性を生かしてこのような用途でも十分使用に耐えられます。

 なお、今年発売されるといわれている新型Kinectでは、距離データの解像度も高解像度になっています。つまり、静止画1枚あたりのデータ量が増加したことにより、高速なフィルター処理の必要性がさらに高まったことになります。また、先行提供版の新型Kinectを利用する機会があったので、距離データにLEADTOOLSを試してみました。高解像度に加えてLEADTOOLSで見やすくすることで、とてもきれいな距離データの見える化が可能です。この成果はコミックマーケット85のマイクロソフト企業ブースで、多くの方にご覧いただくことができました。

図7 先行提供版新型Kinect+LEADTOOLSでの処理結果
図7 先行提供版新型Kinect+LEADTOOLSでの処理結果

この記事は参考になりましたか?

  • このエントリーをはてなブックマークに追加

この記事は参考になりましたか?

この記事をシェア

  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/7573 2016/03/14 11:40

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング