Imports System
Imports System.Linq
Imports System.Windows
Imports System.Windows.Media
Imports System.Windows.Media.Imaging
Imports System.Windows.Shapes

Imports Microsoft.Kinect

''' <summary>
''' Interaction logic for MainWindow.xaml
''' </summary>
''' <remarks></remarks>
Class MainWindow
#Region "メンバー変数"
    Private _KinectDevice As KinectSensor
    Private _DepthImage As WriteableBitmap
    Private _DepthImageRect As Int32Rect
    Private _DepthPixelData() As Short
    Private _DepthImageStride As Integer
    Private _TotalFrames As Integer
    Private _StartFrameTime As DateTime
    Private Const LoDepthThreshold As Integer = 800 '1200
    Private Const HiDepthThreshold As Integer = 4000 '2500
#End Region

#Region "コンストラクタ"
    Sub New()

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

        ' InitializeComponent() 呼び出しの後で初期化を追加します。
        AddHandler KinectSensor.KinectSensors.StatusChanged, AddressOf KinectSensors_StatusChanged
        Me.KinectDevice = KinectSensor.KinectSensors.FirstOrDefault(Function(x)
                                                                        Return x.Status = KinectStatus.Connected
                                                                    End Function)
    End Sub
#End Region

#Region "メソッド"
    Private Sub KinectSensors_StatusChanged(sender As Object, e As StatusChangedEventArgs)
        Select Case e.Status
            Case KinectStatus.Initializing,
                KinectStatus.Connected,
                KinectStatus.NotPowered,
                KinectStatus.NotReady,
                KinectStatus.DeviceNotGenuine
                Me.KinectDevice = e.Sensor
            Case KinectStatus.Disconnected
                'TODO: Give the user feedback to plug-in a Kinect device.                    
                Me.KinectDevice = Nothing
            Case Else
                'TODO: Show an error state
        End Select
    End Sub

    Private Sub KinectDevice_DepthFrameReady(sender As Object, e As DepthImageFrameReadyEventArgs)
        Using frame As DepthImageFrame = e.OpenDepthImageFrame()
            If frame IsNot Nothing Then
                frame.CopyPixelDataTo(Me._DepthPixelData)
                CreateBetterShadesOfGray(frame, Me._DepthPixelData)
                CreateDepthHistogram(frame, Me._DepthPixelData)
            End If
        End Using
        Me._TotalFrames += 1
        FramesPerSecondElement.Text = String.Format("{0:0} fps",
                                                    (Me._TotalFrames / DateTime.Now.Subtract(Me._StartFrameTime).TotalSeconds))
    End Sub

    Private Sub CreateBetterShadesOfGray(depthFrame As DepthImageFrame, pixelData() As Short)
        Dim depth As Integer
        Dim gray As Integer
        Const bytesPerPixel As Integer = 4
        Dim enhPixelData(depthFrame.Width * depthFrame.Height * bytesPerPixel - 1) As Byte

        For i As Integer = 0 To pixelData.Length - 1
            Dim j As Integer = i * bytesPerPixel
            depth = pixelData(i) >> DepthImageFrame.PlayerIndexBitmaskWidth

            If depth < LoDepthThreshold OrElse depth > HiDepthThreshold Then
                gray = &HFF
            Else
                gray = 255 - (255 * depth / &HFFF)
            End If
            enhPixelData(j + 0) = CType(gray, Byte)
            enhPixelData(j + 1) = CType(gray, Byte)
            enhPixelData(j + 2) = CType(gray, Byte)
        Next

        Me._DepthImage.WritePixels(Me._DepthImageRect, enhPixelData, Me._DepthImageStride, 0)
    End Sub

    Private Sub CreateDepthHistogram(depthFrame As DepthImageFrame, pixelData() As Short)
        Dim depth As Integer
        Dim depths(4096 - 1) As Integer
        Dim chartBarWidth As Double = Math.Max(3, DepthHistogram.ActualWidth / depths.Length)
        Dim maxValue As Integer = 0

        DepthHistogram.Children.Clear()

        '1回目のパス - 深度の出現回数のカウント
        For i As Integer = 0 To pixelData.Length - 1
            depth = pixelData(i) >> DepthImageFrame.PlayerIndexBitmaskWidth

            If depth >= LoDepthThreshold AndAlso depth <= HiDepthThreshold Then
                depths(depth) += 1
            End If
        Next

        '2回目のパス - 最も出現回数の多い深度を見つけ、領域にあわせてヒストグラムのサイズを調整
        '              これはUIの見た目を良くするためだけの処理
        For i As Integer = 0 To depths.Length - 1
            maxValue = Math.Max(maxValue, depths(i))
        Next

        '3回目のパス - ヒストグラムの作成
        For i As Integer = 0 To depths.Length - 1
            If depths(i) > 0 Then
                Dim r As New Rectangle()
                r.Fill = Brushes.Black
                r.Width = chartBarWidth
                r.Height = DepthHistogram.ActualHeight * (depths(i) / CType(maxValue, Double))
                r.Margin = New Thickness(1, 0, 1, 0)
                r.VerticalAlignment = System.Windows.VerticalAlignment.Bottom
                DepthHistogram.Children.Add(r)
            End If
        Next
    End Sub
#End Region

#Region "プロパティ"
    Public Property KinectDevice As KinectSensor
        Get
            Return Me._KinectDevice
        End Get
        Set(value As KinectSensor)
            If Me._KinectDevice IsNot value Then
                'Uninitialize
                If Me._KinectDevice IsNot Nothing Then
                    Me._KinectDevice.Stop()
                    RemoveHandler Me._KinectDevice.DepthFrameReady, AddressOf KinectDevice_DepthFrameReady
                    Me._KinectDevice.DepthStream.Disable()

                    Me.Depth_Image.Source = Nothing
                    Me._DepthImage = Nothing
                End If

                Me._KinectDevice = value

                'Initialize
                If Me._KinectDevice IsNot Nothing Then
                    If Me._KinectDevice.Status = KinectStatus.Connected Then
                        Me._KinectDevice.DepthStream.Enable()

                        Dim depthStream As DepthImageStream = Me._KinectDevice.DepthStream
                        Me._DepthImage = New WriteableBitmap(depthStream.FrameWidth,
                                                             depthStream.FrameHeight,
                                                             96,
                                                             96,
                                                             PixelFormats.Bgr32,
                                                             Nothing)
                        Me._DepthImageRect = New Int32Rect(0,
                                                           0,
                                                           Math.Ceiling(Me._DepthImage.Width),
                                                           Math.Ceiling(Me._DepthImage.Height))
                        Me._DepthImageStride = depthStream.FrameWidth * 4
                        ReDim Me._DepthPixelData(depthStream.FramePixelDataLength - 1)
                        Me.Depth_Image.Source = Me._DepthImage

                        AddHandler Me._KinectDevice.DepthFrameReady, AddressOf KinectDevice_DepthFrameReady
                        Me._KinectDevice.Start()

                        Me._StartFrameTime = DateTime.Now
                    End If
                End If
            End If
        End Set
    End Property
#End Region
End Class
