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

SVM model;
BlobDetection theBlobDetection;

Capture video;
PImage src, dst;
PImage img;

ArrayList<Contour> contours;
ArrayList<Contour> polygons;
SVMProblem problem;
OpenCV opencv;
Histogram grayHist, rHist, gHist, bHist;

float[][] trainingPoints;
int[] labels;
float[] checkp;
int checkn;
int num;
float maxcontours = 0;

PGraphics modelDisplay;

void setup() {
  size(640, 480);
  video = new Capture(this, 640/2, 480/2);
  opencv = new OpenCV(this, 640/2, 480/2);
  modelDisplay = createGraphics(640/2, 480/2);
  img = new PImage(640/2, 480/2);
  theBlobDetection = new BlobDetection(img.width, img.height);
  theBlobDetection.setPosDiscrimination(true);
  theBlobDetection.setThreshold(0.2f); // will detect bright areas whose luminosity > 0.2f;

  num = 0;
  trainingPoints = new float[200][2];
  labels = new int[200];
  model = new SVM(this);
  problem = new SVMProblem();
  problem.setNumFeatures(2);
  checkp = new float[2];
  checkp[0] = -10;
  checkp[1] = -10;
  checkn = 3;
  video.start();
}

void drawBlobsAndEdges(boolean drawBlobs, boolean drawEdges)
{
  noFill();
  Blob b;
  EdgeVertex eA, eB;
  for (int n=0; n<theBlobDetection.getBlobNb(); n++)
  {
    b=theBlobDetection.getBlob(n);
    if (b!=null)
    {
      // Edges
      if (drawEdges)
      {
        strokeWeight(3);
        stroke(0, 255, 0);
        for (int m=0; m<b.getEdgeNb(); m++)
        {
          eA = b.getEdgeVertexA(m);
          eB = b.getEdgeVertexB(m);
          if (eA !=null && eB !=null)
            line(
              eA.x*width/2, eA.y*height/2+height/2, 
              eB.x*width/2, eB.y*height/2+height/2
              );
        }
      }

      // Blobs
      if (drawBlobs)
      {
        strokeWeight(1);
        stroke(255, 0, 0);
        rect(
          b.xMin*width/2, b.yMin*height/2+height/2, 
          b.w*width/2, b.h*height/2
          );
      }
    }
  }
}


void drawModel() {
  // start drawing into the PGraphics instead of the sketch
  modelDisplay.beginDraw();
  modelDisplay.background(0);
  // for each row
  if (num>0) {
    for (int x = 0; x < width/2; x++) {
      // and each column
      for (int y = 0; y < height/2; y++) {

        // make a 2-element array with the x and y values
        double[] testPoint = new double[2];
        testPoint[0] = (double)x/(width/2);
        testPoint[1] = (double)y/(height/2);

        // pass it to the model for testing
        double d = model.test(testPoint);

        // based on the result, draw a red, green, or blue dot
        if ((int)d == 1) {
          modelDisplay.stroke(255, 0, 0);
        } else if ((int)d == 2) {
          modelDisplay.stroke(0, 255, 0);
        } else if ((int)d == 3) {
          modelDisplay.stroke(0, 0, 255);
        } else {
          modelDisplay.stroke(255, 255, 255);
        }

        // which will fill up the entire area of the sketch
        modelDisplay.point(x, y);
      }
    }
  }
  // we're done with the PGraphics

  modelDisplay.endDraw();
}

void draw() {
  scale(1);
  background(0);
  opencv.loadImage(video);
  image(video, 0, 0 );
  opencv.gray();
  opencv.threshold(70);
  dst = opencv.getOutput();

  image(modelDisplay, 640/2, 0);
  strokeWeight(1);

  stroke(255);
  for (int i = 0; i < num; i++) {
    if (labels[i] == 1) {
      fill(255, 0, 0);
    } else if (labels[i] == 2) {
      fill(0, 255, 0);
    } else if (labels[i] == 3) {
      fill(0, 0, 255);
    }
    ellipse(trainingPoints[i][0]/maxcontours*width/2+width/2, trainingPoints[i][1]*height/2, 5, 5);
  }

  if (checkn == 1) {
    fill(127, 0, 0);
  } else if (checkn == 2) {
    fill(0, 127, 0);
  } else {
    fill(0, 0, 127);
  }
  ellipse(checkp[0]*width/2+width/2, checkp[1]*height/2, 20, 20);

  image(dst, 0, 480/2);

  img.copy(video, 0, 0, video.width, video.height, 
    0, 0, img.width, img.height);

  theBlobDetection.computeBlobs(img.pixels);
  drawBlobsAndEdges(true, true);

  PFont font;
  font = createFont("Arial", 36);
  textFont(font);
  fill(255);
  if (checkn==1) {
    text("KINOKO", width*3/5, height*3/4);
  } else if (checkn==2) {
    text("TAKENOKO", width*3/5, height*3/4);
  }
}

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

void keyPressed() {
  if (key == ' ') {
    float[][] trainingPointsNormarize = new float[num][2];
    for (int i = 0; i < num; i++) {
      trainingPointsNormarize[i][0] = trainingPoints[i][0]/maxcontours;
      trainingPointsNormarize[i][1] = trainingPoints[i][1];
    }
    problem.setSampleData(labels, trainingPointsNormarize);
    // train the model
    model.train(problem);
    drawModel();
  } else if (key == 'k' || key =='t' || key =='c') {
    float[] p = new float[2];
    Blob b;
    EdgeVertex eA, eB;
    float s = 0;
    int e = 0;
    int k = 0;
    for (int n=0; n<theBlobDetection.getBlobNb(); n++)
    {
      b=theBlobDetection.getBlob(n);
      if (b!=null)
      {
        if (s<b.w+b.h) {
          s=b.w*width*b.h*height;
          e=b.getEdgeNb();
          k = 0;
          for (int i = 0; i < (int)(b.w*width); i++) {
            for (int j = 0; j < (int)(b.h*height); j++) {
              if (red(dst.get(i+(int)(b.xMin*width), j+(int)(b.yMin*height)))==0)
                k++;
            }
          }
        }
      }
    }
    p[0] = e; 
    p[1] = k/s;
    if (key=='c') {
      checkp = p;
      p[0]/=maxcontours;
      checkn = (int)model.test(p);
      println("check: " + checkn);
    } else {
      if (maxcontours<e) {
        maxcontours = e;
      }
      trainingPoints[num] = p;
      if ( key == 'k' ) {
        labels[num] = 1;
      } else if ( key == 't' ) {
        labels[num] = 2;
      }
      num++;
    }
    println("area k: " + k + " s:" + s + " k/s:" + k/s + " e:" + e/maxcontours + " contours");
  }
}