> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.RTNpjlThZ45/rev.54
 * 
 * authors: 
 *   Keith O'Hara

 * license (unless otherwise specified): 
 *   creative commons attribution-share alike 3.0 license.
 *   https://creativecommons.org/licenses/by-sa/3.0/ 
 */ 



/*
 * Keith O'Hara <[email protected]>
 *
 * K-Means Image Segmentation
 *
 * 
 */

/* @pjs preload="/static/uploaded_resources/p.1049/thedress.png"; */

PImage video;

float L = 2;

int numClusters = 2;
int[] classified;
float[][] clusters;

void assignClosest() {
  for (int i = 0; i < video.width*video.height; i++) {
    int closest_k = 0;
    float closest_dist = 1000000000;
    for (int j = 0; j < numClusters; j++) {
      color c = video.pixels[i];
      float r = (c >> 16) & 0xFF;  
      float g = (c >> 8) & 0xFF;   
      float b = c & 0xFF;

      float[] p = clusters[j];
      float cr = p[0];
      float cg = p[1];
      float cb = p[2];

      float d = pow(0
        + pow(abs(cr - r) / 255.0, L)
        + pow(abs(cb - b) / 255.0, L)
        + pow(abs(cg - g) / 255.0, L)
        , 1.0 / L);

      if (d < closest_dist) {
        closest_dist = d;
        closest_k = j;
      }
    }
    classified[i] = closest_k;
  }
}

void computeCentroids()
{
  int[] count = new int[numClusters];

  // clear cluster means
  for (int i = 0; i < numClusters; i++) {
    count[i] = 0;
    for (int j = 0; j < clusters[i].length; j++) {
      clusters[i][j] = 0;
    }
  }

  for (int i = 0; i < video.width*video.height; i++) {
    color c = video.pixels[i];
    int r = (c >> 16) & 0xFF;  
    int g = (c >> 8) & 0xFF;   
    int b = c & 0xFF;

    float v[] = {
      r, g, b
    };

    int clusterCenter = classified[i];
    count[clusterCenter]++;
    for (int z = 0; z < clusters[clusterCenter].length; z++) {
      clusters[clusterCenter][z] += v[z];
    }
  }

  for (int j = 0; j < numClusters; j++) {
    for (int z = 0; z < clusters[j].length; z++) {
      clusters[j][z] /= count[j];
    }
  }
}

void setup() {
  size(782,  490);
  video = loadImage("/static/uploaded_resources/p.1049/thedress.png");
  reset();
}

void reset() {
  clusters = new float[numClusters][3];
  classified = new int[video.width * video.height];
  for (int i = 0; i < numClusters; i++) {
    int z = (int)(random(video.height*video.width));
    color c = video.pixels[z];
    int r = (c >> 16) & 0xFF;  
    int g = (c >> 8) & 0xFF;   
    int b = c & 0xFF;
    float v[] = {
      r, g, b
    };

    clusters[i] = v;
  }
}

void mousePressed() { 
  reset();
}

void draw() {
  video.loadPixels();
  image(video, width/2, 0);
  // Create a new image to store the resulting clustered image
  PImage cImg = createImage(video.width, video.height, RGB);
  cImg.loadPixels();

  assignClosest();
  computeCentroids();

  // Loop through every pixel in the image.
  for (int i = 0; i < cImg.height*cImg.width; i++) { 
    float []v =clusters[classified[i]]; 
    color c = color(v[0], v[1], v[2]);
    cImg.pixels[i] = c;
  }

  cImg.updatePixels();
  image(cImg, 0, 0, video.width, video.height);
}