LEADTOOLSは高速な多機能画像処理コンポーネントですが、それゆえに画像ファイルに対する処理にのみ適用するようなイメージがあります。しかし、Kinectからのデータは連続した画像データとして取得できます。つまり、動画のように見えていても、処理単位は画像データとなるため、LEADTOOLSが適用できるのではないかと考えました。
結論から言えば、非常に良い結果を得ることができました。
Kinect距離データを画面に表示する
(※サンプルファイルの「CZ1312HistogramVB」に対応)
Kinectからの距離データを、WPFのImageコントロールに表示するサンプルを作成します。
KinectModel.vb
KinectModelクラスには、Kinectからの距離データをImageSourceとして公開する処理を記述します。
重要な部分を抜粋すると次のようになります。
: (略) : 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イベントプロシージャで、この中で次の処理を実施しています。
- OpenDepthImageFrameメソッドでフレーム形式の距離データを取得
- CopyPixelDataToメソッドで2バイト配列に格納
- WritePixelsメソッドで2バイト配列をグレースケールのWritebleBitmapに設定
- WritebleBitmapをImageSourceとして公開
MainViewModel.vb
MainViewModelクラスでは、KinectModelが公開しているDepthImageElementプロパティとPropertyChangedイベントを公開しています。
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
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センサーの開始と停止のメソッド起動の記述だけで済みます。
実行結果
距離に応じて手前が濃く、奥が薄いグレースケールで表示できていると思います。いろいろ動いてその変化を確認してみましょう。
LEADTOOLSを適用する
Kinectの距離データが画面に表示できたら、サンプルを拡張してみましょう。
拡張方法はいろいろありますが、WPFらしくImageコントロールにBindingしているDepthImageElementをコンバーターで変換するところにLEADTOOLSを適用します。
そのためには、まずLeadConveterクラスをプロジェクトに追加します。
次に、このクラスの中にLEADTOOLSを使うコードを記述しますが、その前に参照設定を追加します。
参照設定の追加
LEADTOOLSにどのようなフィルタがあってどのような効果があるかは、ヘルプの「LeadTools.ImageProcessing.Color名前空間」の項目を参照してください。代表的なフィルタには、次のようなものがあります。
クラス | 解説 |
---|---|
AddCommand | リスト内の画像を加算合成または平均化します。 |
AddWeightedCommand | リスト内の画像を、重み係数に従って加算合成または平均化します。 |
AutoColorLevelCommand |
画像に対して自動カラーレベル調整を適用します。 この関数は、バーコード認識の結果を向上させるために画像を前処理する場合に役立ちます。 |
ChangeContrastCommand | 画像のコントラストを増減します。 |
ChangeHueCommand | カラーホイールを回転して、画像に含まれる色の色相を変更します。 |
HistogramEqualizeCommand |
指定したカラースペースに基づいて、画像のピクセル数を線形化します。 これは、画像の暗い部分の細部を明瞭にする場合に使用します。 この関数は、バーコード認識の結果を向上させるために画像を前処理する場合に役立ちます。 |
IntensityDetectCommand | 特定の画像にフィルタを適用し、特定のカラーチャネルにある特定の輝度範囲の色を検出することによって、画像を二分化します。 |
InvertCommand | 指定した画像の色調を反転し、写真のネガのような効果を生み出します。 |
SolarizeCommand |
画像にソラリゼーション効果(写真のフィルムを誤って光に露出してしまったような効果)を加えます。 この効果は、指定したしきい値以上の明度を持つすべてのカラーデータを反転することによって作成されます。 |
StretchIntensityCommand | 明度値の範囲を中央に設定して最大化し、比例配分することによって、画像のコントラストを向上させます。 |
SwapColorsCommand | 指定したカラーチャネルを入れ替えます。 |
TemperatureCommand | 画像が冷たい、または暖かい印象になるように、画像の見た目を変更します。 |
今回のサンプルでは画像の暗い部分、つまり、より距離が近い部分について細部を明瞭にしたいのでHistogramEqualizeCommandを使います。
HistogramEqualizeCommandを使うには、LEADTOOLSコンポーネントへの参照設定が必要です。
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
- RasterCommandとしてHistogramEqualizeCommandを定義
- ConvertFromSourceメソッドで元のImageSourceをRasterImageに変換
- RunメソッドによりRasterImageにRasterCommandを実行
- ConvertToSourceで、RasterImageをImageSourceに変換
MainWindow.xaml
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>
実行結果
左右を見比べてみると左手前の凹凸や人物の上半身の凹凸がより分かりやすくなりました。また再遠部分が白く強調されたために全体的にその手前の物体の外観が分かりやすくなっていて、期待通りのフィルター効果が効いています。
別の効果を試してみる(サンプル:CZ1312ColorizeGrayVB)
LEADTOOLSにはグレースケールをカラー化するためのColorizeGrayCommand関数も用意されています。そのため、LeadConverterクラスを次のように変えるだけで新たな見せ方を実現できます。
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
- RasterCommandとしてColorizeGrayCommandを定義
- ConvertFromSourceメソッドで元のImageSourceをRasterImageに変換
- RunメソッドによりRasterImageにRasterCommandを実行
- ConvertToSourceで、RasterImageをImageSourceに変換
実行結果
左右を見比べてみると右側はかなりサイケデリックな色合いになってしまいましたが、距離の違いや細かな凸凹がかなり明確になっています。もし、このような処理をLEADTOOLSを使わないで実現したとしたら全ドットに対して1ドットずつ値を判定して対応するカラー値に変換していかなければならずフレームレートの低下が発生しますが、LEADTOOLSであれば手軽で実行時のフレームレートも低下せずに実装できます。
今回は自動変換を行いましたが値に応じて細かく色指定もできますので、必要な部分だけ色付けをするなどの用途にも使用できます。
まとめ
動画が連続した静止画であることは、アニメがどのように作られているかを知っているとすぐに理解できます。そして静止画1枚に対しての処理時間が短ければ、静止画に対するフィルターをかけながらでも動画として見えるように処理し続けることも可能なのです。LEADTOOLSであれば、その高速性を生かしてこのような用途でも十分使用に耐えられます。
なお、今年発売されるといわれている新型Kinectでは、距離データの解像度も高解像度になっています。つまり、静止画1枚あたりのデータ量が増加したことにより、高速なフィルター処理の必要性がさらに高まったことになります。また、先行提供版の新型Kinectを利用する機会があったので、距離データにLEADTOOLSを試してみました。高解像度に加えてLEADTOOLSで見やすくすることで、とてもきれいな距離データの見える化が可能です。この成果はコミックマーケット85のマイクロソフト企業ブースで、多くの方にご覧いただくことができました。