2.9 Computer Vision

Durch schnellere Hardware, bessere Modelle und Konzepten aus dem Bereich maschinelles Lernen wurde Computer Vision (kurz CV, die Analyse von Bild- und Videomaterial durch Computer) in den letzten Jahren ein sehr populäres Forschungs- und Entwicklungsfeld. Insbesondere selbststeuernde Fahrzeuge , die kontinuierlich ihre Umgebung visuell analysieren, erlangten starke Aufmerksamkeit.

Yu Xiang and Silvio Savarese. Computational Vision and Geometry Lab, Stanfort University, CA.

Brightness Tracking

Zur Analyse von Bildmaterial durch Code stehen unterschiedliche Methoden zur Verfügung. Sie basieren grundsätzlich darauf, dass die Farb- und Helligkeitswerte der Pixel in Echtzeit ausgelesen werden und diese Daten verwendet werden, um Muster zu erkennen. Eine einfache Methode ist im folgenden Beispiel dargestellt. Im Algorithmus werden die Helligkeitswerte aller Pixel ausgelesen und das hellste Pixel im Bild in einer Variable abgelegt. Obwohl die Methode sehr einfach gehalten ist, kann sie bereits genutzt werden, um helle Objekte wie einen Light-Pointer oder dunkle wie Gläser in einem Kamera-Signal zu erkennen:

Interactive Bar Tables, Golan Levin, 2004

Beispiel 2-13


import processing.video.*;          // Die Video-Library importieren

Capture video;                          // Das Capture-Objekt deklarieren

void setup() {
  size(1280, 720);
  video = new Capture(this, 1280, 720);     // Apple Facetime-Cam HD Auflösung
  video.start();
}

void draw() {
  image(video, 0, 0);                   // stelle des Video-Bild im Ausgabefenster dar
   int maxbrightX = 0;                   // X-coordinate of the brightest video pixel
   int maxbrightY = 0;                   // Y-coordinate of the brightest video pixel
   float maxbrightValue = 0; // Brightness of the brightest video pixel
    video.loadPixels();
    int index = 0;
    for (int y = 0; y < video.height; y++) {                // nach dem hellsten Pixel suchen
      for (int x = 0; x < video.width; x++) {               // siehe auch Golan Levin
        int pixelValue = video.pixels[index];
        float pixelBrightness = brightness(pixelValue);
        if (pixelBrightness > maxbrightValue) {            // Testen ob das aktuelle Pixel heller ist als alle vorigen
          maxbrightValue = pixelBrightness;
          maxbrightX = x;
          maxbrightY = y;
        }
        index++;
      }
    }
    fill(0, 30);
    ellipse(maxbrightX, maxbrightY, 50, 50);
}

void captureEvent(Capture video) {
  video.read();                         // das verfügbare Bild der Kamera auslesen
}

In einem Setup für eine Installation könnte nun der Raum eher dunkel gehalten werden und die Bewegung einer Lichtquelle durch das Tracken von hellen Pixeln verfolgt werden. Exemplarisch könnte obiger Code mit dem Zeichen-Beispiel (1-3) aus Kapitel 1-4 kombiniert werden um mit einem "Leuchtstift" einen Pinsel auf den Bildschirm zu steuern. Die folgenden Zeilen müssen dafür auskommentiert bzw. hinzugefügt werden:


//image(video, 0, 0);
…
//fill(0, 30);
    //ellipse(maxbrightX, maxbrightY, 50, 50);
    //paintIt(maxbrightX, maxbrightY);
    float heavyness = 30-((abs(maxbrightX-xOld))+(abs(maxbrightY-yOld)));
    heavyness = constrain(heavyness, 6, 30);
    strokeWeight(heavyness);
    stroke(maxbrightX,100,maxbrightY, 30-heavyness);
    line(xOld, yOld, maxbrightX, maxbrightY);   // 1. Variante
    xOld = maxbrightX;
    yOld = maxbrightY;

Color Tracking

Color Tracking stellt eine weitere Möglichkeit dar, die Position eines Objekt in einem Video-Stream zu verfolgen. Geeignet ist die Technik für Objekte, die sich aufgrund ihrer Farbigkeit stark von ihrer Umgebung abheben. Komplexere Aufgaben der Computer Vision erfordern komplexere Algorithmen zu Analyse und Vergleich von Bilddaten. Inzwischen gibt es einige Libraries für Processing, die entsprechende Funktionen zur Verfügung stellen. Populär ist im Allgemeinen OpenCV, das auch in einer Version für Processing vorliegt.

Um die Library zu nutzen sollte wiederum zunächst die OpenCV-Library für Processing über den Library-Managerinstalliert werden. Eine Beschreibung von OpenCV findet sich hier.

