Kinect SDK – Natural User Interface API  Udostępnij na: Facebook

Autor: Tomasz Kowalczyk

Opublikowano: 2011-09-12

Artykuł ten jest drugim z cyklu artykułów na temat wykorzystania możliwości sensora Kinect, który, po podłączeniu do komputera, pozwala na zastosowanie go w programach poprzez wykorzystanie udostępnionego SDK.

W poprzedniej części opisano, z czego fizycznie składa się Kinect oraz w jaki sposób należy go poprawnie zainstalować. W tej części przedstawiono Kinect od strony programowej, omówiono komponenty programowe Kinect SDK oraz pokazano, jak napisać program przechwytujący obraz z kamery RGB i kamery głębokości wraz ze sterowaniem czujnikiem nachylenia.

Architektura programowa Kinect

Kinect SDK zapewnia dostęp do złożonych możliwości urządzenia w prosty sposób, tzn. poprzez narzędzia i biblioteki, pozwalające programistom tworzyć kod reagujący na zdarzenia, przechwytywane ze świata zewnętrznego. Dzięki temu dano możliwość, aby w łatwy sposób sterować zachowaniem programu. Sensor Kinect wraz z Kinect SDK pozwala na interakcję programu z otoczeniem, zgodnie z poniższym schematem.

Rys. 1. Współpraca między sensorem Kinect a programem.

Warto prześledzić powyższy schemat, który prawidłowo obrazuje proces, w którym dane, pobierane
z otoczenia za pomocą sensora Kinect, są przetwarzane przez biblioteki NUI, a następnie stanowią one doskonały substytut i bazę danych dla tworzonej aplikacji.

Implementacja NUI w Kinect SDK jest ciekawie zorganizowana. Wtym artykule wyjaśniono, co to właściwe jest NUI w Kinect SDK oraz co zrobić, by działało w oczekiwany sposób.

NUI API

NUI (Natural User Interface) – jest interfejsem pomiędzy użytkownikiem a aplikacją, który pozwala na ich wzajemną interakcję, bez potrzeby korzystania z dodatkowych urządzeń wskazujących. Ma pozwalać użytkownikom na korzystanie z aplikacji w sposób naturalny dla człowieka.

Możliwości NUI API zaimplementowane w Kinect SDK:

  • dostęp do sensora Kinect, podłączonego do komputera,
  • dostęp do danych, pochodzących z kamer RGB i głębokości w środowisku programowym,
  • dostarczenie narzędzi, umożliwiające tzw. skeletal tracking, czyli płynne reagowanie na zachowania, wywołane ruchem szkieletu człowieka.

Sterowniki, dostarczone wraz z Kinect SDK, pozwalają na wykorzystanie przez NUI API kilku sensorów Kinect, podłączonych do jednego komputera. Należy jednak pamiętać, że dostęp do pojedynczego sensora Kinect, w danym czasie, posiada jedynie jedna aplikacja.

W poprzednim artykule przedstawiono już, jak należy zainicjować korzystanie z NUI API poprzez poniższy fragment kodu:

Runtime nui = new Runtime();

NUI – opcje inicjalizacyjne

NUI API daje możliwość korzystania ze strumieni danych, takich jak:

  • Color – aplikacja korzysta z danych, dostarczonych przez kamerę RGB,
  • Depth – aplikacja korzysta z danych, dostarczonych przez kamerę głębokości,
  • Depth and player index – aplikacja korzysta z danych, dostarczonych przez kamerę głębokości i na podstawie indeksu śledzonego obiektu jest w stanie wywoływać różne zdarzenia dla różnych obiektów,
  • Skeleton – aplikacja korzysta z danych, dostarczonych poprzez śledzenie pojedynczego obiektu (szkieletu).

Aby zainicjować korzystanie z jednej lub z kilku powyższych opcji, należy wywołać metodę Initialize() obiektu nui z konkretnymi argumentami:

Tab. 1. Opcje inicjalizacji NUI.

Color RuntimeOptions.UseColor
Depth RuntimeOptions.UseDepth
Depth and player index RuntimeOptions.UseDepthAndPlayerIndex
Skeleton RuntimeOptions.UseSkeletalTracking

