ตัวอย่างที่ผ่านมา เราทำการอ่านค่าภาพวิดีโอจาก Kinect มาเก็บไว้ในอาร์เรย์หนึ่งมิติ หลังจากนั้นเราจึงทำการเปลี่ยนค่าในอาร์เรย์แล้วนำไปสร้างเป็นบิตแมพเพื่อแสดงผล
การเข้าถึงข้อมูลภาพวิดีโอด้วยดัชนีของอาร์เรย์ทำได้ค่อนข้างง่ายและสะดวก แต่เมื่อมีภาพจำนวนมากและต้องประมวลผลทุกภาพ ก็จะต้องใช้พลังสูง โดยเฉพาะเมื่อใช้อาร์เรย์ เพราะ .NET framework จำเป็นต้องตรวจสอบว่าดัชนีของอาร์เรย์อยู่ในช่วงที่เป็นไปได้หรือไม่ตลอดเป็นต้น
หากต้องการให้การประมวลผลมีประสิทธิภาพมากขึ้น แลกกับความเสี่ยงในการทำให้โปรแกรมล้มเหลวหากเขียนโค้ดผิดพลาด คือปิดการตรวจสอบช่วงของอาร์เรย์ เราสามารถทำได้โดยการปรับรูปแบบการคอมไพล์ให้เป็นแบบ unsafe และใช้ pointer ในการเข้าถึงหน่วยความจำโดยตรง
ในการกำหนดให้การ build เป็นแบบ unsafe ทำได้ดังรูป
จากนั้นทำการบันทึกไฟล์นี้
ต่อไปเราก็จะมาปรับโค้ดใหม่ โดยแยกส่วนที่เป็นการแก้ไขอาร์เรย์ของภาพออกมาเป็นฟังก์ชันใหม่ ดังนี้
unsafe void updateImage(byte[] colorData)
{
}
โดยการใช้คำสั่ง unsafe คือการระบุว่าโค้ดที่อยู่ใน block นี้จะมีส่วนที่ unsafe นั่นคือเปิดให้ใช้ pointer ในการแก้ไขหน่วยความจำได้โดยตรง และไม่ต้องตรวจสอบเงื่อนไขบางประการ
จากนั้น จะต้องใช้คำสั่ง fixed เพื่อกำหนดให้อาร์เรย์ถูกล็อคไว้ในหน่วยความจำที่ตำแหน่งคงที่ ถ้าไม่ใช้คำสั่งนี้ เมื่อรันโปรแกรมอาร์เรย์อาจจะถูกสร้างไว้ที่ตำแหน่งของหน่วยความจำที่เปลี่ยนไปเรื่อยๆ ทำให้การเข้าถึงหน่วยความจำด้วย pointer เกิดความผิดพลาด
unsafe void updateImage(byte[] colorData)
{
//fixed pixel data array in memory, otherwise it can be moved and be inaccessible by a pointer
fixed (byte* pImage = colorData)
{
}
}
โค้ัดที่เหลือของฟังก์ชันนี้ ก็จะมีประมาณนี้ครับ สังเกตการใช้พอยน์เตอร์ และการใช้สัญลักษณ์ * เพื่อเข้าถึงค่าที่พอยน์เตอร์ชี้อยู่
unsafe void updateImage(byte[] colorData)
{
//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
//set a start pointer to the beginning of image data
byte* pStart = pImage;
//set an end pointer to the end of image data
byte* pEnd = pImage + colorData.Length;
int newValue=0;
//loop through each byte of image data
while (pStart != pEnd)
{
//Each byte is B G R A
//blue
newValue = *pStart + blueOffset;
//clamping
if (newValue < 0)
newValue = 0;
else if (newValue > 255)
newValue = 255;
*pStart = (byte)newValue;
//green
//move to next byte
pStart++;
newValue = *pStart + greenOffset;
//clamping
if (newValue < 0)
newValue = 0;
else if (newValue > 255)
newValue = 255;
*pStart = (byte)newValue;
//red
//move to next byte
pStart++;
newValue = *pStart + redOffset;
//clamping
if (newValue < 0)
newValue = 0;
else if (newValue > 255)
newValue = 255;
*pStart = (byte)newValue;
//move to next 2 bytes ->alpha then blue again
pStart += 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;
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);
}
//------------------------- Update color value from slider ----------------------------------
//color value
private int blueOffset = 0;
private int greenOffset = 0;
private int redOffset = 0;
private void BlueSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
//get slider value
blueOffset = (int)BlueSlider.Value;
}
private void GreenSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
//get slider value
greenOffset = (int)GreenSlider.Value;
}
private void RedSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
//get slider value
redOffset = (int)RedSlider.Value;
}
//--------------------------------------------------------------------------------------------
//************************ Update display when video frame is ready*********************
//color data array
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);
//call unsafe method to modify the pixel data
updateImage(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;
}
}
//**********************************************************************************
//unsafe method to modify pixel data
//------------------------------------------------------------------------------
unsafe void updateImage(byte[] colorData)
{
//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
//set a start pointer to the beginning of image data
byte* pStart = pImage;
//set an end pointer to the end of image data
byte* pEnd = pImage + colorData.Length;
int newValue=0;
//loop through each byte of image data
while (pStart != pEnd)
{
//Each byte is B G R A
//blue
newValue = *pStart + blueOffset;
//clamping
if (newValue < 0)
newValue = 0;
else if (newValue > 255)
newValue = 255;
*pStart = (byte)newValue;
//green
//move to next byte
pStart++;
newValue = *pStart + greenOffset;
//clamping
if (newValue < 0)
newValue = 0;
else if (newValue > 255)
newValue = 255;
*pStart = (byte)newValue;
//red
//move to next byte
pStart++;
newValue = *pStart + redOffset;
//clamping
if (newValue < 0)
newValue = 0;
else if (newValue > 255)
newValue = 255;
*pStart = (byte)newValue;
//move to next 2 bytes ->alpha then blue again
pStart += 2;
}
}
}
//------------------------------------------------------------------------------
}
}