上次跟大家分享了如何在Windows 7裡面使用Kinect,也順便介紹了幾個Kinect SDK中內建的小範例。不過,身為一個稱職的工程師,想要自己也動手來寫一個,是很合理,也很合邏輯的。
所以,這次就來跟大家分享如果自己撰寫支援Kinect的應用程式喔!!這次,我們要使用WPF搭配C#,來建立支援Kinect的應用程式!!一切當然都要從基礎開始,我們就從取得拍攝到的畫面開始吧!!
上次跟大家分享了如何在Windows 7裡面使用Kinect,也順便介紹了幾個Kinect SDK中內建的小範例。不過,身為一個稱職的工程師,想要自己也動手來寫一個,是很合理,也很合邏輯的。
所以,這次就來跟大家分享如果自己撰寫支援Kinect的應用程式喔!!這次,我們要使用WPF搭配C#,來建立支援Kinect的應用程式!!一切當然都要從基礎開始,我們就從取得拍攝到的畫面開始吧!!
Step 1. 建立專案
建立應用程式的第一步,當然是開啟Visual Studio 2010囉!!點選New Project...
在專案樣版中的Visual C# -> Windows -> WPF Application,並且依照自己的喜好設定專案名稱和路徑。
Step 2. 檢查並修改專案執行平台
由於Kinect SDK目前只支援32位元的執行期環境,所以如果使用的作業系統是64位元的話,請記得先確認一下專案的執行平台是不是設定為32位元。
方法為:在Visual Studio 2010中Solution Explorer裡的專案名稱上按下滑鼠右鍵 -> 點選Properties。
接著將出現的專案設定頁面切換到Build頁籤,並且確認Platform target的內容為x86。(如果為x64或是Any CPU的話,到時候將無法正常執行喔!!)修改後請記得存檔。
Step 3. 加入Kinect SDK元件庫的參考
都說好要寫Kinect的應用程式了,所以很自然的就得在專案中引用Kinect SDK中幫我們準備好的元件庫囉!!
方法為:在Solution Explorer中專案下的References上按下滑鼠右鍵,點選Add Reference...。
接著請加為名為Microsoft.Research.Kinect的參考。
再來,在MianWindow.xaml.cs中加入Microsoft.Research.Kinect.Nui這個Namespace的引用。這樣,我們就可以開始真正的透過Kinect SDK提供的API取得拍攝到的畫面囉~~
Step 4. 在MainWindow中顯示Kinect補捉到的畫面
我們可以透過Microsoft.Research.Kinect.Nui這個Namespace中的Runtime類別來跟Kinect互動,包含取得補捉到的畫面、景深感謝及骨架分析資訊等等。
Runtime類別中提供了三個很方便的事件可以讓我們呼叫,以用來取得原始的視訊畫面(VideoFrameReady)、景深感應資訊(DepthFrameReady)以及骨架分析資訊(SkeletonFrameReady),我們只需要針對這三個事件設定好相對應的Event Handler,就可以在Kinect在處理完不同資訊的時候,取得經過Kinect分析完的資訊。
這邊就簡單的來示範一下怎麼來使用這些資訊。
從SDK所附的文件中可以看到,上述的三個事件除了SkelotonFrameReady傳入的參數為SkeletonFrameReadyEventArgs之外,另外兩個事件都會傳出一個類別為ImageFrameReadyEventArgs的參數,其中包含了用來封裝畫面的ImageFrame類別。但是,在WPF中要拿來放在Image控制項中顯示的圖片格式得用BitmapSource類別比較方便,所以我們只需要簡單的在取得ImageFrame之後,將它轉為BitmapSource,再指定給某個Image控制項來顯示即可。
而這次我們要取得的是攝影機拍攝到的畫面而已,所以很簡單的,請在MainWindow.xaml中先放好一個Image控制項。
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" x:Class="Wpf_KinectSample.MainWindow" mc:Ignorable="d" d:DesignWidth="800"
d:DesignHeight="600" Title="MainWindow" Height="Auto" Width="Auto">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.913*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Image x:Name="imgVideoFrame" Grid.Row="0" Grid.Column="0" Margin="10" />
<StackPanel Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center"
Margin="0,10">
<Button x:Name="btnStart" Content="開始擷取" Margin="10,0" Padding="10,1" VerticalAlignment="Center"
FontSize="16" Click="btnStart_Click" Visibility="Collapsed" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction PropertyName="Visibility">
<ei:ChangePropertyAction.Value>
<Visibility>Collapsed</Visibility>
</ei:ChangePropertyAction.Value>
</ei:ChangePropertyAction>
<ei:ChangePropertyAction TargetObject="{Binding ElementName=btnStop}" PropertyName="Visibility"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button x:Name="btnStop" Content="停止擷取" Margin="10,0" Padding="10,1" VerticalAlignment="Center"
FontSize="16" Click="btnStop_Click" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ei:ChangePropertyAction PropertyName="Visibility">
<ei:ChangePropertyAction.Value>
<Visibility>Collapsed</Visibility>
</ei:ChangePropertyAction.Value>
</ei:ChangePropertyAction>
<ei:ChangePropertyAction TargetObject="{Binding ElementName=btnStart}" PropertyName="Visibility"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<Button x:Name="btnSavePicture" Content="儲存圖片" Margin="10,0" Padding="10,1" VerticalAlignment="Center"
FontSize="16" Click="btnSavePicture_Click" />
</StackPanel>
</Grid>
</Window>
再來呢,只要簡單的透過Runtime這個類別幫我們準備好的事件來取得畫面就行啦~而根據SDK中的說明文件裡面的描述,在建立好Runtime物件之後,我們得呼叫Runtime的Initialize()方法,並且指定以RuntimeOptions這個列舉型別定義的參數,以讓Runtime物件可以知道它該擷取哪幾種來源的畫面。
程式的部份原始碼如下:
using System;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Research.Kinect.Nui;
namespace Wpf_KinectSample
{
public partial class MainWindow : Window
{
private Runtime _runtime;
public MainWindow()
{
InitializeComponent();
InitializeRuntime();
}
private void InitializeRuntime()
{
UninitializeRuntime();
_runtime = new Runtime();
//因為我們只要取得基本的鏡頭拍到的畫面而已,所以只需要將RuntimeOptions設定為UseColor即可
_runtime.Initialize( RuntimeOptions.UseColor );
//透過VideoStream.Open方法開始取得視訊串流
_runtime.VideoStream.Open( ImageStreamType.Video , 2 , ImageResolution.Resolution640x480 , ImageType.Color );
//替VideoFrameReady加入EventHandler,把畫面畫在Image中。
_runtime.VideoFrameReady += new EventHandler<ImageFrameReadyEventArgs>( RuntimeVideoFrameReady );
}
private void UninitializeRuntime()
{
if( _runtime == null )
{
return;
}
_runtime.VideoFrameReady -= RuntimeVideoFrameReady;
_runtime.Uninitialize();
_runtime = null;
}
private void RuntimeVideoFrameReady( object sender , ImageFrameReadyEventArgs e )
{
PlanarImage image = e.ImageFrame.Image;
byte[] pixels = image.Bits;
//將取得的圖片轉為BitmapSource,並用來當作Image控制項的Source
imgVideoFrame.Source = BitmapSource.Create( image.Width , image.Height , 96 , 96 , PixelFormats.Bgr32 , null , pixels , image.Width * PixelFormats.Bgr32.BitsPerPixel / 8 );
}
private void btnStart_Click( object sender , RoutedEventArgs e )
{
InitializeRuntime();
}
private void btnStop_Click( object sender , RoutedEventArgs e )
{
UninitializeRuntime();
}
private void btnSavePicture_Click( object sender , RoutedEventArgs e )
{
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
encoder.Frames.Add( BitmapFrame.Create( BitmapFrame.Create( imgVideoFrame.Source as BitmapSource ) ) );
Microsoft.Win32.SaveFileDialog openFileDialog = new Microsoft.Win32.SaveFileDialog();
openFileDialog.FileName = "MyImage";
openFileDialog.DefaultExt = ".jpg";
openFileDialog.Filter = "Jpeg Image (.jpg)|*.jpg";
Nullable<bool> result = openFileDialog.ShowDialog();
string fileName = string.Empty;
if( result == true )
{
fileName = openFileDialog.FileName;
}
else
{
return;
}
using( var stream = new FileStream( fileName , FileMode.Create ) )
{
encoder.Save( stream );
}
}
}
}
喔耶!!大功告成!!來看看執行的畫面~
老樣子,奉上專案原始碼,請自行服用~