Getting distorted image from movie2Serail and Teensy 3.2

Status
Not open for further replies.

cromaled

Member
Hi All,

First of all I would like to Thank you Paul for making such a wonderful library which requires almost negligible code changes to run it.
Great stuff.

I am using Teensy 3.2 to display videos/images on WS2811 driven display. I have tried some prototyping with the RGB modules and things went well.
I got an idea to display the Black and white image which clicked me that what not use the every output of WS2811 chip to drive an individual pixel i.e. OUTR is running 1 pixel, OUTG is running another pixel and OUTB is running another one. In this way I will be able to drive three pixels through a single WS2811 chip.

I design a PCB which has 24 columns and 24 rows and a WS2811 chip on every three pixels. In this way there are 8 chips in every row.
I made some changes in movie2Serial program (as mentioned below) and test it on my newly made PCB.
Now the problem which I am facing is that when I try to display any image (.png) on this small display then that Image is not being properly shown on the display.

Please see the attached file which has both the LED display image as well as the real .png file which I was trying to show on the display.
Also, I tried the same with the video (.mp4) and I got the same results.
I was trying to display letter 'N' image which has white background and letter is written in Black.

I tried many changes in movie2Serial program but with no results.
Can someone please point me that what I am doing wrong over here, as, in my opinion things should work in this way as well as it works with the RGB LEDs.

The main changes which I have done in movie2Serail program is in image2data and colorWiring function.
In image2data function where its converting 8 pixels to 24 bytes, I have changed it to convert 8 pixels to 8 bytes only.
In colorWiring function I have changed the code so that it returns a single color after fetching the value from gamma table.

I am stuck up with this and have been struggling a lot since quite sometime and I would highly appreciate any help from anyone in this regard.


Please note that I am using processing 3.1.2 and OctoWS2811 library.

Code:
import processing.video.*;
import processing.serial.*;
import java.awt.Rectangle;

Movie myMovie = new Movie(this, "C://Users//nshrivas//Documents//Croma//Led Wall//N.png");

float gamma = 1.7;

int numPorts=0;  // the number of serial ports in use
int maxPorts=24; // maximum number of serial ports

Serial[] ledSerial = new Serial[maxPorts];     // each port's actual Serial port
Rectangle[] ledArea = new Rectangle[maxPorts]; // the area of the movie each port gets, in % (0-100)
boolean[] ledLayout = new boolean[maxPorts];   // layout of rows, true = even is left->right
PImage[] ledImage = new PImage[maxPorts];      // image sent to each port
int[] gammatable = new int[256];
int errorCount=0;
float framerate=0;

void setup() {
  String[] list = Serial.list();
  delay(20);
  println("Serial Ports List:");
  println(list);
  //serialConfigure("COM3");  // change these to your port names
 serialConfigure("COM4");
  if (errorCount > 0){ 
    exit();
  }
   for (int i=0; i < 256; i++) {
     gammatable[i] = (int)(pow((float)i / 255.0, gamma) * 255.0 + 0.5);
  }
  size(1000, 1000);  // create the window
  myMovie.loop();  // start the movie :-)
}

 
// movieEvent runs for each new frame of movie data
void movieEvent(Movie m) {
  // read the movie's next frame
  m.read();
  
  //if (framerate == 0) framerate = m.getSourceFrameRate();
  framerate = 30.0; // TODO, how to read the frame rate???
  
  for (int i=0; i < numPorts; i++) {    
    // copy a portion of the movie's image to the LED image
    int xoffset = percentage(m.width, ledArea[i].x);
    int yoffset = percentage(m.height, ledArea[i].y);
    int xwidth =  percentage(m.width, ledArea[i].width);
    int yheight = percentage(m.height, ledArea[i].height);
    
     //println("m.width =" +m.width);
     //println("ledArea[i].x =" +ledArea[i].x);
     //println("m.height =" +m.height);
     //println("ledArea[i].y =" +ledArea[i].y);
     //println("ledArea[i].width =" +ledArea[i].width);
     //println("ledArea[i].height =" +ledArea[i].height);
     
     
    //println("xoffset =" +xoffset);
    //println("yoffset =" +yoffset);
    //println("xwidth =" +xwidth);
    //println("yheight =" +yheight);
    
    ledImage[i].copy(m, xoffset, yoffset, xwidth, yheight,0, 0, ledImage[i].width, ledImage[i].height);
    //ledImage[i].copy(m, 0, 0, 576, 576,0, 0, 24, 24);
    
    // convert the LED image to raw data
    byte[] ledData =  new byte[(ledImage[i].width * ledImage[i].height*3) + 3];
     println("xoffset = "+xoffset);
     println("yoffset = "+yoffset);
     println("Source Image Width = "+xwidth);
     println("Source Image Height = "+yheight);
    
    image2data(ledImage[i], ledData, ledLayout[i]);
    
    if (i == 0) {
      ledData[0] = '*';  // first Teensy is the frame sync master
      int usec = (int)((1000000.0 / framerate) * 0.75);
      ledData[1] = (byte)(usec);   // request the frame sync pulse
      ledData[2] = (byte)(usec >> 8); // at 75% of the frame time
    } else {
      ledData[0] = '%';  // others sync to the master board
      ledData[1] = 0;
      ledData[2] = 0;
    }
    // send the raw data to the LEDs  :-)
    //for(int ii =0;ii<ledData.length;ii++){
    //  println("LEDDATA=  " +ii+" "+ ledData[ii]+"  ");
     
    //}
    ledSerial[i].write(ledData); 
  }
}