W tym artykule przedstawiono przechwytywanie obrazu z kamery RGB i z kamery głębokości, dlatego też jedynie te dwie opcje zostały omówione. Możliwości pozostałych dwóch zostaną przybliżone w kolejnym artykule.

Przechwytywanie danych: Color

Ten sposób pozwala na użycie jednej z dwóch metod przechwytywania obrazu, w zależności od jego jakości:

  • jakość obrazu określa, jak szybko jest on transferowany z sensora do aplikacji;
  • format określa, czy obraz dostarczony z sensora do aplikacji jest w formacie RGB lub YUV.

Szybkość, z jaką przebiega transfer, jest ograniczona możliwościami standardu USB, dlatego należy brać to pod uwagę i pamiętać, że ustawienie większej rozdzielczości przechwytywania obrazu oznacza mniejszą prędkość przesyłu. Dlatego, projektując aplikację, należy zdecydować, czy użytkownik końcowy będzie w stanie tolerować straty, spowodowane transferem obrazów o gorszych parametrach na rzecz zwiększenia szybkości przesyłu.

Dostępne formaty to RGB (32bit) i YUV (16bit).

Przechwytywanie danych: Depth

Ten sposób pozwala programiście określić odległość pojedynczego piksela od sensora Kinect, dystans ten określany jest w milimetrach. SDK pozwala na zdefiniowanie jednej z trzech rozdzielczości ramki, w jakiej będą napływały dane do aplikacji:

  • 640x480px,
  • 320x240px,
  • 80x60px.

Określenie rozdzielczości ramki jest istotne w momencie, gdy należy podjąć decyzję, na jakie dane aplikacja ma reagować. Może to być sposób postępowania na zdarzenia, związane ze śledzeniem szkieletu, lub też brak reakcji na obiekty otoczenia, które pojawiają się w scenerii znajdującej się w zasięgu sensora Kinect.

Stosujemy NUI API w naszej aplikacji

Aplikacja, jaką przygotowano na potrzeby niniejszego artykułu, będzie pokazywała w dwóch oknach (dokładnie w dwóch kontrolkach WPF typuImage) obraz z kamer RGB i głębokości. Dodatkowo, między oknami, umieszczono prosty suwak (kontrolka WPF typu Slider), który po przyciśnięciu guzika (kontrolka WPF typu Button), zmieni wartość kąta nachylenia sensora Kinect o wartość pobraną z suwaka.

Informacja
Wszystkie kody źródłowe projektów, utworzonych w ramach artykułów, będą dostępne na tej stronie.

W poniższym opisie skupiono się jedynie na fragmentach kodu, bezpośrednio związanych z Kinect SDK, oraz omówieniu ich znaczenia, gdyż to jest tematem tego artykułu. Dodatkowo warto pamiętać, że jest to aplikacja WPF, w której oprócz wykorzystania bibliotek, bezpośrednio związanych z Kinect SDK, użyto również biblioteki, napisanej przez zespół portalu Coding4Fun. W dużym stopniu upraszcza ona  m.in. sposób pobierania danych z kamer. Zalecane jest pobranie jej za pomocą menedżera dystrybucji bibliotek NuGet.

Po utworzeniu obiektu nui klasy Runtime, w metodzie Window_Loaded, należy zainicjować pobieranie danych ze strumieni, pochodzących z kamery RGB i kamery głębokości. Za wykonanie tej czynności odpowiada poniższy kod:

nui.Initialize(RuntimeOptions.UseColor | RuntimeOptions.UseDepth);

Następnie wymagane jest, aby przypisać zdarzeniom, odpowiedzialnym za pobieranie danych z konkretnych źródeł, określone metody obiektu nui:

nui.VideoFrameReady += 
new EventHandler<ImageFrameReadyEventArgs>(nui_VideoFrameReady);
nui.DepthFrameReady += 
new EventHandler<ImageFrameReadyEventArgs>(nui_DepthFrameReady);