Bevor die Funktionen von OpenCV genutzt werden können müssen wie immer die benötigten Libraries durch den Befehl import importiert werden. Die Library java.awt.* ist eine Java-Library, sie wird für das Zwischenspeichern der Rechtecke, die die Gesichtserkennung zurückliefert, benötigt.

Da Processing auf der Programmiersprache Java basiert ist es allgemein möglich, auf Java-Libraries zurückzugreifen.

Ausserdem wird die Processing-Library OpenCV selbst importiert:


import gab.opencv.*;
import processing.video.*;
import java.awt.*;

Dann muss ein Objekt jeweils für Video und für OpenCV erstellt werden:


Capture video;
OpenCV opencv;

Beispiel 2-14


siehe Beispiel `HSVColorTracking`der OpenCV-Library

Erkennen von Markern

Ein inzwischen schon klassisches Beispiel dafür, wie das automatische Erkennen von Objekten durch den Computer für Interfaces Verwendung findet ist der Reactable (vgl. beispielsweise hier oder hier). Das gleiche Prinzip wurde in Installationen (bspw. Interactive Bar Tables, Golan Levin, 2004) und im Informationsdesign aufgegriffen.

Bei Reactable handelt es sich um einen modularen Synthesizer (der um Sample-Player, Sequenzer, etc erweitert wurde) mit einem visuellen Interface.

BoofCV ist eine weitere Library, die Methoden für Computer Vision zur Verfügung stellt. Sie muss in ihrer Version für Processing ebenfalls durch den Library-Manager installiert werden. BoofCV verfügt beispielsweise über Funktionen, um Marker in einem Video-Steam zu erkennen und zu verfolgen. Indem man den Code darauf reagieren lässt, wenn bestimmte Marker erkannt werden, lässt sich die grundsätzliche Funktionsweise von Reactable auf eine vereinfachte Art nachvollziehen:

Beispiel 2-15


import processing.video.*;
import boofcv.processing.*;                  // BoofCV importieren
import java.util.*;                          // Die Java Library util importieren

Capture cam;                                 // Cam-Objekt deklarieren
SimpleFiducial detector;                     // Objekt für den Detector erkennen 
PVector coord;

PVector coord;

void setup() {
  size(640, 480);
  coord = new PVector();
  cam = new Capture(this, 640, 480);
  cam.start();
  surface.setSize(cam.width, cam.height);
  detector = Boof.fiducialSquareBinaryRobust(0.1);
  detector.guessCrappyIntrinsic(cam.width,cam.height);
}

void draw() {
  if (cam.available() == true) {
    cam.read();
    List found = detector.detect(cam);
    image(cam, 0, 0);
    for( FiducialFound f : found ) {
      detector.render(this,f);
      float xRaw = (float)f.getFiducialToCamera().getTranslation().getX();
      float yRaw = (float)f.getFiducialToCamera().getTranslation().getY();
      float zRaw = (float)f.getFiducialToCamera().getTranslation().getZ();
      float xs = map(xRaw, -0.37*zRaw, 0.37*zRaw, 0, width);
      float ys = map(yRaw, -0.27*zRaw, 0.27*zRaw, 0, height);
      float zs = zRaw;
      fill(255);
      noStroke();
      ellipse(xs, ys, 40,40);
    }
  }
}

Die Software zur Marker-Erkennung ist unter dem Namen reakTIVision frei verfügbar auch in einer Version für Processing veröffentlicht.

Gesichtserkennung

Beispiel

connected colors (Nobumichi Asai, 2016)

Eyecode (Golan Levin, 2007)

Google Earth Faces (2013) ›An algorithmic robot hovering over the world to spot portraits hidden in the topography on planet earth. The custom application works autonomously to process vast amounts of satellite images through Google Maps by using a face detection algorithm. This endless cycle produces interesting results for reflection on the natural world.‹

Die wiederum von OpenCV bereitgestellte Funktion detect() wird verwendet, um ein Objekt in Echtzeit im dem Video zu erkennen. Die Kriterien, wie ein Objekt aussehen soll, um vom Sketch wahrgenommen zu werden, müssen zuerst festgelegt werden. Dies erfolgt mit der Funktion cascade(), die eine Art Beschreibung des Objekts in den Speicher lädt und den Computer das aktuelle Bild mit dieser "Vorlage" kontinuierlich vergleichen lässt. Diese Beschreibungen kann man selbst erstellen, es existieren praktischerweise aber bereits einige für die Erkennung von Gesichtern oder Körpern. Weitere Cascade Definitionen hier


