Thursday, September 26, 2013

Kinect ve İlk WPF Uygulaması - Derinlik Algılayıcı - [Proje]

Herkese merhaba, 1 yıldan beri Kinect teknolojisini takip ediyorum. Uzun zamandır bir Kinect alıp aklımdaki projeleri uygulamaya geçirmek istiyordum. Ve yaklaşık 1,5 ay önce çok uygun bir fiyata Kinect buldum. Daha önce duymayanlar için kısaca tanıtmak gerekirse Kinect ya da Project Natal, Microsoft'un, Xbox 360 platformu için geliştirdiği ve herhangi bir uzaktan kumanda ya da kontrol aygıtı olmadan oyun oynama imkanı veren alet. İlk olarak 4 Kasım 2010'da, Kuzey Amerika'da satışa sunulmuştur.

Projenin amacı bilgisayar ortamında herhangi bir kontrol çubuğu veya kumanda kullanmadan, sadece el hareketleriyle oyun oynayabilmektir. Bunun yanında ses de ayrı bir işletimi için geçerlidir. Kinect sisteminin el, kol hareketlerini algılaması ise kızılaltı ışın yayan projektörlerle gerçekleşiyor. Bu ışınlar görünmüyor. Işınların elde ettiği veriler, CMOS algılayıcılarında komuta çevriliyor. Bu sayede oyuna komut gidiyor. İşletim sisteminin kalitesinin yanı sıra bu komutlar bir saniyeden çok daha kısa zamanda ulaşmaktadır. 3. boyut grafiğinde gerçekleşmesi gereken hızlı komutları ise bilgisayara ait olan yazılım sağlıyor. Yani Kinect, sadece komutları ulaştırmakla sorumlu bir aygıt.


Benim birazdan anlatacağım uygulama ise Visual Studio Ultimate 2012 üzerinde WPF ile geliştirilmiş "Derinlik Algılama" projesi. Projeyi anlatmaya başlamadan önce Visual Studio Ultimate 2012 ile ilgili birkaç şey söylemek istiyorum. Öncelikle yıllarca C# ve .NET teknolojisinden kaçmış birisi olarak Microsoft'un Visual Studio 2012'si çok hoşuma gitti gerek tasarım gerek hız olarak beklentimin çok üzerinde bir performans çiziyor. Visual Studio 2010'dan sonra 2012'de yazılım geliştirmek gerçekten çok keyifli ve herkese tavsiye ederim. Bunun dışında projemizin çalışması için gerekli olan Kinect SDK'sinin yüklü olduğunu varsayıyorum.


Öncelikle Visual Studio 2012'yi başlatıyoruz. Menuden sırasıyla "FILE > New > Project" seçeneğini tıklıyoruz. Ve açılan pencerede "Templates" altında "Visual C#"'ı seçiyoruz. Ortadaki kısımdan ise "WPF Application" seçeneğini seçip projemize isim verdikten sonra "OK" butonuna basıp projemizi oluşturuyoruz.


Bu arada WPF (Windows Presentation Foundation, kod adı Avalon) .NET Framework 3.0'ın grafik altyapısını oluşturan XAML tabanlı sistemdir. Microsoft'un en yeni işletim sistemleri Windows Vista ve Windows 7'de yüklenmiş olarak gelmektedir. Windows XP SP2 ve Windows Server 2003'te de çalışabilecektir. Son kullanıcılar için Microsoft Windows programlarında yüksek derecede görsel arayüz sağlar. Bu nedenle geleceğin programlama teknolojisi denilebilir. Klasik Win32 uygulamalarına olan en büyük üstünlüğü, eski GDI+ pencereleme sistemini kullanmaması, bunun yerine DirectX üzerine kurulmuş bir mimari sayesinde gelişmiş grafik kartlarının GPU'sunun (Graphics Processing Unit) bütün avantajlarından yararlanmasıdır.


Bütün bunlardan sonra sıra geldi projemize referans eklemeye. Bunun için menuden "PROJECT > Add Reference" seçeneğini tıklayıp açılan pencereden "Browse" butonuna tıklıyoruz ve burada yazının başında bahsettiğim Kinect SDK'sinin yüklenmesi ile birlikte gelen "Microsoft.Kinect.dll" dosyasının yolunu gösteriyoruz. Benim bilgisayarım 64-Bit olduğu için dosya "C:\Program Files\Microsoft SDKs\Kinect\v1.7\Assemblies" klasörü altındaydı. Sizin yükleme yaptığınız yere göre yukarıdaki yol farklı olabilir. Bunuda yaptıktan sonra sıra geldi kodumuzu yazmaya.


"MainWindow.xaml.cs" dosyasının içeriği aşağıdaki gibidir.
using System;
using System.Windows;
using Microsoft.Kinect;
using System.Windows.Media;
using System.Windows.Controls;
using System.Collections.Generic;
using System.Windows.Media.Imaging;

