Teil 3: Verarbeitung von Audiodaten mit Kinect

Kinect hat Ohren
Kommentare

Kinect leistet nicht nur im visuellen Bereich gute Dienste. Neben Kameras und Tiefensensoren hält sie auch vier Mikrofone bereit, die wir in diesem Artikel in die Pflicht nehmen wollen: Jetzt nutzen wir Kinect auch akustisch.

Windows Developer

Der Artikel „Kinect hat Ohren“ von Tam Hanna ist erstmalig erschienen im Windows Developer 6.2012

Der Kinect-Sensor von Microsoft hat uns in den letzten beiden Teilen der Artikelserie im visuellen Bereich gute Dienste geleistet: Das Gerät liefert Farb- und Tiefeninformationen zuverlässig und schnell. Kinect enthält neben den mittlerweile hinreichend bekannten Kameras und Tiefensensoren zusätzlich vier Mikrofone, die von einem in die Hardware integrierten Digitalen Signalprozessor (DSP) unterstützt werden. Dieser DSP erledigt diverse Audioverbesserungen (Rauschreduktion, Echoentfernung etc.) und sogar das Tracking der Position von Geräuschquellen selbsttätig und ohne jede Unterstützung durch die CPU des Host-Systems.

Artikelserie

  1. Erste Schritte mit Kinect
  2. Skeleton API und Interaktion
  3. Verarbeitung von Audiodaten mit Kinect
Recording 101

Die einfachste Form der Verarbeitung von Audiodaten mit Kinect ist das Orten der aktiven Tonquelle. Dabei erkennt Kinect Quellen, die „im Sichtfeld“ vor dem Sensor unterwegs sind. Hinter dem Sensor liegende Quellen können nicht zuverlässig geortet werden.

Das realisieren wir im Beispiel SusKinect6, das Sie auf der Heft-CD finden. Dabei handelt es sich wie in den meisten vorherigen Beispielen wieder um ein WPF-Programm, obwohl wir diesmal statt mit dem bisher verwendeten Namespace Microsoft.Research.Kinect.Nui nun mit Microsoft.Research.Kinect.Audio arbeiten. Logischerweise müssen wir die gesamte Kinect-Bibliothek (also den Namespace) Microsoft.Research.Kinect zu den References des Projekts hinzufügen. Wie immer, erledigen wir dies durch einen Rechtsklick auf den REFERENCES-Ordner des Projekts. Nach dem Erstellen des Projektskeletts und dem Hinzufügen der Referenz öffnen wir die Code-Behind-Datei des Formulars. Die Klasse enthält diesmal drei globale Variable:

public partial class MainWindow : Window
{
  KinectAudioSource myAudioSource;
  System.IO.Stream myStream;
  Thread t;  

Die Variable myAudioSource ist für die Kommunikation mit der Kinect-Engine zuständig. Wir verwenden sie, um den Datenstream myStream zu öffnen und werten ihn später in einem Thread aus. Die anderen beiden Variablen sind Standard-.net. Im Konstruktor aktivieren wir die Kinect-Engine, verbinden sie mit dem Stream und feuern den Thread ab, wie in Listing 1 zu sehen.

Listing 1

public MainWindow()
{
  InitializeComponent();

  try
  {
    myAudioSource = new KinectAudioSource();
    myAudioSource.SystemMode = SystemMode.OptibeamArrayOnly;
    myAudioSource.FeatureMode = true;
    myAudioSource.AutomaticGainControl = false;
    myAudioSource.MicArrayMode = MicArrayMode.MicArrayAdaptiveBeam;
    myStream = myAudioSource.Start();
    t = new Thread(PollSoundLocalization);
    t.Start();
    Closing += MainWindow_Closing;
  }
  catch
  {
    MessageBox.Show("Initialisation gescheitert");
    this.Close();
  }
}  

Die Initialisierung der Audio-Engine ist um einiges simpler als die Initialisierung der bis jetzt verwendeten NUI-Engine: Wir erstellen uns einfach eine Instanz von KinectAudioSource und können sofort mit der Parametrisierung beginnen (das SDK sucht sich das Kinect selbst aus).

SystemMode legt fest, in welchem Betriebsmodus das Mikrofon-Array von Kinect arbeitet. Microsoft verwendet in seinen Beispielen so gut wie immer OptibeamArrayOnly. Das bedeutet, dass Kinect zwar wie ein Mikrofon-Array arbeiten soll, die Echo Cancellation des DSP aber nicht aktiviert ist.

Parameter Nummer 2 ist in allen Beispielen immer true und wird in der beiliegenden Dokumentation nicht näher erklärt (lapidare Beschreibung: Application is in feature mode?). Der Parameter AutomaticGainControl legt fest, ob der DSP die Lautstärke der aufgenommenen Signale automatisch anheben und/oder absenken darf, um so für eine „einheitlichere“ Lautstärke zu sorgen. MicArrayAdaptiveBeam legt fest, dass der Kinect-Sensor das Beam Forming selbst erledigt. Wer will, kann hier auch noch Noise Cancelling aktivieren.

Nach dem Einstellen dieser nur für Audioexperten wirklich interessanten Parameter rufen wir die Methode start() auf. Diese liefert einen Stream zurück, der die Audiodaten enthält. Zusätzlich weist sie Kinect an, ab diesem Zeitpunkt Geräuschquellen in der Umgebung zu lokalisieren.

Zur Verarbeitung der Informationen über die Position der Geräuschquellen feuern wir danach einen Thread an – seine Routine sieht aus wie in Listing 2.

Listing 2

private void PollSoundLocalization()
{
  while (1 == 1)
  {
    if (myAudioSource.SoundSourcePositionConfidence >= 0.2)
    {
      double WinkelInRadiant = myAudioSource.SoundSourcePosition;

      Dispatcher.BeginInvoke(new Action(() =>
      {
        //Aufbauen
        DrawingVisual drawingVisual = new DrawingVisual();
        DrawingContext drawingContext = drawingVisual.RenderOpen();
        //Rendern
        Pen armPen = new System.Windows.Media.Pen(new SolidColorBrush(System.Windows.Media.Color.FromRgb(0, 0, 0)), 2);
        System.Windows.Rect aRect=new Rect();
        aRect.X = 120;
        aRect.Y = 1;
        aRect.Width = 80;
        aRect.Height = 20;
        drawingContext.DrawRectangle(new SolidColorBrush(System.Windows.Media.Color.FromRgb(0, 0, 60)),armPen, aRect);
        int xEnd = (int)(160 + 140 * Math.Cos(-WinkelInRadiant + Math.PI/2));
        int yEnd = (int)(160 + 140 * Math.Sin(-WinkelInRadiant + Math.PI / 2));
        drawingContext.DrawLine(armPen, new Point(160, 0), new Point(xEnd, yEnd));
        //Abbauen
        drawingContext.Close();
        RenderTargetBitmap myTarget = new RenderTargetBitmap(320, 240, 96, 96, PixelFormats.Pbgra32);
        myTarget.Render(drawingVisual);
        image1.Source = myTarget;
      }), DispatcherPriority.Normal);
    }
    Thread.Sleep(25);
  }
}  

Wie die meisten Thread-Methoden besteht auch die Funktion PollSoundLocalization() aus einer Endlosschleife, deren Durchlaufgeschwindigkeit durch einen Aufruf von Thread.Sleep beschränkt wird. Der eigentliche Code der Routine ist eher primitiv: Wir beschaffen uns den Winkel zur Tonquelle durch Aufruf von SoundSourcePosition und rendern ihn wie in SusKinect5 in ein Image-Element auf dem Formular. Das SDK liefert die Winkelwerte in der Einheit Radiant zurück. Dabei handelt es sich um eine Winkelmaßeinheit, die insbesondere im technischen Bereich weit verbreitet ist. In Radiant entspricht eine volle Umdrehung einem Wert von 2*Pi. Die eigentliche Berechnung der Endpunkte des Winkels erfolgt durch elementare Trigonometrie im Einheitskreis. Wer sich dafür näher interessiert, findet sowohl in Wikipedia als auch in der nächsten Ausgabe des Entwickler Magazins 5.2012 mehr Informationen.

Beim Abtragen der KinectAudioSource muss man beachten, dass das SDK im Hintergrund mit COM-Objekten arbeitet. Da die Abtragung von .net-Objekten auch nach der Programmlaufzeit stattfinden kann, käme es bei der Verwendung des Unloaded-Events unter Umständen zu Fehlern nach der Bauart „COM object that has been separated from its underlying RCW cannot be used“. Aus diesem Grund tragen wir die Objekte schon im Event Handler des Closing-Events ab. Nun treten derartige Fehler nicht mehr auf:

void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
      t.Abort();
      myAudioSource.Stop();
      myAudioSource.Dispose();
    }  

Damit haben wir das erste Beispiel geschafft. Führen wir das Programm jetzt aus, sehen wir wie in Abbildung 1 eine schematische Darstellung des Kinect-Sensors und einen Strahl, der auf die zuletzt erkannte Tonquelle zeigt.

In Tests des Autors sind sowohl Reaktionsgeschwindigkeit als auch Genauigkeit noch verbesserungswürdig. Wer will, kann die vom Sensor gelieferten Daten integrieren oder mit anderen mathematischen Prozessen verbessern.

Abb. 1: Kinect nimmt Kontakt zum Autor auf
Abb. 1: Kinect nimmt Kontakt zum Autor auf
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -