How to filter triangles from Delaunay to match human shape

I am working in an Interactive project and I am trying to define the correct area inside the body silhouette.
The sketch is using OpenCV, Delaunay triangulation and the goal is to add Box2D for the interaction.

My question is how to filter those triangles that are not included in the human silhouette? If you run the code, you will see that the triangulation, of course, is taking into account all the points detected by OpenCV, that means it will define triangles outside the human shape; however, the human shape is perfectly defined by contour.getPolygonApproximation().getPoints() and fill(255, 150, 0); beginShape(); is drawing a very nice shadow. Perhaps, getting the colour of the centre pixel of the triangle can tell me if that triangle is inside or outside the human body. Need some help here.
triangulation library is here: http://n.clavaud.free.fr/processing/triangulate/triangulate-20100628.zip

The triangles that are ok are those inside the orange area.
image

The code:

/**
 * Background Subtraction 
 * by Golan Levin. 
 * Example by David Dalmazzo
 * Detect the presence of people and objects in the frame using a simple
 * background-subtraction technique. To initialize the background, press spacebar.
 */
import org.processing.wiki.triangulate.*;
import processing.video.*;
import gab.opencv.*;

int numPixels;
PImage backgroundPixels;
PImage frontPixels;
ArrayList<Contour> contours;
ArrayList<Contour> polygons;
ArrayList triangles = new ArrayList();
ArrayList points = new ArrayList();
int threshold = -74;
boolean isBGReady = false;
boolean drawCoutour = false;

OpenCV opencv;
Capture video;

void setup() {
  size(640, 480, P2D); 
  video = new Capture(this, 640, 480);
  opencv = new OpenCV(this, 640, 480);
  opencv.threshold(100);
  // Start capturing the images from the camera
  video.start();  

  numPixels = video.width * video.height;
  // Create array to store the background image
  if(isBGReady)backgroundPixels = loadImage("backgroundPixels.jpg");
  else backgroundPixels = createImage(width, height, RGB);
  frontPixels = createImage(width, height, RGB);
  // Make the pixels[] array available for direct manipulation
  loadPixels();
}

void draw() {
  background(0);
  if (video.available()) {
    video.read(); // Read a new video frame
    video.loadPixels(); // Make the pixels of video available
    frontPixels = video.copy();
    backgroundPixels.loadPixels();
    frontPixels.loadPixels();

    for (int i = 0; i < numPixels; i++) { // For each pixel in the video frame...
      // Fetch the current color in that location, and also the color
      // of the background in that spot
      color currColor = frontPixels.pixels[i];
      color bkgdColor = backgroundPixels.pixels[i];
      // Extract the red, green, and blue components of the current pixel's color
      int currR = (currColor >> 16) & 0xFF;
      int currG = (currColor >> 8) & 0xFF;
      int currB = currColor & 0xFF;
      // Extract the red, green, and blue components of the background pixel's color
      int bkgdR = (bkgdColor >> 16) & 0xFF;
      int bkgdG = (bkgdColor >> 8) & 0xFF;
      int bkgdB = bkgdColor & 0xFF;
      // Compute the difference of the red, green, and blue values
      int diffR = abs(currR - bkgdR);
      int diffG = abs(currG - bkgdG);
      int diffB = abs(currB - bkgdB);
      // Add these differences to the running tally
      // Render the difference image to the screen
      //frontPixels.pixels[i] = color(diffR, diffG, diffB);
      // The following line does the same thing much faster, but is more technical
      frontPixels.pixels[i] = 0xFF000000 | (diffR << 16) | (diffG << 8) | diffB;
    }
    frontPixels.updatePixels(); // Notify that the pixels[] array has changed
    //println(presenceSum); // Print out the total amount of movement
  }
  if (!isBGReady) {
    image(frontPixels, 0, 0, width, height);
  } else {
    opencv.loadImage(frontPixels);
    opencv.brightness(threshold);
    //find contours in real-time
    contours = opencv.findContours(true, true);
    for (Contour contour : contours) {
      contour.setPolygonApproximationFactor(5.0);
      if (drawCoutour) {
        noFill();
        stroke(0, 150, 250);
        contour.draw();
      } else {
        noStroke ();
        fill(255, 150, 0);
        beginShape();
        for (PVector point : contour.getPolygonApproximation().getPoints()) {
          if (point.x > 1 && point.x < frontPixels.width-2 && point.y > 1 && point.y < frontPixels.height-2) {
            vertex(point.x, point.y);
            points.add(new PVector(point.x, point.y));
          }
        }
        endShape();
      }
    }
  }
  if (points.size() > 3) {
    triangles = Triangulate.triangulate(points);
    stroke(255, 50);
    fill(0, 150, 255, 50);
    beginShape();
    for (int i = 0; i < triangles.size(); i++) {
      Triangle t = (Triangle)triangles.get(i);
      vertex(t.p1.x, t.p1.y);
      vertex(t.p2.x, t.p2.y);
      vertex(t.p3.x, t.p3.y);
    }
    endShape();
     
  }
  points.clear();
  fill(255);
  text(frameRate, 5, 12);
}

