Search code examples
javaprocessingdetectionedge-detectionedges

Drawing Blob edges that are on sketch edge in Processing


I've been working in Processing with the blobDetection library, making contour maps from heightmaps. I am now exporting each layer as SVGs for laser cutting but blobs that are on the edge of the sketch are not complete, the edge vertices are not drawn.

Here is a simplified example:

  1. The image I start with, white areas will become blobs

enter image description here

  1. The SVG output, missing the edges

enter image description here

import processing.svg.*;
import blobDetection.*;
import peasy.*;
import processing.pdf.*;
PeasyCam cam;

PImage img;

float levels = 10;

BlobDetection[] contours = new BlobDetection[int(levels)];

//Creating array to store the blob objects
ArrayList<Ring> rings = new ArrayList<Ring>();

void setup() {

  size(480, 360, P3D);
  surface.setResizable(true);

  img = loadImage("maptest2.jpg");
  surface.setSize(img.width, img.height);

  cam = new PeasyCam(this, img.width, img.height, 0, 1500);
  colorMode(HSB, 360, 100, 100);

  for (int i=0; i<levels; i++) {
    contours[i] = new BlobDetection(img.width, img.height);
    contours[i].setThreshold(i/levels);
    contours[i].computeBlobs(img.pixels);
  }

  for (int i = 0; i < rings.size(); i++) {
    System.out.println("id: " + rings.get(i).getId());
    System.out.println("lvl: " + rings.get(i).getLvl());
    System.out.println("x: " + rings.get(i).getX());
    System.out.println("y: " + rings.get(i).getY());
    System.out.println();
  }

  noLoop();
}


void draw() {

  for (int i=0; i<levels; i++) {  

    beginRecord(SVG, "level-"+i+".svg");

    drawContours(i);
    println("drew level " + i);

    println("saved as: level-"+i+".svg");
    endRecord();
    println();

    if(i == levels-1){
      println("finished");
    }

  }

  System.out.println("Number of blobs (rings.size()): " + rings.size());
   println();

   for (int i = 0; i < rings.size(); i++){
   System.out.println("id: " + rings.get(i).getId());
   System.out.println("lvl: " + rings.get(i).getLvl());
   System.out.println("x: " + rings.get(i).getX());
   System.out.println("y: " + rings.get(i).getY());
   System.out.println();
   }

}

void drawContours(int i) {
  Blob b;
  EdgeVertex eA, eB;
  for (int n=0; n<contours[i].getBlobNb(); n++) {
    b=contours[i].getBlob(n);

    //Condition for drawing only blobs bigger than 5% of width and 5% of height
    if(b.w*width>.05*width && b.h*height>.05*height){

      if (b!=null) {
        stroke(250, 75, 90);

        for (int m=0; m<b.getEdgeNb(); m++) {
          eA = b.getEdgeVertexA(m);
          eB = b.getEdgeVertexB(m);

          //This part draws the blobs.

          if (eA !=null && eB !=null)
           line(
           eA.x*img.width, eA.y*img.height, 
           eB.x*img.width, eB.y*img.height 
           );

           println("eA.x: " + eA.x);
           println("eA.y: " + eA.y);
           println("eB.x: " + eB.x);
           println("eB.y: " + eB.y);
           println();

           ////////////
           //Here are my various attempts at drawing these rogue edges!
           //I commented them out

           /*      
           //Checking if vertex has a point at x=0
           if (b.getEdgeVertexA(m).x == 0 && b.getEdgeVertexB(b.getEdgeNb()).x == 0){


                 line(  b.getEdgeVertexA(0).x*img.width, b.getEdgeVertexA(0).y*img.height, 
                        b.getEdgeVertexA(m).x*img.width, b.getEdgeVertexA(m).y*img.height   );

                 println("////");
                 println("x making line (scaled 0-1): ");
                 //println(eA.x, eA.y, eB.x, eB.y);
                 println(  b.getEdgeVertexA(0).x, b.getEdgeVertexA(0).y, 
                           b.getEdgeVertexA(m).x, b.getEdgeVertexA(m).y   );

                 println("////");
           }

           //Checking if vertex has a point at y=0
           if (b.getEdgeVertexA(m).y == 0 && b.getEdgeVertexB(b.getEdgeNb()).y == 0){

             line(  b.getEdgeVertexA(0).x*img.width, b.getEdgeVertexA(0).y*img.height, 
                    b.getEdgeVertexA(m).x*img.width, b.getEdgeVertexA(m).y*img.height   );

                 println("////");
                 println("y making line (scaled 0-1): ");
                 //println(eA.x, eA.y, eB.x, eB.y);
                 println(  b.getEdgeVertexA(0).x, b.getEdgeVertexA(0).y, 
                           b.getEdgeVertexA(m).x, b.getEdgeVertexA(m).y   );

                 println("////");

           }

           if (b.getEdgeVertexA(m).x == 0 && b.getEdgeVertexB(b.getEdgeNb()).y == 0){

             line(  b.getEdgeVertexA(0).x*img.width, b.getEdgeVertexA(0).y*img.height, 0, 0   );
             println("drew to 0,0");
           }

           if (b.getEdgeVertexA(m).y == 0 && b.getEdgeVertexB(b.getEdgeNb()).x == 0){

             line(  b.getEdgeVertexA(m).x*img.width, b.getEdgeVertexA(m ).y*img.height, 0, 0   );
             println("drew to 0,0");
           }
           */
           ////////////

        }

        //Adding objects to the rings ArrayList
        rings.add(new Ring(String.valueOf(rings.size()+1), (int) i, (double) b.x*100, (double) b.y*100));
        }
    }
  }
}

Now I know I know my code does not include any code to draw these edges, so they are not really missing, but I am kind of lost as to where to start to properly draw these.

I have thought about:

  • checking if any coordinate of a vertex that constitutes the blob is equal to 0 (min width or height) or 1 (max width or height)
  • drawing a strait line from the blob extremities to the top left corner of the sketch if for example the blob is overlapping that corner
  • many other conditions, but so many I am confused...

Anyone have an idea of how to order conditions, or how to approach the problem... Help greatly appreciated!


Solution

  • A colleague of mine stumbled upon a smart solution to this problem, which is to increase the size of the original image by one pixel on each side, and to make these pixels black. So if the image was 100x100px, it would then be 102x102px. We add black pixels in our case, but I think the best way to do this would be to calculate the cutoff color that determines whether to draw a blob or not, and to assign these new border pixels a color slightly or just below/above that (depending on you needs).

    The code is mostly the same as in my original question, except the part where we import and modify the image:

    PImage ref;
    PImage output;
    String filename = "maptest2.jpg";
    
    // // //
    
    //Here I skip some parts that are unchanged
    
    // // //
    
    void setup() {
    
      size(480, 360, P3D);
      surface.setResizable(true);
    
      ref = loadImage(filename);
      
      // create a copy of reference image with a black border
      output = createImage(ref.width+2, ref.height+2, RGB);
      
      for(int i=0; i < output.pixels.length; i++){
        output.pixels[i] = color(0);
      }
      
      output.updatePixels();
      output.set(1, 1, ref);
      
      surface.setSize(output.width, output.height);
    
      cam = new PeasyCam(this, output.width, output.height, 0, 1500);
    

    The resulting SVG output, with the desired outer borders:

    enter image description here

    This is not the solution I was expecting when I asked this question, but it works pretty well. It certainly could be perfected a bit, but I tested it on a bunch of other height maps I'm working with and the results are consistently satisfying.

    Hope this helps!