Aktivität in Bildbereichen feststellen in Processing

Hinzugefügt am von Steffen Fiedler
Video Aktivität erkennen
Speziell für das Interagieren mit der Kamera ist die oben vorgestellte Methode meist zu genau in ihrer Funktionsweise. Unsere Möglichkeiten der Bewegung vor der Kamera erlauben uns nur schwer spezielle Pixel zu beeinflussen. Um dieses Problem zu lösen nutzt dieses Beispiel Abstraktion als Grundlage für das Erfassen von Dynamik. Dabei wird das Bildraster in eine gröbere Repräsentanz unterteilt(VIDEO_COLS Spaltenanzahl, VIDEO_ROWS Zeilenanzahl). Für jedes dieser Segmente wird ein Durchschnittswert ermittelt, welcher die Aktivität repräsentiert. Die Anzahl der Spalten (VIDEO_COLS) und Zeilen (VIDEO_ROWS) des abstrahierten Rasters muss abhängig von der Auflösung des Videos gewählt werden. Bei der Teilung der Bildbreite durch die Spaltenzahl darf kein Rest übrig bleiben (ebenfalls bei der Bildhöhe). Anderenfalls wird Processing den Sketch wegen eines Fehlers nicht starten können. Im unteren Bild sind sechs Beispiele für mögliche Rastergrößen aufgeführt.
Video Aktivität erkennen Auflösung
Wie bereits erwähnt liegt der Unterschied dieses Beispiels im Vergleich zu Aktivität im Bild feststellen in der Abstraktion des Videobildes. Unterteilt in ein Raster wird die Aktivität (Pixelfarbwertänderungen) in Segmente zusammengefasst. Eine for-Schleife durchläuft alle Bildpunkte wenn ein neues Bild von der Kamera gelesen wurde. Jedes Pixel wird dabei mit Pixeln an der selben Position in älteren Bildern verglichen. Das Resultat dieses Vergleichens findet wie im vorherigen Beispiel statt. Im Anschluss legen wir den Wert im Array activity ab. Dieses beinhaltet ein Feld für jedes Rastersegment. Bei 4×3 Feldern besitzt es beispielsweise eine Länge von 12. Pro Pixel muss also die Frage nach der Segmentzugehörigkeit gestellt werden. Dies geschieht mit den folgenden zwei Zeilen aus dem Sketchcode:
int x = (int) ((i % cam.width) / pxPerCol);
int y = (int) ((i / cam.width) / pxPerRow);
Die Variablen x und y präsentieren dabei nicht die genau Position des Pixels im Videobild, sondern die Koordinate des Quadranten. Da das activity Array nach dem gleichen Prinzip wie das pixels Array bei Bildern angelegt ist, können wir im nächsten Schritt den Index herausfinden:
index = y * VIDEO_COLS + x;
Nach dem Auslesen des Videobildes kommt es zum Durchlaufen des activity Arrays. Dabei teilen wir die dort gesammelten Gesamtwerte durch die Anzahl der Pixel pro Segment um die Mittelwerte pro Rasterabschnitt zu erhalten. Mit dem nächsten draw-Durchlauf werden alle Felder das activity Array wieder auf 0 zurückgesetzt.

Der Sketch

import processing.video.*;

final int VIDEO_WIDTH  = 320;
final int VIDEO_HEIGHT = 240;
// Anzahl der Quadranten in der Breite
final int VIDEO_COLS   = 8;
// Anzahl der Quadranten in der Höhe
final int VIDEO_ROWS   = 6;

float[] activity = new float[VIDEO_COLS * VIDEO_ROWS];
float[] buffer1 = new float[VIDEO_WIDTH * VIDEO_HEIGHT];
float[] buffer2 = new float[buffer1.length];
float[] buffer3 = new float[buffer1.length];

Capture cam = null;

void setup () {
  size (VIDEO_WIDTH, VIDEO_HEIGHT);
  cam = new Capture (this, VIDEO_WIDTH, VIDEO_HEIGHT, 15);
  frameRate (15);
}

void draw () {
  
  if (cam.available ()) {
    
    cam.read ();
    
    int index;
    int pxPerCol = VIDEO_WIDTH / VIDEO_COLS;
    int pxPerRow = VIDEO_HEIGHT / VIDEO_ROWS;

    image (cam, 0, 0);
    
    // 'activity' array auf 0 setzen
    for (int i=0; i < activity.length; i++) {
      activity[i] = 0;
    }

    // Für jedes Pixel im Videoraster
    for (int i=0; i < cam.pixels.length; i++) {
      
      // x und y Position des Quadranten berechnen
      int x = (int) ((i % cam.width) / pxPerCol);
      int y = (int) ((i / cam.width) / pxPerRow);
      
      // Quadranten-Zugehörigkeit des Pixels herausfinden.
      // Später wichtig für das 'activity' array.
      index = y * VIDEO_COLS + x;

      // Farbe an der Position 'i' im Kamerabild
      color col = cam.pixels[i];
      // Die Summe aus allen drei Farbkanälen bilden
      float sum = red (col) + green (col) + blue (col);

      // Farbwertänderung des Pixels bezogen auf alle Bilder errechnen
      float deltaPixel = (buffer1[i] + buffer2[i] + buffer3[i]) / 3 - sum;

      if (deltaPixel < 0) {
        deltaPixel *= -1;
      }

      // Die Änderung auf den Gesamtwert des Quadranten addieren
      activity[index] += deltaPixel;

      // Verschiebe das 'Bildgedächnis' um einen Rutsch nach hinten.
      buffer3[i] = buffer2[i];
      buffer2[i] = buffer1[i];
      buffer1[i] = sum;
    }

    // Für jeden Quadranten
    for (int i=0; i < activity.length; i++) {
      
      // Durchschnittliche Farbwertänderung berechnen.
      // Mit dem Teilen der Summe durch die Anzahl der Pixel
      activity[i] /= pxPerCol* pxPerRow;
      
      // Quadrant in das Sketchfenster zeichnen
      stroke (255, 20);
      fill (0, 255, 230, activity[i]);
      rect ((i % VIDEO_COLS) * pxPerCol, (i / VIDEO_COLS) * pxPerRow, pxPerCol, pxPerRow);
    }
  }
}