Kinect’te Sadece Oyuncu Derinlik Görüntüsü

   Bir önceki makalemde sizlere Kinect’ten gelen akışlardan sadece derinlik akışını kullanarak nasıl bir görüntü elde edebileceğinizden bahsetmiştim. Sadece gri tonlama ile yaptığımız bu işlem sonucunda aldığımız görüntü maalesef ki istediğimiz detayda bir sonuç ortaya çıkartmıyordu. Bunun en önemli nedeni gri bir görüntü oluşturabilmek için ARGB renk paletinde kırmızı, yeşil ve mavi aynı değerin verilmesi ve bu kanalların sadece 8-bitlik 0 ile 255 arasında bir değer aralığında bulunuyor olmasıydı. 13-bit olarak iletilen derinlik bilgisinin 8-bit içerisine sığdırılması sırasında kaybedilen 3-bit doğal olarak beraberinde detay görüntünün de kaybolmasına sebep olmaktaydı. Buna karşılık, bu işlemler sonrasında temel düzeyde derinlik bilgisini nasıl işleyebileceğimizi görmüştük. Bu makalemde konuyu bir adım ileri taşıyarak derinlik bilgisini sadece görüntü olarak kullanmaktan öte, işlemeye de başlayacağız. İlk olarak yapacağımız kodumuz da ise gelen görüntüyü ayıklayarak sadece oyuncuyu göstereceğiz. Konuyu önceki yazımdan devam ettiriyor olacağım için, kaçıranlarınızın öncelikle Kinect ile kamera ve derinlik işlemlerini incelemesi daha doğru olacaktır.

   Önceki kodumuzdan devam ettiğimizi göz önüne alırsak, kodunuz içerisinde Kinect cihazınızı ilklendirdiğinizi ve iskelet takibi ile derinlik akışını oyuncu indeksi (DepthAndPlayerIndex) ile başlattığınızı varsayıyorum;

kinect.Initialize(RuntimeOptions.UseDepthAndPlayerIndex | RuntimeOptions.UseSkeletalTracking);

   Önceki yazımdaki nihayi kodumuzu şu şekildeydi;

private void kinect_DepthFrameReady(object sender, ImageFrameReadyEventArgs e) {
    var image = e.ImageFrame.Image;
    var yukseklik = image.Height;
    var genislik = image.Width;
    var derinlikVerisi = image.Bits;
    var bitmap = new byte[yukseklik * genislik * 4];
    for (int y = 0, veriIndeksi = 0; y < yukseklik; y++) {
        var yukseklikOfseti = y * genislik;
        for (var x = 0; x < genislik; x++, veriIndeksi += 2) {
            var gercekUzaklik = derinlikVerisi[veriIndeksi] | (derinlikVerisi[veriIndeksi + 1] << 8);
            var yogunluk = (byte)(255 - (255 * gercekUzaklik / 0x0fff));
            var pikselIndeksi = ((genislik - x - 1) + yukseklikOfseti) * 4;
            bitmap[pikselIndeksi] = yogunluk; //Mavi
            bitmap[pikselIndeksi + 1] = yogunluk; // Yeşil
            bitmap[pikselIndeksi + 2] = yogunluk; // Kırmızı
        }
    }

   Bu sefer oyuncu bilgisinin  de derinlik akışının bir parçası olması nedeniyle gerçek uzaklığı aşağıdaki şekilde hesaplanması gerekecektir (İlk byte sağa 3 bit kaydırılarak oyuncu indeksi bilgisinin sıfırlanması sağlanmakta, ardından ikinci byte 5 bit sağa kaydırılarak ilk byte ile toplanmakta);

var gercekUzaklik =  derinlikVerisi[veriIndeksi] >> 3 | derinlikVerisi[veriIndeksi + 1] << 5;

   Önceki kodumuzda uzaklık bilgisi kullanılarak her bir piksel için gri bir ton elde edilmekte ve bu ton ekranda gösterilmekteydi. Sadece oyuncu görüntüsünün gösterileceği bir senaryoda ise oyuncunun olmadığı piksellerde bir tonlama yapılmamalı. Bunun için öncelikle işlem yaptığımız pikselin bir oyuncuya ait olup olmadığını tespit etmeliyiz. Hatırlarsanız oyuncu bilgisi iki byte’lık derinlik verimizin ilk byte’ının ilk 3 bit’inde yer almaktaydı. Bu durumda ilk byte’ın bu geriye kalan bitlerini sıfırlarsak oyuncu indeksini elde etmiş olacağız. Bunu yapabilmek için ise bit seviyesinde aşağıdaki kadar kolay bir işlem yeterli olacaktır;

int oyuncu = derinlikVerisi[veriIndeksi] & 0x07;

   İşlem yaptığımız piksel’de bir oyuncu bulunması durumunda oyuncu değişkenimiz bize indeksini verecek, diğer durumlarda ise değişkenimiz 0 değerine sahip olacaktır. Bu bilgi yardımıyla ilgili pikselin gösterilip gösterilmeyeceğine karar verebiliriz. Hatırlarsanız görüntüyü oluşturabilmek için ARGB renk paletini kullanmıştık; fakat bu paletin alfa kanalı üzerinde hiç bir işlem yapmamıştık. ARGB renk paletinde alfa kanalı rengin şeffaflığını belirleyecektir ve 255 değeri tam bir görüntü sunarken 0 değeri tamamen görünmez/şeffaf olmasını sağlayacaktır. Bu bilgi ışığında, daha önceden üzerinde işlem yapmadığımız alfa kanalını kullanarak pikselin tam görünür ya da tamamen şeffaf olmasını sağlayabiliriz;

bitmap[pikselIndeksi] = yogunluk; //Mavi
bitmap[pikselIndeksi + 1] = yogunluk; // Yeşil
bitmap[pikselIndeksi + 2] = yogunluk; // Kırmızı                    
bitmap[pikselIndeksi + 3] = oyuncu != 0 ? (byte)255 : (byte)0; //Alfa 

   Son olarak elde ettiğimiz görüntüye ait byte dizisini BitmapSource’a dönüştürürken bir önceki makaleden farklı olarak alfa kanalı da kullanacağımızı belirtmeliyiz;

derinlikVideo.Source = BitmapSource.Create(genislik, yukseklik, 96, 96, PixelFormats.Bgra32, null, bitmap, genislik * PixelFormats.Bgra32.BitsPerPixel / 8);

   Bu işlemler ardından işlenen görüntüde sadece oyunculara ait detaylar bulunacaktır;

Kinect_Sadece_Oyuncu

   Toparladığımızda, sadece kullanıcıya ait derinlik görüntüsü elde etmek için elimizde aşağıdaki gibi bir kod olacaktır;

public partial class MainWindow : Window {
    private Runtime kinect;
 
    public MainWindow() {
        InitializeComponent();
 
        kinect = new Runtime();
    }
 
    private void Window_Loaded(object sender, RoutedEventArgs e) {
        kinect.Initialize(RuntimeOptions.UseDepthAndPlayerIndex | RuntimeOptions.UseSkeletalTracking);
 
        kinect.DepthFrameReady += kinect_DepthFrameReady;
 
        kinect.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.DepthAndPlayerIndex);
    }
 
    private void kinect_DepthFrameReady(object sender, ImageFrameReadyEventArgs e) {
        var image = e.ImageFrame.Image;
        var yukseklik = image.Height;
        var genislik = image.Width;
        var derinlikVerisi = image.Bits;
 
        var bitmap = new byte[yukseklik * genislik * 4];
 
        for (int y = 0, veriIndeksi = 0; y < yukseklik; y++) {
            var yukseklikOfseti = y * genislik;
 
            for (var x = 0; x < genislik; x++, veriIndeksi += 2) {
                var gercekUzaklik = (derinlikVerisi[veriIndeksi] >> 3) | (derinlikVerisi[veriIndeksi + 1] << 5);
 
                var pikselIndeksi = ((genislik - x - 1) + yukseklikOfseti) * 4;
                int oyuncu = derinlikVerisi[veriIndeksi] & 0x07;
 
                var yogunluk = (byte)(255 - (255 * gercekUzaklik / 3245));
 
                bitmap[pikselIndeksi] = yogunluk; //Mavi
                bitmap[pikselIndeksi + 1] = yogunluk; // Yeşil
                bitmap[pikselIndeksi + 2] = yogunluk; // Kırmızı                    
                bitmap[pikselIndeksi + 3] = oyuncu != 0 ? (byte)255 : (byte)0; //Alfa 
            }
        }
 
 
        derinlikVideo.Source = BitmapSource.Create(genislik, yukseklik, 96, 96, PixelFormats.Bgra32, null, bitmap, genislik * PixelFormats.Bgra32.BitsPerPixel / 8);
    }
 
    private void Window_Closed(object sender, System.EventArgs e) {
        kinect.Uninitialize();
    }
}

Fatih Boy

Ankara'da yaşayan Fatih, bir kamu kurumunda danışman olarak çalışmaktadır. ALM süreçleri, kurumsal veri yolu sistemleri, kurumsal altyapı ve yazılım geliştirme konularında destek vermektedir. Boş zamanlarında açık kaynak kodlu projeler geliştirmeyi ve bilgisini yazdığı makalelerle paylaşmayı seven Fatih, aynı zamanda Visual C# ve Visual Studio teknolojileri konusundan Microsoft tarafından altı yıl üst üste MVP (En Değerli Profesyonel) ödülüne layık görülmüştür. İş hayatı boyunca masaüstü uygulamaları, web teknolojileri, akıllı istemciler gibi konularda Asp.Net, Php, C#, Java programlama dilleri ve MySql, MsSql ve Oracle gibi veritabanı yönetim yazılımları ile çalışmıştır. İngilizce ve Türkçe olarak yayınlanan makalelerini gerek İngilizce bloğunda, gerekse de Türkçe bloğunda bulabileceğiniz gibi web sitesinden de açık kaynak kodlu geliştirdiği yazılımlarına ulaşabilirsiniz. vCard - Twitter - Facebook - Google+

Bir Cevap Yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir