2011年12月29日木曜日

KINECT SDK 日本語音声認識 SpeechPlatform11

今までのエントリーで利用してきた音声認識エンジンはSpeechPlatform10.2でした。
しかし、すでにSpeechPlatform11がリリースされています。
今回は新しいバージョンで動くのかチェックしてみました。

すでに10.2をインストールしている場合にはアンインストールする必要があります。

バージョン11のダウンロード先です。

SDK
http://www.microsoft.com/download/en/details.aspx?id=27226

リンク先にはx86用とx64用がありますが使用中のOSに合わせてダウンロードすればいいと思います。SDKは2つともインストールする必要はないでしょう。

Runtime
http://www.microsoft.com/download/en/details.aspx?id=27225

ランタイムはOSが64ビットの場合x86、x64ともにインストールする必要があると思います。


Runtime Languages
http://www.microsoft.com/download/en/details.aspx?id=27224

ダウンロードするファイルは「MSSpeech_SR_ja-JP_TELE.msi」になります。

上記のファイルをすべてインストールしてください。

プログラム上の変更点は認識エンジンのIDを設定する箇所を「SR_MS_ja-JP_TELE.11.0」に変更すればOKです。

ちなみにバージョン11では「SR_MS_en-US_Kinect.10.0」は利用できないので11用のRuntimeLanguageをダウンロードしましょう。


2011年12月26日月曜日

KINECT SDK 日本語で音声認識(VB+WPF)

KINECT SDKの音声認識サンプルプログラムはコンソールアプリでしたが、WPFでも動くようにしてみました。WPFの場合はUI部分と音声認識部分は別スレッドにする必要があります。

まずはXAMLソースです。

---------------------------------------------------------------------------------

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="241" Width="668">
    <Grid Name="Grid1" Height="151">
     
        <TextBox Height="54" HorizontalAlignment="Left" Margin="0,12,0,0" Name="TextBox1" VerticalAlignment="Top" Width="634" FontSize="20" Grid.ColumnSpan="2" Grid.RowSpan="2" />
        <TextBox Height="54" HorizontalAlignment="Left" Margin="0,72,0,0" Name="TextBox2" VerticalAlignment="Top" Width="634" FontSize="20" Grid.ColumnSpan="2" Grid.Row="1" Grid.RowSpan="2" />
    </Grid>
</Window>
--------------------------------------------------------------------------------- 

テキストボックスを2つ配置しています。

メインソースです。
--------------------------------------------------------------------------------- 

Imports Microsoft.Research.Kinect.Audio
Imports Microsoft.Research.Kinect.Nui
Imports Microsoft.Speech.AudioFormat
Imports Microsoft.Speech.Recognition
Imports System.Media
Imports System.IO
Imports System.Threading
Imports System.Windows.Threading

