Improve Ambilight Arduino Code

edited February 2015 in Arduino

I am currently using the code below to provide my tv with Ambilight on 98 zones. :

I have gotten and edited this code from the ozilight tutorial: http://blog.oscarliang.net/arduino-ambilight-tv-processing-ozilight-2/

Now it is working on my I3 4GB laptop which I use as an HTPC, but it only generating 5 fps. I would like to go to a minimum of at least 15 fps.

I believe this can be made a lot faster, and I am asking you guys for help! :)

The most gain can be gotten in the draw() function so I would like any ideas on the following:

  • How to implement Threads and where do you think I should do it? I was wondering about creating threads for each side of the screen, one for sending serial data etc.
  • Is there a faster way to get the pixel data from the screen? is it better to use an external library etc.
  • Any general improvement points.

All help and ideas are welcome.

Draw()

void draw(){

  //get screenshot into object "screenshot" of class BufferedImage
  BufferedImage screenshot = bot.createScreenCapture(dispBounds);

  // Pass all the ARGB values of every pixel into an array
  int[] screenData = ((DataBufferInt)screenshot.getRaster().getDataBuffer()).getData();

  data_index = 2; // 0, 1 are predefined header

  for(int i=0; i<leds.length; i++) {  // For each LED...

    int r = 0;
    int g = 0;
    int b = 0;

    for(int o=0; o<256; o++)    
    {       //ARGB variable with 32 int bytes where               
        int pixel = screenData[ pixelOffset[i][o] ];            
        r += pixel & 0x00ff0000;            
        g += pixel & 0x0000ff00;            
        b += pixel & 0x000000ff;        
    }           
    // Blend new pixel value with the value from the prior frame   

    ledColor[i][0]  = (short)(((( r >> 24) & 0xff) * (255 - fade) + prevColor[i][0] * fade) >> 8);
    ledColor[i][1]  = (short)(((( g >> 16) & 0xff) * (255 - fade) + prevColor[i][1] * fade) >> 8);
    ledColor[i][2]  = (short)(((( b >>  8) & 0xff) * (255 - fade) + prevColor[i][2] * fade) >> 8);

    serialData[data_index++] = (byte)ledColor[i][0];
    serialData[data_index++] = (byte)ledColor[i][1];
    serialData[data_index++] = (byte)ledColor[i][2];

    float preview_pixel_left  = (float)dispBounds.width  /5 / led_num_x * leds[i][0] ;
    float preview_pixel_top    = (float)dispBounds.height /5 / led_num_y * leds[i][1] ;

    color rgb = color(ledColor[i][0], ledColor[i][1], ledColor[i][2]);
    fill(rgb);  
    rect(preview_pixel_left, preview_pixel_top, preview_pixel_width, preview_pixel_height);

  }

  if(port != null) {

    // wait for Arduino to send data
    for(;;){

      if(port.available() > 0){
        int inByte = port.read();
        if (inByte == 'y')
          break;
      }

    }
    port.write(serialData); // Issue data to Arduino

  }

  // Benchmark, how are we doing?
  println("" + frameRate);
  arraycopy(ledColor, 0, prevColor, 0, ledColor.length);

}

All Code

import java.awt.*;
import java.awt.image.*;
import processing.serial.*;
import java.io.FileWriter;

/*
// using 12 RGB LEDs
static final int led_num_x = 4;
static final int led_num_y = 4;
static final int leds[][] = new int[][] {
  {1,3}, {0,3}, // Bottom edge, left half
  {0,2}, {0,1}, // Left edge
  {0,0}, {1,0}, {2,0}, {3,0}, // Top edge
  {3,1}, {3,2}, // Right edge
  {3,3}, {2,3}, // Bottom edge, right half
};

*/

// using 25 RGB LEDs
static final int led_num_x = 10;
static final int led_num_y = 6;
static final int leds[][] = new int[][] {
  {2,5}, {1,5}, {0,5}, // Bottom edge, left half
  {0,4}, {0,3}, {0,2}, {0,1}, // Left edge
  {0,0}, {1,0}, {2,0}, {3,0}, {4,0}, {5,0}, {6,0}, {7,0}, {8,0}, {9,0}, // Top edge
  {9,1}, {9,2}, {9,3}, {9,4}, // Right edge
  {9,5}, {8,5}, {7,5}, {6,5}  // Bottom edge, right half

};

static final short fade = 70;

static final int minBrightness = 120;

// Preview windows
int window_width;
int window_height;
int preview_pixel_width;
int preview_pixel_height;

int[][] pixelOffset = new int[leds.length][256];

// RGB values for each LED
short[][]  ledColor    = new short[leds.length][3],
      prevColor   = new short[leds.length][3];  

byte[][]  gamma       = new byte[256][3];
byte[]    serialData  = new byte[ leds.length * 3 + 2];
int data_index = 0;

//creates object from java library that lets us take screenshots
Robot bot;

// bounds area for screen capture         
Rectangle dispBounds;

// Monitor Screen information    
GraphicsEnvironment     ge;
GraphicsConfiguration[] gc;
GraphicsDevice[]        gd;

Serial           port;

PrintWriter output;

void createLog()
{
 // Create a new file in the sketch directory
  output = createWriter("log.txt");
  output.flush();  
  output.close();
}

