Nummer 31

Der aufmerksame Leser unseres Firmenblogs wird hier wahrscheinlich einen technischen Artikel erwarten, was sich aber unter diesem kryptischen Titel versteckt, ist vielmehr umgangssprachlich formuliert das Brechen einer Lanze. Vorab möchte ich mich aber noch vorstellen: Ich bin Nummer 31, genauer gesagt, Nummer 31 in der aktuellen Mitarbeiterliste der SDX AG.

Eine Nummer also

… und das nach all den Projektjahren, die ich mittlerweile auf dem Buckel habe, nach all den Kämpfen, die ich bestritten habe und nach allen Erfolgen wie auch Misserfolgen, die meinen Weg geprägt haben. Eigentlich sollte mir das zu denken geben, gerade in Zeiten, in denen die Anzahl der Jobangebote in meiner Mailbox die Anzahl der Spammails bei weitem übertrifft. In Zeiten, in denen mir das Gefühl suggeriert wird, innerhalb einer überschaubaren Anzahl von Tastenanschlägen bzw. Telefonaten meine Selbstverwirklichungswünsche im Eiltempo zu erfüllen und darüber hinaus natürlich mit einem exorbitanten Gehalt ausgestattet zu werden.
Was soll ich sagen? Kurz gesagt, es gibt mir nicht zu denken!

Grüne Wieseprojekte

Zugegeben, auch ich träume von den saftigen grünen Wiesen, auf denen man nie gesehene Architekturen baut, auch ich träume davon, ständig auf der Welle der neuesten und hippsten Technologien zu reiten und natürlich träume auch ich davon, dass mein Weg mit frischgedruckten Geldscheinen und spannenden Projekten gepflastert ist … aber dieses ständige Streben nach Glück lässt einen manchmal vielleicht auch das Gespür dafür verlieren, dass es eben auch noch andere Dinge gibt.

Ich bin ich

Und nun schließt sich der Kreis und ich breche die Lanze. Ich bin bei der SDX AG eben nicht nur Nummer 31. Ich bin ich und das mit all meinen Stärken und auch Schwächen. Genauso wie mein Arbeitgeber eben seine Stärken und auch Schwächen hat, mit denen ich mal mehr und mal weniger gut leben kann. Fakt ist aber, dass ich mit all den Erfahrungen, die ich hier – auch gerade im letzten Jahr – sammeln durfte, ein Auge dafür entwickelt habe, was mir wirklich wichtig ist.

Teil einer Familie

Und deshalb möchte ich an dieser Stelle einfach mal Danke sagen. Danke deshalb, weil ich in all den Jahren und Firmen, die meinen Weg ausgemacht haben, noch niemals eine derart persönliche Ansprache und je nach Lebenssituation auch Anteilnahme hatte wie bei SDX. Danke dafür, dass ich im übertragenen Sinn ein Teil einer Familie sein darf, anstatt einfach nur in einer Firma zu arbeiten. Danke dafür, dass ich jederzeit eine offene Tür finde, wenn ich mich in eine andere Richtung entwickeln möchte.

Kommt vorbei

Und für all diejenigen da draußen, die den Text bis hier hin gelesen haben und die auch etwas anderes sein wollen als eben nur eine Nummer, ändert was … oder besser gesagt, kommt vorbei. Ich kann euch nicht versprechen, dass Ihr hier euer Glück findet und es wird sicher auch ein zwei Momente geben in denen es besser laufen könnte. Aber wenn Ihr ähnlich hohe Ansprüche an Technologien, Arbeitsumfeld und einem Miteinander habt wie ich, dann werdet ihr bei SDX sicherlich einen Hafen finden, an dem es sich in dieser schnelllebigen und unpersönlichen Zeit ein wenig ankern lässt. Und das schreibe ich aus freien Stücken und aus vollster Überzeugung.

Das Blau ist mir nicht blau genug!

Drei-Schichten-Architektur, WCF Services, ein schickes Frontend in Microsoft Einheitsgrau, alles aus einem Guss nach bewährten und gängigen Mustern entwickelt für die Ewigkeit, so sah meine heile Welt vor noch nicht allzu langer Zeit aus. Wohlbehütet und mit einem zufriedenen Kunden an der Seite dachte ich, mich könne so schnell nichts mehr überraschen. Und dann ganz plötzlich, geradezu hinterrücks, sprang mir aus dem dichten Dickicht des Technologiedschungels das sagenumwobene "Web" in den Nacken.

Fast fünf Monate ist das nun her und ich lerne langsam aber sicher wieder zu laufen. Für alle, die ein ähnliches Schicksal ereilt, habe ich hier die wichtigsten Bereicherungen meines täglichen Lebens noch mal zusammengefasst.

1. Nie mehr graue Maus
"Zu Mainstream!", "Boah das ist voll Retro Du Noob!" … Worthülsen, die im urbanen Leben zum Standardrepertoire einer ganzen Subkultur gehören, werden Ihre Welt erobern. Seien Sie bereit dafür, zum Hipster der Entwickler aufzusteigen und hecheln Sie neusten technologischen Trends hinterher die schneller aufkommen und wieder verfliegen wie der Furz ein texanischen Sumpfkaninchens am Rande des Río de Sabinas.

2. Erhöhte Frustrationsschwellen
Neigen Sie dazu leicht genervt zu sein? Wutausbrüche gehören zu Ihrem Berufsleben und sind Ihnen peinlich? Web-Development bietet Ihnen die perfekte Lösung! CSS3 ist innerhalb kürzester Zeit erlernbar und hat einen äußerst positiven Aspekt auf die eigenen Frustrationsschwellen. Einmal erlernt lassen sich komplexe Probleme pragmatisch mit einfachsten Mitteln lösen.


Quelle: https://twitter.com/myusuf3/status/317338683852324864

3. Das reichhaltigste Entwicklungsökosystem ever
Gut, böse Zungen würden behaupten, dass es mehr clientseitige Javascript-Frameworks gibt als Apps & Sites zusammen die selbige nutzen, aber Fakt ist nun mal, es gibt fast nichts was es nicht gibt. Sehen Sie es einfach positiv und nutzen Sie die gegebene Vielfalt! Und sei es nur dazu die Beziehung zu Ihren Arbeitskollegen zu verbessern in dem Sie am Ende eines harten Tages folgendes mit Ihren Kollegen tun:

  • Denken Sie sich ein Nomen aus
  • Googeln Sie das Wort "<nomen>.js"
  • Wenn eine Library mit diesem Namen existiert, muss derjenige einen Drink zu sich nehmen

Javascript kann so viel Spaß machen und das Team stärken!

4. Immer die neuste Hardware
Was musste man als "stinknormaler" Entwickler nicht immer betteln, um vom Chef mit den neusten technischen Gadgets ausgestattet zu werden?! Vergessen sind diese Zeiten, als Web Developer ist man diesbezüglich immer Leading Edge! Kein Wunsch wird einem jemals wieder verwehrt bleiben, ganz im Gegenteil, anstatt eines neuen Tablets haben Sie gleich zehn davon, und das mit den unterschiedlichsten Formfaktoren. Der nur geringfügig höhere Testaufwand ist dabei durchaus zu verkraften.


Quelle: “I Heard You Want To Be A Web Developer.” WeKnowMemes

5. Anregende Kommunikation
Was waren die Meetings bzgl. neuer Requirements mit Fachabteilungen doch dröge und langweilig! Haben Sie künftig einfach mehr Spaß mit Ihrem neuen Ansprechpartner … dem Marketing. Diskutieren sie angeregt und ausgedehnt darüber warum man ein Grafik nicht einen halben Pixel nach links schieben kann, freuen sich sich über stundenlanges Brainstorming welchen Text ein Button haben soll und lernen sie ganz nebenbei mit unscharfen Anforderungen wie z.B. "das Blau ist mir nicht blau genug" umzugehen. Egal wie es kommt, bei einem Punkt können Sie gewiss sein, Sie werden zum wahren Meister in Sachen Kommunikation.

6. Entdeckung eines neuen Selbstbewusstseins
Wie lange schon fühlen Sie sich als kleines Rad im Getriebe eines Großprojekts? Wie lange schon leidet Ihr Selbstbewusstsein daran, einfach austauschbar zu sein? Web Development verhilft Ihnen zu einer nie dagewesenen Stärke, werden Sie sich nach all den Jahren des tristen Entwicklerdaseins Ihrer Wichtigkeit bewusst!

Fazit
Diese Liste ließe sich quasi endlos erweitern, gerade dann, wenn man die Welt durch die Brille eines klassischen "Desktop- und Backendentwicklers" sieht. Und doch möchte ich all denen, die das gleiche Schicksal ereilt keine Angst machen. Eines kann ich zumindest aus meiner Sicht sagen, hat man erst mal die größten Schmerzen hinter sich gebracht, dann bringt die Webentwicklung zweifellos eine Menge Spaß mit sich!

Bewegungsdrang (Teil 5): Die Kinect geht unter die Haut

Wie der ein oder andere vielleicht bemerkt hat, ist es ein wenig still geworden um meine Serie. Die langen grauen Monate der Winters und die tägliche Projektarbeit haben Ihren Tribut gefordert. Doch mit dem Frühlingsanfang, der uns heute mal eben 20 cm Neuschnee samt eines atemberaubenden Verkehrskollaps gebracht hat, ist sie wieder da … die Lust am Schreiben … und ich fange dabei mit der Einleitung an die schon seit Oktober meine Festplatte ziert.

Ich glaube das letzte und einzige Skelett welches mich je begeistert hat war "Achmed the dead terrorist". Als sich dieser Clip quasi viral durch sämtliche Social Networks ausbreitete hatte man als Internet affiner Mensch gar keine Möglichkeit sich dem Ganzen zu entziehen. Dass es jemals dazu kommen könnte dass ein Haufen Knochen mich ähnlich begeistert hätte ich nicht gedacht, aber wie meine Oma schon damals sagte, erstens kommt es anders und zweitens als man denkt.

Die Rede ist von meinem eigenen Bewegungsapparat, oder vielmehr über das was die Kinect intern aus mir macht wenn ich mal wieder hektisch vor dem Bildschirm zapple. Selbiges ist vielleicht nicht ganz so lustig wie der Clip, aber immerhin muss ich mir keinen einen Meter großen Handwärmer auf den Arm stülpen und mit mir selbst reden. In diesem Sinne werde ich heute auf die Skeleton API eingehen.

Punkt, Punkt, Komma, Strich

Mittels des Infrarotsensors ist die Kinect in der Lage bis zu sechs Personen zu erkennen wovon zwei aktiv getrackt werden können. Der Skeletonstream spielt dabei die zentrale Rolle denn er sorgt dafür dass bis zu 30 Frames pro Sekunde mit allen relevanten Joints (Knotenpunkte des Körpers) beim Rechner angeliefert werden.

Quelle: MSDN – Skeletal Tracking

Will man die so erzeugten Daten verarbeiten muss man, ebenso wie bei der RGB-Kamera oder dem Tiefensensor, den genannten Stream aktivieren und die so gefeuerten SkeletonFrameReady-Events entsprechend verarbeiten.

   1: // Generell ist es möglich mehr als einen Kinect Sensor an einem Rechner zu betreiben. Für

   2: // unsere Zwecke reicht es deshalb aus den ersten verfügbaren Sensor mit Status Connected 

   3: // zu ermitteln.

   4: this.Kinect = KinectSensor.KinectSensors.FirstOrDefault(ks => ks.Status == KinectStatus.Connected);

   5:  

   6: if (this.Kinect != null)

   7: {

   8:     try

   9:     {

  10:         // Der Skeleton Stream kann mit und ohne TransformSmoothParameters enabled werden. Für ein

  11:         // einfaches Beispiel reicht der Aufruf ohne, was aber beinhaltet dass Jittereffekte sprich

  12:         // Ungenauigkeiten bei der Auswertung der Koordinaten zum Tragen kommen.

  13:         this.Kinect.SkeletonStream.Enable();

  14:         

  15:         // Last-but-not-least sorgen wir dafür dass die FrameReady Events in der Methode 

  16:         // Kinect_SkeletonFrameReady verarbeitet werden können

  17:         this.Kinect.SkeletonFrameReady += this.Kinect_SkeletonFrameReady;

  18:  

  19:         // Starten des Sensors

  20:         this.Kinect.Start();

  21:     }

  22:     catch (IOException)

  23:     {

  24:         this.Kinect = null;

  25:     }

  26: }

Hat man dies getan, so lassen sich bis zu 20 Joints pro getracktem User verarbeiten, je nachdem was im Field Of View der Kinect erkannt wurde oder eben nicht.

Quelle: MSDN – Tracking Users with Kinect Skeletal Tracking

Eine Auswertung der Koordinaten im Eventhandler folgt hierbei oft dem gleichen Schema. Man übernimmt die durch das Frame gelieferten Informationen und analysiert und/oder vergleicht die gelieferten Daten der Joints je nach vorliegender Aufgabenstellung.

So könnte der Code für eine einfache Analyse wie folgt aussehen:

   1: private void Kinect_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)

   2: {

   3:     // Öffnen des Skeleton Frames

   4:     using (SkeletonFrame skeletonFrame = e.OpenSkeletonFrame())

   5:     {

   6:         if (skeletonFrame != null)

   7:         {

   8:             // Erstellen eines Arrays für die Skeleton Daten

   9:             Skeleton[] skeletons = new Skeleton[skeletonFrame.SkeletonArrayLength];

  10:  

  11:             // Übernahme der Skeletinformation in das Array

  12:             skeletonFrame.CopySkeletonDataTo(skeletons);

  13:  

  14:             // Die Daten des ersten "Benutzers" im getracktem Zustand ermitteln

  15:             Skeleton userSkeleton = skeletons.FirstOrDefault(s => s.TrackingState == SkeletonTrackingState.Tracked);

  16:  

  17:             if (userSkeleton != null)

  18:             {

  19:                 // Ermittlung der Position des Kopfes

  20:                 Joint head = userSkeleton.Joints[JointType.Head];

  21:  

  22:                 // Ermittlung der Position des linken Hand

  23:                 Joint leftHand = userSkeleton.Joints[JointType.HandLeft];

  24:  

  25:                 // Prüfen ob linke Hand "über* dem Kopf ist

  26:                 if (leftHand.Position.Y > head.Position.Y)

  27:                 {

  28:                     // Tue was auch immer

  29:                 }

  30:             }

  31:         }

  32:     }

  33: }

Ergänzend sei an dieser Stelle erwähnt, dass alle gelieferten 3D Koordinaten zwischen –1 und 1 angeliefert werden. Für das obige Beispiel heißt der Verzicht auf die X- bzw. Z Koordinaten also dass wir nur die Höhe der linken Hand bezogen auf die Höhe des Kopfes auswerten.

Darüber hinaus sind noch viele andere Dinge möglich die den Rahmen dieses Artikels sprengen würden. So beinhaltet z.B. das Property BoneOrientations des Skeleton Objekts Informationen über die Orientierung der Joints zueinander wie auch bezogen auf die Sicht der Kamera. Es lassen sich Clipping Bereiche definieren oder gar Ungenauigkeiten des Skeletonstreams durch Smoothing ausgleichen. Die Spielwiese hier ist groß und bleibt an dieser Stelle den eifrigen Lesern mit entsprechendem Spieltrieb überlassen.

Eine Frage der Sichtweise

Bevor ich diesen Artikel abschließe möchte ich jedoch noch kurz auf den Near-Mode der Kinect for Windows eingehen, den ich schon das ein oder andere Mal in der Vergangenheit erwähnt hatte. Will man selbigen nutzen, so muss die Initialisierung im obigen Beispiel um den Aufruf der folgenden Methode erweitert werden:

   1: private void EnableNearMode()

   2: {

   3:     if (this.Kinect != null && this.Kinect.DepthStream != null && this.Kinect.SkeletonStream != null)

   4:     {

   5:         // Near Range für Depth aktivieren

   6:         this.Kinect.DepthStream.Range = DepthRange.Near;

   7:  

   8:         // Near-Mode Tracking aktivieren

   9:         this.Kinect.SkeletonStream.EnableTrackingInNearRange = true;

  10:  

  11:         // Von Default auf "Sitzend" umschalten

  12:         this.Kinect.SkeletonStream.TrackingMode = SkeletonTrackingMode.Seated;

  13:     }

  14: }

