Video in ProcessingEinführung in den Umgang mit Video-Daten in Processing
Diese Lesson führt den Umgang mit Live-Video in Processing ein. Nach einer grundlegenden Erläuterung zur Verwendung von Kamera-Videomaterial wird der kreative Umgang mit Videostreams über die Programmierung eigener Filter und einfacher Analyse-Methodiken vermittelt. Dabei spielen sowohl visuelle Effekte als auch das Verfolgen markanter Bildbereiche und das Bestimmen von aktiven Zonen im Kamerabild eine Rolle.
Kamera Input
- Capture ist der Datentyp zum Ablegen von Bewegtbildern die von angeschlossenen Aufnahmegeräten, wie z.B. Webcams, kommen. Mit new Capture (this, BREITE, HÖHE); kann auf diese zugegriffen werden. Optional kann der Gerätename und die Anzahl der Einzelbilder von der Kamera angegeben werden. Wenn kein Gerät namentlich aufgeführt wird, selektiert Processing das letzte aus der mit Capture.list() erfragbaren Liste mit verfügbaren Geräten. Die Anzahl der Einzelbilder pro Sekunde kann im weiteren Verlauf mit der Funktion frameRate() festgelegt werden. siehe Referenz
// Aufnahmeabmaße 640x480 Pixel Capture cam1 = new Capture (this, 640, 480); // 20 Einzelbilder pro Sekunde (fps) Capture cam2 = new Capture (this, 320, 240, 20); // Kamera 'My Webcam', 20fps Capture cam3 = new Capture (this, 800, 600, "My Webcam", 20);
Verfügbarkeit von Geräten ermitteln
- list() gibt eine Liste (String Array) aller in Processing verfügbaren Kameras zurück. Diese Geräte können beim erzeugen einer Capture Instanz mit Namen angegeben werden. siehe Referenz
String[] list = Capture.list (); for (int i=0; i < list.length; i++) { println ('cam ' + i + ':' + list[i]); } - available() gibt true zurück, wenn ein Einzelbild fertig aufgenommen wurde. Nach dem Einlesen mit read() kann es in Processing verarbeitet werden. Wenn das Resultat false lautet, ist die Kamera mit hoher Wahrscheinlichkeit nicht betriebsbereit. siehe Referenz
Capture cam = new Capture (this, 320, 240); if (cam.available ()) { println ("Cam is ready"); }else{ println ("Cam is not ready"); }
Bilder auslesen und darstellen
- crop(x, y, width, height) stellt die von der Kamera kommenden Bilder frei. Die Maske wird durch die beim Zeichnen eines Rechtecks bekannten Angaben festgelegt. siehe Referenz
Capture cam = new Capture (this, 320, 240); cam.crop (10, 10, 300, 220);
- read() liest das aktuelle Einzelbild 'aus der Kamera' und macht es in Processing verfügbar. Der Test ob ein Kamerabild verfügbar ist, siehe available(), sollte vorher durchgeführt werden. siehe Referenz
Capture cam = new Capture (320, 240); if (cam.available ()) { cam.read (); } - image() genau wie bei Bildern wird image() zum Darstellen von Videoframes genutzt. Als ersten Parameter gibt man dabei nicht die PImage sondern die Capture Variable an. siehe Referenz
Capture cam = new Capture (320, 240); if (cam.available ()) { cam.read (); } image (cam, 0, 0);
Bsp.: Kamerabild auslesen
Das erste Beispielt erläutert das Auslesen und Abbilden eines Kamera-Inputs. Mit new Capture (this, with, height, 25); wird die Verbindung zur angeschlossenen Kamera in Processing aufgebaut. Dabei wählt Processing automatisch das letzt der verfügbaren Geräte, siehe list(), aus. Mit 25 Bildern pro Sekunde - jeweils 320x240 Pixel groß - versorgt uns von nun an die Kamera, welche wir mit dem Variablennamen videocam ansprechen. Da die frameRate des Sketches ohne unser Zutun bei 60 liegt, ist nicht bei jedem Sketch-Einzelbild ein neues Kamerabild verfügbar. Um Fehler zu vermeiden prüfen wir deshalb in jedem draw()-Durchlauf mit videocam.available() ob wir das aktuelle Bild der Kamera auslesen können. Ist dies der Fall, geschieht dies durch durch den Aufruf von videocam.read(). Final findet das Abbilden mit dem image() Befehl statt. Positioniert in der oberen-linken Ecke füllt es die gesamte Zeichenfläche aus.import processing.video.*;
// Variable für das Ansprechen
// der Videokamera in P5
Capture videocam;
void setup () {
size (320, 240);
// Größe und Bildanzahl pro Sekunde für
// den Kamara-Input festlegen
videocam = new Capture (this, width, height, 24);
}
void draw () {
// wenn Kamera-Input verfügbar
if (videocam.available ()) {
// liest momentanes Kamerabild aus
videocam.read ();
}
// Abbilden des mit 'read()' ausgelesenen Bildes
// an der Position x=0, y=0 im Sketch
image (videocam, 0, 0);
}Kamerabild verzögert abspielen in Processing
Das simpelsten Beispiel stellt das aktuelle Kamerabild in 'nahezu' Echtzeit dar. Ein Ablegen des Bildes bzw. Modifizieren der Farbwerte einzelner Pixel findet nicht statt. Im nächsten Schritt wird das verzögerte Abspielen des Videoinputs vorgestellt.
Den Kern stellt ein PImage Array namens buffer. Zu Beginn wird im setup() dessen Größe berechnet, um später die gewünschte Anzahl an Einzelbildern zu speichern. Je nach Länge der Verzögerung wird demnach beim Start kein Videobild im Sketch angezeigt. Die Abspiel- und Speicherposition, in welchem Feld das aktuelle Bild abgelegt/abgerufen werden soll, definiert die Variable index. Dabei wird das Array nicht von Anfang bis ins Endlose mit Bilder versorgt, sondern beim Erreichen des letzten Feldes auf das erste verwiesen. Die Variable index verursacht demnach einen Loop durch den Bildspeicher buffer. Bilder die älter als die Angegebene lagSeconds sind wurden abgespielt und werden überschrieben.
import processing.video.*;
// Schnittstelle zur Kamera
Capture cam;
// Speicher für die Einzelbilder aus
// der 'Vergangenheit'
PImage[] buffer;
// Anzahl der Einzelbilder pro
// Sekunde für Sketch und Aufnahme
int fps = 20;
// Zeitverzögerung in Sekunden
int lagSeconds = 2;
// Abspielposition und damit Schlüssel
// für das Einfügen/Auslesen aus dem Buffer
int index = 0;
// Status ob der Bildspeicher schon
// einmalig komplett gefüllt wurde
boolean filled = false;
void setup () {
size (320, 240);
frameRate (fps);
cam = new Capture (this, width, height, 20);
buffer = new PImage[lagSeconds * fps];
}
void draw () {
// Wenn der Bildspeicher einmal
// komplett gefüllt wurde...
if (filled == true) {
// Stelle das Bild im Sketch dar
image (buffer[index], 0, 0);
}
}
void captureEvent (Capture theCam) {
// Lies das aktuelle Kamerabild aus
theCam.read ();
// Speicher eine Kopie im Buffer an
// der Position des Bildes welches
// gerade ausgelesen wurde
buffer[index] = theCam.get ();
// Erhöhe die Position für das Auslesen
// um eins, auf das älteste Bild im Buffer
index++;
// Wenn am Ende des Buffers angekommen,
// springe wieder an den Anfang.
if (index >= buffer.length) {
index = 0;
if (!filled) {
filled = true;
}
}
}Kamerabild-Verlauf im Raster abbilden in Processing
Auf Grundlage des vorherigen Beispiels stellt dieser Sketch das von der Kamera aufgenommene Bild nicht 1:1 dar, sondern zeichnet den gesamten 'Bildspeicher' in die Zeichenfläche. Um keinen Fehler zu verursuchen überprüft die if-Bedingung, mit der Abfrage if (buffer[i] != null) { ob bereits ein Bild vom Typ PImage im Array-Feld vorhanden ist. Ist das der Fall, wird anhand der Zählvariable i die Position des Bildes in der Matrix berechnet.
import processing.video.*;
// Schnittstelle zur Kamera
Capture cam;
// Speicher für die Einzelbilder aus
// der 'Vergangenheit'
PImage[] buffer;
// Anzahl der Einzelbilder pro
// Sekunde für Sketch und Aufnahme
int fps = 21;
// Zeitverzögerung in Sekunden
int lagSeconds = 2;
// Abspielposition und damit Schlüssel
// für das Einfügen/Auslesen aus dem Buffer
int index = 0;
// Status ob der Bildspeicher schon
// einmalig komplett gefüllt wurde
boolean filled = false;
// Anzahl der Spalten im Sketchfenster
int cols = 6;
void setup () {
size (318, 278);
frameRate (fps);
cam = new Capture (this, 320, 240, 20);
buffer = new PImage[lagSeconds * fps];
}
void draw () {
// Bildgröße für Raster ermitteln
float imgWidth = width / cols;
float aspect = imgWidth / cam.width;
float imgHeight = cam.height * aspect;
// Für jedes Feld im 'Bildspeicher'
for (int i=0; i < buffer.length; i++) {
// Wenn ein Bild existiert
if (buffer[i] != null) {
// Berechne die Position im Raster
float x = (i % cols) * imgWidth;
float y = floor (i / cols) * imgHeight;
image (buffer[i], x, y, imgWidth, imgHeight);
}
}
}
void captureEvent (Capture theCam) {
// Lies das aktuelle Kamerabild aus
theCam.read ();
// Speicher eine Kopie im Buffer an
// der Position des Bildes welches
// gerade ausgelesen wurde
buffer[index] = theCam.get ();
// Erhöhe die Position für das Auslesen
// um eins, auf das älteste Bild im Buffer
index++;
// Wenn am Ende des Buffers angekommen,
// springe wieder an den Anfang.
if (index >= buffer.length) {
index = 0;
if (!filled) {
filled = true;
}
}
}Kamerabild verzerren in Processing
Jegliches Auslesen und Modifizieren von Bildinformationen kann wie im Kapitel Bilder auch auf Einzelbilder von Videos angewendet werden. Zur Demonstration verzerrt dieses Beispiel ein Videobild in der Vertikalen (siehe Bilder Scratch).
Im draw() testet eine Bedingung mit videocam.available() ob bereits eine neue Aufnahme der angeschlossenen Kamera zum Abrufen bereit steht und liest diese mit read() ein. Teil zwei besteht aus einer for-Schleife die von 0 bis 319 (Sketchbreite - 1) läuft. Mittels dieser Zählvariable können die Farbwerte der ersten Pixelreihe des Kamerabildes ausgelesen werden. Nach der Selektion des Farbwertes wird eine Linie von der Oberkante zur Unterkante der Zeichenfläche für jeden Pixel in der angesprochenen Reihe gezeichnet. Die Strichfarbe kommt vom ausgelesenen Pixel im Kamerabild. Im Gegensatz zum vorigen Beispiel wird das eigentliche Kamerabild nicht abgebildet.
import processing.video.*;
// Variable für das Ansprechen
// der Videokamera in P5
Capture videocam;
void setup () {
size (320, 240);
// Größe und Bildanzahl pro Sekunde für
// den Kamara-Input festlegen
videocam = new Capture (this, width, height, 24);
}
void draw () {
// wenn Kamera-Input verfügbar
if (videocam.available ()) {
// liest momentanes Kamerabild aus
videocam.read ();
}
// für jeden Pixel in Breite des Sketches
for (int i=0; i < width; i++) {
// lies den Farbwert für jeden Pixel in
// der obersten Spalte des Kamerabildes
// aus (von links nach rechts).
color c = videocam.get (i, 0);
// nutze den Farbwert für die Strichfarbe
stroke (c);
// Zeichne eine vertikale Linie für jeden
// Pixel von oben nach unten
line (i, 0, i, height);
}
}Einfacher Kamerafarbfilter in Processing
Im folgenden Beispiel testen wir ob der Rot-Wert eines jeden Pixels über der Marke von 170 liegt. Ist es der Fall, wird es an seiner Position im Sketchfenster abgebildet. Mittels zweier ineinander verschachtelter for-Schleifen erreichen wir alle Bildpunkte der Kameraaufnahme. Nach der Farbselektion mit get(x, y) entscheidet die Bedingung if (red (c) > 170) {… ob der Pixel gezeichnet wird. Da wir mit point() arbeiten, wird die Einfärbung durch stroke() bestimmt.
import processing.video.*;
// Variable für das Ansprechen
// der Videokamera in P5
Capture videocam;
void setup () {
size (320, 240);
// Größe und Bildanzahl pro Sekunde für
// den Kamara-Input festlegen
videocam = new Capture (this, width, height, 24);
}
void draw () {
background (255);
// wenn Kamera-Input verfügbar
if (videocam.available ()) {
// liest momentanes Kamerabild aus
videocam.read ();
}
// für jeden Pixel in Breite des Sketches
for (int i=0; i < width; i++) {
// für jeden Pixel in der Höhe des Sketches
for (int j=0; j < height; j++) {
// lies den Farbwert für jeden Pixel aus
color c = videocam.get (i, j);
// wenn der Rotanteil groß genug ist
// -> bilde das Pixel an seiner Position ab
if (red (c) > 170) {
stroke (c);
point (i, j);
}
}
}
}Abstrahierte Bilddarstellung in Processing
In der folgenden Version wird das pixels Array, welches alle Bildpunkte des Kamerabildes enthält, in großen Schritten durchlaufen und durch eingefärbe Kreise in der Zeichenfläche repräsentiert. In der Breite wird dabei jedes achte Pixel interpretiert und in der Höhe jede vierte Zeile. Bei der Positionsabfrage auf der y-Achse und der Ermittlung der x-Koordinate wird von Modulo (%) gebrauch gemacht. Modulo gibt den Rest einer Division zurück und ermöglicht dadurch unteranderem die Frage ob ein Wert gerade ist oder nicht. Da das Programm nicht mit dem get() und set() Befehlen zum Auslesen und Ändern von Farbwerten innerhalb des bildes Arbeit, sonder dieser Bereich über das pixels Array abgewickelt wird, schafft Processing eine größere Anzahl von Bildern pro Sekunde abzuarbeiten.
import processing.video.*;
Capture cam;
void setup () {
size (320, 240);
cam = new Capture (this, width, height, 24);
noStroke ();
}
void draw () {
background (0);
// wenn Kamera-Input verfügbar
if (cam.available ()) {
// liest momentanes Kamerabild aus
cam.read ();
}
// für jeden Pixel im Videobild
for (int i=0; i < cam.pixels.length; i += 8) {
// Wenn 'i' an dem erste Pixel einer Zeile
// angekommen ist (Rest aus i/Bildbreite = 0)
if (i % cam.width == 0) {
// Wenn die Zeile einen geraden Index
// besitzt (Rest aus i/2 = 0)
if (i%2 == 0) {
// Verschiebe i um 4 Zeilen
i += cam.width * 4;
}
}
// Berechne die Position des Pixels an der
// Stelle 'i' in der Bildfläche.
int x = i % cam.width;
int y = (int) floor (i / cam.width);
// Setze die Füllfarbe und zeichne eine
// Ellipse an der Position des Pixels
fill (cam.pixels[i]);
ellipse (x, y, 10, 10);
}
}Simpler Green-Screen in Processing

In diesem Beispiel wird ein Weg vorgestellt diesen Effekt mit jeglichem Hintergrund zu erzielen. Dabei nutzen wir nicht eine standardisierte Farbe als Hintergrund, sondern bedienen uns eines vorher aufgenommenen Bildes. Das Bild steht in diesem Fall für den zu entfernenden Hintergrund. Nach dem Starten des Processing Programms wird es durch das Drücken der Taste 'b' aufgenommen und mit der Funktion get() in der Variable back abgelegt. Wie in den vorherigen Beispielen durchlaufen wir im draw-Block jedes einzelne Pixel des Kamerabildes und vergleichen dabei den Farbwert mit dem Hintergrundbild back. Wenn eine Änderung üben den Schwellwert threshold vorliegt wird dieses Pixel im Sketchfenster abgebildet. Anderenfalls ist an dieser Stelle nur der graue Hintergrund sichtbar. Bei der Ermittlung der Farbwerte bilden wird die Summe aus allen drei Farbkanälen und vergleichen diese. Eine präzisere Lösung wäre jeden Kanal einzeln abzufragen und zu vergleichen. Grundsätzlich gilt: Je besser die Szene ausgeläuchtet ist, umso genauer die Freistellung.
import processing.video.*;
// Video-Aufnahme-Auflösung
final int VIDEO_WIDTH = 320;
final int VIDEO_HEIGHT = 240;
// Variable zum Speichern des Hintergrundbildes.
PImage back = null;
// Wert um den sich die Summe der RGB Kanäle mind. ändern
// muss, damit das Pixel als 'aktiv' erkannt wird.
float threshold = 50;
// Objekt zum Zugriff auf die angeschlossene Kamera.
Capture cam = null;
void setup () {
size (VIDEO_WIDTH, VIDEO_HEIGHT);
// Initialisiert die Verbindung zur Kamera und
// gleicht die frame rate von Kamera und Sketch an.
cam = new Capture (this, VIDEO_WIDTH, VIDEO_HEIGHT, 15);
frameRate (15);
}
void draw () {
// Wenn die Videokamrea verfügbar ist
if (cam.available ()) {
// Auslesen des Kamerabildes und
// füllen des Hintergrunds mit grau
cam.read ();
background (40);
// Alle Pixel vom Sketchfenster für die
// spätere Bearbeitung laden.
loadPixels ();
// Wenn ein Hintergrundbild aufgenommen wurde.
if (back != null) {
// Für jedes Pixel in der Zeichenfläche
for (int i=0; i < cam.pixels.length; i++) {
// Farbe an der Position 'i' im Kamerabild
// und im Hintergrundbild auslesen
color c1 = cam.pixels[i];
color c2 = back.pixels[i];
// Die Summe aus allen drei Farbkanälen bilden
float s1 = red (c1) + green (c1) + blue (c1);
float s2 = red (c2) + green (c2) + blue (c2);
// Unterschied zwischen Hintergrundbild und Kamerabild
// errechnen für dieses Pixel.
float deltaPixel = s1 - s2;
// Hat sich dieses Pixel mind. um unseren Schwellenwert
// geändert? Wenn ja – zeichne das Pixel aus dem
// Kamerabild an diese Position.
if (deltaPixel > threshold || deltaPixel < -threshold) {
pixels[i] = cam.pixels[i];
}
}
}
// Aktualisiere das Sketchfenster mit dem Bild
// aus dem von uns modifizierten 'pixels' Array.
updatePixels ();
}
}
void keyReleased () {
// Beim Drücken der Taste 'b' wird das Hintergrundbild
// im Speicher durch die Variable 'back' abgelegt. Dieses
// gilt als Referenz für unseren "blue screen".
if (key == 'b' || key == 'B') {
back = cam.get ();
}
}Hellsten Punkt im Videobild tracken in Processing
Tracking wird als das Lokalisiern von Bildpunkten -abschnitten bezeichnet. Dieser Sketch durchsucht das Kamerabild nach dem hellsten Pixel und zeichnet ein Rechteck um seine Position. In der Variable maxCol speichert Processing den höchsten brightness()-Wert und die dazugehörige Position im Bild (xpos und ypos). Pixel für Pixel wird das aktuelle Maxima mit den verbleibenden Pixeln vergleichen - und wenn überschritten auf die neue Position gesetzt.
import processing.video.*;
Capture videocam;
// tracking Position
int xpos = 0;
int ypos = 0;
void setup () {
size (320, 240);
videocam = new Capture (this, width, height, 24);
noFill ();
strokeWeight (2);
stroke (255, 0, 102);
}
void draw () {
background (255);
float maxCol = 0;
if (videocam.available ()) {
videocam.read ();
}
// für jeden Pixel in Breite des Sketches
for (int i=0; i < width; i++) {
for (int j=0; j < height; j++) {
// lies den Farbwert für jeden Pixel aus
color c = videocam.get (i, j);
// Wenn heller als aktuelles Maximum
// -> ersetze Tracking-Koordinaten
if (brightness (c) > maxCol) {
maxCol = brightness (c);
xpos = i;
ypos = j;
}
}
}
// Zeichne Bild und TrackingBox
image (videocam, 0, 0);
rect (xpos-2, ypos-2, 5, 5);
}Aktivität im Bild feststellen in Processing
if (deltaPixel > threshold || deltaPixel < -threshold) {
/* Wenn die Differenz kleiner als 0 ist,
* kehre das Vorzeichen um. Dadurch werden
* die schwarzen Stellen vermieden.
*/
//if (deltaPixel < 0) {
// deltaPixel = deltaPixel * -1;
//}
pixels[i] = color (deltaPixel);
}import processing.video.*;
// Video-Capture-Auflösung
final int VIDEO_WIDTH = 320;
final int VIDEO_HEIGHT = 240;
// Bildergedächtnis zum Ablegen der letzen
// drei Videobilder. Diese bilden die Grundlage
// für das Erkennen der Aktivität vor der Kamera.
float[] buffer1 = new float[VIDEO_WIDTH * VIDEO_HEIGHT];
float[] buffer2 = new float[buffer1.length];
float[] buffer3 = new float[buffer1.length];
// Wert um den sich die Summe der RGB Kanäle mind. ändern
// muss, damit das Pixel als 'aktiv' erkannt wird.
float threshold = 30;
// Schnittstelle zur Kamera
Capture cam;
void setup () {
size (VIDEO_WIDTH, VIDEO_HEIGHT);
// Da die Videokamera keine 60 Bilder pro Sekunde liefert,
// kann die Framerate des sketches auf 15 angepasst werden.
// Framerate von Kamera und Sketch sind damit gleich.
cam = new Capture (this, VIDEO_WIDTH, VIDEO_HEIGHT, 15);
frameRate (15);
}
void draw () {
// Wenn die Videokamrea verfügbar ist
if (cam.available ()) {
// Auslesen des Kamerabildes und
// füllen des Hintergrunds mit grau
cam.read ();
background (40);
// Alle Pixel vom Sketchfenster für die
// spätere Bearbeitung laden.
loadPixels ();
// Gehe durch jedes Pixel im Bild und Vergleiche
// den Farbwert an dieser Position mit den Farbwerten
// in der Vergangenheit (siehe buffer 1-3).
for (int i=0; i < cam.pixels.length; i++) {
// 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);
// Die mögliche Bewegung an dieser Position im Kamerabild
// wird durch das Vergleichen der Farbwerte praktiziert.
// Dabei werden die letzten drei Bilder und das aktuelle
// einbezogen.
float deltaPixel = (buffer1[i] + buffer2[i] + buffer3[i]) / 3 - sum;
// Falls sich der Farbwert über die festgelegte Grenze
// geändert hat, färbe das Pixel weiß ein. Anderefalls
// zeichne es nicht (bleibt als Hintergrund eingefärbt).
if (deltaPixel > threshold || deltaPixel < -threshold) {
pixels[i] = color (255);
}
// Verschiebe das 'Bildgedächnis' um einen Rutsch nach hinten.
// Überschreibe das älteste Bild (buffer3) und rücke die beiden
// anderen um eine Stelle nach hinten. Das aktuelle Bild wird in
// 'buffer1' abgelegt. (Dies passiert Pixel für Pixel!)
buffer3[i] = buffer2[i];
buffer2[i] = buffer1[i];
buffer1[i] = sum;
}
updatePixels ();
}
}Aktivität in Bildbereichen feststellen in Processing
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.
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);
}
}
}