Class MainWindow
    Inherits Window

    Private nui As Runtime
    Private cam As Camera
    Private readerThread As Thread

    Const ri_ID As String = "SR_MS_ja-JP_TELE_10.0"

    Public Sub New()

        ' この呼び出しはデザイナーで必要です。
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後で初期化を追加します。
        If Runtime.Kinects.Count > 0 Then
            ''KINECT初期化
            nui = Runtime.Kinects(0)
            nui.Initialize(RuntimeOptions.UseColor)
            cam = nui.NuiCamera

            camInit()
        Else
            MsgBox("KINECTが見つかりません。")
            Exit Sub
        End If

    End Sub

    Private Sub Window_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded

        ''音声認識スレッド
        readerThread = New Thread(New ThreadStart(AddressOf SpeechThread))

        ''スレッドスタート
        readerThread.Start()

    End Sub

    Private Sub SpeechThread()


        ''音声認識準備
        Dim source = New KinectAudioSource()

        With source
            .FeatureMode = True
            .AutomaticGainControl = False
            .SystemMode = SystemMode.OptibeamArrayOnly
        End With

        ''認識エンジン選択
        Dim ri As RecognizerInfo = GetKinectRecognizer()

        If IsNothing(ri) = True Then
            MsgBox("認識エンジンが見つかりませんでした。")
            Exit Sub
        End If

        Me.Dispatcher.BeginInvoke(DispatcherPriority.Background, New Action(
                                  Sub()
                                      TextBox1.Text = ri.Name
                                  End Sub))


        Dim sre As New SpeechRecognitionEngine(ri.Id)
        Dim w As New Choices

        With w
            .Add("うえ")
            .Add("した")
            .Add("最初")
            .Add("終わり")
        End With

        Dim gb As New GrammarBuilder

        gb.Culture = ri.Culture
        gb.Append(w)

        Dim g As New Grammar(gb)

        sre.LoadGrammar(g)

        AddHandler sre.SpeechRecognized, AddressOf SreSpeechRecognized
        AddHandler sre.SpeechRecognitionRejected, AddressOf SreSpeechRecognitionRejected


        Dim s As Stream = source.Start
        sre.SetInputToAudioStream(s,
                                  New SpeechAudioFormatInfo(EncodingFormat.Pcm,
                                                            16000,
                                                            16,
                                                            1,
                                                            32000,
                                                            2,
                                                            Nothing))



        sre.RecognizeAsync(RecognizeMode.Multiple)

    End Sub

    Private Sub camInit()
        ''カメラ初期位置
        cam.ElevationAngle = 0
    End Sub

    Private Sub camUp()
        ''カメラ上向き
        Dim nowangle = cam.ElevationAngle + 5
        If nowangle > 25 Then
            cam.ElevationAngle = 25
        Else
            cam.ElevationAngle = nowangle
        End If
    End Sub

    Private Sub camDown()
        ''カメラ下向き
        Dim nowangle = cam.ElevationAngle - 5
        If nowangle < -25 Then
            cam.ElevationAngle = -25
        Else
            cam.ElevationAngle = nowangle
        End If
    End Sub

    Private Function GetKinectRecognizer()
        Return SpeechRecognitionEngine.InstalledRecognizers().Where(Function(r) r.Id = ri_ID).FirstOrDefault
    End Function

    Private Sub SreSpeechRecognized(ByVal sender As Object, ByVal e As SpeechRecognizedEventArgs)
        Me.Dispatcher.BeginInvoke(DispatcherPriority.Background, New Action(
                                  Sub()
                                      TextBox2.Text = "認識しました:" & e.Result.Text
                                  End Sub))


        Select Case e.Result.Text
            Case "うえ"
                camUp()
            Case "した"
                camDown()
            Case "最初"
                camInit()
                ''Case "終わり"

        End Select
    End Sub

    Private Sub SreSpeechRecognitionRejected(ByVal sender As Object, ByVal e As SpeechRecognitionRejectedEventArgs)
        Me.Dispatcher.BeginInvoke(DispatcherPriority.Background, New Action(
                                  Sub()
                                      TextBox2.Text = "認識できません。"
                                  End Sub))

    End Sub

    Private Sub Window_Unloaded(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Unloaded

        ''KINECT終了
        nui.Uninitialize()

        ''スレッド終了
        readerThread.Abort()

        readerThread.Join()

    End Sub
End Class
--------------------------------------------------------------------------------- 
UIスレッドでは音声スレッドのスタートのみ行っています。
コンソール版での音声認識部分を音声認識スレッドとしています。

2011年12月19日月曜日

KINECT SDK Beta2 でスケルトンデータを扱う( VB+ WPF )

このエントリはKINECT SDK Advent Calendar : ATNDの12月19日分です。
kaorun55氏の「KINECT SDK Beta2 でスケルトンデータを扱う( C# + WPF ) のVB版になります。

いつもと同じようにXAMLソースから
--------------------------------------------------------------------------------

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="521" Width="661">
    <Grid>
        <Image Height="480" Name="Image1" Width="640" />
    </Grid>
</Window>
--------------------------------------------------------------------------------
特別なところはありません。Imageコントロールが1つだけです。

メインソースです。
--------------------------------------------------------------------------------
Imports System.Threading
Imports System.Windows.Threading
Imports System.Windows.Media
Imports Microsoft.Research.Kinect.Nui

Class MainWindow
    Inherits Window

    Private readerThread As Thread
    Private shouldRun As Boolean
    Private kinect As Runtime
    Private usercolor() As Color = {Color.FromRgb(0, 0, 0), Colors.Red, Colors.Green, Colors.Blue, Colors.Yellow, Colors.Magenta, Colors.Pink}

    Public Sub New()

        ' この呼び出しはデザイナーで必要です。
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後で初期化を追加します。

        If Runtime.Kinects.Count > 0 Then
            ''KINECT初期化
            kinect = Runtime.Kinects(0)
            kinect.Initialize(RuntimeOptions.UseColor Or RuntimeOptions.UseDepthAndPlayerIndex Or RuntimeOptions.UseSkeletalTracking)
            kinect.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color)
            kinect.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.DepthAndPlayerIndex)

            ''スレッドの開始
            shouldRun = True
            readerThread = New Thread(New ThreadStart(AddressOf RenderThread))
            readerThread.Start()

        End If

    End Sub

    Sub RenderThread()

        While (shouldRun)

            ''タイムアウトは100ms
            Dim video As ImageFrame = kinect.VideoStream.GetNextFrame(100)
            Dim depth As ImageFrame = kinect.DepthStream.GetNextFrame(100)
            Dim skeleton As SkeletonFrame = kinect.SkeletonEngine.GetNextFrame(100)

            ''メインスレッドにイメージデータの書き込みを指示
            Me.Dispatcher.BeginInvoke(DispatcherPriority.Background, New Action(
                Sub()
                    Dim drawingVisual As New DrawingVisual

                    Using drawingContext As DrawingContext = drawingVisual.RenderOpen()
                        drawingContext.DrawImage(DrawPixels(kinect, video, depth), New Rect(0, 0, video.Image.Width, video.Image.Height))

                        ''骨格の部位ごとに座標を求める
                        For Each s In skeleton.Skeletons
                            If s.TrackingState = SkeletonTrackingState.Tracked Then
                                For Each j In s.Joints
                                    Dim point As Point = GetVideoPoint(j)

                                    '円を描く
                                    drawingContext.DrawEllipse(New SolidColorBrush(Colors.Red), New Pen(Brushes.Red, 1), New Point(point.X, point.Y), 5, 5)

                                Next
                            End If
                        Next
                    End Using

                    '描画可能なビットマップを作る
                    Dim bitmap As RenderTargetBitmap = New RenderTargetBitmap(video.Image.Width, video.Image.Height, 96, 96, PixelFormats.Default)
                    bitmap.Render(drawingVisual)

                    Image1.Source = bitmap

                End Sub
            ))

        End While

    End Sub

    Function DrawPixels(ByVal kinect As Runtime, ByVal video As ImageFrame, ByVal depth As ImageFrame) As WriteableBitmap
        Dim x, y, index As Integer
        Dim playerIndex, distance As Integer
        Dim videoX, videoY, videoIndex As Integer
        Dim byte0, byte1 As Byte

        'ピクセルごとのユーザーID
        For y = 0 To depth.Image.Height - 1
            For x = 0 To depth.Image.Width - 1
                index = (x + (y * depth.Image.Width)) * 2
                byte0 = depth.Image.Bits(index)
                byte1 = depth.Image.Bits(index + 1)

                'ユーザーIDと距離
                playerIndex = byte0 And &H7
                distance = byte1 << 5 Or byte0 >> 3

                If playerIndex <> 0 Then
                    videoX = 0
                    videoY = 0

                    ''深度データの座標からカラーデータの座標へ変換
                    kinect.NuiCamera.GetColorPixelCoordinatesFromDepthPixel(ImageResolution.Resolution640x480, New ImageViewArea(), x, y, 0, videoX, videoY)
                    videoIndex = (videoX + (videoY * video.Image.Width)) * video.Image.BytesPerPixel
                    videoIndex = Math.Min(videoX, video.Image.Bits.Length - video.Image.BytesPerPixel)
                    video.Image.Bits(videoIndex) = usercolor(playerIndex).R
                    video.Image.Bits(videoIndex + 1) = usercolor(playerIndex).G
                    video.Image.Bits(videoIndex + 2) = usercolor(playerIndex).B
                End If

            Next
        Next

        'バイト列をビットマップに展開
        '描画可能なビットマップを作る
        Dim bitmap As WriteableBitmap = New WriteableBitmap(video.Image.Width, video.Image.Height, 96, 96, PixelFormats.Bgr32, Nothing)
        bitmap.WritePixels(New Int32Rect(0, 0, video.Image.Width, video.Image.Height), video.Image.Bits, video.Image.Width * video.Image.BytesPerPixel, 0)

        Return bitmap

    End Function

    Function GetVideoPoint(ByVal joint As Joint) As Point

        Dim kinect As Runtime = Runtime.Kinects(0)
        Dim depthX, depthY As Single
        Dim videoX, videoY As Integer

        depthX = 0
        depthY = 0

        ''骨格データ(ベクタ座標)から深度データの座標へ変換
        kinect.SkeletonEngine.SkeletonToDepthImage(joint.Position, depthX, depthY)
        depthX = Math.Min(depthX * kinect.DepthStream.Width, kinect.DepthStream.Width)
        depthY = Math.Min(depthY * kinect.DepthStream.Height, kinect.DepthStream.Height)

        videoX = 0
        videoY = 0

        ''深度データの座標をカラーデータの座標に変換
        kinect.NuiCamera.GetColorPixelCoordinatesFromDepthPixel(ImageResolution.Resolution640x480, New ImageViewArea(), CInt(depthX), CInt(depthY), 0, videoX, videoY)

        Return New Point(Math.Min(videoX, kinect.VideoStream.Width), Math.Min(videoY, kinect.VideoStream.Height))

    End Function

    Private Sub Window_Unloaded(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Unloaded

        shouldRun = False

    End Sub
End Class
--------------------------------------------------------------------------------

Skeletonの座標データはKINECTを中心にXYZの3D座標になります。
XYは-1~1までの単精度浮動小数になるのでこれを2Dの座標に変換しなければいけないのですが、手順としてSkeleton座標>Depth(深度)座標>Video(カラー)座標の順番に変換しています。

Skeleton>Depthの変換はSkeletonToDepthImageで変換します。
Depth>Videoの変換はGetColorPixelCoordinatesFromDepthPixelを使って変換しています。

変換した座標をもとにShapeを描画すればVideo,Depthのイメージに骨格情報を重ねて表示できます。

2011年12月18日日曜日

KINECT SDK Beta2 でユーザーデータを扱う( VB + WPF )


このエントリはKINECT SDK Advent Calendar : ATNDの12月18日分です。

kaorun55氏の「KINECT SDK Beta2 でユーザーデータを扱う( C# + WPF )」 を参考にVBに書き換えました。

まずはXAMLソースから

--------------------------------------------------------------------------------

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="521" Width="661">
    <Grid>
        <Image Height="480" Name="Image1" Width="640" />
    </Grid>
</Window>
--------------------------------------------------------------------------------
今までのサンプルと同様です。

メインソースです。

--------------------------------------------------------------------------------
Imports Microsoft.Research.Kinect.Nui
Imports System.Threading
Imports System.Windows.Threading

Class MainWindow

    Inherits Window

    Private readerThread As Thread
    Private shuldRun As Boolean
    Private kinect As Runtime
    Private usercolor() As Color = {Color.FromRgb(0, 0, 0), Colors.Red, Colors.Green, Colors.Blue, Colors.Yellow, Colors.Magenta, Colors.Pink}

    Public Sub New()

        ' この呼び出しはデザイナーで必要です。
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後で初期化を追加します。

    End Sub

    Private Sub Window_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded

        If Runtime.Kinects.Count > 0 Then

            ''KINECT初期化
            kinect = Runtime.Kinects(0)
            kinect.Initialize(RuntimeOptions.UseColor Or RuntimeOptions.UseDepthAndPlayerIndex)
            kinect.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color)
            kinect.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.DepthAndPlayerIndex)

            shuldRun = True
            ''スレッド作成
            readerThread = New Thread(New ThreadStart(AddressOf RenderThread))
            ''スレッド開始
            readerThread.Start()

        End If

    End Sub

    Sub RenderThread()

        ''Imageコントロールに書き込むスレッド

        While (shuldRun)

            ''映像データの取り込み
            kinect = Runtime.Kinects(0)
            ''タイムアウトは100ms
            Dim video As ImageFrame = kinect.VideoStream.GetNextFrame(100)
            Dim depth As ImageFrame = kinect.DepthStream.GetNextFrame(100)


            For y = 0 To depth.Image.Height - 1
                For x = 0 To depth.Image.Width - 1
                    Dim index As Integer = (x + (y * depth.Image.Width)) * 2
                    Dim byte0 As Byte = depth.Image.Bits(index)
                    Dim byte1 As Byte = depth.Image.Bits(index + 1)

                    Dim playerIndex As Integer = byte0 And &H7
                    Dim distance As Integer = byte1 << 5 Or byte0 >> 3

                    If playerIndex <> 0 Then
                        Dim videoX As Integer = 0
                        Dim videoY As Integer = 0

                        ''深度データからカラーデータに座標変換
                        kinect.NuiCamera.GetColorPixelCoordinatesFromDepthPixel(ImageResolution.Resolution640x480, New ImageViewArea(), x, y, 0, videoX, videoY)

                        Dim videoIndex As Integer = (videoX + (videoY * video.Image.Width)) * video.Image.BytesPerPixel
                        videoIndex = Math.Min(videoIndex, video.Image.Bits.Length - video.Image.BytesPerPixel)
                        video.Image.Bits(videoIndex) = usercolor(playerIndex).R
                        video.Image.Bits(videoIndex + 1) = usercolor(playerIndex).G
                        video.Image.Bits(videoIndex + 2) = usercolor(playerIndex).B

                    End If
                Next
            Next

            ''メインスレッド経由でイメージの書込み
            Me.Dispatcher.BeginInvoke(DispatcherPriority.Background, New Action(
                Sub()
                    Image1.Source = BitmapImage.Create(video.Image.Width, video.Image.Height, 96, 96, PixelFormats.Bgr32, Nothing, video.Image.Bits, video.Image.Width * video.Image.BytesPerPixel)
                End Sub))

        End While

    End Sub

    Private Sub Window_Unloaded(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Unloaded

        shuldRun = False

    End Sub
End Class
--------------------------------------------------------------------------------

7人まで認識するはずですが、試していません。

2011年12月17日土曜日

KINECT SDK 挿抜状態検出サンプル

このエントリはKINECT SDK Advent Calendar : ATNDの12月17日分です。

kaorun55氏の「KINECT SDK Beta2 で、挿抜状態に応じたアプリの動作をする 」 を参考にVBに書き換えました。

まずはXAMLソースから

--------------------------------------------------------------------------------

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="557" Width="669">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="149*" />
            <RowDefinition Height="389*" />
        </Grid.RowDefinitions>
        <TextBox Name="kinectCount" Margin="0,0,0,97" FontSize="18" Text="Text" TextAlignment="Center" FontWeight="Bold" FontStretch="Normal" TextWrapping="NoWrap" VerticalContentAlignment="Center" />
        <Image Name="image1" Height="240" Width="320" Margin="0,50,332,248" Grid.RowSpan="2" />
        <Image Height="240" Margin="326,50,6,248" Name="image2" Width="320" Grid.RowSpan="2" />
        <Image Height="240" Margin="0,149,332,0" Name="image3" Width="320" Grid.Row="1" />
        <Image Height="240" Margin="326,149,6,0" Name="image4" Width="320" Grid.Row="1" />

    </Grid>
</Window>

--------------------------------------------------------------------------------

TextBoxが1つとGrid内に4つのImageコントロールを配置しています。


メインソースです。
--------------------------------------------------------------------------------

Imports Microsoft.Research.Kinect.Nui

Class MainWindow

    ''Imageコントロール用配列
    Public images As New ArrayList

    Public Sub New()

        ' この呼び出しはデザイナーで必要です。
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後で初期化を追加します。

        'イベントハンドラの追加
        AddHandler Runtime.Kinects.StatusChanged, AddressOf Kinects_StatusChanged

        ''接続されているKINECTを確認
        ShowKinectCount()

        ''接続されているKINECTの初期化
        Dim kinect As Runtime

        For Each kinect In Runtime.Kinects
            InitKinect(kinect)
        Next

        ''配列にImegeコントロールを追加
        With images
            .Add(image1)
            .Add(image2)
            .Add(image3)
            .Add(image4)
        End With

    End Sub

    Sub InitKinect(kinect As Runtime)

        ''KINECTの初期化
        kinect.Initialize(RuntimeOptions.UseColor)
        kinect.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color)

        ''イベントハンドラの追加
        AddHandler kinect.VideoFrameReady, AddressOf Kinect_VideoFrameReady

    End Sub

    Sub Kinects_StatusChanged(ByVal sender As Object, ByVal e As StatusChangedEventArgs)

        ''KINECT接続数の確認
        ShowKinectCount()

        ''ステータス別に処理
        If e.Status = KinectStatus.Connected Then
            ''接続されたとき初期化
            InitKinect(e.KinectRuntime)

        ElseIf e.Status = KinectStatus.Disconnected Then
            ''切断されたとき
            ''一度すべてのImegeコントロールを初期化
            Dim i As Integer

            For i = 0 To Runtime.Kinects.Count
                images(i).Source = Nothing
            Next

            ''イベントハンドラの削除
            RemoveHandler e.KinectRuntime.VideoFrameReady, AddressOf Kinect_VideoFrameReady

            ''KINECTの終了処理
            e.KinectRuntime.Uninitialize()
        End If

    End Sub

    Sub ShowKinectCount()

        ''接続されている台数を確認
        kinectCount.Text = Runtime.Kinects.Count & "台のKINECTが有効です"

    End Sub

    Sub Kinect_VideoFrameReady(ByVal sender As Object, ByVal e As ImageFrameReadyEventArgs)

        ''切断された瞬間は書き込まないようにする
        If IsNothing(sender) = False And sender.InstanceIndex >= 0 Then

            Dim source As PlanarImage = e.ImageFrame.Image

            images(sender.InstanceIndex).Source = BitmapSource.Create(source.Width, source.Height, 96, 96, PixelFormats.Bgr32, Nothing, source.Bits, source.Width * source.BytesPerPixel)

        End If

    End Sub

End Class
--------------------------------------------------------------------------------


KINECTを1台しか持っていないので複数台接続してのチェックはできていません。
0台の状態での動作は大丈夫でした。


2011年12月13日火曜日

KINECT SDK Beta2 で距離データを扱う( VB + WPF )

このエントリはKINECT SDK Advent Calendar : ATNDの12月13日分です。

kaorun55氏の「KINECT SDK Beta2 で距離データを扱う( C# + WPF )」をVBに変更してみました。

まずはXAMLのソースから

-------------------------------------------------------------------------------

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="521" Width="663">
    <Grid>
        <Image  Name="DepthImage" Height="480" Width="640" />
    </Grid>
</Window>
-------------------------------------------------------------------------------

特に変わったところはないです。Imegeコントロールを1つ貼り付けているだけです。

次にメインのソースです。

-------------------------------------------------------------------------------
Imports Microsoft.Research.Kinect.Nui

Class MainWindow
    Inherits Window

    Public nui As Runtime

    Public Sub New()

        ' この呼び出しはデザイナーで必要です。
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後で初期化を追加します。

        'KINECT確認
        If Runtime.Kinects.Count > 0 Then
            nui = Runtime.Kinects(0)
        Else
            MsgBox("KINECTが接続されていません。")
            Exit Sub
        End If

    End Sub

    Private Sub Window_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded

        'KINECT初期化
        nui.Initialize(RuntimeOptions.UseDepth)
        nui.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution640x480, ImageType.Depth)

        'イベント追加
        AddHandler nui.DepthFrameReady, AddressOf Kinect_DepthFrameReady

    End Sub

    Private Sub Kinect_DepthFrameReady(ByVal sender As Object, ByVal e As ImageFrameReadyEventArgs)

        'Imageコントロールに1フレーム分のデータを設定
        Dim image As PlanarImage = e.ImageFrame.Image
        DepthImage.Source = BitmapSource.Create(image.Width, image.Height, 96, 96, PixelFormats.Gray16, Nothing, ConvGrayScale(image).Bits, image.Width * image.BytesPerPixel)

        'リソース不足になるので強制的にGCを実行
        System.GC.Collect()

    End Sub

    Private Function ConvGrayScale(ByVal source As PlanarImage) As PlanarImage

        '深度情報をグレースケールに変換
        Dim i As Integer
        Dim depth As Integer

        For i = 0 To source.Bits.Length - 2 Step 2

            depth = CInt((source.Bits(i) Or (source.Bits(i + 1) << 8)))

            depth = CInt(&HFFFF - (&HFFFF * depth / &HFFF))

            source.Bits(i) = CByte(depth And &HFF)
            source.Bits(i + 1) = CByte((depth >> 8) And &HFF)

        Next

        ConvGrayScale = source

    End Function

End Class
-------------------------------------------------------------------------------

距離カメラのデータをグレースケールに変更する必要があります。

実はこの辺りの処理をしてくれるtoolがあります。

http://c4fkinect.codeplex.com/からToolkitをダウンロードして、適当な場所に解凍してください。

プロジェクトの参照の追加で先ほど解凍したフォルダの中の「Coding4Fun.Kinect.Wpf.dll」を追加しましょう。

次にメインソースの先頭に「Imports Coding4Fun.Kinect.Wpf」を書き加えます。

Kinect_DepthFrameReadyサブルーチン内の「Dim image As PlanarImage = e.ImageFrame.Image」は削除してください。

次に
「DepthImage.Source = BitmapSource.Create(image.Width, image.Height, 96, 96, PixelFormats.Gray16, Nothing, ConvGrayScale(image).Bits, image.Width * image.BytesPerPixel)」を
「DepthImage.Source = e.ImageFrame.ToBitmapSource()」に変更します。

「ConvGrayScale」ファンクションは必要ありません。

これだけで距離データをグレースケールで表示できます。

変更したソースは以下のようになります。

-------------------------------------------------------------------------------
Imports Microsoft.Research.Kinect.Nui
Imports Coding4Fun.Kinect.Wpf

Class MainWindow
    Inherits Window

    Public nui As Runtime

    Public Sub New()

        ' この呼び出しはデザイナーで必要です。
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後で初期化を追加します。

        'KINECT確認
        If Runtime.Kinects.Count > 0 Then
            nui = Runtime.Kinects(0)
        Else
            MsgBox("KINECTが接続されていません。")
            Exit Sub
        End If

    End Sub

    Private Sub Window_Loaded(sender As System.Object, e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded

        'KINECT初期化
        nui.Initialize(RuntimeOptions.UseDepth)
        nui.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution640x480, ImageType.Depth)

        'イベント追加
        AddHandler nui.DepthFrameReady, AddressOf Kinect_DepthFrameReady

    End Sub

    Private Sub Kinect_DepthFrameReady(ByVal sender As Object, ByVal e As ImageFrameReadyEventArgs)

        'Imageコントロールに1フレーム分のデータを設定

        DepthImage.Source = e.ImageFrame.ToBitmapSource()

        'リソース不足になるので強制的にGCを実行
        System.GC.Collect()

    End Sub

