Makale Özeti

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...

Makale

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 statementler 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();
 
            // İ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.

Umut Erkal
www.uerkal.com | @uerkal

Uygulama Kodları