Dies führt dann auch tatsächlich dazu, dass ausschließlich die oberen Joints eines Skeletons getrackt werden.

Ausblick

Heute habe ich Ihnen die Grundlagen des Skeleton API geliefert. Selbige werde ich im nächsten Teil aufgreifen um das Thema Gesten ein wenig zu betrachten. Diese stellen meiner Meinung nach mit den zentralsten Aspekt dar, wenn es darum geht ob und wie weit man dieses kleine schwarze Gadget künftig in unserem Büroalltag vorfinden wird. Ich würde mich freuen wenn Sie auch dann wieder ein bisschen Bewegungsdrang verspüren.

Mit IlumniRoom in fremden Welten

Der ein oder andere Leser mag sich vielleicht noch an die diverse Episoden aus Serie Star Trek erinnern. Episoden in denen kurz vor dem Weltraumkoller stehende Crew-Mitglieder Urlaub auf dem Holodeck machten und hier in virtuelle und fremde Welten abtauchten. Wer hätte damals in den Zeiten von 66Mhz PC’s und Monitoren mit dem gefühlten Gewicht eines Kleinwagens gedacht, dass diese Fiktion so schnell in greifbare Nähe kommen kann? Ich für meinen Teil jedenfalls nicht und doch hat Microsoft kürzlich auf der CES das Projekt IlumniRoom vorgestellt.

Stellen Sie sich vor Sie sitzen im heimischen Wohnzimmer und Ihre Spielekonsole ist mit ein wenig Technik-Schnick-Schnack nicht mehr nur auf Ihren 50 Zoll LED Fernseher beschränkt, sondern sie nutzt vielmehr den Großtteil des Raumes um Sie in eine virtuelle Welt zu entführen. Wäre das nicht, um es mit Spock’s Worten auszudrücken, faszinierend?

Das offizielle Microsoft Video, dass laut Angaben ohne special effects ausgekommen ist, gibt hierzu jedenfalls einen ersten Geschmack auf die neue Techologie die derzeit noch im Status "proof-of-concept" weilt.

Basis hierfür ist die Kinect, welche die Geometrie des Raumes erfasst. Diese wird durch einen Beamer rund um das Display ergänzt und lässt somit die Grenzen zwischen der virtuellen und realen Welt verschwimmen.

Auch wenn die Idee des "Surround Video" nicht neu ist und bereits 2006 durch die BBC ein EU und weltweites Patent angestrebt wurde bleibt es spannend ob und in wie weit sich unser heimisches Wohnzimmer künftig verändert. Näheres dazu wird man wohl erst im Frühjahr auf der ACM CHI 2013 in Paris erfahren. Für diesen Event hat Microsoft Research weitere Informationen angekündigt.

Microsoft kündigt Kinect Roadmap für den Herbst an

Als ich im April in "Quo Vadis Kinect?" noch über die Zukunft der Kinect im Kontext von Businessanwendungen sinnierte erntete ich im besten Fall ein wenig Interesse kombiniert mit einem leichten Schmunzeln von meinem Gegenüber. Seither sind einige Monate vergangen und nicht nur das Interesse in meinem Umfeld wurde größer -nein- Microsoft hat vor einigen Tagen im offiziellen Kinect for Windows Blog die Roadmap für den Herbst vorgestellt und nebenbei das nächste Runtime bzw. SDK Release für Oktober angekündigt.

Quelle: Kinect for Windows Blog – Roadmap

So kann man die Kinect für Windows künftig in sieben weiteren Märkten käuflich erwerben. Die jedoch wesentlich wichtigere Information ist meiner Meinung nach die Ankündigung dass künftig Windows 8 Desktop Applikationen, Microsoft .NET 4.5 und Microsoft Visual Studio 2012 unterstützt werden.

Hält man sich dies und die folgenden Punkte vor Augen…

  • Das USB Kabel der Windows Variante wurde sehr kurz gehalten und der Controller
    muss deshalb nah am Rechner/Laptop stehen.
  • Die Firmware wurde explizit verbessert um nahe Objekte zu erkennen.
  • Das SDK wurde erweitert um die Entwicklung zu vereinfachen und den Programmierern
    mehr Flexibilität für eigene Ideen zu geben.
  • Microsoft hat sich darauf commited das Thema NUI (Natural User Interface) über mehr als ein Jahrzehnt hinweg zu verfolgen.

… so kann das eigentlich nur eines heißen: Microsoft setzt auf künftige Innovationen seitens des Marktes die den Controller in unser tägliches Leben und somit auch in unsere Arbeitswelt integrieren. Business-Szenarien, wie ich Sie mitunter im ersten Teil meiner Blogreihe vorgestellt habe, werden somit immer wahrscheinlicher.

Sicherlich werden die bereits vorhandenen und bewährten Bedienkonzepte nicht verdrängt, ergänzen und bereichern kann die Kinect diese aber durchaus. Ob sich diese dann auch wirklich dauerhaft durchsetzen wird dann auch zum großen Teil von der Akzeptanz der Marktes abhängen.

Bewegungsdrang (Teil 4): Die tiefen Weiten des Kinect Universums

Die Leser unter Ihnen, die, so wie ich, als Kind der 80er aufgewachsen sind, werden sich sicherlich erinnern. Damals rannte ein blaues pelziges Etwas namens Grobi hektisch durch das Programm der Sesamstraße und versuchte uns die Begriffe "Nah & Fern" zu erklären. Heute, also gute 30 Jahre später, habe ich das Vergnügen diese Begriffe noch mal neu für mich zu entdecken. Und wie damals sitze ich wieder gebannt vor dem Bildschirm und erfreue mich der Dinge. Kinect sei Dank!

Sie können sich sicherlich denken worum es geht. Während wir uns im letzten Artikel Über die Kinect im Bilde noch mit Oberflächlichkeiten begnügen mussten, gehen wir heute einen Schritt weiter. Wir verpassen einer Anwendung den nötigen Tiefgang indem wir uns dieses Mal den Stream des Tiefensensors zu nutze machen.

Eine Sache des Blickwinkels

Im ersten Teil dieser Serie hatte ich geschrieben, dass der maßgebliche Unterschied zwischen der Kinect für Windows verglichen mit der Xbox 360 Variante im Tiefensensor liegt. Heute möchte ich dies ein wenig besser spezifizieren, denn Microsoft hat hier nicht – wie man glauben könnte – einen verbesserten Sensor verbaut. Nein, stattdessen wurde eine neue Firmware entwickelt die besser mit den Lichtverhältnissen bezogen auf nahe Objekte umgehen kann.

Dies hat zur Folge dass die Kinect je nach Aufgabe im Default Mode und im Near Mode betrieben werden kann. Auf das Blickfeld (field of view), oder präziser gesagt auf die Distanz in der Objekte erkannt werden, hat das folgenden Einfluss:

Quelle: Kinect for Windows Blog, Near Mode – What t is (and isn’t)

Wie man also sieht gibt es, trotz der räumlichen Einschränkungen die durch die Kinect gegeben sind, genug Freiraum um ein wenig Kreativität in Sachen Interaktion ausleben zu können.

Alles grau in grau

So, nach all den einleitenden Worten raus aus der grauen Theorie und rein in die graue Praxis. Wir wollen wie auch im letzten Teil dieser Blogserie eine einfache WPF Applikation erstellen in der unser Antlitz erstrahlt. Dieses mal wird selbiges allerdings nicht ganz so farbgewaltig sein wie zuletzt.