// When a spacebar is pressed, capture the background image into the backgroundPixels
void keyPressed() {
  if (key == ' ') {
    video.loadPixels();
    backgroundPixels = video.copy();
    backgroundPixels.save("backgroundPixels.jpg");
    isBGReady = true;
  }
  if (key == 'c') {
    drawCoutour =! drawCoutour;
  }
}

void mouseDragged() {
  threshold = (int)map(mouseX, 0, width, -1, -255);
  println(threshold );
}
1 Like

Ok, I have found one option, calculating the centre pixel of the triangle, then checking it has the correct colour pixel.

/**
 * Background Subtraction 
 * by Golan Levin. 
 * Example by David Dalmazzo
 * Detect the presence of people and objects in the frame using a simple
 * background-subtraction technique. To initialize the background, press spacebar.
 */
import org.processing.wiki.triangulate.*;
import processing.video.*;
import gab.opencv.*;

int numPixels;
PImage backgroundPixels;
PImage frontPixels;
ArrayList<Contour> contours;
ArrayList<Contour> polygons;
ArrayList triangles = new ArrayList();
ArrayList points = new ArrayList();
ArrayList centerPoints = new ArrayList();
int threshold = -68;
boolean isBGReady = true;
boolean drawCoutour = false;
float triangleDistance = 250.0;
OpenCV opencv;
Capture video;

void setup() {
  size(640, 480, P2D); 
  video = new Capture(this, 640, 480);
  opencv = new OpenCV(this, 640, 480);
  opencv.threshold(100);
  // Start capturing the images from the camera
  video.start();  

  numPixels = video.width * video.height;
  // Create array to store the background image
  if (isBGReady)backgroundPixels = loadImage("backgroundPixels.jpg");
  else backgroundPixels = createImage(width, height, RGB);
  frontPixels = createImage(width, height, RGB);
  // Make the pixels[] array available for direct manipulation
  loadPixels();
}