End Class
-------------------------------------------------------------------------------

ずいぶんと短くなります。

ToolkitはライブラリですのでC#からも利用できます。
ちなみに初期のころにToolkitの名称で公表されていたライブラリはToolboxに名前が変更になったようです。Toolboxの方はKINECT SDKが標準で持っていないジェスチャー認識ができるライブラリのようです。



2011年12月12日月曜日

KINECT SDK VBでカラーイメージを表示


このエントリはKINECT SDK Advent Calendar : ATNDの12月12日分です。

kaorun55氏のC#+WPFを参考にVB+WPFに変更しました。
基本となるRGB画像を表示します。

いきなりソースコードから

MainWindow.xaml
----------------------------------------------------------------------------

<Window x:Class="MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="518" Width="665">
    <Grid>
        <Image Height="480"  Name="kinectImage"  Width="640" />
    </Grid>
</Window>
----------------------------------------------------------------------------

画面についてはImageコントロールのNameプロパティだけ注意すれば特に問題はないですね。


予め、メニューの「プロジェクト」->「参照の追加」->「.NET」タブから「Microsoft.Reseach.Kinect」を追加するようにしましょう。(C#だとソリューションエクスプローラーに「参照設定」が表示されるのにVBにはないんですよね。なぜなのかな?)



MainWindow.xaml.vb
----------------------------------------------------------------------------
Imports Microsoft.Research.Kinect.Nui

Class MainWindow
    Inherits Window

    Public nui As Runtime

    Public Sub New()

        ' この呼び出しはデザイナーで必要です。
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後で初期化を追加します。

        'KINECTの確認
        If Runtime.Kinects.Count > 0 Then
            nui = Runtime.Kinects(0)
        Else
            MsgBox("KINECTが接続されていません。")
            Exit Sub
        End If

    End Sub

    Private Sub Window_Loaded(sender As System.Object,  e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded

        ''KINECT初期化
        nui.Initialize(RuntimeOptions.UseColor)

        ''Videoデータの取得
        nui.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color)

        ''イベントハンドルの追加
        AddHandler nui.VideoFrameReady, AddressOf kinect_VideoFrameReady

    End Sub

    Private Sub kinect_VideoFrameReady(ByVal sender As Object, ByVal e As ImageFrameReadyEventArgs)

        Dim image As PlanarImage = e.ImageFrame.Image

        ''ImageコントロールにVideoストリームを割り当て
        kinectImage.Source = BitmapSource.Create(image.Width, image.Height, 96, 96, PixelFormats.Bgr32, Nothing, image.Bits, image.Width * image.BytesPerPixel)

        'リソース不足でエラーが出るので強制的にGC
        System.GC.Collect()

    End Sub

End Class
----------------------------------------------------------------------------


処理の大まかな流れは

1.初期化とKINECTの確認
2.Window表示
3.KINECT初期化
4.VideoStreamのオープン後、1フレームごとにインベント呼び出し
5.ImageコントロールにVideoStreamデータを割り当て

となります。

内容についてはコメントを見てもらえれば何をしているのかはわかると思います。





2011年12月10日土曜日

KINECT SDK 日本語で音声認識

先日の英語バージョンをちょっと変更して日本語も認識できるようにしてみました。

予め以下のランゲージパックをダウンロードしてインストールしてください。

http://www.microsoft.com/download/en/details.aspx?id=21924より

「MSSpeech_SR_ja-JP_TELE.msi」をダウンロードしてください。

リストの下の方には「MSSpeech_TTS_ja-JP_Haruka.msi」がありますがこちらは音声合成用のランゲージパックになりますので間違えないようにしてください。
それから最新のSpeechPlatfprmのバージョンは11ですが、ランゲージパックはバージョンに依存するみたいですので間違えないようにしましょう。KINECTの環境は10.2になります。

次にソースコードです。

---------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.Research.Kinect.Nui;        //追加
using Microsoft.Research.Kinect.Audio;      //追加
using Microsoft.Speech.Recognition;         //追加
using Microsoft.Speech.AudioFormat;         //追加


namespace SpeechElevation
{
    public partial class Program
    {
        static Runtime nui;
        static Camera cam;

        public static void Main(string[] args)
        {
            //初期化
            if (Runtime.Kinects.Count == 0)
            {
                return;
            }

            nui = Runtime.Kinects[0];
            nui.Initialize(RuntimeOptions.UseColor);
            cam = nui.NuiCamera;

            //カメラの位置を初期化
            CamInit();

            //オーディオソース
            using (var source = new KinectAudioSource())
            {
                source.FeatureMode = true;
                source.AutomaticGainControl = false;  //音声認識のときは必ずfalse
                source.SystemMode = SystemMode.OptibeamArrayOnly;

                //認識エンジンの選択
                RecognizerInfo ri = GetKinectRecognizer();

                if (ri == null)
                {
                    Console.WriteLine("認識エンジンが見つかりません。");
                    return;
                }

                Console.WriteLine("認識エンジン: {0}", ri.Name);

                //認識エンジンの準備
                using (var sre = new SpeechRecognitionEngine(ri.Id))
                {
                    //認識する語句の準備
                    var wd = new Choices();
                    wd.Add("ぜろ");
                    wd.Add("うえ");
                    wd.Add("した");
                    wd.Add("最初");   //漢字も使える

                    var gb = new GrammarBuilder();
                    //GrammerBuilderのCultureと認識エンジンのCultureを合わせる
                    gb.Culture = ri.Culture;
                    gb.Append(wd);

                    var g = new Grammar(gb);

                    sre.LoadGrammar(g);

                    //イベントハンドラの追加
                    sre.SpeechRecognized += SreSpeechRecognized;
                    sre.SpeechHypothesized += SreSpeechHypothesized;
                    sre.SpeechRecognitionRejected += SreSpeechRecognitionRejected;

                    //認識の開始
                    using (Stream s = source.Start())
                    {
                        sre.SetInputToAudioStream(s,
                                                  new SpeechAudioFormatInfo(
                                                      EncodingFormat.Pcm, 16000, 16, 1,
                                                      32000, 2, null));

                        Console.WriteLine("認識開始:");

                        sre.RecognizeAsync(RecognizeMode.Multiple);

                        Console.ReadLine();

                    }
                }
            }
        }

        private static RecognizerInfo GetKinectRecognizer()
        //認識エンジンを直に設定
        {
            return SpeechRecognitionEngine.InstalledRecognizers().Where(r => r.Id == "SR_MS_ja-JP_TELE_10.0").FirstOrDefault();
        }

        static void SreSpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
        {
            Console.WriteLine("\n認識できません。");
        }

        static void SreSpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
        {
            Console.Write("\r入力音声: \t{0}", e.Result.Text);
        }

        static void SreSpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            Console.WriteLine("\n認識しました。: \t{0}", e.Result.Text);

            switch (e.Result.Text)
            {
                case "うえ":
                    CamUp();
                    break;
                case "した":
                    CamDown();
                    break;
                case "ぜろ":
                    CamInit();
                    break;
                case "最初":
                    CamInit();
                    break;
            }

        }

        //カメラを上に
        static void CamUp()
        {
            var nowangle = cam.ElevationAngle + 5;

            if (nowangle > 25)
            {
                cam.ElevationAngle = 25;
            }
            else
            {
                cam.ElevationAngle = nowangle;
            }
        }
        //カメラを下に
        static void CamDown()
        {
            var nowangle = cam.ElevationAngle - 5;

            if (nowangle < -25)
            {
                cam.ElevationAngle = -25;
            }
            else
            {
                cam.ElevationAngle = nowangle;
            }
        }

        //カメラを水平に
        static void CamInit()
        {
            cam.ElevationAngle = 0;
        }

    }

}

