C#: Kinect SDK ile Sensör ve Kamera Erişimi
Microsoft'un oyun konsulu XBOX 360 ile kullanılmak üzere
geliştirdiği Kinect sensörü, Kinect SDK ile Windows uygulamalarında da
kullanılabiliyor. Özetle, Kinect SDK ile kameradan görüntü yakalayabiliyoruz,
her bir noktanın sensörden uzaklığına erişebiliyoruz, 20 farklı iskelet
özelliğinin X, Y ve Z koordinatını elde edebiliyoruz, mikrofon dizisini
kullanabiliyoruz ve cihazın açısını kontrol edebiliyoruz.
Kinect'in Yapısı ve Çalışma Mantığı
Kinect'in üzerinde 3 adet göz, sıra mikrofonlar ve hareket
sağlayıcı bir motor mekanizması bulunuyor. Soldaki göz lazer projeksiyonu
yaparken, sağdaki kızılötesi sensör bu ışınların gidiş - geliş süresini
hesaplayarak 320x240 çözünürlüğünde her bir noktanın mesafesini bildiriyor.
Kinect firmware'i ise bu veriler ışığında iskelet yapısını hesaplıyor ve bunu
XBOX yada PC'ye gönderiyor. Firmware üzerine kayıtlı 200 poz, insan
vüdudunun bir kısmı görüş alanı dışına çıkarsa bile iskelet yapısının görünmeyen
kısmını tahmin etmek için kullanılıyor. Kinect'in ortasında bulunan göz ise
640x480 çözünürlüğünde 30fps bir VGA kamera. Yakalanan görüntü, saniyede 30 kez resim olarak uygulamaya iletiliyor. -Video
akışı olarak gönderilmiyor.

Kinect'in hemen altında boydan boya uzanan mikrofon dizisi,
temiz bir ses iletimi sağlıyor. Cihazın geniş tasarlanma sebebi ise, sol
tarafında 1, sağ tarafında ise 3 tane mikrofonun aşağıya bakar şekilde
yerleştirilmiş olması. Kinect'in mikrofon sistemi geliştirilirken, test amaçlı
250 evin her birinde 16 mikrofon ile denemeler yapıldı ve farklı ülkelerden
kişilerin konuşmaları kaydedilerek en doğru mikrofon dizimi ve yazılım
geliştirildi.

Cihazın alt bölümünde ufak bir DC motor gizli. Bu motor,
sensörün +/- 28 derece yukarı ve aşağı hareket edebilmesini sağlıyor.
Uygulama Geliştirmek için Öngereksinimler
Kinect SDK ile sensör ve kamera özelliklerine erişebilmek için:
- Çift çekirdekli 2.66 Ghz işlemci
- En az 2 GB RAM
- 32 yada 64 bit Windows 7 işletim sistemine
sahip olmanız gerekiyor.
Uygulama geliştirmek için ise:
- Visual Studio 2010'un herhangi bir sürümüne sahip olmanız,
- Microsoft Kineck SDK'yı indirmeniz, (buradan indirebilirsiniz)
- İsteğe bağlı olarak bazı metodlar için Coding4Fun Kinect Toolkit'i indirmeniz gerekli.
(buradan
indirebilirsiniz)
Donanım Kurulumu
Kinect sensörünün bağlantı portu yeni nesil XBOX 360'lar için
tasarlanmış. PC ile kullanabilmek için özel adaptörünü kullanmamız gerekiyor.
Kinect sensör paketini satın almışsanız bu adaptör paketten çıkacaktır. Adaptör
kablosunun dişi ucunu Kinect'e, USB ucunu ise PC'ye bağladığımızda, Windows
aygıt sürücülerini yüklemeye başlayacak. Sürücüler yüklendikten sonra, aygıt
yöneticisinde Microsoft Kinect ve ses denetleyicileri altına toplam 4 tane cihaz
eklenecek.

Kamera
donanımının aygıt yöneticisinin görüntü aygıtları arasında yer almamasının
nedeni, Kinect kamerasının aslında fotoğraf makinesi gibi davranıp görüntüyü
resimler halinde iletmesidir. Bu sayede uygulama içerisinde görüntüye çok daha
kolay ulaşabiliyoruz. Kinect'in yüksek hassaslıktaki mikrofonunu ise Windows
uygulamalarında kullanabiliyoruz.

