在F#中使用Kinect骨架跟踪出现问题

3
我正在使用由KinectContrib提供的F#骨骼跟踪模板。C#中执行相同功能的模板可以正常工作,因此我知道硬件没有问题。
我正在使用Windows Kinect SDK v1.8
该程序偶尔会进行一次跟踪,但没有一致的模式。我已经在昨晚开始研究代码,所以我希望有人可以在另一个系统上确认相同的行为或提供任何更改代码的指针。
提前感谢。
以下是模板代码:
#light

open System
open System.Windows
open System.Windows.Media.Imaging
open Microsoft.Kinect
open System.Diagnostics

let sensor = KinectSensor.KinectSensors.[0]

//The main canvas that is handling the ellipses
let canvas = new System.Windows.Controls.Canvas()
canvas.Background <- System.Windows.Media.Brushes.Transparent

let ds : byte = Convert.ToByte(1)
let dummySkeleton : Skeleton = new Skeleton(TrackingState = SkeletonTrackingState.Tracked)

// Thanks to Richard Minerich (@rickasaurus) for helping me figure out
// some array concepts in F#.
let mutable pixelData : byte array = [| |]
let mutable skeletons : Skeleton array = [| |]

//Right hand ellipse
let rhEllipse = new System.Windows.Shapes.Ellipse()
rhEllipse.Height <- 20.0
rhEllipse.Width <- 20.0
rhEllipse.Fill <- System.Windows.Media.Brushes.Red
rhEllipse.Stroke <- System.Windows.Media.Brushes.White

//Left hand ellipse
let lhEllipse = new System.Windows.Shapes.Ellipse()
lhEllipse.Height <- 20.0
lhEllipse.Width <- 20.0
lhEllipse.Fill <- System.Windows.Media.Brushes.Red
lhEllipse.Stroke <- System.Windows.Media.Brushes.White

//Head ellipse
let hEllipse = new System.Windows.Shapes.Ellipse()
hEllipse.Height <- 20.0
hEllipse.Width <- 20.0
hEllipse.Fill <- System.Windows.Media.Brushes.Red
hEllipse.Stroke <- System.Windows.Media.Brushes.White

canvas.Children.Add(rhEllipse) |> ignore
canvas.Children.Add(lhEllipse) |> ignore
canvas.Children.Add(hEllipse) |> ignore

let grid = new System.Windows.Controls.Grid()

let winImage = new System.Windows.Controls.Image()
winImage.Height <- 600.0
winImage.Width <- 800.0

grid.Children.Add(winImage) |> ignore
grid.Children.Add(canvas) |> ignore

//Video frame is ready to be processed.
let VideoFrameReady (sender : obj) (args: ColorImageFrameReadyEventArgs) = 
    let receivedData = ref false

    using (args.OpenColorImageFrame()) (fun r -> 
        if (r <> null) then
            (
                pixelData <- Array.create r.PixelDataLength ds
                //Array.Resize(ref pixelData, r.PixelDataLength)
                r.CopyPixelDataTo(pixelData)
                receivedData := true
        )

        if (receivedData <> ref false) then
            (
        winImage.Source <- BitmapSource.Create(640, 480, 96.0, 96.0, Media.PixelFormats.Bgr32, null, pixelData, 640 * 4)
    )
    )

//Required to correlate the skeleton data to the PC screen
//IMPORTANT NOTE: Code for vector scaling was imported from the Coding4Fun Kinect Toolkit
//available here: http://c4fkinect.codeplex.com/
//I only used this part to avoid adding an extra reference.
let ScaleVector (length : float32, position : float32)  =
    let value = (((length / 1.0f) / 2.0f) * position) + (length / 2.0f)
    if value > length then
        length
    elif value < 0.0f then
        0.0f
    else
        value

//This will set the ellipse positions depending on the passed instance and joint
let SetEllipsePosition (ellipse : System.Windows.Shapes.Ellipse, joint : Joint) =
    let vector = new Microsoft.Kinect.SkeletonPoint(X = ScaleVector(640.0f, joint.Position.X), Y=ScaleVector(480.0f, -joint.Position.Y),Z=joint.Position.Z)
    let mutable uJoint = joint
    uJoint.TrackingState <- JointTrackingState.Tracked
    uJoint.Position <- vector

    System.Windows.Controls.Canvas.SetLeft(ellipse,(float uJoint.Position.X))
    System.Windows.Controls.Canvas.SetTop(ellipse,(float uJoint.Position.Y))

//Triggered when a new skeleton frame is ready for processing
let SkeletonFrameReady (sender : obj) (args: SkeletonFrameReadyEventArgs) = 
    let receivedData = ref false

    using (args.OpenSkeletonFrame()) (fun r -> 
        if (r <> null) then
            (
                skeletons <- Array.create r.SkeletonArrayLength dummySkeleton
                r.CopySkeletonDataTo(skeletons)

                for i in skeletons do
                    Debug.WriteLine(i.TrackingState.ToString())

                receivedData := true
        )

        if (receivedData <> ref false) then
            (
                for i in skeletons do
                    if i.TrackingState <> SkeletonTrackingState.NotTracked then
                        (
                        let currentSkeleton = i

                        SetEllipsePosition(hEllipse, currentSkeleton.Joints.[JointType.Head])
                        SetEllipsePosition(lhEllipse, currentSkeleton.Joints.[JointType.HandLeft])
                        SetEllipsePosition(rhEllipse, currentSkeleton.Joints.[JointType.HandRight])
                   )
        )
        )