Nach dem erklärten Schema erstellen wir also zuerst eine WPF Applikation mit einem klangvollen Namen, platzieren ein Image Control, hängen uns an den Window_Loaded bzw. Window_Closed Event und fügen noch ein wenig Kinect Voodoo als letzte Zutat hinzu um zu folgendem Ergebnis zu kommen:

   1: <Window x:Class="DepthSensorFundamentals.MainWindow"

   2:         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   3:         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   4:         Title="MainWindow" Height="540" Width="640" MinWidth="640" MinHeight="480" 

   5:         Loaded="Window_Loaded" Closed="Window_Closed">

   6:     <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">

   7:         <Image Name="ImageDepth" Width="640" Height="480" MouseUp="ImageDepth_MouseUp" MouseMove="ImageDepth_MouseMove" />

   8:     </StackPanel>

   9: </Window>

   1: /// <summary>

   2: /// Interaction logic for MainWindow.xaml

   3: /// </summary>

   4: public partial class MainWindow : Window

   5: {

   6:     /// <summary>

   7:     /// Kinect Sensor

   8:     /// </summary>

   9:     public KinectSensor Kinect { get; set; }

  10:  

  11:     /// <summary>

  12:     /// Die Bitmap die in der Anwendung angezeigt wird

  13:     /// </summary>

  14:     public WriteableBitmap DepthBitmap { get; set; }

  15:  

  16:     /// <summary>

  17:     /// Pixel Array welches ein Image des Streams enthält

  18:     /// </summary>

  19:     public short[] PixelData { get; set; }

  20:  

  21:     public MainWindow()

  22:     {

  23:         InitializeComponent();

  24:     }

  25:  

  26:     private void Window_Loaded(object sender, RoutedEventArgs e)

  27:     {

  28:         // Generell ist es möglich mehr als einen Kinect Sensor an einem Rechner zu betreiben. Für

  29:         // unsere Zwecke reicht es deshalb aus den ersten verfügbaren Sensor mit Status Connected 

  30:         // zu ermitteln.

  31:         this.Kinect = KinectSensor.KinectSensors.FirstOrDefault(ks => ks.Status == KinectStatus.Connected);

  32:  

  33:         if (this.Kinect != null)

  34:         {

  35:             try

  36:             {

  37:                 // Initialisieren der DepthStreams

  38:                 InitializeDepthStream();

  39:  

  40:                 // Starten des Sensors

  41:                 this.Kinect.Start();

  42:             }

  43:             catch (IOException)

  44:             {

  45:                 this.Kinect = null;

  46:             }

  47:         }

  48:     }

  49:  

  50:     private void Window_Closed(object sender, EventArgs e)

  51:     {

  52:         if (this.Kinect != null)

  53:         {

  54:             // Stoppen des Sensors

  55:             this.Kinect.Stop();

  56:         }

  57:     }

  58:  

  59:     private void InitializeDepthStream()

  60:     {

  61:         // Fenstertitel dahingehend ändern das der aktuelle Modus angezeigt wird

  62:         this.Title = this.Kinect.DepthStream.Range.ToString();

  63:  

  64:         // Der jeweilige Stream muß für die Verarbeitung enabled werden. Wahlweise können auch

  65:         // die Formate Resolution320x240Fps30 sowie Resolution80x60Fps30 verarbeitet werden

  66:         // YuvResolution640x480Fps15 verarbeitet werden. Sie können Enable auch ohne ImageFormat

  67:         // aufrufen dann wird den das unten angegebene Format ist per defualt gesetzt.

  68:         this.Kinect.DepthStream.Enable(DepthImageFormat.Resolution640x480Fps30);

  69:  

  70:         // Zwar könnte man auch eine normale Bitmap nehmen aber die WriteableBitmap eignet sich

  71:         // durch die Benutzun von internen Puffern besser zum rendern von Frames. Da wir den Depth

  72:         // Stream nutzen wir das PixelFormat Gray16. Dieses besagt dass 16 Pixel pro Tiefeninformation

  73:         // genutzt werden, was insgesamt 65536 Schattierungen von Grau bedeutet. Alternativ können 

  74:         // Sie natürlich auch andere Formate nutzen wie z.B: BGR32, allerdings sollten Sie vorher 

  75:         // darüber nachdenken wie sinnvoll das für Ihren Fall ist. Wenn Sie dies tun, dann sollten

  76:         // Sie allerdings daran denken der Pixel Array entsprechend zu dimensionieren. Bei BGR32 

  77:         // wäre dies z.B. byte[] pixelData = new byte[frame.PixelDataLength*sizeof(int)]  weil das

  78:         // BGR32 Format ja 32 Bit anstatt 16 Bit ist. Hinzu kommt dass man den Intensitätsgrad dann

  79:         // immer manuell für Rot, Grün und Blau setzen muss.

  80:         this.DepthBitmap = new WriteableBitmap(this.Kinect.DepthStream.FrameWidth, this.Kinect.DepthStream.FrameHeight,

  81:             96.0, 96.0, PixelFormats.Gray16, null);

  82:  

  83:         // Die Source des Images in der Anwendung ist natürlich die vorher definierte WriteableBitmap

  84:         this.ImageDepth.Source = this.DepthBitmap;

  85:  

  86:         // Last-but-not-least sorgen wir dafür dass die FrameReady Events in der Methode 

  87:         // Kinect_DepthFrameReady verarbeitet werden können

  88:         this.Kinect.DepthFrameReady += this.Kinect_DepthFrameReady;

  89:     }

  90:  

  91:     private void Kinect_DepthFrameReady(object sender, DepthImageFrameReadyEventArgs e)

  92:     {

  93:         // Der DepthImageFrame stellt den Container für die Daten des Sensors

  94:         using (DepthImageFrame frame = e.OpenDepthImageFrame())

  95:         {

  96:             if (frame != null)

  97:             {

  98:                 // Der Byte Array der vom Tiefensensor geliefert wird ist Array of short wobei

  99:                 // jeder Wert des Arrays zusammengesetzt ist aus Tiefeninformation und PlayerIndex

 100:                 PixelData = new short[frame.PixelDataLength];

 101:  

 102:                 // Übername der Daten aus dem Frame

 103:                 frame.CopyPixelDataTo(PixelData);

 104:  

 105:                 // Die WriteableBitmap benötigt beim rendern die Angabe welcher Bereich aktualisiert werden

 106:                 // soll. Hier benutzen wir die gesamte Fläche der Bitmap

 107:                 Int32Rect depthBitmapRect = new Int32Rect(0, 0, this.Kinect.DepthStream.FrameWidth, this.Kinect.DepthStream.FrameHeight);

 108:  

 109:                 // Die Angabe wieviele Pixel jeweils auf ein mal upgedated werden sollen. Mit der vorliegenden

 110:                 // Definition geschieht dies also Zeilenweise

 111:                 int depthBitmapStride = this.Kinect.DepthStream.FrameWidth * this.Kinect.DepthStream.FrameBytesPerPixel;

 112:  

 113:                 // Erstellen der neuen Bitmap

 114:                 this.DepthBitmap.WritePixels(depthBitmapRect, PixelData, depthBitmapStride, 0);

 115:             }

 116:         }

 117:     }

 118: }

So weit, so gut, was aber noch fehlt ist die Initialisierung sowie die Verarbeitung des Tiefensensor-Streams. Bevor ich jedoch dazu komme, möchte ich Ihnen noch den grundlegendsten Unterschied zum RGB Stream aufzeigen. Jedes DepthFrame wird nämlich durch ein short Array repräsentiert bei dem jedes Element eine einzelne Tiefeninformation enthält. Und da die 16 Bit mehr als genug Platz haben die Tiefe zu speichern, ist in jedem Element noch zusätzlich der sogenannte Playerindex zu finden (auf den ich ein anderes mal eingehe). Dies sieht dann in der Praxis wie folgt aus:

Mit diesen Informationen im Kopf und mit ein bisschen Kinect API Know-How können wir schließlich unsere Applikation vollenden. Und auch wenn wir damit sicherlich nicht den Olymp der Kinect Applikationen erklimmen werden, klar ist das wir mit recht einfachen Mitteln sehr schnell ans Ziel gelangen. Und weil dies der Fall ist fügen wir unserer Applikation noch ein Textfeld unterhalb des Image ein in der wir noch gerne den Abstand des Punktes in Millimetern zum Sensor ausgeben lassen wollen. Dazu fügen wir folgenden Code für den MouseMove-Event hinzu:

   1: private void ImageDepth_MouseMove(object sender, MouseEventArgs e)

   2: {

   3:     // Position innerhalb des Image ermitteln

   4:     Point point = e.GetPosition(ImageDepth);

   5:     

   6:     // Index des Pixels berechnen

   7:     int pixelIndex = (int)(point.X + ((int)point.Y * this.DepthBitmap.Width));

   8:  

   9:     // Tiefeninformation extrahieren (letzte drei Bits im short Wert sind ja der Player index

  10:     int depth = this.PixelData[pixelIndex] >> DepthImageFrame.PlayerIndexBitmaskWidth;

  11:  

  12:     // Player index extrahieren

  13:     int playerIndex = this.PixelData[pixelIndex] & DepthImageFrame.PlayerIndexBitmask;

  14:  

  15:     TextDepthInfo.Text = string.Format("Depth {0} mm - Player {1} ", depth, playerIndex);

  16: }

Aus der Nähe betrachtet

Haben Sie die Anwendung schon einmal gestartet? Nun … wenn Sie selbst stolzer Besitzer einer Kinect für Windows sind, dann könnte das Fenster das Ihren Bildschirm ziert in etwa so aussehen:

Da wir oben keine konkrete Initialisierung vorgenommen haben arbeitet die Kinect hier gerade im Default Mode. Stellt sich die Frage wie wir nun in den Near Mode wechseln und was für Auswirkungen das hat. Die Antwort ist denkbar einfach, aber bevor ich Sie Ihnen gebe sollten wir das WPF Fenster noch schnell erweitern. Der Einfachheit halber ergänzen wir die Anwendung einfach um ein Mouseup-Event in dem wir folgende Zeilen hinzufügen:

   1: private void ImageDepth_MouseUp(object sender, MouseButtonEventArgs e)

   2: {

   3:     if (this.Kinect != null)

   4:     {

   5:         // Zu beachten ist dass dieser Code eine InvalidOperationException werfen würde

   6:         // wenn er mit einer Xbox 360 Kinect ausgeführt werden würde 

   7:         if (this.Kinect.DepthStream.Range == DepthRange.Default)

   8:         {

   9:             this.Kinect.DepthStream.Range = DepthRange.Near;

  10:         }

  11:         else

  12:         {

  13:             this.Kinect.DepthStream.Range = DepthRange.Default;

  14:         }

  15:  

  16:         this.Title = this.Kinect.DepthStream.Range.ToString();

  17:     }

  18: }

Klicken wir nun zur Laufzeit auf das Image dann switchen wir zwischen Default- und Near-Mode hin und her. Optisch lässt sich die Veränderung hier sehr gut erkennen:.

Der Vollständigkeit halber finden sie den kompletten Source-Code hier.

Ausblick

Aus dem heutigen Artikel haben Sie wahrscheinlich zwei Dinge mitgenommen. Einerseits wie man den Zugriff auf die Daten vom Tiefensensor realisiert und andererseits, dass die guten alten Lehrstunden der Sesamstraße immer noch gut genug sind den Artikel eines Blogs einzuleiten. Ich hoffe Ihnen hat der Artikel genauso viel Spaß gemacht wie mir und ich würde mich freuen wenn Sie beim nächsten Mal wieder ein wenig Bewegungsdrang verspüren. Dann wird Ihnen die Kinect unter die Haut gehen.

Bewegungsdrang (Teil 3): Über die Kinect im Bilde

Ein verregneter Sonntag und im Fernsehen laufen zum x-ten mal Filme die ich schon zu Jugendzeiten in die Kategorie alte Schinken eingeordnet habe. Was also spricht dagegen einen eigenen kleinen Film zu machen? Schließlich steht da ein neues Spielzeug auf dem Tisch, welches mich im Stile von "Nummer 5 lebt!" mitleidig anschaut und darauf wartet in Betrieb genommen zu werden.

Gesagt, getan! Und so gibt ein kleines grünes Licht schließlich den Startschuss für den nächsten Flurfunk-Artikel, der sich wie in meinem letzten Beitrag "Hello (Kinect) World!" angekündigt ganz und gar um die bewegten Bilder dreht. Selbiges scheint mir wesentlich spannender als der Film der im Hintergrund läuft und dessen Dialoge mein Unterbewusstsein wahrscheinlich schon auswendig kennt. Ich hoffe Sie verspüren ähnlichen Bewegungsdrang!

Stromaufwärts

Als ich Ihnen vor ein paar Wochen den Kinect Controller vorgestellt hatte erwähnte ich schon, dass sowohl RGB-Kamera als auch der Tiefensensor mit 30fps arbeiten. Die Frage aber ist, wie gelangen diese Daten bzw. Bilder nun in die eigene Anwendung? Und wie Sie sich sicher vorstellen können ist die Antwort denkbar einfach und lautet Datenströme oder zu neudeutsch Streams.

Die NUI Library (Natural User Interface) ist an dieser Stelle vereinfacht gesagt nichts anderes als die Runtime der Kinect. Sie umfasst somit alles was für die Nutzung notwendig ist, angefangen vom Treiber, über den Kernel, bis hin zu Bibliotheken die in Visual Studio referenziert werden können. Anschaulich wird das Ganze in dem folgenden SDK Architektur Überblick:

Wie wir sehen sind die Daten also bereits durch einige Schichten "geströmt" bevor Sie in einer Anwendung über das NUI API das Licht der Welt erblicken. Selbiges möchte ich Ihnen in den nächsten Abschnitten für die RGB-Kamera veranschaulichen.

Lauter bunte Bilder

Schluss mit der Theorie, rein in die Praxis! Nachdem wir das SDK ja bereits installiert haben, starten wir Visual Studio und erstellen uns einfach eine neue WPF Applikation, dessen Name ich einfach mal Ihrer eigenen Kreativität überlasse. Ist dies vollbracht brauchen wir als erstes Zugriff auf das NUI API und das lösen wir durch einfaches einbinden der Kinect Library, die im Verzeichnis “C:Program FilesMicrosoft SDKsKinectv1.5Assemblies” zu finden ist.

Mit dem hochgesteckten Ziel ein bewegtes Bild zu sehen und mit dem Wissen dass die Kinect uns Frames durch Streams zur Verfügung stellt, platzieren wir in unserem WPF Hauptfenster wohlweislich schon mal ein Image Control. Ihr XAML sollte dann in etwa so aussehen:

   1: <Window x:Class="RGBCameraFundamentals.MainWindow"

   2:         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   3:         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

   4:         Title="MainWindow" Height="480" Width="640" MinWidth="640" 

   5:         MinHeight="480" Loaded="Window_Loaded" Closed="Window_Closed">

   6:     <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">

   7:         <Image Name="ImageRGB" Width="640" Height="480"/>

   8:     </StackPanel>

   9: </Window>

Bevor jedoch unser Anlitz das soeben erstellte Control ziert, gilt es erst noch die ein oder andere Zeile Code zu implementieren. In unserem Fall wäre es also durchaus sinnvoll sich an das Window_Loaded bzw. Window_Closed Event zu hängen um die Frame Capture Engine der Kinect parallel zur Anwendung zu starten bzw. zu beenden.

   1: using System;

   2: using System.Linq;

   3: using System.Windows;

   4: using Microsoft.Kinect;

   5: using System.IO;

   6: using System.Windows.Media.Imaging;

   7: using System.Windows.Media;

   8:  

   9: namespace RGBCameraFundamentals

  10: {

  11:     /// <summary>

  12:     /// Interaction logic for MainWindow.xaml

  13:     /// </summary>

  14:     public partial class MainWindow : Window

  15:     {

  16:         /// <summary>

  17:         /// Kinect Sensor

  18:         /// </summary>

  19:         public KinectSensor Kinect { get; set; }

  20:  

  21:         public MainWindow()

  22:         {

  23:             InitializeComponent();

  24:         }

  25:  

  26:         private void Window_Loaded(object sender, RoutedEventArgs e)

  27:         {

  28:             // Generell ist es möglich mehr als einen Kinect Sensor 

  29:             // an einem Rechner zu betreiben. Für unsere Zwecke reicht 

  30:             // es deshalb aus den ersten verfügbaren Sensor mit Status 

  31:             // Connected zu ermitteln.

  32:             this.Kinect = KinectSensor.KinectSensors

  33:                 .FirstOrDefault(ks => ks.Status 

  34:                     == KinectStatus.Connected);

  35:  

  36:             if (this.Kinect != null)

  37:             {

  38:                 try

  39:                 {

  40:                     // Starten des Sensors

  41:                     this.Kinect.Start();

  42:                 }

  43:                 catch (IOException)

  44:                 {

  45:                     this.Kinect = null;

  46:                 }

  47:             }

  48:         }

  49:  

  50:         private void Window_Closed(object sender, EventArgs e)

  51:         {

  52:             if (this.Kinect != null)

  53:             {

  54:                 // Stoppen des Sensors

  55:                 this.Kinect.Stop();

  56:             }

  57:         }

  58:     }

  59: }

So weit, so gut, zwar haben wir die Kinect jetzt eingebunden, aber unserem Ziel sind wir nicht sonderlich viel näher gekommen. Zeit für ein bisschen Initialisierung:

   1: private void Window_Loaded(object sender, RoutedEventArgs e)

   2: {

   3:     ....

   4:  

   5:             try

   6:             {

   7:                 // Initialisieren der ColorStreams

   8:                 InitializeColorStream();

   9:  

  10:                 // Starten des Sensors

  11:                 this.Kinect.Start();

  12:             }

  13:             catch (IOException)

  14:             {

  15:                 this.Kinect = null;

  16:             }

  17:  

  18:     ....

  19: }

  20:  

  21: private void InitializeColorStream()

  22: {

  23:     // Der jeweilige Stream muß für die Verarbeitung enabled werden. 

  24:     // Wahlweise können auch die Formate RgbResolution1280x960Fps12, 

  25:     // RawYuvResolution640x480Fps15 und YuvResolution640x480Fps15 

  26:     // verarbeitet werden

  27:     this.Kinect.ColorStream.Enable

  28:         (ColorImageFormat.RgbResolution640x480Fps30);

  29:  

  30:     // Zwar könnte man auch eine normale Bitmap nehmen aber die 

  31:     // WriteableBitmap eignet sich durch die Benutzun von internen 

  32:     // Puffern besser zum rendern von Frames. Da wir einen RGB Stream

  33:     // benutzen ist das PixelFormat Bgr32 was einem Standard RGB Format 

  34:     // mit 32 Bits pro Pixel entspricht. Falls Sie eine eigene Palette 

  35:     // benutzen können Sie desweiteren den null Parameter ersetzen, in 

  36:     // der Regel ist dies aber nicht nötig. Und um es nicht zu vergessen 

  37:     // Windows samt WPF nutzen per Default 96dpi, so gesehen sollten 

  38:     // Ihnen die zwei bisher nicht genannten Parameter durchaus klar 

  39:     // sein.

  40:     this.ColorBitmap = new WriteableBitmap(

  41:         this.Kinect.ColorStream.FrameWidth, 

  42:         this.Kinect.ColorStream.FrameHeight,

  43:         96.0, 96.0, PixelFormats.Bgr32, null);

  44:  

  45:     // Die Source des Images in der Anwendung ist natürlich die vorher 

  46:     // definierte WriteableBitmap

  47:     this.ImageRGB.Source = this.ColorBitmap;

  48: }

Die Frage die sich jetzt noch stellt ist die, wie den nun der Zugriff auf die von der Kinect erstellten Frames aus dem ColorStream realisiert wird? Hierfür gibt es generell zwei Möglichkeiten. Einerseits lassen sich die Daten abrufen (Poll-Model) und andererseits können Sie die FrameReady-Events nutzen die seitens der Frame Capture Engine gefeuert werden (Event-Model).

Ich für meinen Teil bevorzuge letztere Variante, denn warum soll ich mich in diesem Kontext mit Threads, Timern oder gar Endlosschleifen herumschlagen wenn ich mich mit einfachsten Mitteln an einen bereits vorhandenen Eventhandler hängen kann? Sollten Sie das anders sehen oder gar zur seltenen Spezies der XNA Entwickler gehören (die Events pollen müssen), dann dürfen Sie gerne einen Blick auf die Methode OpenNextFrame der Klasse ColorImageStream werfen.

   1: private void InitializeColorStream()

   2: {

   3:     ....

   4:  

   5:     // Last-but-not-least sorgen wir dafür dass die FrameReady Events 

   6:     // in der Methode Kinect_ColorFrameReady verarbeitet werden können

   7:     this.Kinect.ColorFrameReady += this.Kinect_ColorFrameReady;

   8: }

   9:  

  10: private void Kinect_ColorFrameReady(object sender, 

  11:     ColorImageFrameReadyEventArgs e)

  12: {

  13:     // Der ColorImageFrame stellt den Container für die Daten des Sensors

  14:     using (ColorImageFrame frame = e.OpenColorImageFrame())

  15:     {

  16:         if (frame != null)

  17:         {

  18:             // Ersten des für die Erstellung des Image nötigen Byte Arrays. 

  19:             // Die Größe des Arrays von 1228800 Byte errechnet sich durch 

  20:             // 640x480x4 (letzteres siehe BGR32)

  21:             byte[] pixelData = new byte[frame.PixelDataLength];

  22:  

  23:             // Übername der Daten aus dem Frame

  24:             frame.CopyPixelDataTo(pixelData);

  25:  

  26:             // Die WriteableBitmap benötigt beim rendern die Angabe welcher 

  27:             // Bereich aktualisiert werden soll. Hier benutzen wir die gesamte 

  28:             //Fläche der Bitmap

  29:             Int32Rect colorBitmapRect = 

  30:                 new Int32Rect(0, 0, this.Kinect.ColorStream.FrameWidth, 

  31:                 this.Kinect.ColorStream.FrameHeight);

  32:  

  33:             // Die Angabe wieviele Pixel jeweils auf ein mal upgedated werden 

  34:             // sollen. Mit der vorliegenden Definition geschieht dies also 

  35:             //Zeilenweise

  36:             int colorBitmapStride = this.Kinect.ColorStream.FrameWidth * 

  37:                 this.Kinect.ColorStream.FrameBytesPerPixel;

  38:  

  39:             // Erstellen der neuen Bitmap

  40:             this.ColorBitmap.WritePixels(colorBitmapRect, pixelData, colorBitmapStride, 0);

  41:         }

  42:     }

  43: }

So, nun ist der große Moment gekommen in dem Sie Ihrer ersten Kinect Applikation eine Chance geben dürfen. Starten Sie die Anwendung und empfinden Sie Freude beim Anblick Ihres Gesichts.

Das kleine grüne Männchen in mir

Bevor ich das Projekt RGB Kamera vorerst schließe, will ich noch einen kurzen und einfachen Exkurs Richtung Imagemanipulation machen. Sicherlich können Sie mit GDI+ die Images vor der Anzeige manipulieren. Worauf ich aber in meinem Beispiel hinaus will, ist die einfache Manipulation des Farbschemas durch Anpassung der Frames bzw. des damit verbundenen Byte-Arrays.

Passen wir unsere Methode Kinect_ColorFrameReady doch einfach mal wie folgt an:

   1: private void Kinect_ColorFrameReady(

   2:     object sender, ColorImageFrameReadyEventArgs e)

   3: {

   4:             ....

   5:  

   6:             // Übername der Daten aus dem Frame

   7:             frame.CopyPixelDataTo(pixelData);

   8:  

   9:             // Wie BGR schon sagt sind die vier Byte pro Pixel Blau, Grün 

  10:             // und Rot gefolgt von einer null die in anderen Formaten wie

  11:             // Bgra32, Pbgra32 den Alpha Wert darstellt. In unserem Fall 

  12:             // reicht es also die Blau und Rot Werte auf 0 zu setzen

  13:             for (int i = 0; i < pixelData.Length; i += frame.BytesPerPixel)

  14:             {

  15:                 pixelData[i] = 0x00;

  16:                 pixelData[i + 2] = 0x00;

  17:             }

  18:  

  19:             ....

  20: }