opencv.loadCascade(OpenCV.CASCADE_FRONTALFACE); 

Die Funktion detect() liefert dann rechteckige Bereiche im Bild zurück, in denen sich ein Objekt – in unserem Fall ein Gesicht - befindet, das den Kriterien entspricht.


Rectangle[] faces = opencv.detect();

Im folgenden Beispiel werden Gesichter im Webcam-Video getrackt. Wenn die Taste s gedrückt wird, werden alle im Bild erkannten Gesichter als .jpg abgespeichert.

Beispiel 2-16


import gab.opencv.*;
import processing.video.*;
import java.awt.*;

Capture video;
OpenCV opencv;
Rectangle[] faces;

void setup() {
  size(1280, 760);
  fill(255,50);
  stroke(255);
  strokeWeight(3);
  video = new Capture(this, 1280, 720);
  opencv = new OpenCV(this, 1280, 720);
  opencv.loadCascade(OpenCV.CASCADE_FRONTALFACE);  
  video.start();
}

void draw() {
  opencv.loadImage(video);
  image(video, 0, 0 );
  faces = opencv.detect();

  for (int i = 0; i < faces.length; i++) {
    //println(faces[i].x + "," + faces[i].y);
    rect(faces[i].x, faces[i].y, faces[i].width, faces[i].height);
  }
}

void captureEvent(Capture c) {
  c.read();
}

void keyPressed(){
  if (key == 's'){ 
     for(int i = 0; i < faces.length; i++){
       int w = int(faces[i].width);
       int h = int(faces[i].height);
       int rectX = faces[i].x;
       int rectY = faces[i].y;
       PImage face = createImage(w,h,RGB);
       for(int x = 0; x < faces[i].width; x++){
         for(int y = 0; y < faces[i].height; y++){
            face.set(x, y, get(x+rectX,y+rectY));
         }
       }
       face.save("test.jpg");
     }
  }
}

Objekt-Tracking mit BoofCV

Die Library BoofCV bietet Funktionen zum Objekt-Tracking.

Beispiel-Code Objekt-Tracking

Kinect

Tutorials
Daniel Shiffman Kinect for Windows App
Shiffman Kinect & Processing Basics

Eine Kinect ist eine Kamera mit integriertem Infrarotsensor, die von Microsoft eigentlich für ihre XBOX-Spielekonsole entwickelt wurde. Sie ist in der Lage, räumliche Informationen zu verarbeiten und bspw. ein dreidimensionales Modell von SpielerInnen digital zu verarbeiten. Bald wurde sie daher in Hacks ausserhalb ihres eigentlichen Kontexts für interaktive Installationen oder Medienkunstprojekte verwendet.

Beispiel: Interaktive Installation mit der Kinect als Sensor

Um besonders nahe liegende Extremitäten zu erkennen kann man den das Graustufenbild der Kinect, das die z-Position abbildet, für eine Blob detection verwenden.

Zunächst sollte die OpenKinect-Library von Daniel Shiffman importiert werden. Mit den Funktionen


import org.openkinect.freenect.*;
import org.openkinect.freenect2.*;
import org.openkinect.processing.*;

Dann wird ein Kinect-Objekt deklariert:


Kinect2 kinect2;

Folgende Funktionen werden von der openkinect-Library zur Verfügung gestellt, um die verschieden Bildquellen der Kinect auszulesen und in weiterer Folge die entsprechenden Daten zu nutzen:

Function Output
getVideoImage() RGB-Bild
getDepthImage() 3D Graustufenbild
getIrImage() Infrarotbild

Beispiel 2-17


//Kinect-Library von Daniel Shiffman

import org.openkinect.freenect.*;        // Die benötigten Kinect-Libraries
import org.openkinect.freenect2.*;       // von Daniel Shiffmann importieren
import org.openkinect.processing.*;

int xPos = 512;                          // Breite des Kinect-Bildes
int yPos = 424;                          // Höhe des Kinect-Bildes

Kinect2 kin;                             // Das Kinect-Objekt deklarieren

void setup() {
  size(1024, 848, P2D);
  kin = new Kinect2(this);
  kin.initVideo();
  kin.initDepth();
  kin.initIR();
  kin.initRegistered();
  kin.initDevice();                      // Kinect-Daten starten
}

void draw() {
  background(0);                        // Bild bei jedem Durchlauf löschen
  image(kin.getVideoImage(), 0, 0, kin.colorWidth*0.267, kin.colorHeight*0.267);
  image(kin.getDepthImage(), xPos, 0);
  image(kin.getIrImage(), 0, yPos);
  image(kin.getRegisteredImage(), xPos, yPos);
}