Last active
September 14, 2017 07:02
-
-
Save vishnumaiea/f3e84b11c684bac347196f374b83a372 to your computer and use it in GitHub Desktop.
Freehand algorithm for my Mini CNC Plotter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//=========================================================================// | |
// | |
// -- Frehand plotting algorithm -- | |
// | |
// Author : Vishnu M Aiea | |
// Date created : 11:50:01 PM, 23-06-2017, Friday | |
// Last modified : 12:28 PM 14-09-2017, Thursday | |
// | |
// It now works for every solid image. | |
// | |
//=========================================================================// | |
//import java.util.*; | |
//import java.lang.*; | |
static final int EDGE_MARKER = 120; //pixel markers | |
static final int END_MARKER = 50; | |
static final int NIB_MARKER = 57; | |
static final int START_MARKER = 68; | |
static final int CORNER_MARKER = 70; | |
static final int ISOLATED_MARKER = 87; | |
static final int POINT_MARKER = 93; | |
static final int INVALID = -1; | |
static final int DEFAULT_IMAGE_WIDTH = 327; | |
static final int DEFAULT_IMAGE_HEIGHT = 250; | |
imgObject selectedImage; //array for image | |
imgObject outlineImage; | |
imgObject solidImage; | |
int iterationCount; | |
int EXIT_AT_ERROR = 1; | |
int DEBUG_LEVEL = 0; | |
int L = -1; | |
int TL = -1 * (DEFAULT_IMAGE_WIDTH + 1); //assuming opened image has default W and H | |
int T = -1 * (DEFAULT_IMAGE_WIDTH); | |
int TR = -1 * (DEFAULT_IMAGE_WIDTH - 1); | |
int R = 1; | |
int BR = (DEFAULT_IMAGE_WIDTH + 1); | |
int B = DEFAULT_IMAGE_WIDTH; | |
int BL = (DEFAULT_IMAGE_WIDTH - 1); | |
//scanning pattern for array postitions | |
int [] scanningPattern = new int [] {L, TL, T, TR, R, BR, B, BL}; //square pattern | |
//int [] scanningPattern = new int [L, T, R, B, TL, TR, BR, BL]; //diamond pattern | |
//int [] scanningPattern = new int [TL, TR, BR, BL, L, T, R, B]; //star pattern | |
//scanning pattern for XY coordinates | |
//int [] scanningPatternX = new int [] {-1, -1, 0, 1, 1, 1, 0, -1}; //L, TL, T, TR, R, BR, B, BL - cyclic pattern | |
//int [] scanningPatternY = new int [] {0, -1, -1, -1, 0, 1, 1, 1}; //L, TL, T, TR, R, BR, B, BL | |
int [] scanningPatternX = new int [] {-1, 0, 1, 0, -1, -1, 1, 1}; //L, T, R, B, BL, TL, TR, BR - diamond pattern (friends first) | |
int [] scanningPatternY = new int [] {0, -1, 0, 1, 1, -1, -1, 1}; //L, T, R, B, BL, TL, TR, BR | |
//=========================================================================// | |
public class imgObject { | |
public class pixelObject { | |
int X, Y, position; | |
int [] adjacentPixels = new int[8]; | |
//=========================================================================// | |
pixelObject () { //empty constructor | |
X = 0; | |
Y = 0; | |
position = 0; | |
} | |
//-------------------------------------------------------------------------// | |
pixelObject (int a) { //takes pixel postion in array | |
position = a; | |
X = (position % imageWidth); | |
Y = (position - (position % imageWidth)) / imageWidth; | |
} | |
//-------------------------------------------------------------------------// | |
pixelObject (pixelObject sourceObject) { //copy constructor | |
this.X = sourceObject.X; | |
this.Y = sourceObject.Y; | |
sourceObject.position = (sourceObject.X + (sourceObject.Y * imageWidth)); | |
this.position = sourceObject.position; | |
} | |
//=========================================================================// | |
int getPosition() { | |
calculatePosition(); //find the pixel location in PImage from coords | |
return position; | |
} | |
//=========================================================================// | |
void set(int a) { //takes array position | |
if(a >= 0) { | |
position = a; | |
calculateXY(); | |
findAdjacentPixels(); | |
} | |
else if(a == INVALID) { | |
position = INVALID; | |
X = INVALID; | |
Y = INVALID; | |
} | |
else { | |
println("Error at set(int) : Negative input => " + a); | |
println("Current Pixel : Position = " + currentPixel.position + " X = " + currentPixel.X + " Y = " + currentPixel.Y); | |
println(); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
} | |
} | |
//-------------------------------------------------------------------------// | |
void set(pixelObject sourceObject) { //takes a pixel object | |
this.X = sourceObject.X; //copy X value | |
this.Y = sourceObject.Y; //copy Y value | |
sourceObject.position = (sourceObject.X + (sourceObject.Y * imageWidth)); //recalculate both positions | |
this.position = sourceObject.position; | |
findAdjacentPixels(); | |
} | |
//-------------------------------------------------------------------------// | |
void set(int x, int y) { | |
if((x >= 0) && (y >= 0) && (x < imageWidth) && (y < imageHeight)) { | |
X = x; | |
Y = y; | |
calculatePosition(); | |
findAdjacentPixels(); | |
} | |
else { | |
println("Error at set(int, int) : XY out of bound"); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
} | |
} | |
//=========================================================================// | |
void calculateXY() { | |
X = (position % imageWidth); //calculate X from position | |
Y = (position - (position % imageWidth)) / imageWidth; //calculate Y from position | |
} | |
//=========================================================================// | |
void calculatePosition() { | |
position = (X + (Y * imageWidth)); //find the pixel location in PImage from coords | |
} | |
//=========================================================================// | |
//finds all the 8 (excpet boundary pixels) adjacent pixels of a pixel | |
void findAdjacentPixels () { | |
calculateXY(); | |
int x, y; | |
for(int i=0; i<8; i++) { | |
x = X + scanningPatternX[i]; //calculate the adjacent pixel's x | |
y = Y + scanningPatternY[i]; //calculate the adjacent pixel's y | |
if((x>=0) && (y>=0) && (x < imageWidth) && (y < imageHeight)) { //if within limits | |
adjacentPixels[i] = (x + (y * imageWidth)); //save as positions | |
} | |
else { | |
adjacentPixels[i] = INVALID; | |
} | |
} | |
} | |
//=========================================================================// | |
//determines if a pixel is friend of the pixel object | |
boolean isFriend (int pos) { //position in image array | |
if((pos < 0) || (pos > ((imageWidth * imageHeight)-1))) { | |
println("Error at isFriend(int) : invalid position parameter"); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
} | |
int x = (pos % imageWidth); //calculate X from position | |
int y = (pos - (pos % imageWidth)) / imageWidth; //calculate Y from position | |
calculateXY(); //recalculate pixel objects coordinates | |
if(!isPixelBlack(pos)) return false; | |
else { | |
if((x == X) && (y == (Y-1))) return true; //top | |
else if((x == (X+1)) && (y == Y)) return true; //right | |
else if((x == X) && (y == (Y+1))) return true; //bottom | |
else if((x == (X-1)) && (y == Y)) return true; //left | |
else return false; | |
} | |
} | |
//-------------------------------------------------------------------------// | |
//overloaded version | |
boolean isFriend (int x, int y) { | |
if((x<0) || (y<0) || (x > (imageWidth-1)) || (y > (imageHeight-1))) { | |
println("Error at isFriend(int, int) : invalid input coordinates"); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
} | |
calculateXY(); //recalculate pixel objects coordinates | |
if(!isPixelBlack(x, y)) return false; | |
else { | |
if((x == X) && (y == (Y-1))) return true; //top | |
else if((x == (X+1)) && (y == Y)) return true; //right | |
else if((x == X) && (y == (Y+1))) return true; //bottom | |
else if((x == (X-1)) && (y == Y)) return true; //left | |
else return false; | |
} | |
} | |
//=========================================================================// | |
//checks if a pixel is neighbor of the pixel object | |
boolean isNeighbor (int pos) { | |
if((pos < 0) || (pos > ((imageWidth * imageHeight)-1))) { | |
println("Error at isNeighbor(int) : invalid position parameter"); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
} | |
int x = (pos % imageWidth); //calculate X from position | |
int y = (pos - (pos % imageWidth)) / imageWidth; //calculate Y from position | |
calculateXY(); //recalculate pixel objects coordinates | |
if(!isPixelBlack(pos)) return false; | |
else { | |
if((x == (X-1)) && (y == (Y-1))) return true; //top-left | |
else if((x == (X+1)) && (y == (Y-1))) return true; //top-right | |
else if((x == (X+1)) && (y == (Y+1))) return true; //bottom-right | |
else if((x == (X-1)) && (y == (Y+1))) return true; //bottom-left | |
else return false; | |
} | |
} | |
//-------------------------------------------------------------------------// | |
//overloaded version | |
boolean isNeighbor (int x, int y) { | |
if((x<0) || (y<0) || (x > (imageWidth-1)) || (y > (imageHeight-1))) { | |
println("Error at isNeighbor(int, int) : invalid input coordinates"); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
} | |
calculateXY(); //recalculate pixel objects coordinates | |
if(!isPixelBlack(x, y)) return false; | |
else { | |
if((x == (X-1)) && (y == (Y-1))) return true; //top-left | |
else if((x == (X+1)) && (y == (Y-1))) return true; //top-right | |
else if((x == (X+1)) && (y == (Y+1))) return true; //bottom-right | |
else if((x == (X-1)) && (y == (Y+1))) return true; //bottom-left | |
else return false; | |
} | |
} | |
//=========================================================================// | |
//determines if a pixel is either a neighbor or friend of the pixel object | |
boolean isAdjacent (int pos) { | |
if((pos < 0) || (pos > ((imageWidth * imageHeight)-1))) { | |
println("Error at isAdjacent(int) : invalid position parameter"); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
} | |
calculateXY(); //recalculate pixel objects coordinates | |
if(!isPixelBlack(pos)) return false; | |
else if(isFriend(pos) || isNeighbor(pos)) return true; | |
else return false; | |
} | |
//-------------------------------------------------------------------------// | |
boolean isAdjacent (int x, int y) { | |
if((x<0) || (y<0) || (x > (imageWidth-1)) || (y > (imageHeight-1))) { | |
println("Error at isNeighbor(int, int) : invalid input coordinates"); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
} | |
calculateXY(); //recalculate pixel objects coordinates | |
if(!isPixelBlack(x, y)) return false; | |
else if(isFriend(x, y) || isNeighbor(x, y)) return true; | |
else return false; | |
} | |
//=========================================================================// | |
} //pixelObject ends | |
//imgObject declarations and functions | |
PImage imageArray; | |
int imageWidth, imageHeight; | |
int blackPixelCount, edgePixelCount, nibPixelCount, nibPixelUsedCount, segmentCount; | |
int [] edgePixelArray; | |
int [] nibPixelArray; | |
int [] segmentArray; | |
int [] segmentMarkerArray; | |
int [] ignoredPixelArray; | |
int segmentMarkerCount, segmentEndMarkerCount, segmentStartMarkerCount, segmentPointMarkerCount, redundantMarkerCount; | |
int ignoredPixelCount, imageXIndexLimit, imageYIndexLimit, segmentArrayLength; | |
pixelObject currentPixel, prevPixel, lastStartPixel, lastEndPixel, lastPointPixel, lastNibPixel; | |
boolean isTracingCompleted, scanningFinished, scanningError, edgeDetected, nibDetected; | |
//=========================================================================// | |
//accepts image path | |
imgObject (String imageName) { | |
imageArray = loadImage(imageName); | |
initImgObject(); | |
} | |
//=========================================================================// | |
//acceptss an inbuilt PImage object | |
imgObject (PImage sourceImageArray) { | |
imageArray = sourceImageArray; | |
initImgObject(); | |
} | |
//=========================================================================// | |
//accepts another imgObject | |
imgObject (imgObject sourceImage) { | |
imageArray = sourceImage.imageArray; | |
initImgObject(); | |
} | |
//=========================================================================// | |
//creates image with default sizes | |
imgObject () { | |
imageArray = createImage(DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT, RGB); | |
initImgObject(); | |
} | |
//=========================================================================// | |
//initialize rest of the object elements | |
void initImgObject () { | |
imageHeight = imageArray.height; | |
imageWidth = imageArray.width; | |
edgePixelArray = new int [imageWidth * imageHeight]; //array for outline | |
nibPixelArray = new int [imageWidth * imageHeight]; //array for EOF pixels | |
segmentArray = new int [imageWidth * imageHeight]; //array for segments | |
segmentMarkerArray = new int [imageWidth * imageHeight]; | |
imageXIndexLimit = imageWidth - 1; | |
imageYIndexLimit = imageHeight - 1; | |
blackPixelCount = 0; | |
edgePixelCount = 0; | |
nibPixelCount = 0; | |
nibPixelUsedCount = 0; | |
segmentCount = 0; | |
segmentMarkerCount = 0; | |
segmentStartMarkerCount = 0; | |
segmentEndMarkerCount = 0; | |
segmentPointMarkerCount = 0; | |
segmentArrayLength = 0; | |
isTracingCompleted = true; | |
scanningFinished = false; | |
scanningError = false; | |
edgeDetected = false; | |
nibDetected = false; | |
lastStartPixel = new pixelObject(); | |
lastEndPixel = new pixelObject(); | |
lastPointPixel = new pixelObject(); | |
currentPixel = new pixelObject(); | |
prevPixel = new pixelObject(); | |
findBlackPixels(); | |
} | |
//=========================================================================// | |
//finds edge pixels from a bitmap/png image and stores them to an array | |
public int findEdgePixels () { | |
edgePixelCount = 0; | |
edgeDetected = false; | |
for(int y=0; y<imageHeight; y++) { | |
for(int x=0; x<imageWidth; x++) { | |
if(isEdgePixel(x, y)) { //edge of a solid fill | |
edgePixelArray [edgePixelCount] = convertToPosition(x, y); //mark that pixel | |
edgePixelCount++; | |
//println("Edge pixel found : " + "X = " + x + ", Y = " + y + ", N = " + getNeighborsCount(x,y) + ", F = " + getFriendsCount(x,y)); | |
} | |
} | |
} | |
println("Edge pixel count = " + edgePixelCount); | |
edgeDetected = true; | |
return edgePixelCount; | |
} | |
//=========================================================================// | |
//finds end of line pixels from a bitmap/png image and stores them to an array | |
public int findNibPixels () { | |
nibPixelCount = 0; | |
nibDetected = false; | |
for(int y=0; y<imageHeight; y++) { | |
for(int x=0; x<imageWidth; x++) { | |
if(isNibPixel(x, y)) { //edge of a solid fill | |
nibPixelArray [nibPixelCount] = convertToPosition(x, y); | |
nibPixelCount++; | |
//println("End pixel found : " + "X = " + x + ", Y = " + y + ", N = " + getNeighborsCount(x,y) + ", F = " + getFriendsCount(x,y)); | |
} | |
} | |
} | |
println("End pixel count = " + nibPixelCount); | |
nibDetected = true; | |
return nibPixelCount; | |
} | |
//=========================================================================// | |
//finds all the black pixels in an image | |
public int findBlackPixels () { | |
blackPixelCount = 0; | |
for(int y=0; y<imageHeight; y++) { | |
for(int x=0; x<imageWidth; x++) { | |
if(isPixelBlack(x, y)) { //edge of a solid fill | |
blackPixelCount++; | |
} | |
} | |
} | |
return blackPixelCount; //total count | |
} | |
//=========================================================================// | |
//finds curved and straight lines, loops and other segments in the image | |
//saves them to a 1D array ready to be processed by the plotting function | |
public int findSegments () { | |
segmentCount = 0; | |
findNibPixels(); | |
findEdgePixels(); | |
println("Finding segments.."); | |
while((segmentArrayLength < (edgePixelCount+redundantMarkerCount)) && (!scanningError) && (!scanningFinished)) { //loop until all the black pixels are fetched | |
if((isTracingCompleted) && (!scanningError) && (!scanningFinished)) { //we only need to do this if an end marker was set or we're just starting scanning | |
if((nibPixelCount > nibPixelUsedCount) && (!scanningError) && (!scanningFinished)) { //nibs are used as start pixels | |
currentPixel.set(nibPixelArray[nibPixelUsedCount]); //set current as one of the nib pixes | |
//println(); | |
//println("Pixel update 01 : P = " + currentPixel.position + ", X = " + currentPixel.X + ", Y = " + currentPixel.Y); | |
segmentArray[segmentArrayLength] = nibPixelArray[nibPixelUsedCount]; //copy the nib pixel to segment array | |
segmentMarkerArray[segmentArrayLength] = START_MARKER; //add marker to segment marker array | |
prevPixel.set(segmentArray[segmentArrayLength]); //set the current pixel as prev pixel | |
lastStartPixel.set(segmentArray[segmentArrayLength]); //last start position is now current pixel | |
segmentArrayLength++; //segment array has advanced one position | |
segmentStartMarkerCount++; //so these | |
segmentMarkerCount++; | |
nibPixelUsedCount += 2; //becasue nib pixel array stores start and end pixels as [S, E, S, E..] | |
if(DEBUG_LEVEL == 1) | |
printVerbose(); | |
//if((segmentArray[segmentArrayLength-1] < 0) || (segmentArray[segmentArrayLength-1] > ((imageWidth * imageHeight)-1))) { | |
if(segmentArray[segmentArrayLength-1] < 0) { //segmentArrayLength can now be greater than total number of pixels | |
println("Error : Corrupted segment array!"); | |
println("Invalid co-ordinates found in segment array."); | |
println("Program will now terminate."); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
} | |
} | |
//when there's no more black pixels to scan | |
else if((!scanningError) && (!scanningFinished)){ | |
if(getFirstBlackPixel() == INVALID) { | |
scanningFinished = true; //finish the scanning | |
isTracingCompleted = true; | |
if(segmentMarkerArray[segmentMarkerCount] != END_MARKER) { | |
segmentMarkerArray[segmentMarkerCount] = END_MARKER; //mark the end of segment | |
lastEndPixel.set(segmentArray[segmentArrayLength]); | |
segmentMarkerCount++; | |
segmentEndMarkerCount++; | |
segmentArrayLength++; | |
segmentCount++; | |
} | |
if(DEBUG_LEVEL == 1) | |
printVerbose(); | |
} | |
else { //if there are still black pixels | |
//if there are no nib pixels, we could use the first unmarked pixel from the edge pixel array | |
currentPixel.set(getFirstBlackPixel()); //find the first black pixel which need not to be a nib pixel | |
//println(); | |
//println("Pixel update 02 : P = " + currentPixel.position + ", X = " + currentPixel.X + ", Y = " + currentPixel.Y); | |
segmentArray[segmentArrayLength] = currentPixel.getPosition(); //save it to segment array | |
segmentMarkerArray[segmentArrayLength] = START_MARKER; //it's a starting point | |
prevPixel.set(segmentArray[segmentArrayLength]); //save as previous pixel | |
lastStartPixel.set(segmentArray[segmentArrayLength]); //also as start pixel | |
segmentArrayLength++; | |
segmentMarkerCount++; | |
segmentStartMarkerCount++; | |
if(DEBUG_LEVEL == 1) | |
printVerbose(); | |
} | |
} | |
} | |
//currentPixel is a temp pixel object for pixel manipulation | |
//at start, current pixel will be start pixel | |
if((!scanningError) && (!scanningFinished)) { //we check for errors all the time because it could happen any time! | |
if(getFirstAdjacentPixel() == INVALID) { | |
currentPixel.set(getFirstBlackPixel()); //find the first black pixel which need not to be a nib pixel | |
//println(); | |
//println("Pixel update 10 : P = " + currentPixel.position + ", X = " + currentPixel.X + ", Y = " + currentPixel.Y); | |
segmentArray[segmentArrayLength] = currentPixel.getPosition(); //save it to segment array | |
segmentMarkerArray[segmentArrayLength] = START_MARKER; //it's a starting point | |
prevPixel.set(segmentArray[segmentArrayLength]); //save as previous pixel | |
lastStartPixel.set(segmentArray[segmentArrayLength]); //also as start pixel | |
segmentArrayLength++; | |
segmentMarkerCount++; | |
segmentStartMarkerCount++; | |
if(DEBUG_LEVEL == 1) | |
printVerbose(); | |
} | |
else { | |
currentPixel.set(getFirstAdjacentPixel()); //now we have a marker set, let's find the next pixel | |
//println(); | |
//println("Pixel update 03 : P = " + currentPixel.position + ", X = " + currentPixel.X + ", Y = " + currentPixel.Y); | |
if(DEBUG_LEVEL == 1) | |
printVerbose(); | |
//delay(500); | |
} | |
} | |
//if we find a nib pixel | |
if((isNibPixel(currentPixel)) && (isEdgePixel(currentPixel)) && (!scanningError) && (!scanningFinished)) { //send as object | |
segmentArray[segmentArrayLength] = currentPixel.getPosition(); //save tehe pixel to the segment array | |
segmentMarkerArray[segmentArrayLength] = END_MARKER; //mark it as END | |
prevPixel.set(currentPixel.getPosition()); //save as prev pixel | |
lastEndPixel.set(currentPixel.getPosition()); //we found an end pixel | |
isTracingCompleted = true; //tracing is fully scanning a single segment | |
segmentArrayLength++; | |
segmentMarkerCount++; | |
segmentEndMarkerCount++; | |
segmentCount++; | |
if(DEBUG_LEVEL == 1) | |
printVerbose(); | |
} | |
//if the getFirstAdjacentPixel() return an already marked pixel | |
else if((isMarked(currentPixel)) && (isEdgePixel(currentPixel)) && (!scanningError) && (!scanningFinished)) { //check if currentPixel (object) is already marked | |
if(lastStartPixel.position == prevPixel.position) { //the last start pixel has nowhere to connect | |
segmentArray[segmentArrayLength] = currentPixel.getPosition(); //save the already mared pixel to the segment array | |
segmentMarkerArray[segmentArrayLength] = END_MARKER; //mark it as END | |
prevPixel.set(currentPixel.getPosition()); //save as prev pixel | |
lastEndPixel.set(currentPixel.getPosition()); //we found an end pixel | |
isTracingCompleted = true; //tracing is fully scanning a single segment | |
segmentArrayLength++; | |
segmentMarkerCount++; | |
segmentEndMarkerCount++; | |
redundantMarkerCount++; //becasue we used an already marked pixel as an end pixel | |
segmentCount++; | |
//at the end we'll have a segment wil only two pixels; one start and one end | |
if(DEBUG_LEVEL == 1) | |
printVerbose(); | |
} | |
//otherwise it could be an end of loop | |
else if (isEdgePixel(currentPixel)){ | |
segmentMarkerArray[segmentArrayLength-1] = END_MARKER; //it's not an end pixel but an end of loop | |
lastEndPixel.set(segmentArray[segmentArrayLength-1]); | |
currentPixel.set(prevPixel.position); //set current pixel to previous pixel : from postion value | |
//println(); | |
//println("Pixel update 05 : P = " + currentPixel.position + ", X = " + currentPixel.X + ", Y = " + currentPixel.Y); | |
isTracingCompleted = true; //completed a segment | |
segmentPointMarkerCount -= 1; //becasue we've just used a previously marked point pixel as end pixel (no change in segmentArraylength) | |
segmentEndMarkerCount++; | |
segmentCount++; | |
if(DEBUG_LEVEL == 1) | |
printVerbose(); | |
} | |
} | |
//finally we check if the pixel is a point in line (not start or end) | |
else if((isEdgePixel(currentPixel)) && (!scanningError) && (!scanningFinished)) { //just to make sure that it's apoint in a line | |
segmentArray[segmentArrayLength] = currentPixel.getPosition(); | |
segmentMarkerArray[segmentArrayLength] = POINT_MARKER; | |
prevPixel.set(currentPixel.getPosition()); | |
isTracingCompleted = false; | |
segmentArrayLength++; | |
segmentMarkerCount++; | |
segmentPointMarkerCount++; | |
if(DEBUG_LEVEL == 1) | |
printVerbose(); | |
} | |
} //while ends | |
if(!scanningError) { | |
if(segmentMarkerArray[segmentMarkerCount-1] != END_MARKER) { | |
segmentMarkerArray[segmentMarkerCount-1] = END_MARKER; | |
lastEndPixel.set(segmentArray[segmentArrayLength-1]); | |
segmentEndMarkerCount++; | |
segmentPointMarkerCount--; | |
segmentCount++; | |
} | |
println(); | |
println("--- Scanning Finished ----"); | |
printVerbose(); | |
//println(segmentMarkerArray[segmentMarkerCount-1]); | |
//println(segmentArray[segmentArrayLength-1]); | |
return segmentCount; | |
} | |
else return -1; | |
} | |
//=========================================================================// | |
//prints all the variable values and flag status | |
void printVerbose () { | |
println(); | |
println("Current Pixel : P = " + currentPixel.position + ", X = " + currentPixel.X + ", Y = " + currentPixel.Y); | |
println("Prev Pixel : P = " + prevPixel.position + ", X = " + prevPixel.X + ", Y = " + prevPixel.Y); | |
println("Last Start Pixel : P = " + lastStartPixel.position); | |
println("Last End Pixel : P = " + lastEndPixel.position); | |
println("isPixelBlack = " + isPixelBlack(currentPixel.position)); | |
println("isNibPixel = " + isNibPixel(currentPixel.position)); | |
println("isEdgePixel = " + isEdgePixel(currentPixel.position)); | |
println("isMarked = " + isMarked(currentPixel.position)); | |
println("Black Pixel Count = " + blackPixelCount); | |
println("Nib Pixel Count = " + nibPixelCount); | |
println("Nib Used Pixel Count = " + nibPixelUsedCount); | |
println("Edge Pixel Count = " + edgePixelCount); | |
println("Segment Array Length = " + segmentArrayLength); | |
println("Segment Count = " + segmentCount); | |
println("Segment Marker Count = " + segmentMarkerCount); | |
println("Segment Start Marker Count = " + segmentStartMarkerCount); | |
println("Segment End Marker Count = " + segmentEndMarkerCount); | |
println("Segment Point Marker Count = " + segmentPointMarkerCount); | |
println("Segment Redundant Marker Count = " + redundantMarkerCount); | |
} | |
//=========================================================================// | |
//returns first unmarked black pixel in the image | |
int getFirstBlackPixel () { | |
for(int y=0; y<imageHeight; y++) { | |
for(int x=0; x<imageWidth; x++) { | |
if((isEdgePixel(x, y)) && (!isMarked(x, y))) { | |
return (x + (y * imageWidth)); | |
} | |
} | |
} | |
return INVALID; | |
} | |
//=========================================================================// | |
//checks if a pixel was already marked | |
boolean isMarked (int x, int y) { | |
int pixelPosition = (x + (y * imageWidth)); | |
if(segmentArrayLength == 0) | |
return false; | |
else { | |
for(int i=0; i<segmentArrayLength; i++) { | |
if(segmentArray[i] == pixelPosition) | |
return true; | |
} | |
return false; | |
} | |
} | |
//-------------------------------------------------------------------------// | |
boolean isMarked (int pixelPosition) { | |
int x = (pixelPosition % imageWidth); //calculate X from position | |
int y = (pixelPosition - (pixelPosition % imageWidth)) / imageWidth; //calculate Y from position | |
return isMarked(x, y); | |
} | |
//-------------------------------------------------------------------------// | |
boolean isMarked (pixelObject sourceObject) { | |
return isMarked(sourceObject.X, sourceObject.Y); | |
} | |
//=========================================================================// | |
//determines if a pixel is a point in line | |
boolean isPointPixel(int x, int y) { | |
if(((getNeighborsCount(x, y) + getFriendsCount(x, y)) == 2) && isEdgePixel(x,y)) | |
return true; | |
else return false; | |
} | |
//-------------------------------------------------------------------------// | |
boolean isPointPixel(int pixelPosition) { | |
int x = (pixelPosition % imageWidth); //calculate X from position | |
int y = (pixelPosition - (pixelPosition % imageWidth)) / imageWidth; //calculate Y from position | |
return isPointPixel(x, y); | |
} | |
//-------------------------------------------------------------------------// | |
boolean isPointPixel(pixelObject sourceObject) { | |
return isPointPixel(sourceObject.X, sourceObject.Y); | |
} | |
//=========================================================================// | |
int getFirstAdjacentFriend (int position) { | |
return INVALID; | |
} | |
//=========================================================================// | |
int getFirstAdjacentNeighbor (int position) { | |
return INVALID; | |
} | |
//=========================================================================// | |
//checks if one of the surrounding pixel is black and then returns the | |
//relative position which can be 0 to 8. If found, returns the first one | |
//intercepted in a clockwise rotation. Whenever we refer to "pixel" or | |
//"PIXEL", the pixel is assumed to be black. | |
public int getFirstAdjacentPixel () { | |
//println("At getFirstAdjacentPixel() : currentPixel X = " + currentPixel.X + " Y = " + currentPixel.Y); | |
return getFirstAdjacentPixel(currentPixel.X, currentPixel.Y); | |
} | |
//-------------------------------------------------------------------------// | |
public int getFirstAdjacentPixel (int position) { | |
//convert to x, y and send it | |
return getFirstAdjacentPixel((position % imageWidth), ((position - (position % imageWidth)) / imageWidth)); | |
} | |
//-------------------------------------------------------------------------// | |
public int getFirstAdjacentPixel (pixelObject a) { | |
a.calculateXY(); | |
return getFirstAdjacentPixel(a.X, a.Y); | |
//return getFirstAdjacentPixel((a.position % imageWidth), ((a.position - (a.position % imageWidth)) / imageWidth)); | |
} | |
//-------------------------------------------------------------------------// | |
public int getFirstAdjacentPixel (int x, int y) { | |
if(!isPixelValid(x, y)) { | |
println(); | |
println("Invalid pixel coordinates at getFirstAdjacentPixel() " + "X = " + x + " Y = " + y); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
return INVALID; | |
} | |
else if(isPixelBlack(x, y)) { //check if it's a black pixel anyway | |
for(int i=0; i<8; i++) { //to find unmarked pixels | |
int X = x + scanningPatternX[i]; //calculate the coordinates of adjacent pixels using the predefined scanning pattern | |
int Y = y + scanningPatternY[i]; | |
if(isPixelValid(X, Y) && isPixelBlack(X, Y) && isEdgePixel(X, Y)) { //check also if edge pixel | |
if(!isMarked(X, Y)) { //if not marked | |
return convertToPosition(X, Y); | |
} | |
} | |
} | |
for(int i=0; i<8; i++) { //if there's no unmarked pixels, then retrun a marked one | |
int X = x + scanningPatternX[i];//calculate the coordinates of adjacent pixels using the predefined scanning pattern | |
int Y = y + scanningPatternY[i]; | |
if(isPixelValid(X, Y) && isPixelBlack(X, Y) && isEdgePixel(X, Y)) { | |
return convertToPosition(X, Y); //no need to check for unmarked | |
} | |
} | |
} | |
return INVALID; | |
//if everything fails! | |
//println(); | |
//println("--- 08 ---"); | |
//println("Couldn't find any adjacent unmarked black pixel."); | |
//println("Error at findSegments() : Couldn't resolve pixel : Position " + currentPixel.position + ", X = " + currentPixel.X + ", Y = " + currentPixel.Y); | |
//printVerbose(); | |
//if(EXIT_AT_ERROR == 1) System.exit(-1); | |
//return INVALID; | |
} | |
//=========================================================================// | |
public int convertToPosition (int x, int y) { | |
return (x + (y * imageWidth)); | |
} | |
//=========================================================================// | |
//determines if a pixel is black and is at the edge of the image or at the | |
//edge of any solif shape | |
public boolean isEdgePixel (pixelObject sourceObject) { | |
return isEdgePixel(sourceObject.X, sourceObject.Y); | |
} | |
//-------------------------------------------------------------------------// | |
public boolean isEdgePixel(int position) { | |
return isEdgePixel((position % imageWidth), ((position - (position % imageWidth)) / imageWidth)); | |
} | |
//-------------------------------------------------------------------------// | |
public boolean isEdgePixel (int x, int y) { | |
if(!isPixelValid(x, y)) { | |
println(); | |
println("Error : Invalid pixel coordinates at isEdgePixel() " + "X = " + x + " Y = " + y); | |
println("Program will now terminate."); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
return false; | |
} | |
if(edgeDetected) { | |
int position = convertToPosition(x, y); | |
for(int i=0; i<edgePixelCount; i++) { | |
if(edgePixelArray[i] == position) | |
return true; | |
} | |
return false; | |
} | |
else if(isPixelBlack(x, y)) { //if a pixel is black | |
if(isBorderPixel(x, y)) //if the pixel is already at image border | |
return true; | |
else { | |
int friendPixelCount = 0; | |
if(isPixelBlack(x-1, y)) //left pixel | |
friendPixelCount++; | |
if(isPixelBlack(x+1, y)) //right pixel | |
friendPixelCount++; | |
if(isPixelBlack(x, y-1)) //top pixel | |
friendPixelCount++; | |
if(isPixelBlack(x, y+1)) //bottom pixel | |
friendPixelCount++; | |
if((friendPixelCount > 0) && (friendPixelCount < 4)) return true; //if < 4 friends, it's an edge pixel | |
else return false; | |
} | |
} | |
else return false; | |
} | |
//=========================================================================// | |
//determines if a pixel is end of a line segment | |
public boolean isNibPixel (pixelObject sourceObject) { | |
return isNibPixel(sourceObject.X, sourceObject.Y); | |
} | |
//-------------------------------------------------------------------------// | |
public boolean isNibPixel(int position) { | |
return isNibPixel((position % imageWidth), ((position - (position % imageWidth)) / imageWidth)); | |
} | |
//-------------------------------------------------------------------------// | |
public boolean isNibPixel (int x, int y) { | |
if(!isPixelValid(x, y)) { | |
println(); | |
println("Error : Invalid pixel coordinates at isNibPixel(int, int) " + "X = " + x + " Y = " + y); | |
println("Program will now terminate."); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
return false; | |
} | |
if(nibDetected) { | |
int position = convertToPosition(x, y); | |
for(int i=0; i<nibPixelCount; i++) { | |
if(nibPixelArray[i] == position) | |
return true; | |
} | |
return false; | |
} | |
else if(isPixelBlack(x, y) && isEdgePixel(x, y)) { | |
if((getNeighborsCount(x, y) + getFriendsCount(x,y)) == 1) //only one neighbor or friend | |
return true; | |
else return false; | |
} | |
else return false; | |
} | |
//=========================================================================// | |
//gets the number of friends of a pixel | |
public int getFriendsCount(pixelObject sourceObject) { | |
return getFriendsCount(sourceObject.X, sourceObject.Y); | |
} | |
public int getFriendsCount (int x, int y) { | |
int friendPixelCount = 0; | |
if(!isPixelValid(x, y)) { | |
println(); | |
println("Error : Invalid pixel coordinates at getFriendsCount() " + "X = " + x + " Y = " + y); | |
println("Program will now terminate."); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
return INVALID; | |
} | |
for(int i=0; i<4; i++) { //assuming first four entries of scanning pattern are friends | |
int X = x + scanningPatternX[i]; | |
int Y = y + scanningPatternY[i]; | |
if(isPixelValid(X, Y) && isPixelBlack(X, Y)) { | |
friendPixelCount++; | |
} | |
} | |
return friendPixelCount; | |
} | |
//=========================================================================// | |
//gets the number of neighbors (black) of a pixel | |
public int getNeighborsCount(pixelObject sourceObject) { | |
return getNeighborsCount(sourceObject.X, sourceObject.Y); | |
} | |
public int getNeighborsCount (int x, int y) { | |
int neighborPixelCount = 0; | |
if(!isPixelValid(x, y)) { | |
println(); | |
println("Error : Invalid pixel coordinates at getNeighborsCount() " + "X = " + x + " Y = " + y); | |
println("Program will now terminate."); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
return INVALID; | |
} | |
for(int i=4; i<8; i++) { //assuming last four in the scanning pattern are neighbors | |
int X = x + scanningPatternX[i]; | |
int Y = y + scanningPatternY[i]; | |
if(isPixelValid(X, Y) && isPixelBlack(X, Y)) { | |
neighborPixelCount++; | |
} | |
} | |
return neighborPixelCount; | |
} | |
//=========================================================================// | |
//determines if a pixel is either at corner or border of an image | |
public boolean isBorderPixel (int x, int y) { | |
if(!isPixelValid(x, y)) { | |
println(); | |
println("Error : Invalid pixel coordinates at isBorderPixel() " + "X = " + x + " Y = " + y); | |
println("Program will now terminate."); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
return false; | |
} | |
if((y==0) || (x==0) || (y==imageYIndexLimit) || (x==imageXIndexLimit)) //check if pixel is at edge of image | |
return true; | |
else return false; | |
} | |
//-------------------------------------------------------------------------// | |
public boolean isBorderPixel (int position) { | |
return isBorderPixel((position % imageWidth), ((position - (position % imageWidth)) / imageWidth)); | |
} | |
//=========================================================================// | |
//determines if a pixel is at border of an image and is not a corner pixel | |
public boolean isSideBorderPixel (int x, int y) { | |
if(!isPixelValid(x, y)) { | |
println(); | |
println("Error : Invalid pixel coordinates at isSideBorderPixel() " + "X = " + x + " Y = " + y); | |
println("Program will now terminate."); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
return false; | |
} | |
if (!isCornerPixel(x, y)) { //if not a corner pixel | |
if ((x==0) && (y<imageYIndexLimit)) //left border | |
return true; | |
else if ((y==0) && (x<imageXIndexLimit)) //top border | |
return true; | |
else if ((x==imageXIndexLimit) && (y<imageYIndexLimit)) //right border | |
return true; | |
else if ((y==imageYIndexLimit) && (x<imageXIndexLimit)) //bottom border | |
return true; | |
else return false; | |
} | |
else return false; //if a corner pixel | |
} | |
//=========================================================================// | |
//determines if a pixel is at either of the 4 corners of the image | |
public boolean isCornerPixel (int x, int y) { | |
if(!isPixelValid(x, y)) { | |
println(); | |
println("Error : Invalid pixel coordinates at isCornerPixel() " + "X = " + x + " Y = " + y); | |
println("Program will now terminate."); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
return false; | |
} | |
if ((x==0) && (y==0)) | |
return true; | |
else if ((x==0) && (y==imageYIndexLimit)) | |
return true; | |
else if ((x==imageXIndexLimit) && (y==0)) | |
return true; | |
else if ((y==imageYIndexLimit) && (x==imageXIndexLimit)) | |
return true; | |
else return false; | |
} | |
//=========================================================================// | |
//checks if pixel coordinates are valid | |
public boolean isPixelValid (int position) { | |
return isPixelValid((position % imageWidth), ((position - (position % imageWidth)) / imageWidth)); | |
} | |
public boolean isPixelValid (pixelObject sourceObject) { | |
return isPixelValid(sourceObject.X, sourceObject.Y); | |
} | |
public boolean isPixelValid (int x, int y) { | |
if((x<0) || (y<0) || (x>imageXIndexLimit) || (y>imageYIndexLimit)) { | |
println(); | |
println("Error : Invalid pixel coordinates at isPixelValid() " + "X = " + x + " Y = " + y); | |
println("Program will now terminate."); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
return false; | |
} | |
else return true; | |
} | |
//=========================================================================// | |
//checks if a pixel in the image is black | |
public boolean isPixelBlack (int position) { | |
return isPixelBlack((position % imageWidth), ((position - (position % imageWidth)) / imageWidth)); | |
} | |
public boolean isPixelBlack (pixelObject sourceObject) { | |
return isNibPixel(sourceObject.X, sourceObject.Y); | |
} | |
public boolean isPixelBlack (int x, int y) { | |
if(!isPixelValid(x, y)) { | |
println(); | |
println("Error : Invalid pixel coordinates at isPixelBlack() " + "X = " + x + " Y = " + y); | |
println("Program will now terminate."); | |
if(EXIT_AT_ERROR == 1) System.exit(-1); | |
return false; | |
} | |
int pixelLocation = (x+(y*imageWidth)); //calculate the pixel location in the PImage array from cords | |
int pixelRed = (int) red(imageArray.pixels[pixelLocation]); //get red value of pixel | |
int pixelGreen = (int) green(imageArray.pixels[pixelLocation]); //get green value of pixel | |
int pixelBlue = (int) blue(imageArray.pixels[pixelLocation]); //get blue value of pixel | |
if ((pixelRed > 0) && (pixelGreen > 0) && (pixelBlue > 0)) { //check if white | |
return false; | |
} | |
else if ((pixelRed < 10) && (pixelGreen < 10) && (pixelBlue < 10)) { //check if black | |
return true; | |
} | |
else return false; //default return | |
} | |
} //imgObject class ends | |
//=========================================================================// | |
//runs once and sets up everything | |
void setup () { | |
size(800, 655); | |
background(255); | |
//sample images for testing (must be available in the data folder) | |
//selectedImage = new imgObject("git.png"); | |
//selectedImage = new imgObject("multiline.png"); | |
//selectedImage = new imgObject("singleline.png"); | |
//selectedImage = new imgObject("washer.png"); | |
//selectedImage = new imgObject("slits.png"); | |
//selectedImage = new imgObject("rectangle.png"); | |
//selectedImage = new imgObject("h.png"); | |
//selectedImage = new imgObject("m.png"); | |
//selectedImage = new imgObject("cloud.png"); | |
//selectedImage = new imgObject("sudhee.png"); | |
//selectedImage = new imgObject("arduino.png"); | |
//selectedImage = new imgObject("symbol.png"); | |
//selectedImage = new imgObject("camera.png"); | |
//selectedImage = new imgObject("lion.png"); | |
//selectedImage = new imgObject("woman.png"); | |
//selectedImage = new imgObject("ganesh.png"); | |
//selectedImage = new imgObject("hotgirl.png"); | |
//selectedImage = new imgObject("wolf.png"); | |
//selectedImage = new imgObject("dancer.png"); | |
selectedImage = new imgObject("flower.png"); | |
//selectedImage = new imgObject("flowers.png"); | |
//selectedImage = new imgObject("deer.png"); | |
//selectedImage = new imgObject("snoopy.png"); | |
//selectedImage = new imgObject("panda.png"); | |
} | |
//=========================================================================// | |
//main loop function does everything displayed on screen | |
void draw () { | |
if(iterationCount == 0) { //only do once | |
createOutlineImage(selectedImage); | |
selectedImage.findSegments(); | |
//selectedImage.findEdgePixels(); | |
//selectedImage.findNibPixels(); | |
//println("Edge pixel count = " + selectedImage.edgePixelCount); | |
//println("Edge pixel count = " + outlineImage.edgePixelCount); | |
iterationCount++; | |
} | |
else { | |
//image(selectedImage.imageArray, 250, 50); //original image | |
//image(outlineImage.imageArray, 250, 340); //outline image | |
drawImage(); | |
drawEdgeImage(); | |
delay(1); | |
} | |
} | |
//=========================================================================// | |
//creates an outline image containing only edge pixels generated by edge | |
//detection algorithm | |
boolean createOutlineImage (imgObject sourceImage) { | |
outlineImage = new imgObject(sourceImage); | |
outlineImage.imageArray = createImage(outlineImage.imageWidth, outlineImage.imageHeight, RGB); //create image | |
sourceImage.findEdgePixels(); //first find the edge pixels from source image | |
//println("Edge pixel count = " + sourceImage.edgePixelCount); | |
outlineImage.imageArray.loadPixels(); //load pixels for altering | |
for(int y=0; y<outlineImage.imageHeight; y++) { | |
for(int x=0; x<outlineImage.imageWidth; x++) { | |
int pixelLocation = (x+(y*(sourceImage.imageWidth))); //find the pixel location in PImage from coords | |
outlineImage.imageArray.pixels[pixelLocation] = color(255, 255, 255); | |
} | |
} | |
for(int i=0; i <sourceImage.edgePixelCount; i++) { | |
outlineImage.imageArray.pixels[sourceImage.edgePixelArray[i]] = color(0, 0, 0); | |
} | |
outlineImage.imageArray.updatePixels(); //update the image | |
return true; | |
} | |
//=========================================================================// | |
//simulates CNC drawing using the segment array | |
int loopCount = 0; | |
int loopCountTwo = 0; | |
void drawImage() { | |
int originX = 400; //set origins for the image | |
int originY = 200; | |
int X, Y; | |
X = (selectedImage.segmentArray[loopCount] % 327); //calculate X from position | |
Y = (selectedImage.segmentArray[loopCount] - (selectedImage.segmentArray[loopCount] % 327)) / 327; //calculate Y from position | |
point(originX+X, originY+Y); | |
if(loopCount <(selectedImage.segmentArrayLength-1)) loopCount++; | |
} | |
//=========================================================================// | |
//draws the image using the edge pixel array | |
void drawEdgeImage() { | |
int originX = 50; //set origins for the image | |
int originY = 200; | |
int X, Y; | |
X = (selectedImage.edgePixelArray[loopCountTwo] % 327); //calculate X from position | |
Y = (selectedImage.edgePixelArray[loopCountTwo] - (selectedImage.edgePixelArray[loopCountTwo] % 327)) / 327; //calculate Y from position | |
point(originX+X, originY+Y); | |
if(loopCountTwo <(selectedImage.edgePixelCount-1)) loopCountTwo++; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment