Wednesday, April 15, 2015

การประมวลผลภาพโดยการสะท้อน (Mirror) และการจับภาพจากวิดีโอเฟรมของ Kinect ตอนที่ 1

ลองมาทำ mirror effect ให้กับวิดีโอและทำปุ่มสำหรับบันทึกรูป ให้ได้ผลลัพธ์ประมาณนี้กันครับ

เมื่อคลิกที่ checkbox ที่มีข้อความว่า Reflect ผลลัพธ์ก็จะเปลี่ยนเป็น

นอกจากนี้เรายังสามารถกดปุ่ม Save เพื่อบันทึกรูปขณะนั้นได้

ส่วนของ UI
<Window x:Class="KinectMirrorSave.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" SizeToContent="WidthAndHeight" Loaded="Window_Loaded">
    <StackPanel Orientation="Horizontal">
        <Image Name="kinectVideo" Height="480" Width="640" />
        <StackPanel Width="80">
            <CheckBox Content="Reflect" Name="cbReflect" Margin="10,20"/>
            <Button Name="bttSave" Content="Save" Margin="10,20" Click="onSave" HorizontalAlignment="Center" VerticalAlignment="Center"></Button>            
        </StackPanel>
    </StackPanel>
</Window>
โค้ด (รายละเอียดจะอธิบายในตอนที่ 2 นะครับ)
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;
using System.IO;

namespace KinectMirrorSave
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        KinectSensor myKinect;

        public MainWindow()
        {
            InitializeComponent();
        }

        //When a window loads
        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);
        }

        //******** Update display when video frame is ready*********
        //color data array
        byte[] colorData = null;
        WriteableBitmap imageBitmap = null;

        private 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);

                //call unsafe method to modify the pixel data
                if (cbReflect.IsChecked == true)
                {
                    //reflect a half left image to a half right
                    reflectImage(colorData, colorFrame.Width, colorFrame.Height);
                }

                //prepare a saved frame
                if (takePicture)
                {
                    //create a bitmap from the current frame
                    capBitmap = BitmapSource.Create(
                        colorFrame.Width, colorFrame.Height, 96, 96, PixelFormats.Bgr32, null,
                        colorData, colorFrame.Width * colorFrame.BytesPerPixel);
                    //switch a photo captured flag
                    takePicture = false;
                }

                //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;
            }
        }

        //************ unsafe method to modify pixel data **************
        //Reflect the half left of the image to the half right
        private unsafe void reflectImage(byte[] colorData, int width, int height)
        {
            //fixed pixel data array in memory, otherwise it can be moved and be inaccessible by a pointer
            fixed (byte* pImage = colorData)
            {
                //in this block the data array in memory will be fixed                
                //we use integer pointer to move by one pixel, faster than by one byte
                //however, we cannot get color data, OK for reflection                

                //loop through each row
                for (int row = 0; row < height; row++)
                {
                    //set a start pointer to the beginning of each row
                    int* pStart = (int*)pImage + (row * width);
                    //set an end pointer to the end of each row
                    int* pEnd = pStart + width - 1;
                    //loop through each column of the row
                    while (pStart < pEnd)
                    {
                        //assign right pixel to left pixel
                        *pEnd = *pStart;
                        //move start pointer to right
                        pStart++;
                        //move end pointer to left
                        pEnd--;
                    }
                }//end for                               
            }
        }//end unsafe method

        //*********** Click a save button *********************
        bool takePicture;
        BitmapSource capBitmap = null;      //bitmap for captured frame

        private void onSave(object sender, RoutedEventArgs e)
        {
            //set a flag to capture image in video frame event
            takePicture = true;
            // Configure save file dialog box
            Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
            dlg.FileName = "capture"; // Default file name
            dlg.DefaultExt = ".jpg"; // Default file extension
            dlg.Filter = "Pictures (.jpg)|*.jpg"; // Filter files by extension

            //if select Dialog OK
            if (dlg.ShowDialog() == true)
            {
                // Save document
                string filename = dlg.FileName;
                using (FileStream stream = new FileStream(filename, FileMode.Create))
                {
                    //encode and save to JPG format
                    JpegBitmapEncoder encoder = new JpegBitmapEncoder();
                    encoder.Frames.Add(BitmapFrame.Create(capBitmap));
                    encoder.Save(stream);
                }
            }
        }
    }
}

No comments:

Post a Comment