// image2data converts an image to OctoWS2811's raw data format.
// The number of vertical pixels in the image must be a multiple
// of 8.  The data array must be the proper size for the image.
void image2data(PImage image, byte[] data, boolean layout) {
  int offset = 3;
  int x, y, xbegin, xend, xinc, mask, modulo;
  int linesPerPin = image.height / 8;
  int pixel[] = new int[8];
  
  for (y = 0; y < linesPerPin; y++) {
    if ((y & 1) == (layout ? 0 : 1)) {
      // even numbered rows are left to right
      xbegin = 0;
      xend = image.width;
      xinc = 1;
    } else {
      // odd numbered rows are right to left
      xbegin = image.width - 1;
      xend = -1;
      xinc = -1;
    }
    int j =0;
    for (x = xbegin; x != xend; x += xinc) {
      for (int i=0; i < 8; i++) {
        // fetch 8 pixels from the image, 1 for each pin
        pixel[i] = image.pixels[x + (y + linesPerPin * i) * image.width];
        modulo = (x + (y + linesPerPin * i) * image.width)%3;
        //println("i m here pixel " + i +"=  " + pixel[i]);
       //println("Pixel Number ="+ (x + (y + linesPerPin * i) * image.width));
       pixel[i] = colorWiring(pixel[i], modulo);
       
         // println("ColorWiring Pixel j= " +j +"  "+ pixel[i]);
          j++;
      }
      // convert 8 pixels to 24 bytes
      for (mask = 0x80; mask != 0; mask >>= 1) {
        byte b = 0;
        for (int i=0; i < 8; i++) {
          if ((pixel[i] & mask) != 0){ 
              
            //b = b | (1 << i); 
            b |= (1 << i);
          }
           // b |= (1 << i);
        }
        int temp = 0;
        int tempOffset = offset-3;;
        if(b <0){          
          temp = (b+255);
          println("data[" + tempOffset + "]=" +temp);  
        }else{
          temp = b;
         println("data[" + tempOffset + "]=" +temp); 
        }
        
        data[offset++] = b;
      }
    }
  } 
}

// translate the 24 bit color from RGB to the actual
// order used by the LED wiring.  GRB is the most common.
int colorWiring(int c, int modulo) {
  int red = 0;
  int green = 0;
  int blue = 0;
  int colorToShow =0;
  if(modulo == 0){         // Red Case
    red = (c & 0xFF0000) >> 16;
    red = gammatable[red];
    colorToShow = red ;//<< 16;
  }
  if(modulo == 1){   // Green Case
    green = (c & 0x00FF00) >> 8;
    green = gammatable[green]; 
    colorToShow = green; //<<8;
  }
  if(modulo == 2){    //Blue Case
    blue = (c & 0x0000FF);
    blue =gammatable[blue];
    colorToShow = blue;
  }
  //int red = (c & 0xFF0000) >> 16;
  //int green = (c & 0x00FF00) >> 8;
  //int blue = (c & 0x0000FF);
  //red = gammatable[red];
  //green = gammatable[green];
  //blue = gammatable[blue];
 // return (green << 16) | (red << 8) | (blue); // GRB - most common wiring
 return colorToShow;

 
}