Dwie powyższe linijki przedstawiają zastosowanie podstaw programowania zdarzeniowego. Dwóm zdarzeniom, możliwym doprzechwycenia przez sensor: VideoFrameReady i DepthFrameReady, przypisano dwie metody: Nui_VideoFrameReady i Nui_DepthFrameReady, które zostaną uruchomione w momencie przechwycenia obrazu przez obydwie kamery.

W kolejnym kroku otworzono strumienie pobierania danych dla obrazów wideo oraz dla obrazów, które pozwalają na określenie odległości obiektów od sensora Kinect. Jako argumenty metody Open() należy podać kolejno, jakiego rodzaju danych należy się spodziewać, jakiej rozdzielczości ma być przechwytywany obraz oraz jaki jest jego typ:

nui.VideoStream.Open(ImageStreamType.Video, 2,
                ImageResolution.Resolution640x480, ImageType.Color);
nui.DepthStream.Open(ImageStreamType.Depth, 2,
                ImageResolution.Resolution320x240, ImageType.Depth);

W związku z tym, że interfejsem użytkownika tworzonej aplikacji są m.in. dwie kontrolki WPF typuImage, które identyfikowane w kodzie źródłowym będą jako image1 i image2, należy teraz określić ich zawartość. Do tego zadania  można wykorzystać bibliotekę, pochodzącą z portalu Coding4Fun, która jest swego rodzaju Helperem, ułatwiającym korzystanie z rozbudowanych możliwości, dotyczących przetwarzania obrazu w prosty sposób.

Poniższe dwa kody źródłowe stanowią implementację metod, które zostały wywołane wcześniej w odpowiedzi na zdarzenia, wychwycone przez kamery:

Metoda odpowiedzialna za transmisję z kamery RGB:

void nui_VideoFrameReady(object sender, ImageFrameReadyEventArgs e)
        {
            image1.Source = e.ImageFrame.ToBitmapSource();
        }

Metoda odpowiedzialna za transmisję z kamery głębokości:

void nui_DepthFrameReady(object sender, ImageFrameReadyEventArgs e)
        {
            image2.Source = e.ImageFrame.ToBitmapSource();
        }

Aby osiągnąć zamierzony cel, co do funkcjonalności aplikacji, pozostaje jedynie dodać kontrolkę slider i odpowiednio ją skonfigurować oraz przypisać do kontrolki button odpowiednie zachowanie, umożliwiające sterowanie kątem nachylenia sensora:

private void button1_Click(object sender, RoutedEventArgs e)
        {
            nui.NuiCamera.ElevationAngle = (int)slider1.Value;
        }

Operacja ta jest jak widać bardzo prosta. Kinect SDK poprzez NUI API daje programistom swobodny dostęp do wielu opcji konfiguracyjnych sensora. Warto pamiętać, że ograniczenia sprzętowe urządzenia pozwalają zmieniać kąt nachylenia sensora jedynie o 27 stopni w górę i w dół. Wtaki też sposób należy skonfigurować kontrolkęslider, a dokładnie jej atrybut Minimum i Maximum, pamiętając również że, ze względów bezpieczeństwa i dbałości o urządzenie, nie powinno się zmieniać kąta nachylenia sensora częściej niż 15 razy na 2minuty. Zwiększenie tej częstotliwości może doprowadzić do uszkodzeń mechanicznych sensora.

Efekt końcowy rozłożenia kontrolek w aplikacji może wyglądać na przykład tak:

Rys. 2. Wygląd końcowy naszej aplikacji.

Osiągnięto to stosując metodę Drag&Drop środowiska Visual Studio 2010.

Podsumowanie

W tym artykule przedstawiono, jak zorganizowana jest architektura programowa środowiska Kinect SDK,  dzięki temu można wzbogacić wiedzę na temat NUI oraz sposobu jego implementacji i wykorzystania w omawianej bibliotece. Zaimplementowano również prosty program, pokazujący te rzeczy w praktyce.

W następnym artykule zostanie dokładnie omówiony proces określania odległości za pomocą sensora Kinect i jego opcji inicjalizacyjnej RuntimeOptions.UseDepthAndPlayerIndex. Przedstawiony również zostanie Skeletal Tracking.