Monday, April 13, 2015

การปรับการแสดงผลวิดีโอจาก Kinect ให้ใช้ Memory ได้มีประสิทธิภาพขึ้น

ก่อนหน้านี้ เราได้เขียนโค้ดเพื่อรองรับความผิดพลาดที่อาจเกิดขึ้นจากการเชื่อมต่อกับ Kinect แล้วสามารถแสดงผลภาพวิดีโอได้

หากสังเกตโค้ดเดิม จะเห็นว่าทุกครั้งที่มีการอ่าน frame ของวิดีโอจาก Kinect จะมีการสร้างบิตแมพด้วยคำสั่ง BitmapSource.Create() เพื่อนำไปแสดงผล

สมมติว่าในหนึ่งวินาทีเราทำการอ่านภาพจากวิดีโอ 30 ภาพ ก็จะมีการสร้างบิตแมพถึง 30 ครั้ง ซึ่งก็จะใช้หน่วยความจำเพิ่มขึ้นเรื่อยๆ จนกระทั่ง .NET framework เห็นว่าใช้มากไป ก็จะเรียก garbage collector มาทำลายหน่วยความจำของบิตแมพที่เราไม่ได้ใช้แล้ว หน่วยความจำที่ใช้ก็จะลดลง แต่ต่อมาก็จะใช้เพิ่มขึ้นอีกตามบิตแมพที่สร้าง วนไปแบบนี้เรื่อยๆ

วิธีที่สามารถทำให้การใช้หน่วยความจำมีประสิทธิภาพมากขึ้น โดยไม่ขึ้นๆลงๆตามที่ได้อธิบายไป คือ การเปลี่ยนจากการใช้ BitmapSource มาเป็น WritableBitmap ซึ่งสามารถเขียนทับได้เรื่อยๆโดยไม่ต้องสร้างบิตแมพใหม่ ก็จะทำให้การใช้หน่วยความจำลดลงและคงที่มากกว่า

โค้ดใหม่ที่ปรับปรุงก็จะเป็นตามนี้ครับ สังเกตว่ามีการเปลี่ยนเฉพาะส่วนของเหตุการณ์ ColorImageFrameReadyEvent
using System;
using System.Collections.Generic;
using System.Linq;
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;

namespace KinectCam
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        KinectSensor myKinect;
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            //Check if there is any connecting Kinect
            if (KinectSensor.KinectSensors.Count == 0)
            {
                MessageBox.Show("No Kinect detected!", "Error");
                //end this app
                Application.Current.Shutdown();
            }

            //Try to initialize Kinect
            try
            {
                //Get the first Kinect connected to this computer
                myKinect = KinectSensor.KinectSensors[0];
                //Enable the color video stream
                myKinect.ColorStream.Enable();
                //Start the sensor
                myKinect.Start();
            }
            catch
            {
                MessageBox.Show("Initialize Kinect failed!", "Error");
                //end this app
                Application.Current.Shutdown();
            }

            //Video event handler
            myKinect.ColorFrameReady += new EventHandler<ColorImageFrameReadyEventArgs>(myKinect_ColorFrameReady);           
        }

        byte[] colorData = null;
        WriteableBitmap imageBitmap = null;
        void myKinect_ColorFrameReady(object sender, ColorImageFrameReadyEventArgs e)
        {
            using (ColorImageFrame colorFrame = e.OpenColorImageFrame())
            {
                //if get new video frame
                if (colorFrame == null)
                    return;
                //create an array of pixel data
                if(colorData==null)
                    colorData = new byte[colorFrame.PixelDataLength];
                //extract pixel data from the frame to the array
                colorFrame.CopyPixelDataTo(colorData);

                //For the first video frame, no bitmap, create a writable bitmap of video size
                if (imageBitmap == null)
                {
                    imageBitmap = new WriteableBitmap(
                        colorFrame.Width,
                        colorFrame.Height,
                        96,     //dpiX
                        96,     //dpiY
                        PixelFormats.Bgr32,
                        null    //palette:none
                        );
                }
                //For other video frames, write video data to bitmap
                imageBitmap.WritePixels(
                    new Int32Rect(0, 0, colorFrame.Width, colorFrame.Height),
                    colorData,  //video data
                    colorFrame.Width * colorFrame.BytesPerPixel,      //stride
                    0   //offset to the array
                    );
                //set bitmap to image view
                kinectVideo.Source = imageBitmap;
            }
        }
    }
}

No comments:

Post a Comment