let WindowLoaded (sender : obj) (args: RoutedEventArgs) = 
    sensor.Start()
    sensor.ColorStream.Enable()
    sensor.SkeletonStream.Enable()
    sensor.ColorFrameReady.AddHandler(new EventHandler<ColorImageFrameReadyEventArgs>(VideoFrameReady))
    sensor.SkeletonFrameReady.AddHandler(new EventHandler<SkeletonFrameReadyEventArgs>(SkeletonFrameReady))

let WindowUnloaded (sender : obj) (args: RoutedEventArgs) = 
    sensor.Stop()

//Defining the structure of the test window
let window = new Window()
window.Width <- 800.0
window.Height <- 600.0
window.Title <- "Kinect Skeleton Application"
window.Loaded.AddHandler(new RoutedEventHandler(WindowLoaded))
window.Unloaded.AddHandler(new RoutedEventHandler(WindowUnloaded))
window.Content <- grid
window.Show()

[<STAThread()>]
do 
    let app = new Application() in
    app.Run(window) |> ignore
2个回答

1

我最终根据这篇文章http://channel9.msdn.com/coding4fun/kinect/Kinecting-with-F重新编写了代码,现在骨架跟踪已经可以工作了。仍然想知道原始代码为什么不能正常工作。

// Learn more about F# at http://fsharp.net
#light

open System
open System.Windows
open System.Windows.Media.Imaging
open System.Windows.Threading
open Microsoft.Kinect
open System.Diagnostics

[<STAThread>]
do
    let sensor = KinectSensor.KinectSensors.[0]
    sensor.SkeletonStream.Enable()
    sensor.Start()

    // Set-up the WPF window and its contents
    let width = 1024.
    let height = 768.
    let w = Window(Width=width, Height=height)
    let g = Controls.Grid()
    let c = Controls.Canvas()
    let hd = Shapes.Rectangle(Fill=Media.Brushes.Red, Width=15., Height=15.)
    let rh = Shapes.Rectangle(Fill=Media.Brushes.Blue, Width=15., Height=15.)
    let lh = Shapes.Rectangle(Fill=Media.Brushes.Green, Width=15., Height=15.)
    ignore <| c.Children.Add hd
    ignore <| c.Children.Add rh
    ignore <| c.Children.Add lh
    ignore <| g.Children.Add c
    w.Content <- g

    w.Unloaded.Add(fun args -> sensor.Stop())

    let getDisplayPosition w h (joint : Joint) =
        let x = ((w * (float)joint.Position.X + 2.0) / 4.0) + (w/2.0)
        let y = ((h * -(float)joint.Position.Y + 2.0) / 4.0) + (h/2.0)
        System.Console.WriteLine("X:" + x.ToString() + " Y:" + y.ToString())
        new Point(x,y)

    let draw (joint : Joint) (sh : System.Windows.Shapes.Shape) =
        let p = getDisplayPosition width height joint
        sh.Dispatcher.Invoke(DispatcherPriority.Render, Action(fun () -> System.Windows.Controls.Canvas.SetLeft(sh, p.X))) |> ignore
        sh.Dispatcher.Invoke(DispatcherPriority.Render, Action(fun () -> System.Windows.Controls.Canvas.SetTop(sh, p.Y))) |> ignore

    let drawJoints (sk : Skeleton) =
        draw (sk.Joints.Item(JointType.Head)) hd
        draw (sk.Joints.Item(JointType.WristRight)) rh
        draw (sk.Joints.Item(JointType.WristLeft)) lh

    let skeleton (sensor : KinectSensor) =
        let rec loop () =
            async {
                let! args = Async.AwaitEvent sensor.SkeletonFrameReady
                use frame = args.OpenSkeletonFrame()
                let skeletons : Skeleton[] = Array.zeroCreate(frame.SkeletonArrayLength)
                frame.CopySkeletonDataTo(skeletons)
                skeletons 
                |> Seq.filter (fun s -> s.TrackingState <> SkeletonTrackingState.NotTracked)
                |> Seq.iter  (fun s ->  drawJoints s)
                return! loop ()
            }
        loop ()

    skeleton sensor |> Async.Start

    let a = Application()
    ignore <| a.Run(w)

1
在 F# 中,任何值绑定(例如 letdo)在模块内部声明时,将在第一次打开或从另一个模块访问该模块时执行。如果您熟悉 C#,可以将这些值绑定视为在类型构造函数(即 static 构造函数)中执行。
我怀疑你的代码第二个版本能够正常工作,但第一个版本不能,是因为在第二个版本中,你正在创建 Window 并将形状绘制到其中,而这是在运行应用程序消息循环的 STA 线程中执行的。在第一个版本中,我猜测该代码正在某个其他线程上执行,这就是为什么它不能按预期工作的原因。
第二个版本的代码没有问题,但更典型的 F# 方法是将函数(getDisplayPositiondraw 等)提升到顶层的 do 绑定之外。这使得代码更容易阅读,因为它明显表明这些函数不会捕获在 do 中创建的任何局部值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接