---------------------------------------------------------------------------------

変更したところはテキストの色を変えていますが、一番のキモは認識エンジンに読み込ませるランゲージパックの名前です。
このことから他の言語でも同じくランゲージパックをインストールしてあげれば問題なく動作すると思います。
すこし謎なのが、なぜわざわざKINECT用にランゲージパックを用意しているのかですが、実際にSpeechPlatform用en-USランゲージパックとKINECT用では内容が少し違うようです。推測ですがKINECT用は文法関係の辞書に何か手を加えてあるのかもしれません。容量はKINECT用の方が大きいですので。

しかし、SpeechPlatformの情報は少ないです。
今は単語レベルでの認識までですが、短めの文章なんかも認識できるのか試してみたいです。

2011年12月9日金曜日

KINECT SDK 音声認識


KINECT SDKのSpeechサンプルプログラムにちょっと手を加えてみました。
サンプルは音声認識だけですが、それだけでは面白くないので声で首ふりできるようにしてみました。
C#のプログラムは初めてなので少し変なところもあるかもしれないですが、動作は問題ないはず・・・・

環境は
Windows7 Ultimate 64bit

KINECT SDK Beta2

SpeechPlatform SDK 10.2
SpeechPlatform Runtime 10.2
Kinect for Windows Language Pack, version 0.9
上の3つはKINECT SDKのダウンロードページ左下のリンクからダウンロードできます。

Visual C# 2010 Express

以下はソースです。

-------------------------------------------------------------------------------




using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.Research.Kinect.Nui;        //追加
using Microsoft.Research.Kinect.Audio;      //追加
using Microsoft.Speech.Recognition;         //追加
using Microsoft.Speech.AudioFormat;         //追加


namespace SpeechElevation
{
    public partial class Program
    {
        static Runtime nui;
        static Camera cam;

        public static void Main(string[] args)
        {
            //初期化
            if (Runtime.Kinects.Count == 0)
            {
                return;
            }

            nui = Runtime.Kinects[0];
            nui.Initialize(RuntimeOptions.UseColor);
            cam = nui.NuiCamera;

            //カメラの位置を初期化
            CamInit();

            //オーディオソース
            using (var source = new KinectAudioSource())
            {
                source.FeatureMode = true;
                source.AutomaticGainControl = false;  //音声認識のときは必ずfalse
                source.SystemMode = SystemMode.OptibeamArrayOnly;

                //認識エンジンの選択
                RecognizerInfo ri = GetKinectRecognizer();

                if (ri == null)
                {
                    Console.WriteLine("認識エンジンが見つかりません。");
                    return;
                }

                Console.WriteLine("認識エンジン: {0}", ri.Name);

                //認識エンジンの準備
                using (var sre = new SpeechRecognitionEngine(ri.Id))
                {
                    //認識する語句の準備
                    var wd = new Choices();
                    wd.Add("init");
                    wd.Add("up");
                    wd.Add("down");
                    wd.Add("suihei");   //ローマ字も使える

                    var gb = new GrammarBuilder();
                    //GrammerBuilderのCultureと認識エンジンのCultureを合わせる
                    gb.Culture = ri.Culture;
                    gb.Append(wd);

                    var g = new Grammar(gb);

                    sre.LoadGrammar(g);

                    //イベントハンドラの追加
                    sre.SpeechRecognized += SreSpeechRecognized;
                    sre.SpeechHypothesized += SreSpeechHypothesized;
                    sre.SpeechRecognitionRejected += SreSpeechRecognitionRejected;

                    //認識の開始
                    using (Stream s = source.Start())
                    {
                        sre.SetInputToAudioStream(s,
                                                  new SpeechAudioFormatInfo(
                                                      EncodingFormat.Pcm, 16000, 16, 1,
                                                      32000, 2, null));

                        Console.WriteLine("認識開始:");

                        sre.RecognizeAsync(RecognizeMode.Multiple);

                        Console.ReadLine();

                    }
                }
            }
        }

