Пользовательский интерфейс зависает при обработке кадров из Kinect с использованием EmguCV в приложении WPF C#

Я работаю над проектом, который включает в себя извлечение функций из кадров цвета и глубины из Kinect Camera. Проблема, с которой я сталкиваюсь, заключается в том, что всякий раз, когда я пытаюсь отобразить 2 изображения, пользовательский интерфейс зависает. Когда я пытался выполнить отладку, depthFrame и colorFrame были нулевыми. Если включить только цветовой поток, то и colorImage, и featureImage1 отображаются правильно, и если я включу только поток глубины, он будет работать как надо. Но когда я включаю их обоих, пользовательский интерфейс зависает. Я понятия не имею, что вызывает проблему. У меня есть следующий код для моего приложения Kinect. В чем причина этой проблемы и как ее исправить? Конфигурация: Windows 8 Pro 64bit, 2Ghz Core2Duo, VisualStudio 2012 Ultimate, EmguCV 2.4.0.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Kinect;
using Emgu.CV;
using Emgu.CV.WPF;
using Emgu.CV.Structure;
using Emgu.Util;
namespace features
{
public partial class MainWindow : Window
{  
    public MainWindow()
    {
        InitializeComponent();
    }
    private Image<Bgra, Byte> cvColorImage;
    private Image<Gray, Int16> cvDepthImage;
    private int colorWidth = 640;
    private int colorHeight = 480;
    private int depthWidth = 640;
    private int depthHeight = 480;
    private static readonly int Bgr32BytesPerPixel = (PixelFormats.Bgr32.BitsPerPixel + 7) / 8;
    private byte[] colorPixels;
    private byte[] depthPixels;
    private short[] rawDepthData;
    private bool first = true;
    private bool firstDepth = true;
    Image<Bgra, byte> image2;
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        kinectSensorChooser.KinectSensorChanged += new DependencyPropertyChangedEventHandler(kinectSensorChooser_KinectSensorChanged);
    }
    void kinectSensorChooser_KinectSensorChanged(object sender, DependencyPropertyChangedEventArgs e)
    {
        KinectSensor oldSensor = (KinectSensor)e.OldValue;
        KinectStop(oldSensor);
        KinectSensor _sensor = (KinectSensor)e.NewValue;
        _sensor.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);
        _sensor.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30);
        _sensor.DepthFrameReady += new EventHandler<DepthImageFrameReadyEventArgs>(_sensor_DepthFrameReady);
        _sensor.ColorFrameReady += new EventHandler<ColorImageFrameReadyEventArgs>(_sensor_ColorFrameReady);
        _sensor.DepthStream.FrameHeight);
        try
        {
            _sensor.Start();
        }
        catch
        {
            kinectSensorChooser.AppConflictOccurred();
        }
    }
    void KinectStop(KinectSensor sensor)
    {
        if (sensor != null)
        {
            sensor.Stop();
        }
    }

    private void Window_Closed(object sender, EventArgs e)
    {
        KinectStop(kinectSensorChooser.Kinect);
    }

    void _sensor_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
    {
        using (ColorImageFrame colorFrame = e.OpenColorImageFrame())
        {
            if (colorFrame == null) return;
            if (first)
            {
                this.colorPixels = new Byte[colorFrame.PixelDataLength];
                first = false;
            }
            colorFrame.CopyPixelDataTo(this.colorPixels); //raw data in bgrx format
            processColor();
        }
    }
    void _sensor_DepthFrameReady(object sender, DepthImageFrameReadyEventArgs e)
    {
        using (DepthImageFrame depthFrame = e.OpenDepthImageFrame())
        {
            if (depthFrame == null) return;
            if (firstDepth)
            {
                this.rawDepthData = new short[depthFrame.PixelDataLength];
                firstDepth = false;
            }
            depthFrame.CopyPixelDataTo(rawDepthData);
            processDepth();
        }
    }
    private void processColor(){...}
    private void processDepth(){...}
}
}

Функция processDepth выглядит следующим образом. Я просто делаю изображение из данных глубины RAW.

private void processDepth() {
    GCHandle pinnedArray = GCHandle.Alloc(this.rawDepthData, GCHandleType.Pinned);
    IntPtr pointer = pinnedArray.AddrOfPinnedObject();
    cvDepthImage = new Image<Gray, Int16>(depthWidth, depthHeight, depthWidth << 1, pointer);
    pinnedArray.Free();
    depthImage.Source = BitmapSourceConvert.ToBitmapSource(cvDepthImage.Not().Bitmap);
}

Функция processColor выглядит следующим образом. Вот только ради этого я пытаюсь отобразить клонированное изображение вместо извлечения функций, просто чтобы проверить отставание. Когда оба потока включены (цвет и глубина), следующая функция правильно отображает colorImage, но как только я раскомментирую закомментированные строки, пользовательский интерфейс зависает.

private void processColor() {
    GCHandle handle = GCHandle.Alloc(this.colorPixels, GCHandleType.Pinned);
    Bitmap image = new Bitmap(colorWidth, colorHeight, colorWidth<<2, System.Drawing.Imaging.PixelFormat.Format32bppRgb, handle.AddrOfPinnedObject());
    handle.Free();
    cvColorImage = new Image<Bgra, byte>(image);
    image.Dispose();
    BitmapSource src = BitmapSourceConvert.ToBitmapSource(cvColorImage.Bitmap);
    colorImage.Source = src;
    //image2 = new Image<Bgra, byte>(cvColorImage.ToBitmap()); //uncomment and it hangs
    //featureImage1.Source = BitmapSourceConvert.ToBitmapSource(image2.Bitmap); //uncomment and it hangs
}

person thinkquester    schedule 20.10.2012    source источник


Ответы (1)


Я вижу код, который много работает в обработчиках событий. Я почти уверен, что обработчики вызываются в потоке GUI. Я предлагаю вам извлечь ваш код в подпрограмму фонового потока.
Не забывайте, что обновление элементов управления формы (depthImage и controlImage) должно производиться с помощью метода BeginInvoke родительской формы,

person zabulus    schedule 20.10.2012
comment
Я знаю, что мой код требует много работы с обработчиками событий, и в будущем мне потребуется еще больше обработки. Не могли бы вы предложить мне несколько лучших практик и ссылки на некоторые учебные пособия? Я не знаю, как извлечь мой код в подпрограмму фонового потока. Я новичок в платформе .NET, и ваша помощь будет очень признательна. - person thinkquester; 20.10.2012
comment
DepthViewer как часть пространства имен WpfViewers в примерах 1.6 SDK Toolkit показывает, как обрабатывать DepthView в фоновом потоке. - person Nicholas Pappas; 20.10.2012