Java tutorial
/** * WhiteSocket * Copyright (C) 2015-2017 - Dave Daggett - Blooprint, LLC * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * run this API (JAR file) from the Blooprint application (https://github.com/blooprint/blooprint) * java -jar Blooprint.jar <args> * * purpose: input image from hard drive -> returns processed image to hard drive for display * * for now, it's recommended the input image aspect ratio EQUALS the output image aspect ratio */ package main.java.whiteSocket; import main.java.whiteSocket.*; import java.awt.Color; import java.awt.Graphics2D; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.Statement; import java.util.ArrayList; import java.util.LinkedList; import java.util.Queue; import javax.imageio.ImageIO; import java.io.FileReader; //import java.util.Iterator; import org.apache.commons.io.FileUtils; public class Bloop { private static boolean jarMode = false; public static ArrayList<Area> eraseAreas = null; /* TODO * little redundant, but can fix later * */ public static boolean[][] totalErase = null; public static String title = ""; // sketch image name (timestamp) public static String inMode = ""; // write/erase/calibrate public static String writeColor = "black"; // default black public static int markerHex = 0x000000; // default black public static BufferedImage sketch; public static BufferedImage blooprint; private static BufferedImage statusImg; public static double topSlope, bottomSlope, leftSlope, rightSlope; public static int aax, aay, bbx, bby, ccx, ccy, ddx, ddy; public static double unit_aax, unit_aay, unit_bbx, unit_bby, unit_ccx, unit_ccy, unit_ddx, unit_ddy; public static double clientWidth, clientHeight; public static boolean[][] areaOfInterest; public static boolean[][] totalEraseArea; public static int tx, ty; /* * brightThreshold = value at white any value less must be marker pixel * TODO: tinker with proper value or better method -> see getBrightnessThreshold() * */ public static int brightThreshold; public static String blooprintFile = ""; public static String sketchFile = ""; public static String test_sketch = ""; public static String test_image = ""; public static void main(String[] args) throws Exception { System.out.println( "****************\n****************\n\nYou are using Blooprint \u00ae software.\n\nPlease refer to our license here:\nhttp://github.com/blooprint/whiteSocket/blob/master/LICENSE\n\n****************"); title = args[0]; jarMode = (args[4].toLowerCase().equals("true") ? true : false); if (jarMode) { System.out.println("\nwhiteSocket: PRODUCTION EXECUTABLE/JAR MODE\n"); sketchFile = "/input/" + args[0] + ".bmp"; blooprintFile = "/output/" + args[1] + ".bmp"; } else { System.out.println("\nwhiteSocket: DEVELOPMENT MODE\n"); sketchFile = "./io/input/" + args[0] + ".bmp"; blooprintFile = "./io/output/" + args[1] + ".bmp"; } inMode = args[2]; markerHex = Integer.parseInt(args[3], 16); System.out.println("marker decimal value = " + markerHex); if (args[1] != null) blooprint = loadBlooprint(); sketch = loadSketch(); brightThreshold = getBrightnessThreshold(); switch (inMode) { case "write": calibrate(); write(); saveBlooprint(); break; case "erase": calibrate(); eraseAreas = new ArrayList<Area>(); Area.totalErase = new boolean[sketch.getHeight()][sketch.getWidth()]; Color pxColor = null; for (int row = 0; row < sketch.getHeight(); row++) { for (int col = 0; col < sketch.getWidth(); col++) { pxColor = new Color(sketch.getRGB(col, row)); /* * TODO: * !totalErase[][] - to make sure the pixel isn't yet considered as an eraser pixel * */ if (areaOfInterest[row][col] && isMarker(pxColor) && !Area.totalErase[row][col]) { eraseAreas.add(new Area(col, row)); } } } for (Area some : eraseAreas) { erase(some.area); } saveBlooprint(); break; default: break; } System.exit(0); }//END main() public static void write() throws IOException { /** * bloop blooprint.image pixel location intended by user bloop action * sets Color.RED,BLUE,GREEN according to user intension * */ System.out.println("writing......"); int[] xyOUT = new int[2]; for (int row = 0; row < sketch.getHeight(); row++) { for (int col = 0; col < sketch.getWidth(); col++) { int xIN = col; int yIN = row; Color pxColor = new Color(sketch.getRGB(xIN, yIN)); try { if (areaOfInterest[yIN][xIN]) { if (isMarker(pxColor)) { xyOUT = Stretch.stretch(xIN, yIN); blooprint.setRGB(xyOUT[0], xyOUT[1], markerHex); } } } catch (Exception e) { System.out.println("\nERROR write(): xIN = " + xIN + " yIN = " + yIN); System.out.println("xyOUT " + e.getMessage()); System.out.println("xOUT = " + xyOUT[0] + " yOUT = " + xyOUT[1]); pegBadInput(xIN, yIN); System.exit(0); } } } }//END write() public static void erase(boolean[][] eraseArea) { /** * erase the area found inside the outer border of marker line drawn * */ System.out.println("erasing......"); int[] xyOUT = new int[2]; for (int row = 0; row < sketch.getHeight(); row++) { for (int col = 0; col < sketch.getWidth(); col++) { int xIN = col; int yIN = row; try { if (eraseArea[yIN][xIN]) { xyOUT = Stretch.stretch(xIN, yIN); blooprint.setRGB(xyOUT[0], xyOUT[1], 0xffffff); } } catch (Exception e) { e.printStackTrace(); } } } }//END erase() public static boolean comparePixels(Color a, Color b) { if (Bloop.isMarker(a) && !Bloop.isMarker(b)) { return true; } return false; }//END comparePixels() public static int[] zoomToBox() { /* * returns box coordinates wrapping a border * */ Border border = null; int[] box = new int[4]; here: for (int row = 0; row < sketch.getHeight(); row++) { for (int col = 0; col < sketch.getWidth(); col++) { int xIN = col; int yIN = row; Color pixel = new Color(sketch.getRGB(xIN, yIN)); if (areaOfInterest[yIN][xIN] && isMarker(pixel)) { border = new Border(xIN, yIN); break here; } } } box[0] = border.yMin - 2; box[1] = border.yMax + 2; box[2] = border.xMin - 2; box[3] = border.xMax + 2; return box; }//END zoomToBox() public static int[] getScanBoxCorners(int ymin, int ymax, int xmin, int xmax) { /** * finds user defined projector corners (xy-coordinates) on whiteboard * * action occurs on the input camera image * * returns int[8] - corner order UL, UR, LL, LR in x,y sequence * */ int[] some = new int[8]; Color pixel = null; boolean hit = false; int rowStart = ymin; int row; Next1: try { while (!hit) { rowStart++; int col = xmin; row = rowStart; while (row > ymin) { pixel = new Color(sketch.getRGB(col, row)); if (isMarker(pixel)) { some[0] = col; some[1] = row; System.out.println("ULx = " + some[0]); System.out.println("ULy = " + some[1]); hit = true; break Next1; } col++; row--; } } //end UL corner } catch (Exception e) { System.out.println("\nCalibration.getScanBoxCorners() ERROR: UP-LEFT corner\n" + e.getMessage()); } // upper right hit = false; rowStart = ymin; Next2: try { while (!hit) { rowStart++; int col = xmax; row = rowStart; while (row > ymin) { pixel = new Color(sketch.getRGB(col, row)); if (isMarker(pixel)) { some[2] = col; some[3] = row; System.out.println("URx = " + some[2]); System.out.println("URy = " + some[3]); hit = true; break Next2; } col--; row--; } } //end UR corner } catch (Exception e) { System.out.println("\nCalibration.getScanBoxCorners() ERROR: UP-RIGHT corner\n" + e.getMessage()); } // lower left hit = false; rowStart = ymax; Next3: try { while (!hit) { rowStart--; int col = xmin; row = rowStart; while (row < ymax) { pixel = new Color(sketch.getRGB(col, row)); if (isMarker(pixel)) { some[4] = col; some[5] = row; System.out.println("LLx = " + some[4]); System.out.println("LLy = " + some[5]); hit = true; break Next3; } col++; row++; } } //end LL corner } catch (Exception e) { System.out.println("\nCalibration.getScanBoxCorners() ERROR: LOW-LEFT corner\n" + e.getMessage()); } // lower right hit = false; rowStart = ymax; Next4: try { while (!hit) { rowStart--; int col = xmax; row = rowStart; while (row < ymax) { pixel = new Color(sketch.getRGB(col, row)); if (isMarker(pixel)) { some[6] = col; some[7] = row; System.out.println("LRx = " + some[6]); System.out.println("LRy = " + some[7]); hit = true; break Next4; } col--; row++; } } //end LR corner } catch (Exception e) { System.out.println("\nCalibration.getScanBoxCorners() ERROR: LOW-RIGHT corner\n" + e.getMessage()); } return some; }//END getScanBoxCorners() public static float[] setUnitTextbox(int[] corners) { /* returns Rectangle to -> x,y,width,height */ Rectangle rect = new Rectangle(); int x = corners[0]; int y = corners[1]; // corner order UL, UR, LL, LR in x,y sequence int width = 0; int height = 0; if (corners[2] >= corners[6]) { width = corners[6] - x; } else { width = corners[2] - x; } if (corners[5] > corners[7]) { height = corners[7] - y; } else { height = corners[5] - y; } // scales to display output double x2 = (double) x / (double) sketch.getWidth() * (double) blooprint.getWidth(); double y2 = (double) y / (double) sketch.getHeight() * (double) blooprint.getHeight(); double width2 = (double) width / (double) sketch.getWidth() * (double) blooprint.getWidth(); double height2 = (double) height / (double) sketch.getHeight() * (double) blooprint.getHeight(); int aa = (int) Math.round(x2); int bb = (int) Math.round(y2); int cc = (int) Math.round(width2); int dd = (int) Math.round(height2); rect.setBounds(aa, bb, cc, dd); /* Unit boc in order to scale to any client side browser screen dimensions. Must be re-scaled to web application DOM element textarea location */ float[] unit = new float[4]; unit[0] = (float) aa / (float) blooprint.getWidth(); unit[1] = (float) bb / (float) blooprint.getHeight(); unit[2] = (float) cc / (float) blooprint.getWidth(); unit[3] = (float) dd / (float) blooprint.getHeight(); return unit; }//END setUnitTextbox() public static void calibrate() throws Exception { System.out.println("init calibration ..."); Area.getLightBounds(); areaOfInterest = getAreaOfInterestBorder(); statusImg = printImgBool(areaOfInterest, "./io/tests/aoi-border"); /* * start flooding right below center of topSlope * */ tx = (Stretch.ax + Stretch.bx) / 2; ty = (Stretch.ay + Stretch.by) / 2 + 5; areaOfInterest = Area.floodBorder(null, areaOfInterest, tx, ty); // Area.printImgBool(areaOfInterest, "aoi-fill"); Stretch.width = blooprint.getWidth(); Stretch.height = blooprint.getHeight(); Tilt.setTiltedCalibration(); }//END calibrate() public static void setCorners() { /** * sets -> ax,ay,bx,by,cx,cy,dx,dy * gets corner values user draws on whiteboard (corners of lit projection area) * * TODO: * */ boolean hit = false; int rowStart = 0; int row; Color pixel = null; Next1: try { while (!hit) { rowStart++; int col = 0; row = rowStart; while (row > 0) { pixel = new Color(sketch.getRGB(col, row)); if (areaOfInterest[row][col] && isMarker(pixel)) { Stretch.ax = col; Stretch.ay = row; System.out.println("Corner 1:\tULx = " + Stretch.ax + "\tULy = " + Stretch.ay); hit = true; break Next1; } col++; row--; } } //end UL corner } catch (Exception e) { System.err.println("\nCalibration.setCorners() ERROR: UP-LEFT corner\n"); e.getMessage(); e.printStackTrace(); } // upper right hit = false; rowStart = 0; Next2: try { while (!hit) { rowStart++; int col = sketch.getWidth() - 1; row = rowStart; while (row > 0) { pixel = new Color(sketch.getRGB(col, row)); if (areaOfInterest[row][col] && isMarker(pixel)) { Stretch.cx = col; Stretch.cy = row; System.out.println("Corner 2:\tURx = " + Stretch.cx + "\tURy = " + Stretch.cy); hit = true; break Next2; } col--; row--; } } //end UR corner } catch (Exception e) { System.err.println("\nCalibration.setCorners() ERROR: UP-RIGHT corner\n"); e.getMessage(); e.printStackTrace(); } // lower left hit = false; rowStart = sketch.getHeight() - 1; Next3: try { while (!hit) { rowStart--; int col = 0; row = rowStart; while (row < sketch.getHeight() - 1) { pixel = new Color(sketch.getRGB(col, row)); if (areaOfInterest[row][col] && isMarker(pixel)) { Stretch.dx = col; Stretch.dy = row; System.out.println("Corner 3:\tLLx = " + Stretch.dx + "\tLLy = " + Stretch.dy); hit = true; break Next3; } col++; row++; } } //end LL corner } catch (Exception e) { System.err.println("\nCalibration.setCorners() ERROR: LOW-LEFT corner\n"); e.getMessage(); e.printStackTrace(); } // lower right hit = false; rowStart = sketch.getHeight() - 1; Next4: try { while (!hit) { rowStart--; int col = sketch.getWidth() - 1; row = rowStart; while (row < sketch.getHeight() - 1) { pixel = new Color(sketch.getRGB(col, row)); if (areaOfInterest[row][col] && isMarker(pixel)) { Stretch.bx = col; Stretch.by = row; System.out.println("Corner 4:\tLRx = " + Stretch.bx + "\tLRy = " + Stretch.by); hit = true; break Next4; } col--; row++; } } //end LR corner } catch (Exception e) { System.err.println("\nCalibration.setCorners() ERROR: LOW-RIGHT corner\n"); e.getMessage(); e.printStackTrace(); } }//END setCorners() public static BufferedImage loadBlooprint() throws IOException { /** * load image from DB table - either an input sketch or a compiled blooprint image * sketch areg = "null" * BLOB object to binary stream to BufferedImage object * */ System.out.println("loadBlooprint() from " + blooprintFile); InputStream stream = null; BufferedImage some = null; try { /** * http://stackoverflow.com/questions/39081215/access-a-resource-outside-a-jar-from-the-jar * */ stream = Bloop.class.getClass().getResourceAsStream(blooprintFile); // stream = null; if (!jarMode) { System.out.println("loading blooprint as test image"); some = ImageIO.read(new File(blooprintFile)); return some; } System.out.println("blooprint stream = " + stream); } catch (Exception e) { System.out.println("error blooprint to stream: " + e.getMessage()); } try { some = ImageIO.read(stream); } catch (Exception exc) { exc.getMessage(); } return some; }//END loadBlooprint() public static BufferedImage loadSketch() throws Exception { /** * load image from DB table - either an input sketch or a compiled blooprint image * sketch arg = "null" * BLOB object to binary stream to BufferedImage object * */ System.out.println("loadSketch() from " + sketchFile); InputStream stream = null; BufferedImage some = null; try { /** * http://stackoverflow.com/questions/39081215/access-a-resource-outside-a-jar-from-the-jar * */ stream = Bloop.class.getClass().getResourceAsStream(sketchFile); // stream = null; if (!jarMode) { some = ImageIO.read(new File(sketchFile)); Stretch.fx = some.getWidth() - 1; Stretch.gx = some.getWidth() - 1; Stretch.fy = some.getHeight() - 1; Stretch.hy = some.getHeight() - 1; return some; } System.out.println("sketch stream = " + stream); } catch (Exception e) { System.out.print("error sketch to stream: "); System.out.println(e.getMessage()); } try { some = ImageIO.read(stream); Stretch.fx = some.getWidth() - 1; Stretch.gx = some.getWidth() - 1; Stretch.fy = some.getHeight() - 1; Stretch.hy = some.getHeight() - 1; } catch (Exception exc) { System.out.print("error stream to image: "); System.out.println(exc.getMessage()); } return some; }//END loadSketch() public static void saveBlooprint() throws IOException { System.out.println("saveBlooprint()..."); try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(blooprint, "bmp", baos); InputStream stream = new ByteArrayInputStream(baos.toByteArray()); File outputfile = null; if (jarMode) outputfile = new File("./output/" + title + ".bmp"); else outputfile = new File("./io/output/" + title + ".bmp"); FileUtils.copyInputStreamToFile(stream, outputfile); } catch (Exception ex) { System.out.println("ERROR saveBlooprint(): " + ex.getMessage()); } }//END saveBlooprint() public static boolean[][] getAreaOfInterestBorder() { /** * dealing with lit projector area on whiteboard * sets binary border for future use in floodBorder() method * */ boolean[][] border = new boolean[sketch.getHeight()][sketch.getWidth()]; for (int x = Stretch.ax; x <= Stretch.cx; x++) {//top double intersect_double = Stretch.cy - (topSlope * Stretch.cx); int intersect = (int) Math.round(intersect_double); double y_double = (topSlope * x) + intersect; int y = (int) Math.round(y_double); border[y][x] = true; } for (int x = Stretch.dx; x <= Stretch.bx; x++) {//bottom double intersect_double = Stretch.by - (bottomSlope * Stretch.bx); int intersect = (int) Math.round(intersect_double); double y_double = (bottomSlope * x) + intersect; int y = (int) Math.round(y_double); border[y][x] = true; } for (int y = Stretch.ay; y <= Stretch.dy; y++) {//left double intersect_double = Stretch.dy - (leftSlope * Stretch.dx); int intersect = (int) Math.round(intersect_double); double x_double = (y - intersect) / leftSlope; int x = (int) Math.round(x_double); border[y][x] = true; } for (int y = Stretch.cy; y <= Stretch.by; y++) {//right double intersect_double = Stretch.by - (rightSlope * Stretch.bx); int intersect = (int) Math.round(intersect_double); double x_double = (y - intersect) / rightSlope; int x = (int) Math.round(x_double); border[y][x] = true; } return border; }//END getAreaOfInterestBorder() public static boolean isMarker(Color x) { int brightness = (int) (0.2126 * x.getRed() + 0.7152 * x.getGreen() + 0.0722 * x.getBlue()); if (brightness < brightThreshold) return true; else return false; }//END isMarker() private static int getBrightnessThreshold() { /** * get average pixel brightness of white pixels whiteboard input image * NOTE: lower brightness value = darker the pixel * using relative luminance * https://en.wikipedia.org/wiki/Relative_luminance * * rough threshold value = 30% less than average of darkest corner * assumes average corner spans 60% brightness (+/-30%) */ Color color = null; float brightness = 0; int r, g, b; int count = 0; int avg_UL, avg_UR, avg_LL, avg_LR; float totalBrightness = 0; for (int row = 0; row < 10; row++) { // UL for (int col = 0; col < 10; col++) { count++; color = new Color(sketch.getRGB(col, row)); r = color.getRed(); g = color.getGreen(); b = color.getBlue(); brightness = (float) (0.2126 * r + 0.7152 * g + 0.0722 * b); totalBrightness += brightness; } } avg_UL = (int) (totalBrightness / count); totalBrightness = 0; count = 0; for (int row = 0; row < 10; row++) { // UR for (int col = sketch.getWidth() - 10; col < sketch.getWidth(); col++) { count++; color = new Color(sketch.getRGB(col, row)); r = color.getRed(); g = color.getGreen(); b = color.getBlue(); brightness = (float) (0.2126 * r + 0.7152 * g + 0.0722 * b); totalBrightness += brightness; } } avg_UR = (int) (totalBrightness / count); totalBrightness = 0; count = 0; for (int row = sketch.getHeight() - 10; row < sketch.getHeight(); row++) { // LL for (int col = 0; col < 10; col++) { count++; color = new Color(sketch.getRGB(col, row)); r = color.getRed(); g = color.getGreen(); b = color.getBlue(); brightness = (float) (0.2126 * r + 0.7152 * g + 0.0722 * b); totalBrightness += brightness; } } avg_LL = (int) (totalBrightness / count); totalBrightness = 0; count = 0; for (int row = sketch.getHeight() - 10; row < sketch.getHeight(); row++) { // LR for (int col = sketch.getWidth() - 10; col < sketch.getWidth(); col++) { count++; color = new Color(sketch.getRGB(col, row)); r = color.getRed(); g = color.getGreen(); b = color.getBlue(); brightness = (float) (0.2126 * r + 0.7152 * g + 0.0722 * b); totalBrightness += brightness; } } avg_LR = (int) (totalBrightness / count); int threshold = (int) (0.7 * Math.min(Math.min(avg_LL, avg_LR), Math.min(avg_UL, avg_UR))); // System.out.println("\nthreshold = "+threshold); return threshold; } public static BufferedImage createWhiteImage() { BufferedImage testOut = new BufferedImage(Bloop.blooprint.getWidth(), Bloop.blooprint.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics2D graphics = testOut.createGraphics(); graphics.setPaint(Color.white); graphics.fillRect(0, 0, testOut.getWidth(), testOut.getHeight()); return testOut; }//END createWhiteImage public static BufferedImage printImgBool(boolean[][] some, String path) throws Exception { // BufferedImage testOut = createWhiteImage(); BufferedImage testOut = Bloop.loadSketch(); for (int row = 0; row < Bloop.sketch.getHeight(); row++) { for (int col = 0; col < Bloop.sketch.getWidth(); col++) { if (some[row][col]) { testOut.setRGB(col, row, 0); } } } saveImg(testOut, path); return testOut; }//END printImgBool() public static void saveImg(BufferedImage some, String path) throws IOException { try { File outputfile = new File(path); ImageIO.write(some, "bmp", outputfile); } catch (Exception ex) { System.out.println("\nERROR: Area.saveImg()"); System.out.println(ex.getMessage()); } }// END saveImg() private static void pegBadInput(int x, int y) throws IOException { /* * prints yellow highlight of default pixel * */ for (int row = y - 10; row <= y + 10; row++) { for (int col = x - 10; col <= x + 10; col++) { statusImg.setRGB(col, row, 0xffff00); } } saveImg(statusImg, "./io/tests/pegs"); }//END peg() }