Skip to content

Instantly share code, notes, and snippets.

@vishnumaiea
Last active September 14, 2017 07:02
Show Gist options
  • Save vishnumaiea/f3e84b11c684bac347196f374b83a372 to your computer and use it in GitHub Desktop.
Save vishnumaiea/f3e84b11c684bac347196f374b83a372 to your computer and use it in GitHub Desktop.
Freehand algorithm for my Mini CNC Plotter
//=========================================================================//
//
// -- 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