        private  static RecognizerInfo GetKinectRecognizer()
        //認識エンジンを直に設定
        {
            return SpeechRecognitionEngine.InstalledRecognizers().Where(r=>r.Id=="SR_MS_en-US_Kinect_10.0").FirstOrDefault();
        }

        static void SreSpeechRecognitionRejected(object sender,SpeechRecognitionRejectedEventArgs e)
        {
            Console.WriteLine("\n認識できません。");
        }

        static void SreSpeechHypothesized(object sender,SpeechHypothesizedEventArgs  e)
        {
            Console.Write("\r入力音声: \t{0}",e.Result.Text);
        }

        static void SreSpeechRecognized(object sender,SpeechRecognizedEventArgs e)
        {
            Console.WriteLine("\n認識しました。: \t{0}", e.Result.Text);

            switch (e.Result.Text)
            {
                case "up":
                    CamUp();
                    break;
                case "down":
                    CamDown();
                    break;
                case "init":
                    CamInit();
                    break;
                case "suihei":
                    CamInit();
                    break;
            }

        }

        //カメラを上に
        static void CamUp()
        {
            var nowangle = cam.ElevationAngle + 5;

            if (nowangle > 25)
            {
                cam.ElevationAngle = 25;
            }
            else
            {
                cam.ElevationAngle = nowangle;
            }
        }
        //カメラを下に
        static void CamDown()
        {
            var nowangle = cam.ElevationAngle - 5;

            if (nowangle < -25)
            {
                cam.ElevationAngle = -25;
            }
            else
            {
                cam.ElevationAngle = nowangle;
            }
        }

        //カメラを水平に
        static void CamInit()
        {
            cam.ElevationAngle = 0;
        }

    }

}



--------------------------------------------------------------------------------

ソースコードを見ればわかると思いますが、「up」、[down」で上下にカメラが首を振ります。
「init」、「suihei」で初期位置(水平位置)に移動します。
KINECTの可動範囲は27°~-27°らしいですが壊れると困るので念のために25°~-25°までにしています。
ローマ字でも認識してくれます。すべてではないでしょうけどね。
初めは初期位置にするのに「zero」にしていたのですが「r」の発音が悪いらしくなかなか認識してくれないので変更しました^^;

次はこのソースを少し変更して日本語で認識できるようにします。

2011年11月29日火曜日

KINECT 始めました

KINECT 購入しました。Xboxは持ってません^^;

PCに接続して色々試していますが、面白いですね。

KINECT 関係の本を数週間で6冊も購入しちゃいました。

当所VB2010でプログラムを書くつもりでしたが、やはりサンプルプログラムの関係でC#2010で書くことにしました。

C#初めてです。 まぁ、最近のVBと違いがほとんどないというか最近のVBもあまりわかってないですけど・・・・(VB2005ぐらいまでしかわかりません。)

音声認識をいろいろ試していますが、SDKは英語しかありません。
ただ、単語レベルでの認識はローマ字でもちゃんと認識するので、まったく使えないわけではないようです。