// ask a Teensy board for its LED configuration, and set up the info for it.
void serialConfigure(String portName) {
  if (numPorts >= maxPorts) {
    println("too many serial ports, please increase maxPorts");
    errorCount++;
    return;
  }
  try {
    ledSerial[numPorts] = new Serial(this, portName);
    if (ledSerial[numPorts] == null) throw new NullPointerException();
    ledSerial[numPorts].write('?');
  } catch (Throwable e) {
    println("Serial port " + portName + " does not exist or is non-functional");
    errorCount++;
    return;
  }
  delay(50);
  String line = ledSerial[numPorts].readStringUntil(10);
  if (line == null) {
    println("Serial port " + portName + " is not responding.");
    println("Is it really a Teensy 3.0 running VideoDisplay?");
    errorCount++;
    return;
  }
  String param[] = line.split(",");
  if (param.length != 12) {
    println("Error: port " + portName + " did not respond to LED config query");
    errorCount++;
    return;
  }
  // only store the info and increase numPorts if Teensy responds properly
  ledImage[numPorts] = new PImage(Integer.parseInt(param[0]), Integer.parseInt(param[1]));
  ledArea[numPorts] = new Rectangle(Integer.parseInt(param[5]), Integer.parseInt(param[6]),
                     Integer.parseInt(param[7]), Integer.parseInt(param[8]));
  ledLayout[numPorts] = (Integer.parseInt(param[5]) == 0);
  numPorts++;
}

// draw runs every time the screen is redrawn - show the movie...
void draw() {
  // show the original video
  image(myMovie, 0, 80);
  
  // then try to show what was most recently sent to the LEDs
  // by displaying all the images for each port.
  for (int i=0; i < numPorts; i++) {
    // compute the intended size of the entire LED array
    int xsize = percentageInverse(ledImage[i].width, ledArea[i].width);
    int ysize = percentageInverse(ledImage[i].height, ledArea[i].height);
    // computer this image's position within it
    int xloc =  percentage(xsize, ledArea[i].x);
    int yloc =  percentage(ysize, ledArea[i].y);
    // show what should appear on the LEDs
    image(ledImage[i], 240 - xsize / 2 + xloc, 10 + yloc);
  } 
}

// respond to mouse clicks as pause/play
boolean isPlaying = true;
void mousePressed() {
  if (isPlaying) {
    myMovie.pause();
    isPlaying = false;
  } else {
    myMovie.play();
    isPlaying = true;
  }
}

// scale a number by a percentage, from 0 to 100
// num is movie and percent is ledArea size
int percentage(int num, int percent) {
  double mult = percentageFloat(percent);
  double output = num * mult;
  return (int)output;
}

// scale a number by the inverse of a percentage, from 0 to 100
int percentageInverse(int num, int percent) {
  double div = percentageFloat(percent);
  double output = num / div;
  return (int)output;
}

// convert an integer from 0 to 100 to a float percentage
// from 0.0 to 1.0.  Special cases for 1/3, 1/6, 1/7, etc
// are handled automatically to fix integer rounding.
double percentageFloat(int percent) {
  if (percent == 33) return 1.0 / 3.0;
  if (percent == 17) return 1.0 / 6.0;
  if (percent == 14) return 1.0 / 7.0;
  if (percent == 13) return 1.0 / 8.0;
  if (percent == 11) return 1.0 / 9.0;
  if (percent ==  9) return 1.0 / 11.0;
  if (percent ==  8) return 1.0 / 12.0;
  return (double)percent / 100.0;
}
 

Attachments

  • 20170109_171158.jpg
    20170109_171158.jpg
    51.5 KB · Views: 61
  • N.jpeg
    N.jpeg
    24.9 KB · Views: 106
Status
Not open for further replies.
Back
Top