Wenn man die Anwendung nun startet sieht man sich, sofern man das so ausdrücken kann, plötzlich in einem ganz neuen Licht.

Der Vollständigkeit halber finden sie den kompletten Source-Code hier.

Ausblick

Der aufmerksame Leser mag gemerkt haben, dass ich ursprünglich angekündigt hatte auch auf den Tiefensensor einzugehen. Asche auf mein Haupt, aber letztlich sind die Themen einfach zu umfangreich (und zu spannend). Somit vertröste ich Sie an dieser Stelle auf den nächsten Artikel in dem ich der Entwicklung den nötigen Tiefgang verpasse. Ich würde mich freuen Sie auch dann wieder die Lust auf ein wenig Bewegungsdrang verspüren.

Bewegungsdrang (Teil 2): Hello (Kinect) World!

Es ist Donnerstag Vormittag und mein Smartphone vibriert lautlos in der rechten Innentasche meines Jackets. Dem Display entnehme ich nur die Worte "Dein Packet ist da ;)". Das dezente Glückgefühl, welches mich beschleicht, führt zu einem leichten aber doch sichtbaren Grinsen in meinem Gesicht. Die Kinect für Windows ist da und gleichzeitig mit ihr auch der Tüftler und Bastler in mir.

Während ich mich in meinem ersten Artikel "Der Kinect Controller" noch auf Internetrecherche beschränken musste, bin ich nun in der komfortablen Lage erste Praxiserfahrungen zu sammeln. Was also bleibt mir übrig, als meinen Arbeitstag pünktlich zu beenden und schon mal vorweg einen langen Abend einzuplanen?

Software und Installation

Der Controller ist ausgepackt, alle Teile des Kartons liegen in der Ecke des Raumes verteilt und wer kennt ihn nicht, diesen tiefen Wunsch der einen Tekkie beim Anblick eines neuen USB Geräts direkt neben dem heimischen Rechner beschleicht? Tun Sie es nicht! Windows findet hierzu keine Treiber und nach der Installation sind Sie nur stolzer Besitzer eines etwa 200€ teurem generischen USB Hubs.

Sparen Sie sich also die Arbeit ein nicht erkanntes Gerät über den Gerätemanager zu entfernen und halten Sie sich an die Release Notes, die man als gestandener Entwickler gerne mal überliest. Und in denen heißt es "Make sure the Kinect sensor is not plugged into the USB port on the computer prior to running setup".

Eventuellen Komplikationen zum Trotz (und mitunter schweren Herzens) ist es also erst mal sinnvoll die folgenden Downloads zu tätigen:

Dabei will ich es auch vorerst belassen, denn primär geht es in dem vorliegenden Artikel um die Installation und Inbetriebnahme und nicht darum ein perfektes Toolset unser eigen nennen zu dürfen. Ist zumindest das SDK installiert kommt schließlich der große Moment, die Kinect gibt ein erstes Lebenszeichen von sich nachdem ein weiterer USB Port des Laptops seine Bestimmung findet.

Kurz noch einen Blick in den Geräte-Manager ob alles korrekt installiert ist

und schon können wir mit einem Klick auf den in der Gruppe Kinect for Windows SDK 1.5 befindlichen Developer Toolkit Browser nach der wohl wichtigsten Demo-Applikation suchen.

Startet man hier den Kinect Explorer so strahlt Ihnen schließlich (wie mir übrigens auch) das Antlitz eines völlig übermüdeten Entwicklers entgegen, mit dem die Leidenschaft zu später Stunde mal wieder durchgegangen ist.

Einen besonderen Hinweis gebe ich an dieser Stelle noch allen Lesern die ähnlich ungeduldig waren wie ich und das Developer Toolkit in der Version 1.5.0 vorab installiert haben. Ihre Kinect ist nicht kaputt, nein, die Entwickler haben sich nur den Spaß gemacht ein kleines Globalisierungs bzw. Cultureproblem einzubauen.

Dies hat den netten Nebeneffekt, dass bei der Konvertierung von Strings in Fließkommazahlen fast alle Beispielapplikationen abstürzen, weil selbige in unserem Sprachraum nun mal mit Komma anstatt Punkt getrennt werden. Stellen Sie in diesem Fall also einfach sicher, dass Sie das Update 1.5.1 vom Juni nachinstallieren.

Ausblick

Im nächsten Teil dieser Blogreihe werde ich auf die Verarbeitung der Streams eingehen. Sie werden dort lesen wie RGB Kamera und Tiefensensor in einer WPF Anwendung auf einfachste Art und Weise genutzt werden können.

Ich würde mich freuen, wenn Sie auch dann wieder die Lust verspüren mir bei meinem Bewegungsdrang zu folgen.

Bewegungsdrang (Teil 1): Der Kinect Controller

Die Begriffe ‘Touch’ bzw. ‘Multitouch’ sind derzeit in aller Munde und somit macht es für einen Entwickler durchaus Sinn sich ein wenig der Gestensteuerung zu widmen. Doch warum sollte man sich dabei auf ein paar wenige Zoll eines Tablet-Displays und ein paar Finger beschränken?

Halten wir uns doch einfach an das Motto "Think big" und nutzen ein paar Quadratmeter Eingabefläche samt unseres ganzen Körpers. Die Herausforderungen sind prinzipiell sehr ähnlich, wenn auch ungleich komplexer.

Die vorliegende Blogreihe "Bewegungsdrang" beschäftigt sich mit dem Microsoft Kinect Controller und der Entwicklung von Anwendungen auf Basis des dazugehörigen SDK’s.

Motivation

Gehören Sie auch zu der Sorte Entwickler, die sich gerade die Frage stellt warum man sich den nun auch noch mit "Gaming Hardware" im .NET Kontext beschäftigen sollte?

Seien Sie sich gewiss, auch ich habe das getan und die Antwort auf diese Frage ist durchaus vielfältig. Im Kern jedoch findet sich ein einziger Grund. Der Grund, dass die Kinect weitaus mehr Möglichkeiten bietet als sich als Jedi Krieger im heimischen Wohnzimmer zu beweisen.

So können Chirugen bereits bei Operationen unterstützt werden, es enstehen virtuelle Ankleidekabinen oder aber die Kinect wird zusammen mit einem Beamer zum einem überall einsetzbaren 3D Multitouch-Screen. Und das sind nur wenige Beispiele, die Ideen zahlreicher "Bastler" und Firmen geht hier durchaus weiter. Selbst Microsoft Research präsentierte vor ein paar Monaten im TechForum einen Einkaufswagen der sich selbst steuert.

Wer also sagt Ihnen, dass Sie mit dieser Technologie nicht früher oder später konfrontiert werden? Egal ob im Projekt, in Ihrer nächsten Präsentation oder durch den privaten Laptop, es ist durchaus möglich, dass Ihnen der Sensor früher oder später begegnet.

Historie

Aber blicken wir erst mal zurück. Seit nun mehr 1 1/2 Jahren kann man ihn kaufen, den Controller der vormals unter dem Projektnamen Natal von Microsoft und der Firma PrimeSense entwickelt wurde. Schon damals war klar, dass nicht nur die Xbox 360 Konsolenjunkies mit dieser hippen kostengünstigen Innovation beglückt werden – nein – Bill Gates höchstpersönlich kündigte ein gutes halbes Jahr vor Verkaufsstart an die Technologie auch auf Windows zu portieren.

Seither sind viele Monate ins Land gezogen. Monate in denen der ein oder andere findige Geek bereits den doch recht nützlichen USB Stecker am Ende des Kabels entdeckte. Und so kam es wie es kommen mußte, ein wahrer Krieg auf YouTube & Co. tobte darüber, wer den nun den originellsten Hack mit Libraries wie CLNUI, OpenKinect oder gar OpenNI implementiert.

