Kinetic Interfaces: Pixel manipulation(Billy Zou)

The basic idea of this assignment was manipulating pixels according to the characteristic of the input image. It has two functions. First, it can compute the average red, green and blue values of all pixels of the image and get a “dominant color” out of red, green and blue. Then based on the proportion each of the three colors take in the whole image, we can get a “dominant coefficient” representing how dominant the color is. Finally, the color value of the other two color channels will decrease. In detail, they will multiply by the coefficient. This function adds a filter effect to the image and the color of the filter is the “dominant color”. Below are two snapshot of the program. Generally color of the interior environment is warm, so red becomes the dominant color. When I put a water bottle with blue wrapper the dominant color becomes blue.

The other function is that the program can detect the change of the image. It can compute the difference between two sequential frames according to the RGB value. If the difference is large, there will be a grid effect. Totally there are three sizes of grids depending on the value of difference.

Above is an example when I shake the camera violently. However there is still a problem with this function. As the frame rate is quite large, even if I shake the camera sometimes there exists two sequential frames that the difference value is 0, therefore the grid effect is on and off constantly. Overall this effect works well.

When it comes to the code, I designed a class called manipulator, which can listen to a camera input, analyze image characteristics, manipulate pixels and finally export the image. The class can do all the work and the main function can be quite simple, which is of only 25 lines.

import processing.video.*;

Capture cam;
Manipulator mani;
PImage img;

void setup() {
   size(640, 480);
   
   cam = new Capture(this, width, height);
   cam.start();
   
   mani = new Manipulator(width, height);
}

void draw() {
  if (cam.available()) {
    cam.read();
    cam.loadPixels();
  }
  mani.load(cam);
  mani.filt();
  mani.update();
  mani.display();
}

class Manipulator {
  int[] pxs;
  int wid, hei, size;
  float diff;
  float ravg, gavg, bavg;
  int domi;
  float domiCoef;
  PImage img;

  Manipulator(int _wid, int _hei) {
    this.wid = _wid;
    this.hei = _hei;
    this.size = _wid * _hei;
    this.diff = 0;
    this.ravg = 0;
    this.gavg = 0;
    this.bavg = 0;
    this.domi = 0;
    this.domiCoef = 0;
    this.pxs = new int[this.size];
    for (int i = 0; i < this.size; i++) {
      this.pxs[i] = 0;
    }
    this.img = createImage(this.wid, this.hei, RGB);
    noStroke();
    rectMode(CENTER);
  }

  void load(Capture cam) {
    int r = 0;
    int g = 0;
    int b = 0;
    for (int i = 0; i < this.size; i++) {
      int currPixel = cam.pixels[i];
      r += int(red(currPixel));
      g += int(green(currPixel));
      b += int(blue(currPixel));
      this.pxs[i] = currPixel;
    }
    r /= this.size;
    g /= this.size;
    b /= this.size;
    this.diff = abs((r + g + b) - (this.ravg + this.gavg + this.bavg));
    this.ravg = r;
    this.gavg = g;
    this.bavg = b;
    println(this.diff);
  }

  void filt() {
    float colorSum = this.ravg + this.gavg + this.bavg;
    float rp = this.ravg / colorSum;
    float gp = this.gavg / colorSum;
    float bp = this.bavg / colorSum;
    if (rp > gp && rp > bp) {
      this.domi = 0;
      this.domiCoef = map(rp, 0.33, 1.0, 1.0, 0.0) / 2;
    } else if (gp > rp && gp > bp) {
      this.domi = 1;
      this.domiCoef = map(gp, 0.33, 1.0, 1.0, 0.0) / 2;
    } else {
      this.domi = 2;
      this.domiCoef = map(bp, 0.33, 1.0, 1.0, 0.0) / 2;
    }
  }

  void update() {
    for (int i = 0; i < this.size; i++) {
      int r = int(red(this.pxs[i]));
      int g = int(green(this.pxs[i]));
      int b = int(blue(this.pxs[i]));

      if      (this.domi == 0) this.img.pixels[i] = color(r, g * this.domiCoef, b * this.domiCoef);
      else if (this.domi == 1) this.img.pixels[i] = color(r * this.domiCoef, g, b * this.domiCoef);
      else                     this.img.pixels[i] = color(r * this.domiCoef, g * this.domiCoef, b);
    }
    this.img.updatePixels();
  }

  void display() { 
    int step = 0;
    if      (this.diff >= 2)  step = 10;
    else if (this.diff >= 5)  step = 20;
    else if (this.diff >= 10) step = 40;
    
    if (this.diff < 2) image(this.img, 0, 0);
    else {
      for (int y = 0; y < this.hei; y += step) {
        for (int x = 0; x < this.wid; x += step) {
          int i = x + y * this.wid;

          int r = int(red(this.img.pixels[i]));
          int g = int(green(this.img.pixels[i]));
          int b = int(blue(this.img.pixels[i]));

          fill(r, g, b);
          rect(x, y, step, step);
        }
      }
    }
  }
}

Leave a Reply