void draw() {
  background(0);
  if (video.available()) {
    video.read(); // Read a new video frame
    video.loadPixels(); // Make the pixels of video available
    frontPixels = video.copy();
    backgroundPixels.loadPixels();
    //frontPixels.loadPixels();
    frontPixels = backgroundSubstraction(frontPixels);
  }
  if (!isBGReady) {
    image(frontPixels, 0, 0, width, height);
  } else {
    opencv.loadImage(frontPixels);
    opencv.brightness(threshold);
    //find contours in real-time
    contours = opencv.findContours(false, false);
    for (Contour contour : contours) {
      contour.setPolygonApproximationFactor(5.0);
      if (drawCoutour) {
        noFill();
        stroke(0, 150, 250);
        contour.draw();
      } else {
        noStroke ();
        fill(255, 150, 0);
        beginShape();
        for (PVector point : contour.getPolygonApproximation().getPoints()) {
          if (point.x > 1 && point.x < frontPixels.width-2 && point.y > 1 && point.y < frontPixels.height-2) {
            vertex(point.x, point.y);
            points.add(new PVector(point.x, point.y));
          }
        }
        endShape();
      }
    }
  }
  if (points.size() > 3) {
    triangles = Triangulate.triangulate(points);
    stroke(255, 100);
    noFill();
    beginShape(TRIANGLES);

    for (int i = 0; i < triangles.size(); i++) {
      Triangle t = (Triangle)triangles.get(i);
      float x = (t.p1.x + t.p2.x + t.p3.x)/3.0;
      float y = (t.p1.y + t.p2.y + t.p3.y)/3.0;
      
      color c = get((int)x, (int)y);
      /*
      float d1x = abs(t.p2.x - t.p1.x);
      float d2x = abs(t.p3.x - t.p2.x);
      float d3x = abs(t.p1.x - t.p3.x);
      float d1y = abs(t.p2.y - t.p1.y);
      float d2y = abs(t.p3.y - t.p2.y);
      float d3y = abs(t.p1.y - t.p3.y);
      */

      if (c == -27136) {
        vertex(t.p1.x, t.p1.y);
        vertex(t.p2.x, t.p2.y);
        vertex(t.p3.x, t.p3.y);
        centerPoints.add(new PVector(x, y));
      }

      // print(c + " ");
    }
    endShape();

    for (int i = 0; i < centerPoints.size(); i++) {
      PVector pointFromArray = (PVector) centerPoints.get(i);
      noStroke();
      fill(255, 0, 0);
      ellipse(pointFromArray.x, pointFromArray.y, 2, 2);
    }
  }

  points.clear();
  centerPoints.clear();
  fill(255);
  text(frameRate, 5, 12);
}

// When a spacebar is pressed, capture the background image into the backgroundPixels
void keyPressed() {
  if (key == ' ') {
    video.loadPixels();
    backgroundPixels = video.copy();
    backgroundPixels.save("backgroundPixels.jpg");
    isBGReady = true;
  }
  if (key == 'c') {
    drawCoutour =! drawCoutour;
  }
}

void mouseDragged() {
  threshold = (int)map(mouseX, 0, width, -1, -255);
  println(threshold );
}

PImage backgroundSubstraction(PImage in) {
  for (int i = 0; i < numPixels; i++) { // For each pixel in the video frame...
    // Fetch the current color in that location, and also the color
    // of the background in that spot
    color currColor = in.pixels[i];
    color bkgdColor = backgroundPixels.pixels[i];
    // Extract the red, green, and blue components of the current pixel's color
    int currR = (currColor >> 16) & 0xFF;
    int currG = (currColor >> 8) & 0xFF;
    int currB = currColor & 0xFF;
    // Extract the red, green, and blue components of the background pixel's color
    int bkgdR = (bkgdColor >> 16) & 0xFF;
    int bkgdG = (bkgdColor >> 8) & 0xFF;
    int bkgdB = bkgdColor & 0xFF;
    // Compute the difference of the red, green, and blue values
    int diffR = abs(currR - bkgdR);
    int diffG = abs(currG - bkgdG);
    int diffB = abs(currB - bkgdB);
    // Add these differences to the running tally
    // Render the difference image to the screen
    //frontPixels.pixels[i] = color(diffR, diffG, diffB);
    // The following line does the same thing much faster, but is more technical
    frontPixels.pixels[i] = 0xFF000000 | (diffR << 16) | (diffG << 8) | diffB;
  }
  in.updatePixels(); // Notify that the pixels[] array has changed
  //println(presenceSum); // Print out the total amount of movement
  return in;
}

2 Likes