Contour Detection

edited April 2015 in Share Your Work

=================

Hi, this is my code for generate gcode from a b/w image using square trace algorithm, i solved some troubles using the answer of PhiLo in:
http://forum.processing.org/two/discussion/6842/user-selected-images-at-setup-and-during-draw

I made this 'couse i wanna people could use personal cnc machines or reprap based machines to create, print, cut, burn whatever... 2D forms from painted files, downloaded ones or scanned ones, No CAD software required, NO manuffacturing skills required, i´ll try to obtain internal contours too.

I wanna autoload metadata of image file to feed ppi (resolution of the image) if someone knows how.. i´ll always grateful i hope someone finds it useful and esay to read, feedback me please

==================

//Created by Fernando Jimenez Diaz Universidad Santo Tomás Col. 2015

//for GCODE
float Z1 = 0.350;
float Z2 = 3.350;
float F = 1800.000;

float pix2mm;
int i,j;
int tooldiameter = 1; //in mm


float ppi = 200.0; //resolution of the image
float mmpin = 25.4; //milimeter per inch


PrintWriter output;

void gcode(){ 

  println("Convirtiendo a Gcode");
  output = createWriter("prueba.gcode");
  output.println("G21 ; set units to millimeters");
  output.println("G28 ; home all axes");
  output.println("G90 ; use absolute coordinates");
  output.println("G1 Z" + Z1 + " F" + F);
  output.println("G1 Z" + Z2 + " F" + F);
  output.println("G1 X" + float(contour[0].XValue())/pix2mm + " Y" + ((img.height-float(contour[0].YValue()))/pix2mm));
  output.println("G1 Z" + Z1 + " F" + F);
  for(i=1;i<b;i++){
    output.println("G1 X" + float(contour[i].XValue())/pix2mm + " Y" + ((img.height-float(contour[i].YValue()))/pix2mm));
  }
  output.println("G1 Z" + Z2);
  output.println("G28 X0  ; home X axis");
  output.println("M84     ; disable motors");
  output.close();  
  println("Gcode finalizado");   
}
//for contour detection
class coord
{

    int x;
    int y;
    coord(int newX,int newY){
         x = newX;
         y = newY;
    }
    public int XValue(){      
      return x;      
    }
    public int YValue(){      
      return y;      
    }    
}

PImage img;  
String input_Path; //Path of the image
String output_Path; //Path of the outputfiles
boolean fileSelected;

coord[] contour;
coord   ap;

byte dir;
int b;

 void setup() {

  size(400, 400);
  selectFile();  

}

void draw() {

  if (fileSelected) 
  {
    //img = loadImage(input_Path);
    //size(img.width, img.height);
    //frame.setSize(img.width, img.height);
    square_tracing();
    fileSelected = false;
  }
  if (img != null)
  {
    //image(img, 0, 0);
  }

}

void square_tracing(){

  img = loadImage(input_Path);  // Load the image into the program 
  size(img.width, img.height);
  frame.setSize(img.width, img.height);
  pix2mm=ppi/mmpin;
  background(255);
  image(img, 0, 0);
  color sample = color(0,0,0);
  color strk = color(255,255,0);
  contourid(sample,strk);
  //save("a.bmp");
  sample = color(255,255,0);
  strk = color(0,255,255);
  contourid(sample,strk);
  //save("b.bmp");
  gcode();   
}  

boolean isColor(int x, int y, color sample){

  color c = get(x,y);
  boolean isColor;
  isColor = (((red(sample)-5)<=red(c))&&(red(c)<=(red(sample)+5)))&&(((green(sample)-5)<=green(c))&&(green(c)<=(green(sample)+5)))&&(((blue(sample)-5)<=blue(c))&&(blue(c)<=(blue(sample)+5)));
  return isColor;

}

void firstPixel(color sample){

  boolean findStartPixel = false;
  j = img.height-1;
  i= 1;

  while((findStartPixel==false) && (j>0)){

      i = 1;      
      while((findStartPixel==false) && (i<img.width)){
        findStartPixel=isColor(i,j,sample);
        if(findStartPixel==true){
          contour[b++] = new coord(i,j);
          ap = new coord(i,j); 
        }
        i=i+1;
    }  
    j=j-1;
  }
}

void turnLeft(){  

  println("Turn Left");
  dir = byte(byte(dir-1) &3);
  //println(byte(dir));
  move();  
}

void turnRight(){  

  println("Turn Right");
  dir = byte(byte(dir+1) &3);
  //println(byte(dir));
  move();  
}

void move(){

  switch (dir){
    case 0: ap = new coord(ap.XValue(),ap.YValue()-1);
            //println(ap.XValue() + ","+ap.YValue() );
    break;
    case 1: ap = new coord(ap.XValue()+1,ap.YValue());
            //println(ap.XValue() + ","+ap.YValue() );
    break;
    case 2: ap = new coord(ap.XValue(),ap.YValue()+1);
            //println(ap.XValue() + ","+ap.YValue() );
    break;
    case 3: ap = new coord(ap.XValue()-1,ap.YValue());
            //println(ap.XValue() + ","+ap.YValue() );
    break;    
  }  
}

void contourid(color sample, color strk){

  dir = 0;
  b=0;
  contour = new coord[(img.width)*(img.height)];
  firstPixel(sample);
  turnLeft();
  while((ap.XValue() != contour[0].XValue() ) || (ap.YValue() != contour[0].YValue() )){
    if(isColor(ap.XValue(),ap.YValue(),sample)==true){
      contour[b++] = new coord(ap.XValue(),ap.YValue());
      turnLeft();      
    }
    else{
      turnRight(); 
    }
  }
  stroke(red(strk),green(strk),blue(strk));
  strokeWeight(tooldiameter*pix2mm);
  for(i=0;i<b;i++){
    point(contour[i].XValue(),contour[i].YValue());
  }

}

/////////////Open file
void fileSelected(File selection) {

  if (selection != null) 
  {
    input_Path = selection.getAbsolutePath();
    fileSelected = true;
    println("User selected " + selection.getAbsolutePath());
  }
} 

void selectFile()
{
  selectInput("Select a file to process:", "fileSelected");
}  

Comments

  • edited April 2015

    Sorry!!! thanks GoToLoop :D i already formated it.

  • edited April 2015

    Hello ! Does it really work ? - I would be surprise to be honest, I did some works with border-detection and your code looks a bit too simple.... I need to try it ! -

    Can you send a B/W image that you use to use in your test ?

    It doesn't work with my B/W picture

  • Hi tlecoz, yeap, my code works so good, try using a B/W image which black pixels never touch the borders, code doesn´t identify internal bordes, only the first one from bottom to top and left to right. (sorry for my english if you find something wrong)

    try something like

    apple-logo-1998

    But, it only take first perimeter.

    don´t forget adjust the resolution here

    float ppi = 200.0;

  • Hello ! Well, it works... but it's far from being perfect.

    "I made this 'couse i wanna people could use personal cnc machines or reprap based machines to create, print, cut, burn whatever... 2D forms from painted files"

    Did you really try to build/print/cut something using your border-data ? I think the resolution is too poor.

    I actually don't understand why you can set a custom resolution for your image, the border detection should be applyed pixel-by-pixel and every pixel has the same size, even if you use a very large picture.

    I built an algo to do exactly what you want some years ago. I made a lot of version of this algo during 3-4 years and at the end, I changed my point of view on the problem : instead of trying to get the border of a shape, I tryed to create a small "video game" where the hero was a kind of wheel able to move "one pixel to the left" or "one pixel to the right", by frame, following the "path of the landscape"and rotating on its own axis. My "wheel" looked like a 2 pixels-stick actually, do you see what I mean ?...

    Another "well-known" solution is "the marching square algorithm". It works very well, the code is easy to implement, and it runs very fast. But it doesn't work 100% of the time (rarely you come into an infinite loop, you will have to modify the size of the image (to modify a bit the border-path) to solve the issue

    http://en.wikipedia.org/wiki/Marching_squares

    Good luck !

  • edited April 2015

    I'm trying to understand your code. It's interesting because at first sight I was surprised to see that it worked to be honnest - no offense :) - .

    I didn't see the usual code I use to see with others border-detection-source-code (like the definition of the 8 pixels around the current pixel, for example) and that's why I was suspicious :)

    What I can say from it now I read the code twice is... "hmm, not sure to understand how it works :D"

    Could you try to explain how it works please ? :)

    EDIT :

    Is it possible to get a better result from your current code with a different configuration ?

  • edited May 2015

    Hi, emmm to many questions, the resolution of the image is reflected in the machinign process, if the image have a good resolution, machining will be smooth, this code works based on this algorithm

    imageprocessingplace.com/downloads_V3/root_downloads/tutorials/contour_tracing_Abeer_George_Ghuneim/square.html

    the relative position was the hardest thing i made, but it result very easy at end using boolean operations, and a cross with a clockwise assignation of two bits binary numbers like this Untitled

    ap means Actual Possition

    I dont need the deffinition of 8 pixels in this algorithm, and.. emm i save each contour coord in a class array. i can´t understand your question "Is it possible to get a better result from your current code with a different configuration ?" what configuration?

    I hope i answered some of your questions.

  • "I hope i answered some of your questions." You did ! Thanks !

    "what configuration?" I thought about the résolution of the image. But actually I didn't understand exactly how it worked.

    The way you reject useless ineer-pixels is smart ! congratulations :)

    I didn't know the square tracing algorithm, it's a funny thing... Thank you for sharing :)

  • it´s funny, i´d never thought reject useless ineer-pixels, i only tried to get the external pixels coordinates, i always ignored the inner ones, i think thats why i got that way XD

    About Emm resolution ... everything comes from the need of keep the size of a scanned piece , my inspiration was to redesign my electric guitar cover from the original one only by the scanning of it and editing it without CAD software, something like "ms paint" hahaha, then put a acrylic in a modified 3d printer that i´m building and get a simple 2D cut.

    when you scan something, the resolution says how many pixels are used to represent a lineal inch, by this way i get enough information to keep the real size of the original image.

    i must to thank you for make me explain my code, i code unvoluntarily some nice algorithms, explaining them, i finally understand what i did correctly hahahaha it took me a time remmember what i was thinking when i made this code :D

Sign In or Register to comment.