Speech Recognition gibi Windows uygulamalarında mikrofon türünü
belirlerken
Microphone Array'i seçmek, Kinect mikrofon dizisinin gelişmiş özelliklerinden
faydalanabilmemize olanak sağlayacaktır.
Adım Adım Uygulama Geliştirmek
Kinect SDK ile C#, VB.NET ve C++ dilleriyle WPF ve Windows Forms
uygulamaları geliştirebiliyoruz. Örnek uygulamamızda C# ile bir WPF uygulaması
üzerinde duracağız. Öncelikle, Visual Studio ile oluşturduğumuz WPF
uygulamamızın referansları arasına "Microsoft.Research.Kinect.dll" ve
"Coding4Fun.Kinect.Wpf.dll" bileşenlerini ekleyelim. Kinect SDK'sına ve yardımcı
metodlara ulaşmak için ise using statementlar arasına
using Microsoft.Research.Kinect.Nui;
using Coding4Fun.Kinect.Wpf;
satırlarını eklememiz gerekecektir. Bu aşamadan sonra Kinect'in
özelliklerine ve yardımcı metodlara ulaşabiliyor olacağız.
Adım 1: Renkli Kamera Görüntüsü
İlk adımda MainWindow içerisine ekleyeceğimiz Image elementini
kullanarak kamera görüntüsüne ulaşacağız. Öncelikle Kinect runtime'ı
oluşturacağız, runtime ayarlarında yalnızca görüntü kullanacağımızı
bildireceğiz, her bir görüntü karesi aktarıldığında tetiklenecek eventi
belirleyeceğiz, görüntü akışını açacağız ve gelen görüntüleri Image elementine
aktaracağız.
[XAML]
<Window x:Class="KinectSensorKameraErisimi.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Kinect" Height="400" Width="700" Loaded="Window_Loaded" Closed="Window_Closed">
<Grid>
<Image Height="150" HorizontalAlignment="Left" Margin="12,12,0,0" Name="imgVideo"
Stretch="Fill" VerticalAlignment="Top" Width="200" />
</Grid>
</Window>
[C#]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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.Research.Kinect.Nui;
using Coding4Fun.Kinect.Wpf;
namespace KinectSensorKameraErisimi
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
Runtime kinectNui = new Runtime();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Yalnızca renkli görüntü aktarımı yapacak şekilde Kinect runtime'ı başlat
kinectNui.Initialize(RuntimeOptions.UseColor);
// Renkli görüntü iletiminde tetiklenecek event
kinectNui.VideoFrameReady +=
new EventHandler<ImageFrameReadyEventArgs>(kinectNui_VideoFrameReady);
// Renkli görüntü aktarımını başlat
kinectNui.VideoStream.Open(ImageStreamType.Video, // Video yayını
2, // Önbellekteki resim sayısı
ImageResolution.Resolution640x480, // Video çözünürlüğü
ImageType.Color // Yayınlanan video türü
);
}
void kinectNui_VideoFrameReady(object sender, ImageFrameReadyEventArgs e)
{
// Gelen görüntüyü Image elementinde göster
imgVideo.Source = e.ImageFrame.ToBitmapSource();
}
private void Window_Closed(object sender, EventArgs e)
{
// Kaynakları serbest bırak
kinectNui.Uninitialize();
}
}
}
Uygulamayı çalıştırdığımızda renkli kamera görüntüsünü Image
elementi içerisinde göreceğiz.