void writeLog(String toWrite)
{   
  File f = new File("log.txt");
  if(!f.exists()){
    println("Creating log");
    createLog();
  }
  try {
    FileWriter output = new FileWriter(sketchPath + "/log.txt", true);
    output.write(toWrite);
    output.write("\r\n");
    output.flush();
    output.close();
  }catch (IOException e){
      e.printStackTrace();
  }


//  output = createWriter("positions.txt");  
//  output.println(toWrite);
//  output.flush();
}

void setup(){

  int[] x = new int[16];
  int[] y = new int[16];

  // ge - Grasphics Environment
  ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
  // gd - Grasphics Device
  gd = ge.getScreenDevices();
  DisplayMode mode = gd[0].getDisplayMode();
  dispBounds = new Rectangle(0, 0, mode.getWidth(), mode.getHeight());
  println("display width: ");
  println(dispBounds.width);

  println("mode width: ");
  println(mode.getWidth());

  println("mode height : ");
  println(mode.getHeight());


  // Preview windows
  window_width      = mode.getWidth()/5;
  window_height      = mode.getHeight()/5;
  preview_pixel_width     = window_width/led_num_x;
  preview_pixel_height   = window_height/led_num_y;

  // Preview window size
  size(window_width, window_height);

  //standard Robot class error check
  try   {
    bot = new Robot(gd[0]);
  }
  catch (AWTException e)  {
    println("Robot class not supported by your system!");
    exit();
  }

  float range, step, start;

  for(int i=0; i<leds.length; i++) { // For each LED...

    // Precompute columns, rows of each sampled point for this LED

    // --- for columns -----
    range = (float)dispBounds.width / led_num_x;
    // we only want 256 samples, and 16*16 = 256
    step  = range / 16.0; 
    start = range * (float)leds[i][0] + step * 0.5;

    for(int col=0; col<16; col++) {
      x[col] = (int)(start + step * (float)col);
    }

    // ----- for rows -----
    range = (float)dispBounds.height / led_num_y;
    step  = range / 16.0;
    start = range * (float)leds[i][1] + step * 0.5;

    for(int row=0; row<16; row++) {
      y[row] = (int)(start + step * (float)row);
    }

    // ---- Store sample locations -----

    // Get offset to each pixel within full screen capture
    for(int row=0; row<16; row++) {
      for(int col=0; col<16; col++) {
        pixelOffset[i][row * 16 + col] = y[row] * dispBounds.width + x[col];
      }
    }

  }

  // Open serial port. this assumes the Arduino is the
  // first/only serial device on the system.  If that's not the case,
  // change "Serial.list()[0]" to the name of the port to be used:
  // you can comment it out if you only want to test it without the Arduino

 //port = new Serial(this, Serial.list()[0], 115200);

  // A special header expected by the Arduino, to identify the beginning of a new bunch data.  
  serialData[0] = 'o';
  serialData[1] = 'z';

  println("Writing pixels offset");
 // WritePixelOffset();  
}

void WritePixelOffset()
{
  writeLog("x length : " + pixelOffset.length + " y length : " + pixelOffset[0].length);

  for(int f = 0; f < pixelOffset.length; f++)
  {
     for(int g = 0; g < pixelOffset[f].length; g ++)
     {
        writeLog("f: " + f + "  g: " + g + " value: " + pixelOffset[f][g]); 
     }
  }
}

void draw(){

  //get screenshot into object "screenshot" of class BufferedImage
  BufferedImage screenshot = bot.createScreenCapture(dispBounds);

  // Pass all the ARGB values of every pixel into an array
  int[] screenData = ((DataBufferInt)screenshot.getRaster().getDataBuffer()).getData();

  data_index = 2; // 0, 1 are predefined header

  for(int i=0; i<leds.length; i++) {  // For each LED...

    int r = 0;
    int g = 0;
    int b = 0;

    for(int o=0; o<256; o++)    
    {       //ARGB variable with 32 int bytes where               
        int pixel = screenData[ pixelOffset[i][o] ];            
        r += pixel & 0x00ff0000;            
        g += pixel & 0x0000ff00;            
        b += pixel & 0x000000ff;        
    }           
    // Blend new pixel value with the value from the prior frame   

    ledColor[i][0]  = (short)(((( r >> 24) & 0xff) * (255 - fade) + prevColor[i][0] * fade) >> 8);
    ledColor[i][1]  = (short)(((( g >> 16) & 0xff) * (255 - fade) + prevColor[i][1] * fade) >> 8);
    ledColor[i][2]  = (short)(((( b >>  8) & 0xff) * (255 - fade) + prevColor[i][2] * fade) >> 8);

    serialData[data_index++] = (byte)ledColor[i][0];
    serialData[data_index++] = (byte)ledColor[i][1];
    serialData[data_index++] = (byte)ledColor[i][2];

    float preview_pixel_left  = (float)dispBounds.width  /5 / led_num_x * leds[i][0] ;
    float preview_pixel_top    = (float)dispBounds.height /5 / led_num_y * leds[i][1] ;

    color rgb = color(ledColor[i][0], ledColor[i][1], ledColor[i][2]);
    fill(rgb);  
    rect(preview_pixel_left, preview_pixel_top, preview_pixel_width, preview_pixel_height);

  }

  if(port != null) {

    // wait for Arduino to send data
    for(;;){

      if(port.available() > 0){
        int inByte = port.read();
        if (inByte == 'y')
          break;
      }

    }
    port.write(serialData); // Issue data to Arduino

  }

  // Benchmark, how are we doing?
  println("" + frameRate);
  arraycopy(ledColor, 0, prevColor, 0, ledColor.length);

}

Answers

Sign In or Register to comment.