Close

Processing on the Pi - Gradation

A project log for Networked Low Resolution DMD Projectors

WiFi networked electro-mechanical 7x7 pixel flip-dot displays as giant DMDs (Digital Micromirror Devices) for low resolution projection

Michael ShaubMichael Shaub 02/24/2016 at 22:570 Comments

Date: 2/4/2016

The projectors should be able to display anything, text, patterns, and maybe even photographic images. To do so the system would need to be able to convert color and greyscale images to 1-bit color. I found some sample code on the Processing forum that showed how to create a dithering effect.

Below is an image of a gradation generated in Processing:

Here's the same gradation dithered by the code:

And, below is the code that creates an animated gradation, dithered for the flip-dot display:

// Gradation 4
// 1-bit image dithering
// 
// For LinkSprite V3 and AlfaZeta flip-dot display
// by Michael Shaub 2016

int dotSize = 50; //diameter of the dots - for preview
PImage pix = createImage(28,7, RGB); //PImage to hold the actual size image
int pixLength = pix.width*pix.height; //total amount of pixels
boolean drawRow = false; //use to alternate drawing a row of dots or skipping

float a = 0.0;
float inc = TWO_PI/25.0;
PImage pixOrig = createImage(28,7, RGB); //PImage to hold the actual size image

void setup(){
  size(1400,350);
  ellipseMode(CORNER);
  noStroke();
  frameRate(3);
  
  //preset the PImage
  pix.loadPixels();
  pixOrig.loadPixels();
  for(int i=0; i<pixLength; i++){
    pix.pixels[i] = color(0); //set each pixel to black
    pixOrig.pixels[i] = color(0); //set each pixel to black
  }
  pix.updatePixels();
  pixOrig.updatePixels();
}

void draw(){
  background(35);
  
  //Draw a gradation
  pixOrig.loadPixels();
  for(int j=0; j<7; j++){
    for(int i=0; i<28; i++){
      pixOrig.pixels[i+j*pix.width] = int(128+sin(a)*128.0); //set the fill color to a smooth gradation
      a = a + inc;
    }
  }
  pixOrig.updatePixels();
  
  //do dithering biz here
  pix.loadPixels();
  pixOrig.loadPixels();
  for(int y=0; y<7; y++){
    for(int x=0; x<28; x++){
      color oldpixel = pixOrig.get(x, y);
      color newpixel = findClosestColor( color(brightness(oldpixel) + random(-100,100)) ); //the wider the range of randomness the "softer" the result. Lower is harder edge
      pix.pixels[x+y*pix.width] = newpixel; //copy colors from one image to the other
    }
  }
  pix.updatePixels();
  
  pix.loadPixels();
  for(int j=0; j<7; j++){
    for(int i=0; i<28; i++){
      fill( pix.pixels[i+j*pix.width] ); //read pixel values, set fill color to that
      ellipse(i*dotSize,j*dotSize,dotSize,dotSize);
    }
  }
}

color findClosestColor(color c) {
  color r;
  // Treshold function
  if (brightness(c) < 128) {
    r = color(0);
  }
  else {
    r = color(255);
  }
  return r;
}

Discussions