namespace KinectDepthStream
{
    /// 
    /// Interaction logic for MainWindow.xaml
    /// 
    public partial class MainWindow : Window
    {
        // Her bir noktanın derinlik değerini tutacak short dizisi
        private short[] depthPixelData;
        // Image elementinin kaynağı olarak atanacak görüntü
        private WriteableBitmap depthOutputImage;
        // Karşılaştırmayı yapacağımız derinlik görüntüsü biçimi
        private DepthImageFormat lastDepthFormat = DepthImageFormat.Undefined;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            // Kinectler sisteme bağlıysa
            if (KinectSensor.KinectSensors.Count > 0)
            {
                // İlk Kinect'i başlatır
                KinectSensor.KinectSensors[0].Start();
                // Derinlik bilgisi akışını 320x240 30fps biçiminde başlatır
                KinectSensor.KinectSensors[0].DepthStream.Enable(DepthImageFormat.Resolution320x240Fps30);
                // Yeni görüntü geldiğinde tetiklenecek event handler'ı oluşturur
                KinectSensor.KinectSensors[0].DepthFrameReady += new EventHandler(KinectDepthFrameReady);
            }
        }

        private void KinectDepthFrameReady(object sender, DepthImageFrameReadyEventArgs e)
        {
            // Derinlik bilgisini açıp depthFrame'e aktarır
            using (DepthImageFrame depthFrame = e.OpenDepthImageFrame())
            {
                if (depthFrame != null)
                {
                    bool haveNewFormat = lastDepthFormat != depthFrame.Format;

                    // Önceki derinlik biçimi ile yeni gelen derinlik biçimi farklı mı?
                    if (haveNewFormat)
                    {
                        // Yeni derinlik bilgisi boyutuna göre diziyi boyutlandırır
                        depthPixelData = new short[depthFrame.PixelDataLength];
                    }

                    // Derinlik bilgisini depthPixelData dizisine aktar:
                    depthFrame.CopyPixelDataTo(depthPixelData);

                    if (haveNewFormat)
                    {
                        // depthOutputImage'ın özelliklerini değiştirir
                        depthOutputImage = new WriteableBitmap(depthFrame.Width, depthFrame.Height, 96, 96, PixelFormats.Gray16, null);
                        // imgDepthFrame elementinin kaynağını depthOutputImage olarak belirler
                        imgDepthFrame.Source = depthOutputImage;
                    }

                    // depthOutputImage'ın içeriğini günceller
                    depthOutputImage.WritePixels(new Int32Rect(0, 0, depthFrame.Width, depthFrame.Height), depthPixelData, depthFrame.Width * depthFrame.BytesPerPixel, 0);
                    // Bir sonraki derinlik biçimi karşılaştırması için, yeni derinlik biçimini kaydeder
                    lastDepthFormat = depthFrame.Format;
                }
            }
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            // Kinectler sisteme bağlıysa
            if (KinectSensor.KinectSensors.Count > 0)
            {
                // Tüm Kinectleri durdurur
                foreach (KinectSensor sensor in KinectSensor.KinectSensors)
                {
                    sensor.Stop();
                }
            }
        }
    }
}
"MainWindow.xaml" dosyasının içeriği aşağıdaki gibidir.

    
        
    

Projemizi yukarıdaki kodlara göre yapılandırıp çalıştırdığımız zaman aşağıdaki gibi bir görüntü elde edeceğiz. Siyah-Beyaz skalasında derinlik bilgisi veren ilk projem "Visual Studio Ultimate 2012" ile yazılıp derlendi. Kullandığım Kinect SDK versiyonu 1.7, çalıştığım işletim sistemi ise Windows 7'dir. Yazmış olduğum kod bu şartlar altında sorunsuz çalışmaktadır.


Şimdilik anlatacaklarım bu kadar ancak Kinect ile ilgili daha yapılacak bir çok şey var. Eğer fırsat bulursam yapacaklarımın arasında OpenCV kütüphanesi ile bağlantı kurup gesture recognition, motion understanding, motion tracking, face recognition gibi uygulamalar geliştirmek bulunuyor. Eğer ilgilenen ve merak eden varsa projenin tüm kodlarına buradaki linkten erişebilir. Şimdilik hoşçakalın.

3 comments:

  1. güzel bir çalışma olmuş teşekkürler.

    ReplyDelete
    Replies
    1. Yardımcı olduysa ne mutlu, asıl ben teşekkür ederim.

      Delete
  2. Taner Bey Merhabalar,
    Blogunuzla yeni tanışmama rağmen içerikleriniz ve odak noktalarınız gerçekten çok ilgi çekici. Adım Akan ve bir üniversitede öğretim üyesi olarak görev yapıyorum. Yeni medyaları iletişim eğitiminde kullanılabilir hale getirmek için çabalıyoruz. Kinect'de de iletişim eğitimi için çok fazla fırsat var ve projelerle hayata geçirmek istiyoruz. Aslında bu teknolojiyi projeye çevirebilecek bilgi birikimine sahipmisiniz bilmiyoruz ama öyleyseniz akanyanik@gmail.com adresine iletişim numaranızı gönderirseniz ücreti mukabilinde bir destek teklifinde bulunabiliriz. İyi çalışmalar dilerim.

    ReplyDelete