ArrayIndex error on Voronoi diagram with Byron's Mesh lib

edited May 2015 in Library Questions

Hi all,

I'm playing with the Mesh library to create a voronoi diagram based on an existing image. I want to generate the voronoi sites based on the brightness of interesting pixels in the picture, with the goal of achieving something like what Golan Levin created back in 2000 http://www.flong.com/projects/zoo/.

My approach is to generate a bunch of random points in a 1D array containing "vpoint" objects, assign them a "worthiness" level based on the brightness of the pixel that lays on the randomly created point, and then discriminate the points with a threshold (only add significant points to the "float sites[][]" array to create the voronoi).

I'm able to do all I want but create the voronoi diagram. The 1D array of object points gets nicely populated, then the 2D array containing the "worthy" points gets nicely filled by appending new rows to the array. I decided to append to the array so it became a "dynamic" array that would only contain the points of interest without empty rows. It all works, but when I run the myVoronoi = new Voronoi( sites ); line, I get the ArrayPointerOutOfRangeException: 0; error (Processing highlights this same line of code), and I just can't figure out what I'm doing wrong.

If I understand correctly, that error means the array has a length of 0 therefore can't be read, is this correct? If that's the case, I have a couple of lines that print the length of this array in different parts of the code and it always get populated. Another proof is the following picture. The picture on the left is the source, the image on the right is the plot of all the points contained in the sites[][] array.

proof

Here's my code. I hope you guys can help me see where I'm messing things up.

` /* define a threshold Load the image pixels pic a random location For each point location, measure the brightness of the corresponding pixel Assign a likelihood of a valid voronoi site location based on the brightness value of the pixel Add the location as a voronoi site to the array of points only if the likelihood is greater than the threshold create the voronoi diagram based on the array of voronoi points draw the edges of the voronoi diagram */

// Get Byron's lib for Voronoi and Delaunay
import megamu.mesh.*;

PImage img;           // Source image
float V_prob = 0;     // probability that a voronoi site lays here 
float threshold = 70.0;   // % of max brightness
int max_sites = 15000;    // max number of sites we'll create

Voronoi myVoronoi;
VPoint[] vpoints;       // 1D array to store the potential point objects
float[][] sites;      // 2D array to store the voronoi sites (worthy points)
float[][] myEdges;    // 2D array to store the voronoi edges

int diam = 2;         // diameter of the ellipses that indicate the voronoi sites

void setup() {
  img = loadImage("8.jpg");         // Load the image to evaluate
  size(img.width, img.height);      // Create a window the size of the source img 
  background(255);                  // With White BG
  sites = new float[1][2];          // Create the 2D array to save the points
  sites[0][0]=0;
  sites[0][1]=0;
  vpoints = new VPoint[max_sites];  // initialize the array for points
  for(int i=0; i<max_sites;i++){
    vpoints[i] = new VPoint();      // Fill the array with random points
  }
}

void draw(){    


  initPoints();                // Get the voronoi sites
  //print(sites[sites.length-1][0]);print(",");println(sites[sites.length-1][1]);
  //println(sites.length);

  myVoronoi = new Voronoi(sites);          // Create the voronoi diagram
  myEdges = myVoronoi.getEdges();          // Get the edges of the voronoi
  drawSites();                             // indicate where the sites are
  drawEdges();                           // draw the voronoi edges
  noLoop();                                // we don't need to loop
  saveFrame("Voronoied-such a test.png");  // save dat art!

}

void initPoints(){
 img.loadPixels();         // Get the pixels so we can work with them

  for(int i=0; i<max_sites; i++){
   // Get the brightness of the corresponding pixel
   // at the point location to see if it's worth it   
   int locX = vpoints[i].x;  
   int locY = vpoints[i].y;
   int loc = locX + locY*img.width;     // find the pixel in the 1D array that stores the pixels
   //debug
   //print(locX);print(" ");print(locY);print(" ");println(loc);
   //print(img.width); print(img.height); println(img.pixels.length);
   float luma = brightness(img.pixels[loc]);  // Get the brightness of the pixel
   // Assing the probability to the voronoi point
   // based on the brightness of the pixel
   vpoints[i].prob = map(luma,0,255,0,100);
   // now, compare this probability to the threshold
   if(vpoints[i].prob < threshold){
     //Add the point as a voronoi site
     sites = (float[][])append(sites, new float[]{locX,locY});
     if(sites.length > max_sites) break;
     //println("Site added");
     //print(sites[sites.length-1][0]);print(",");println(sites[sites.length-1][1]);
     //println(sites.length);
   } else { /*println("Not worth it");*/}

 }//end for

}

void drawSites(){
  for(int h=0;h<sites.length;h++){
   //print(sites[h][0]);print(",");println(sites[h][1]);
   float px = sites[h][0];
   float py = sites[h][1];
   ellipse(px,py,diam,diam);
  }
}

void drawEdges(){
// Draw the Voronoi Edges
 for(int i=0; i<myEdges.length; i++){
  float startX = myEdges[i][0];
  float startY = myEdges[i][1];
  float endX = myEdges[i][2];
  float endY = myEdges[i][3];
  stroke(10);                        // almost black
  line(startX, startY, endX, endY);
 }
}


// Let's create an object for each voronoi site
class VPoint
{
  int x, y;    // Site coordinates
  color c;     // to store color information
  float prob;    // to store the probability of worthiness

  VPoint()
  { // Assign the point location
    x = int(random(img.width));
    y = int(random(img.height));
    c = color(random(128), random(128,192), random(128,255));
  }
}`