Görüntü alamazsanız, ImageResolution ayarını 640x480 olarak ayarladığınızdan
emin olun.
Adım 2: Derinlik Görüntüsü
Bu aşamada uygulamamıza bir Image elementi daha ekleyip derinlik
görüntüsüne ulaşacağız. Görüntü yayınına ulaşırken kullandığımız yöntemin
aynısını derinlik için de kullanabiliriz. Önce runtime parametrelerine derinliği
ekleyeceğiz. Daha sonra DepthFrameReady eventinde çalışacak metodu
oluşturacağız ve derinlik bilgisini "imgDepth" elementinde göstereceğiz.
Derinlik verisi de kamera görüntüsünde olduğu gibi ardarda resimler olarak
uygulamamıza aktarılır ve işlenir. Elde edeceğimiz görüntü, sensöre yakın
noktaları beyaz, uzak noktaları da koyu gri tonlarında gösterecektir.
[XAML]
<Window x:Class="KinectSensorKameraErisimi.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Kinect" Height="368" Width="700" Loaded="Window_Loaded" Closed="Window_Closed">
<Grid>
<Image Height="150" HorizontalAlignment="Left" Margin="12,12,0,0" Name="imgVideo"
Stretch="Fill" VerticalAlignment="Top" Width="200" />
<Image Height="150" HorizontalAlignment="Left" Margin="12,168,0,0" Name="imgDepth"
Stretch="Fill" VerticalAlignment="Top" Width="200" />
</Grid>
</Window>
[C#]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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.Research.Kinect.Nui;
using Coding4Fun.Kinect.Wpf;
namespace KinectSensorKameraErisimi
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
Runtime kinectNui = new Runtime();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Kinect runtime'ı başlat
kinectNui.Initialize(RuntimeOptions.UseColor | RuntimeOptions.UseDepth);
// Renkli görüntü iletiminde tetiklenecek event
kinectNui.VideoFrameReady +=
new EventHandler<ImageFrameReadyEventArgs>(kinectNui_VideoFrameReady);
// Derinlik görüntüsü iletiminde tetiklenecek event
kinectNui.DepthFrameReady +=
new EventHandler<ImageFrameReadyEventArgs>(kinectNui_DepthFrameReady);
// Renkli görüntü aktarımını başlat
kinectNui.VideoStream.Open(ImageStreamType.Video, // Video yayını
2, // Önbellekteki resim sayısı
ImageResolution.Resolution640x480, // Video çözünürlüğü
ImageType.Color // Yayınlanan video türü
);
// Derinlik görüntüsü aktarımını başlat
kinectNui.DepthStream.Open(ImageStreamType.Depth, // Derinlik görüntüsü yayını
2, // Önbellekteki resim sayısı
ImageResolution.Resolution320x240, // Video çözünürlüğü
ImageType.Depth // Yayınlanan video türü
);
}
void kinectNui_VideoFrameReady(object sender, ImageFrameReadyEventArgs e)
{
// Gelen görüntüyü Image elementinde göster
imgVideo.Source = e.ImageFrame.ToBitmapSource();
}
void kinectNui_DepthFrameReady(object sender, ImageFrameReadyEventArgs e)
{
// Gelen görüntüyü Image elementinde göster
imgDepth.Source = e.ImageFrame.ToBitmapSource();
}
private void Window_Closed(object sender, EventArgs e)
{
// Kaynakları serbest bırak
kinectNui.Uninitialize();
}
}
}
Uygulamamızın bu halini çalıştırdığımızda ise aşağıdaki gibi bir
görüntü elde edeceğiz:

Adım 3: İskelet Hareketleri
Kinect'in en heyecan verici özelliği, şüpesiz eklem
posizyonlarını algılayabilmesidir. İnsan vücudunun 20 noktasının X, Y ve Z
koordinatlarına SDK ile erişebiliyoruz. Aynı anda 2 kişiye ait iskelet verisine
bu yolla erişilebiliyor. O an sensör tarafından izlenen kişi görüş açısının bir
parça dışına çıksa dahi, Kinect diğer eklemlere oranla görüntünün dışına çıkan
eklemin konumunu tahminen hesaplayıp yine uygulamamıza iletecektir. Kinect
tarafından tanımlanan eklemlere, JointID'leri ile ulaşabiliyoruz.