Doch damit ist jetzt Schluß! Seit dem 1. Februar ist sie endlich erhältlich, die Kinect für Windows. Nun ist es für jeden Entwickler ganz offiziell und ohne Nebenwirkungen möglich, ein wenig Spieltrieb zu entwickeln … auch oder gerade wenn man aufgrund der tausenden Zeilen Line-Of-Business-Code vielleicht ein wenig eingerostet ist.

Hardware

Aber wie funktioniert es eigentlich, dieses kleine schicke schwarze Gadget, welches das obere Ende meines Flatscreens ziert und mir bisweilen das Gefühl gibt irgendwie unter Beobachtung zu stehen? Ein Blick auf die Hardware zeigt oberflächlich gesehen erst mal folgende Komponenten:

Will man aber ein wenig mehr wissen als einem Verpackung und Beipackzettel mit auf den Weg geben wird man schnell merken, dass man am Web nicht vorbei kommt. Und so präsentiert sich die Kinect nach ein wenig Recherche mit folgenden Details:

  • RGB Kamera: Die RGB Kamera liefert Bilder mit einer Auflösung von 1280×960 bei einer Bildwiederholfrequenz von 30fps. Damit lassen sich zwar keine High-Speed-Sportfilme machen, allerdings ist ein durchaus flüssiges Bild in einer eigenen Anwendung möglich.
  • Infrarot Projektor: Hier wird konstant ein spezielles Infrarot Lichtmuster ausgesendet dessen Wellenlänge so angepasst ist, das es gegenüber dem Umgebungslicht weitestgehend unempfindlich ist. Dieses Licht wiederum wird durch Objekte reflektiert und mit dem Tiefensensor empfangen.
  • Tiefensensor: Einer der maßgeblichen Unterschiede zur Xbox360 Variante der Kinect ist hier versteckt. Der Tiefensensor in Form eines CMOS Sensors empfängt die Reflektionen nämlich auch für Objekte die nur etwa 40cm entfernt sind. Heraus kommt dabei ein monochromes Bild das über Intensitätstiefe den Abstand wiedergibt. Wie bei der RGB Kamera auch, werden hier 30fps verarbeitet.
  • Mikrofon-Array: Hier sind ganze vier Mikrophone verbaut die zusammen mit einem 24-Bit Analog-Digital-Wandler und Kinect internen Echo- und Rauschunterdrückungs-Verfahren dafür sorgen, dass Audioquellen entsprechend lokalisiert und mit einer Abtastrate von 16kHz verarbeitet werden können.
  • Motorisierter Tilt: Der vertikale Blickfeldwinkel der Kinect liegt bei 43°. Je nachdem in welcher Höhe der Controller steht kann man selbigen um 27° nach oben bzw. unten neigen um die Objekte ins Kinect Sichtfeld zu bringen.

Aber genug der nackten technischen Daten die bei näherer Betrachtung eher in die für mich fremden und unendlichen Weiten der Elektrotechnik und Physik fallen. Denn primär bin ich Entwickler und als solcher geht es für mich zu Begin eines jeden Projekts erst mal darum "die Kiste zum Laufen zu bringen" – um es mal umgangssprachlich auszudrücken. Für all diejenigen unter Ihnen die sich dennoch berufen fühlen sei an dieser Stelle auf das US Patent US 2010/0020078 hingewiesen das sich die Methode des Structured-Light 3D Scannings zu Nutze macht.

Ausblick

Während ich Ihnen in diesem Artikel die Grundlagen geliefert habe, werde ich ab dem nächsten Teil langsam ans Eingemachte gehen. Sie werden dabei unter anderem Themen wie Streaming, Skeleton API, Voice Recognition und Face Tracking kennenlernen. Und wer weiß, vielleicht gehören auch Sie bald zum Kreise der Kinect-Koryphäen. Ganz so, wie diese beiden jungen Herren des Microsoft SQL Server Teams.

Quo vadis Kinect?

 
Es ist gar nicht so lange her, da haben iPhone und iPad eine neue Generation von Endgeräten geprägt, Endgeräte die mittlerweise kaum noch aus unserem Alltag wegzudenken sind. Mit Ihnen kamen Buzzwords wie ‘Swipen’, ‘Multi-Touch’ oder einfach nur ‘Apps’ in unseren Sprachschatz. Und spätestens seit der Windows 8 Consumer Preview ist klar, all das wird uns künftig in unserem täglich im Business begegnen.
 
Jeder der im Microsoft-Umfeld innovativ sein möchte beschäftigt sich derzeit mit diesen Themen. Aber das erstaunliche daran ist eigentlich, dass es auf dem Zeitstrahl der IT gemessen bereits ein "alter Hut" ist. Zumindest wenn man den Launch des ersten iPhone im Jahr 2007 als Referenzpunkt nimmt. Fünf Jahre sind seither vergangen und ich habe immer noch diese gewissen Erinnerungen im Kopf, Erinnerungen daran wie viele Blackberry Benutzer damals nur ein müdes Lächeln für die neuen Touchgeräte übrig hatten.

Letztens da erging es mir ähnlich. Da überkam mich das Gefühl nicht ernst genommen zu werden als ich den Begriff Kinect in den Mund nahm. Ein leicht verschmitztes Lächeln zeichnete sich im Gesicht meines Gegenübers ab und eine Frage füllte lautlos aber doch einnehmend den Raum. Kinect und Business, das passt doch nicht zusammen, oder?

Zugegeben, dass Kind in mir lebt noch und als selbstverstandener Digital Native fragt man sich halt nach den Monaten der Euphorie, in denen man wild zappelnd vor dem Flatscreen im Wohnzimmer weilte, was da sonst noch alles möglich ist. Mit welchen Anwendungen kann man sich noch lächerlich machen, wenn die Nachbarn von gegenüber mal wieder ins heimische Wohnzimmer blicken?

Zumindest Microsoft hat hier in einer offiziellen Werbebotschaft die ein oder andere Idee parat:

Eines ist klar, die Zahlen für Kinect Respektive die Entertainmentsparte von Microsoft stellen mittlerweile mit 4,25 Mrd USD im letzten Quartal einen respektablen Anteil am Gesamtumsatz des Riesen aus Redmond dar.

Das Potential ist somit gegeben und der Wunsch die Kinect im Businessmarkt zu etablieren auch. Das kombiniert mit der Tatsache das Innovationen durch Contests wie dem Kinect Accelerator 2012 oder gar durch Projekte von Microsoft Research wie z.B. dem Holodesk gefördert werden, zeigt die Wichtigkeit und in welche Richtung es gehen kann.

Die Zukunft läßt also einiges erwarten, gerade auch weil in Kürze beim erscheinenden Kinect SDK 1.5 das Zusammenspiel mit dem PC forciert wird.

Sie sehen dass nicht so? Hand auf’s Herz, haben Sie sich vor 15 Jahren mit einem Motorola ‘Knochen’ jemals vorstellen können über das gleiche Gerät Onlinebanking betreiben zu können oder es gar als vollständiges Navigationsgerät zu nutzen? Sicher nicht, oder?

In den folgenden Wochen bzw. Monaten werde ich das ‘Spielzeug’ Kinect in einer Reihe von Artikeln aufbohren. Ich werde anreißen wie der Sensor arbeitet, wie man dafür entwickelt, welche Möglichkeiten man hat und dabei immer mal wieder schauen was sich in Sachen Forschung und Markt tut.

Denn in einem bin ich mir sicher, Gesten und Sprachkontrolle werden unser Arbeitsumfeld nachhaltig verändern wenn auch die bewährten Geräte nach wie vor Anwendung finden. Vielleicht passiert dies nicht heute, morgen oder gar übermorgen, aber im Umfeld der IT ist eines sicher und das ist der stetige und mitunter schnelle Wandel.

Und auch hier werden wieder neue Buzzwords Einzug halten, dann ‘swipen’ wir mitunter in verstaubten Meetingräumen ganz ohne Tablet durch die neusten Umsatzzahlen, dann ‘voicen’ wir uns vielleicht durch die neusten Aktienkurse oder eine nette Stimme macht uns darauf aufmerksam dass wir ergonomisch gesehen mal wieder wie ein Schluck Wasser in der Kurve vor dem Rechner sitzen.

Meiner Meinung nach wird es in jedem Fall spannend.