Many thanks! Frix

Answers

  • Hm... gets weirder. I tried the Delaunay triangulation with the same array of points and it does work:

    Voronoied-such a test-8a

    Any clue why the Voronoi might be saying ArrayIndexOutOfBoundsException: 0 ??!

    So frustrating.

  • edited May 2015 Answer ✓

    OK... don't worry no more! Rewrote the program, cleaned it up a lot by separating the different processes as independent functions (which may not be the best way, but helped me keep everything neat and tidy), and now it works!

    The "meat" of the program is the discriminatePoints() function. The rest of the additional functions are to plot the edges or the sites. I did this to have the flexibility of changing the color of the lines or to be able to plot both the Voronoi and the Delaunay without writing more lines.

    I'm also drawing the Delaunay triangulation just for the kicks:

    PicBeenVoronoied

    The [uncommented] code: `

    /** Voronoi that pic
    Oscar Frias (oscar@oscarfrias.com)
    Create a Voronoi Diagram from an existing picture.
    The Voronoi sites are randomly generated and discriminated
    by using the brightness of the corresponding pixel
    
    Thanks to:
    Lee Byron @leeb for the library
    Golan Levin @golan for the inspiration
    */
    import megamu.mesh.*;
    
    PImage img;
    float threshold = 50;  //% of max brightness
    
    int maxPoints = 15000;
    float[][] points;
    float[][] vPoints;
    
    void setup(){
      img = loadImage("8.jpg");
      size(img.width, img.height);
      background(255);
    
      points = new float[maxPoints][2];
      fillRandomPoints(points);
      vPoints = new float[points.length][2];
      println(vPoints.length);
      vPoints = discriminatePoints(points, vPoints, img);
      println(vPoints.length);
    
      noLoop();
    }
    
    void draw(){
      background(255);
      println(vPoints.length);
      Voronoi myVoronoi = new Voronoi(vPoints);
      Delaunay myDelaunay  = new Delaunay(vPoints);
      float[][] myVEdges = myVoronoi.getEdges();
      float[][] myDEdges = myDelaunay.getEdges();
      drawEdges(myVEdges,0);  //draw them black
      drawEdges(myDEdges,1);  //draw them red
      drawSites(vPoints,5);    //5pix ellipses
    }
    
    void drawSites(float[][] vPoints, int diam){
       for(int h=0;h<vPoints.length;h++){
       //print(sites[h][0]);print(",");println(sites[h][1]);
       float px = vPoints[h][0];
       float py = vPoints[h][1];
       stroke(0);  //black
       fill(0);
       ellipse(px,py,diam,diam);
      }
    }
    
    void drawEdges(float[][] vEdges, int col){
      switch(col){
        case 0:      //black
          stroke(0);
          break;
        case 1:      //red
          stroke(255,0,0); 
          break;
        case 2:      //green
          stroke(0,255,0);
          break; 
        case 3:      //blue
          stroke(0,0,255);
          break;
        case 4:      //white
          stroke(255);
          break;  
      }  
    
     for(int i=0; i<vEdges.length; i++){
      float startX = vEdges[i][0];
      float startY = vEdges[i][1];
      float endX = vEdges[i][2];
      float endY = vEdges[i][3];
      line(startX, startY, endX, endY);
      }
    }
    
    void fillRandomPoints(float[][] pArray){
        for(int i=0; i<pArray.length;i++){
        pArray[i][0] = random(width);
        pArray[i][1] = random(height);
      }  
    }
    
    
    float[][] discriminatePoints(float[][] inArray, float[][]outArray, PImage pic){
     pic.loadPixels();
     int counter=0;
    
     for(int i=0; i<inArray.length;i++){
       int x = int(inArray[i][0]);
       int y = int(inArray[i][1]);
       int pos = x + y*pic.width;
    
       float luma = brightness(pic.pixels[pos]);
       float prob = map(luma,0,255,0,100);
    
       if(prob < threshold){
         outArray[counter][0] = inArray[i][0];
         outArray[counter][1] = inArray[i][1];
         counter++;
         //print("So worthy: ");println(counter);
       } else { /*println("Not worthy");*/}
    
     }
       float tempArray[][] = new float[counter-1][2];
       arraycopy(outArray,0,tempArray,0,tempArray.length);
       outArray = tempArray;
    
    return outArray;
     }
    

    `

Sign In or Register to comment.