İskelet yapısına kısaca göz attıkran sonra, uygulamamızın 3.
aşamasına geçebiliriz. Bu aşamada, Canvas üzerine ekleyeceğimiz Ellipse
elementlerini baş, omuz ve elleri temsil edecek şekilde hareketlendireceğiz.
(Aynı yöntemle diğer eklemleri de ekleyebilirsiniz.) Bunun için, runtime
parametrelerine UseSkeletalTracking'i eklememiz ve iskelet verisi
gönderildiğinde tetiklenecek eventi oluşturmamız gerekiyor. Dipnot olarak,
iskelet verisi saniyede 30 kareden az, video ile eşleşmeyen frame oranında
gelebilir. Kinect, yalnızca eklemleri yakaladığı ve hesapladığı anlarda veri
yollamaktadır.
İskelet hareketlerinin daha yumuşak olabilmesi için, runtime'ı
başlattıktan hemen sonra SmoothParameters ile ince ayarları yapabiliriz.
Uygulamamızın bu aşamasına ekleyeceğimiz bir başka özellik ise, 2. aşamada ele
aldığımız derinlik görüntüsüne player index'i eklemek olacak. Yani, Kinect
tarafından iskelet yapısı algılandığında, o kişiye ait bölge, derinlik
grafiğinde kırmızı, mavi, sarı gibi gri tonlamadan farklı bir renkte
gösterilecek. Uygulama kodlarımızın buraya kadarki hali aşağıdaki gibi olacak:
[XAML]
<Window x:Class="KinectSensorKameraErisimi.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Kinect" Height="368" Width="700" Loaded="Window_Loaded" Closed="Window_Closed">
<Grid>
<Image Height="150" HorizontalAlignment="Left" Margin="12,12,0,0" Name="imgVideo"
Stretch="Fill" VerticalAlignment="Top" Width="200" />
<Image Height="150" HorizontalAlignment="Left" Margin="12,168,0,0" Name="imgDepth"
Stretch="Fill" VerticalAlignment="Top" Width="200" />
<Canvas Height="306" HorizontalAlignment="Left" Margin="218,12,0,0" Name="canvas1"
VerticalAlignment="Top" Width="448">
<Ellipse Canvas.Left="6" Canvas.Top="6" Fill="#FF9090FF" Height="20"
Name="ellipseHead" Stroke="{x:Null}" Width="20" />
<Ellipse Canvas.Left="32" Canvas.Top="6" Fill="#FF9090FF" Height="20"
Name="ellipseHandLeft" Stroke="{x:Null}" Width="20" />
<Ellipse Canvas.Left="58" Canvas.Top="6" Fill="#FF9090FF" Height="20"
Name="ellipseHandRight" Stroke="{x:Null}" Width="20" />
<Ellipse Canvas.Left="84" Canvas.Top="6" Fill="#FF9090FF" Height="20"
Name="ellipseShoulderLeft" Stroke="{x:Null}" Width="20" />
<Ellipse Canvas.Left="110" Canvas.Top="6" Fill="#FF9090FF" Height="20"
Name="ellipseShoulderCenter" Stroke="{x:Null}" Width="20" />
<Ellipse Canvas.Left="136" Canvas.Top="6" Fill="#FF9090FF" Height="20"
Name="ellipseShoulderRight" Stroke="{x:Null}" Width="20" />
</Canvas>
</Grid>
</Window>
[C#]
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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.Research.Kinect.Nui;
using Coding4Fun.Kinect.Wpf;
namespace KinectSensorKameraErisimi
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
Runtime kinectNui = new Runtime();
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Kinect runtime'ı başlat
kinectNui.Initialize(RuntimeOptions.UseColor | RuntimeOptions.UseDepth |
RuntimeOptions.UseDepthAndPlayerIndex | RuntimeOptions.UseSkeletalTracking);
// İskelet hareketlerini yumuşatmak için gerekli parametreler
kinectNui.SkeletonEngine.TransformSmooth = true;
kinectNui.SkeletonEngine.SmoothParameters = new TransformSmoothParameters
{
Smoothing = 0.75f,
Correction = 0.0f,
Prediction = 0.0f,
JitterRadius = 0.05f,
MaxDeviationRadius = 0.04f
};
// Renkli görüntü iletiminde tetiklenecek event
kinectNui.VideoFrameReady +=
new EventHandler<ImageFrameReadyEventArgs>(kinectNui_VideoFrameReady);
// Derinlik görüntüsü iletiminde tetiklenecek event
kinectNui.DepthFrameReady +=
new EventHandler<ImageFrameReadyEventArgs>(kinectNui_DepthFrameReady);
// İskelet bilgisi iletiminde tetiklenecek event
kinectNui.SkeletonFrameReady +=
new EventHandler<SkeletonFrameReadyEventArgs>(kinectNui_SkeletonFrameReady);
// Renkli görüntü aktarımını başlat
kinectNui.VideoStream.Open(ImageStreamType.Video, // Video yayını
2, // Önbellekteki resim sayısı
ImageResolution.Resolution640x480, // Video çözünürlüğü
ImageType.Color // Yayınlanan video türü
);
// Derinlik görüntüsü aktarımını başlat
kinectNui.DepthStream.Open(ImageStreamType.Depth, // Derinlik görüntüsü yayını
2, // Önbellekteki resim sayısı
ImageResolution.Resolution320x240, // Video çözünürlüğü
ImageType.DepthAndPlayerIndex // Yayınlanan video türü
);
}
void kinectNui_VideoFrameReady(object sender, ImageFrameReadyEventArgs e)
{
// Gelen görüntüyü Image elementinde göster
imgVideo.Source = e.ImageFrame.ToBitmapSource();
}
void kinectNui_DepthFrameReady(object sender, ImageFrameReadyEventArgs e)
{
// Gelen görüntüyü Image elementinde göster
imgDepth.Source = e.ImageFrame.ToBitmapSource();
}
void kinectNui_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
{
// Tüm iskelet verileri
SkeletonFrame allSkeletons = e.SkeletonFrame;
// Takip edilen ilk iskeletin verileri
SkeletonData skeleton = (from s in allSkeletons.Skeletons
where s.TrackingState == SkeletonTrackingState.Tracked
select s).FirstOrDefault();
// Takip edilen iskelet varsa
if (skeleton != null)
{
// İskelet verilerine göre Ellipse elementlerini komunlandır
SetEllipsePosition(ellipseHead, skeleton.Joints[JointID.Head]);
SetEllipsePosition(ellipseHandLeft, skeleton.Joints[JointID.HandLeft]);
SetEllipsePosition(ellipseHandRight, skeleton.Joints[JointID.HandRight]);
SetEllipsePosition(ellipseShoulderLeft, skeleton.Joints[JointID.ShoulderLeft]);
SetEllipsePosition(ellipseShoulderCenter, skeleton.Joints[JointID.ShoulderCenter]);
SetEllipsePosition(ellipseShoulderRight, skeleton.Joints[JointID.ShoulderRight]);
}
}
private void SetEllipsePosition(FrameworkElement ellipse, Joint joint)
{
// -1 ile 1 arasında gelen iskelet verilerini Canvas boyutlarına oranla
var skeletonJoint = joint.ScaleTo(448, 306, .99F, .5F);
// Ellipse'in Canvasun yukarısından ve solundan uzaklığını belirle
Canvas.SetLeft(ellipse, skeletonJoint.Position.X);
Canvas.SetTop(ellipse, skeletonJoint.Position.Y);
}
private void Window_Closed(object sender, EventArgs e)
{
// Kaynakları serbest bırak
kinectNui.Uninitialize();
}
}
}
Uygulamamızı son haliyle çalıştırdığımızda, Kinect iskelet
yapısını tanıdıktan sonra aşağıdaki gibi bir görüntü oluşacak:

Kinect'in iskelet yapısını algılayabilmesi için, sensörden en az 1,8 metre
uzakta durmanız gerekiyor.
Adım 4: Dikey Motor Hareketi
Kinect sensörü, dikey eksende 55 derece (+/- 27 derece
değerleri) hareket edebilir. Hareket mekanizması, başlangıçta hizzalama yaparak
uygulama boyunca o açıda kalmak üzere tasarlanmış. Bu nedenle 20 saniyede 15'den
fazla hareket yapıldığında önce hareket komutu işlemez, sonrasında ise exception
oluşur. Motoru hareket ettirmek için ElevationAngle değerini belirlememiz
yeterli olacaktır. Örneğin;
kinectNui.NuiCamera.ElevationAngle = 17;
komutu, Kinect'i dikey olarak 17 derece pozisyonuna
getirecektir.
Uygulama kodlarını
buradan
indirebilirsiniz.