diff --git a/src/peery/Mosaic.java b/src/peery/Mosaic.java index 2d8d7ee..1b26efb 100644 --- a/src/peery/Mosaic.java +++ b/src/peery/Mosaic.java @@ -10,10 +10,10 @@ import peery.picture.ImageAnalyzer; public class Mosaic extends Thread{ public static final String programmName = "Mosaic", - versionString = "Alpha-0.32"; + versionString = "Alpha-0.33"; private static final String outputName = "Output"; - private static final int gridX = 200, gridY = 200, targetMulti = 2, + private static final int gridX = 100, gridY = 50, targetMulti = 2, alphaThreshhold = 30, adaptionCount = 300, inputWorkerLimit = 10, @@ -21,14 +21,14 @@ public class Mosaic extends Thread{ matchWorkerLimit = 10, placeWorkerLimit = 2; private static final double adaptionStep = 1.1, gridErrorThresh = 0.15; + private static boolean keepRatio = false; /* * * Performance: * * * FIX: - * somewhere I'm loosing pixels from the target (the output is cut off as if it was smaller) - * investigate picture stretching -> is ImageUtils.resizeImage() even used? + * rasterization doesn't cover everything! * alphaThreshhold is currently dead * * Feature: @@ -42,7 +42,8 @@ public class Mosaic extends Thread{ public Mosaic(){ fh = new FileHandler("resources"); Log.log(LogLevel.Info, "Starting "+programmName+" "+versionString); - ia = new ImageAnalyzer(fh, inputWorkerLimit, targetWorkerLimit, matchWorkerLimit, placeWorkerLimit, alphaThreshhold); + ia = new ImageAnalyzer(fh, inputWorkerLimit, targetWorkerLimit, matchWorkerLimit, + placeWorkerLimit, alphaThreshhold, keepRatio); this.start(); } @@ -81,6 +82,7 @@ public class Mosaic extends Thread{ e.printStackTrace(); } } + cutOutGrid(); Log.log.perfLog("Finished Placement!"); Log.log(LogLevel.Info, "Finished placement. Output is done ..."); Log.log.perfLog("Saving output to file ..."); @@ -95,8 +97,8 @@ public class Mosaic extends Thread{ * Starts threads to index all not indexed images and rasterizes and classifies the Target. */ public void prepMatching(){ - ia.updateIndex(); ia.rasterizeTarget(gridX, gridY, targetMulti, gridErrorThresh, adaptionCount, adaptionStep); + ia.updateIndex(); ia.classifyTarget(); } @@ -109,6 +111,12 @@ public class Mosaic extends Thread{ Log.log(LogLevel.Info, "Starting Creation of Mosaic !"); ia.placeFragments(); } + + public void cutOutGrid(){ + Log.log(LogLevel.Info, "Cutting out what the grid covered!"); + Log.log(LogLevel.Debug, "Cutting out 0 0 "+ia.gridEnd[0]+" "+ia.gridEnd[1]); + ia.canvas = ia.canvas.getSubimage(0, 0, ia.gridEnd[0], ia.gridEnd[1]); + } public static void main(String[] args){ new Mosaic(); diff --git a/src/peery/file/FileHandler.java b/src/peery/file/FileHandler.java index f62cdef..379ca87 100644 --- a/src/peery/file/FileHandler.java +++ b/src/peery/file/FileHandler.java @@ -138,6 +138,7 @@ public class FileHandler { + " Attempted to write at "+file.getAbsolutePath()); e.printStackTrace(); } + Log.log(LogLevel.Info, "Saved "+file.getName()+" !"); } /** diff --git a/src/peery/picture/ImageAnalyzer.java b/src/peery/picture/ImageAnalyzer.java index a2a3bf5..7ab4377 100644 --- a/src/peery/picture/ImageAnalyzer.java +++ b/src/peery/picture/ImageAnalyzer.java @@ -29,6 +29,8 @@ public class ImageAnalyzer { private HashMap index; private int alphaThreshhold; + public final boolean keepRatio; + public int[] gridEnd; //Input Classification Worker private int inputWorkersLimit; @@ -57,10 +59,12 @@ public class ImageAnalyzer { // public ImageAnalyzer(FileHandler fh, int inputWorkersLimit, int targetWorkersLimit, - int matchWorkersLimit, int placeWorkersLimit, int alphaThreshhold){ + int matchWorkersLimit, int placeWorkersLimit, int alphaThreshhold, boolean keepRatio){ this.fh = fh; this.target = fh.loadImage(fh.TargetImageFile); this.alphaThreshhold = alphaThreshhold; + this.keepRatio = keepRatio; + this.gridEnd = new int[2]; this.inputWorkersLimit = inputWorkersLimit; this.targetWorkersLimit = targetWorkersLimit; @@ -179,8 +183,9 @@ public class ImageAnalyzer { */ public void classifyTarget(){ Log.log(LogLevel.Info, "Starting Target Classification. Calculating workload and spawning worker(s) ..."); - this.targetWorkers = new TargetImageAnalyzerWorker[targetWorkersLimit]; - int workload = this.slotCount / this.targetWorkersLimit; + this.targetWorkers = new TargetImageAnalyzerWorker[targetWorkersLimit+1]; + Log.log(LogLevel.Debug, slotCount+" slot(s) need to be classified!"); + int workload = this.slotCount / (this.targetWorkersLimit); int initialWork = this.slotCount % workload; int currWorker = 0; if(initialWork != 0){ @@ -193,6 +198,8 @@ public class ImageAnalyzer { targetWorkers[i] = new TargetImageAnalyzerWorker(this, this.targetWorkerName+Integer.toString(i), currWork, currWork+workload); currWork += workload; } + targetWorkers[targetWorkersLimit] = new TargetImageAnalyzerWorker(this, this.targetWorkerName+Integer.toString(targetWorkersLimit), currWork, currWork+workload); + Log.log(LogLevel.Debug, "Ended on assigning "+(currWork+workload)+" slot(s)!"); Log.log(LogLevel.Info, "Spawned "+(currWorker+1)+" target worker(s)"); } @@ -202,10 +209,11 @@ public class ImageAnalyzer { * (invoked by target worker instances to deliver finished workloads) * @param clFragment HashMap with classifications and coordinates (slot) as key. */ - public synchronized void addSlotClassifications(HashMap clFragment){ + public synchronized void addSlotClassifications(HashMap clFragment, String workerName){ for(int[] key: clFragment.keySet()){ if(!slotClassifications.containsKey(ImageUtils.parseCoord(key))){ this.slotClassifications.put(ImageUtils.parseCoord(key), clFragment.get(key)); + Log.log(LogLevel.Debug, "Got a classification added by "+workerName+" "+key[0]+" "+key[1]); /*if(key[0] == 199 && key[1] == 0){ Log.log(LogLevel.Error, "ImageAnalyzer.addSlotClassifications() - key[0]==30 && key[1]==0"); Log.log(LogLevel.Error, "Brrrriiiing!"); @@ -215,6 +223,7 @@ public class ImageAnalyzer { Log.log(LogLevel.Error, ""); }*/ }else{ + Log.log(LogLevel.Error, "Caused by ["+workerName+"] with "+key[0]+" "+key[1]); Log.log(LogLevel.Error, "Multiple classifcation of target slot detected! Workloads were not sliced correctly or coordinates are screwed up!"); continue; } @@ -347,12 +356,27 @@ public class ImageAnalyzer { * @param canvas * @return */ - public synchronized void placeImage(int gridX, int gridY, BufferedImage input){ + public synchronized void placeImage(int gridX, int gridY, BufferedImage input, boolean keepRatio){ assert(gridX < slotX && gridY < slotY); assert(input.getWidth() < postSlotWidth && input.getHeight() < postSlotHeight); + int picWidth, picHeight; + if(keepRatio){ + picWidth = input.getWidth(); + picHeight = input.getHeight(); + }else{ + picWidth = postSlotWidth; + picHeight = postSlotHeight; + } + Graphics2D g2 = (Graphics2D)canvas.getGraphics(); - g2.drawImage(input, gridX*postSlotWidth, gridY*postSlotHeight, postSlotWidth, postSlotHeight, null); + g2.drawImage(input, gridX*postSlotWidth, gridY*postSlotHeight, input.getWidth(), input.getHeight(), null); + if(gridEnd[0] < gridX*postSlotWidth+postSlotWidth){ + gridEnd[0] = gridX*postSlotWidth+postSlotWidth; + } + if(gridEnd[1] < gridY*postSlotHeight+postSlotHeight){ + gridEnd[1] = gridY*postSlotHeight+postSlotHeight; + } g2.dispose(); //Log.log(LogLevel.Error, "Drawn picture at "+gridX*postSlotWidth+" "+gridY*postSlotHeight+" with "+input.getWidth()+"x"+input.getHeight()); } diff --git a/src/peery/picture/ImageUtils.java b/src/peery/picture/ImageUtils.java index 9107f0a..70a47b2 100644 --- a/src/peery/picture/ImageUtils.java +++ b/src/peery/picture/ImageUtils.java @@ -19,8 +19,8 @@ public class ImageUtils { g2.dispose(); return img; } - if(targetSize.getWidth() > targetSize.getHeight()){ - tmp = input.getScaledInstance(-1, (int)targetSize.getHeight(), Image.SCALE_SMOOTH); + if(input.getWidth() > input.getHeight()){ + tmp = input.getScaledInstance((int)targetSize.getWidth(), -1, Image.SCALE_SMOOTH); img = new BufferedImage(tmp.getWidth(null), tmp.getHeight(null), BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = (Graphics2D) img.getGraphics(); @@ -28,7 +28,7 @@ public class ImageUtils { g2.dispose(); return img; }else{ - tmp = input.getScaledInstance((int)targetSize.getWidth(), -1, Image.SCALE_SMOOTH); + tmp = input.getScaledInstance(-1, (int)targetSize.getHeight(), Image.SCALE_SMOOTH); img = new BufferedImage(tmp.getWidth(null), tmp.getHeight(null), BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = (Graphics2D) img.getGraphics(); @@ -38,6 +38,19 @@ public class ImageUtils { } } + /** + * Converts the given number into the slot coordinates (not pixel coordinates). + * @param ia + * @param num + * @return + */ + public static int[] getSlotCoord(ImageAnalyzer ia, int num){ + int[] coords = new int[2]; + coords[0] = num%(ia.slotX+1); + coords[1] = num/(ia.slotX+1); + return coords; + } + /** * Converts from a numbered Slot to a specific slot start coordinate. * @@ -45,7 +58,16 @@ public class ImageUtils { * @param preMagnification true if slot sizes before the magnification are to be used. * @return */ - public static int[] getSlotCoord(ImageAnalyzer ia, int num, boolean preMagnification){ + public static int[] getSlotCoordPixels(ImageAnalyzer ia, int[] coords, boolean preMagnification){ + //TODO BUGGGSSS + /* + * Error collision values (num1 num2 preSlotDimensions -> result): + * 18614 18855 7x3 -> 399x174 + * 18565 18806 7x3 -> 56x174 + * 6735 6976 7x3 -> 1596x63 + * 5833 6074 7x3 -> 343x54 + * 382 623 7x3 -> 987x3 + */ int slotWidth, slotHeight; if(preMagnification){ slotWidth = ia.preSlotWidth; @@ -56,10 +78,8 @@ public class ImageUtils { } //TODO -----> FIX überschlag von Zeile 0 in 1; x zählt zu viel! - int ySlots = num/(ia.slotY-1); - int xSlots = num%(ia.slotX-1); - int[] coords = {xSlots*slotWidth, ySlots*slotHeight}; - return coords; + int[] pixelCoords = {coords[0]*slotWidth, coords[1]*slotHeight}; + return pixelCoords; } public static String parseCoord(int[] coord){ diff --git a/src/peery/picture/worker/MatchWorker.java b/src/peery/picture/worker/MatchWorker.java index bb8eccb..ebc1102 100644 --- a/src/peery/picture/worker/MatchWorker.java +++ b/src/peery/picture/worker/MatchWorker.java @@ -47,10 +47,10 @@ public class MatchWorker extends ImageAnalyzerWorker{ continue; } if(index == null || slotClassifications.get(ImageUtils.parseCoord(coord)) == null){ //TODO remove - Log.log(LogLevel.Error, "MatchWorker run() -> slotClass.get(ImageUtils.parse(coord)==null"); + Log.log(LogLevel.Error, "MatchWorker run() -> slotClassifications.get(ImageUtils.parseCoord(coord)==null"); Log.log(LogLevel.Error, "BRrrring"+slotClassifications.get(ImageUtils.parseCoord(coord))); Log.log(LogLevel.Error, "parsed: "+ImageUtils.parseCoord(coord)); - Log.log(LogLevel.Error, ""+coord[0]+" "+coord[1]); + Log.log(LogLevel.Error, "Unparsed: "+coord[0]+" "+coord[1]); Log.log(LogLevel.Error, ""); for(String key: slotClassifications.keySet()){ //Log.log(LogLevel.Error, key); diff --git a/src/peery/picture/worker/PlacementWorker.java b/src/peery/picture/worker/PlacementWorker.java index 778ba1d..507147d 100644 --- a/src/peery/picture/worker/PlacementWorker.java +++ b/src/peery/picture/worker/PlacementWorker.java @@ -1,5 +1,6 @@ package peery.picture.worker; +import java.awt.Dimension; import java.awt.image.BufferedImage; import java.io.File; import java.util.ArrayList; @@ -8,6 +9,7 @@ import java.util.HashMap; import peery.log.Log; import peery.log.LogLevel; import peery.picture.ImageAnalyzer; +import peery.picture.ImageUtils; public class PlacementWorker extends ImageAnalyzerWorker{ @@ -29,11 +31,13 @@ public class PlacementWorker extends ImageAnalyzerWorker{ } BufferedImage img = ia.fh.loadImage(file); ArrayList coords = coordMap.get(file); + img = ImageUtils.resizeImage(img, new Dimension(ia.postSlotWidth, ia.postSlotHeight), ia.keepRatio); + Log.log(LogLevel.Debug, "["+this.getName()+"] Resized image "+file.getName()+" to "+img.getWidth()+"x"+img.getHeight()+" !"); - Log.log(LogLevel.Info, "["+this.getName()+"] Going to place "+file.getName()+" "+coords.size()+" time(s)!"); + Log.log(LogLevel.Info, "["+this.getName()+"] Going to place \""+file.getName()+"\" "+coords.size()+" time(s)!"); int count = 0; for(int[] coord: coords){ - ia.placeImage(coord[0], coord[1], img); + ia.placeImage(coord[0], coord[1], img, ia.keepRatio); if(count % 100 == 0){ Log.log(LogLevel.Info, "["+this.getName()+"] Placed "+count+"/"+coords.size()+" instances! Continuing ..."); } diff --git a/src/peery/picture/worker/TargetImageAnalyzerWorker.java b/src/peery/picture/worker/TargetImageAnalyzerWorker.java index 8ce808e..a132c01 100644 --- a/src/peery/picture/worker/TargetImageAnalyzerWorker.java +++ b/src/peery/picture/worker/TargetImageAnalyzerWorker.java @@ -37,7 +37,7 @@ public class TargetImageAnalyzerWorker extends ImageAnalyzerWorker{ public void run(){ Log.log(LogLevel.Info, "Worker "+this.getName()+" up and running! Let's roll ..."); HashMap result = this.classifyArea(startCoord, endCoord); - ia.addSlotClassifications(result); + ia.addSlotClassifications(result, this.getName()); Log.log(LogLevel.Info, "Worker "+this.getName()+" finished work! Terminating ..."); } @@ -53,9 +53,9 @@ public class TargetImageAnalyzerWorker extends ImageAnalyzerWorker{ public HashMap classifyArea(int startCoord, int endCoord){ BufferedImage target = ia.target; HashMap results = new HashMap(); - System.out.println("Was ordered: "+startCoord+" "+endCoord); + Log.log(LogLevel.Debug, "["+this.getName()+"] Was ordered to classify "+startCoord+" "+endCoord); for(int i = startCoord; i < endCoord; i++){ - int[] coordPixels = ImageUtils.getSlotCoord(ia, i, true); + int[] coordPixels = ImageUtils.getSlotCoordPixels(ia, ImageUtils.getSlotCoord(ia, i), true); if(coordPixels[0]+ia.preSlotWidth >= ia.target.getWidth() || coordPixels[1]+ia.preSlotHeight >= ia.target.getHeight()){ //Dirty FIX //This will inevitably land outside the Raster otherwise. I should prevent these Coords from the start @@ -64,7 +64,9 @@ public class TargetImageAnalyzerWorker extends ImageAnalyzerWorker{ } int rgb = classifySlot(coordPixels[0], coordPixels[1], ia.preSlotWidth, ia.preSlotHeight, ia.target); int[] coordSlot = {coordPixels[0]/ia.preSlotWidth, coordPixels[1]/ia.preSlotHeight}; - + Log.log(LogLevel.Debug, "["+this.getName()+"] Classified on slot "+coordSlot[0]+" "+coordSlot[1]+" i:"+i + +" coordPixels:"+coordPixels[0]+"x"+coordPixels[1]+" preslotDimensions:"+ia.preSlotWidth+"x"+ia.preSlotHeight+ + " slotX:"+ia.slotX+" slotY:"+ia.slotY); /*if(coordPixels[0] == 210 && coordPixels[1] == 0){//TODO remove System.out.println("Brrrring!"); Log.log(LogLevel.Error, "Brrrriiing"+rgb+" "); @@ -87,8 +89,7 @@ public class TargetImageAnalyzerWorker extends ImageAnalyzerWorker{ * @return average Color as RGB value */ public int classifySlot(int gridX, int gridY, int slotWidth, int slotHeight, BufferedImage target){ - System.out.println(gridX+" "+gridY+" "+slotWidth+" "+slotHeight); - Log.log(LogLevel.Debug, "["+this.getName()+"] Slicing slot "+gridX+"x"+gridY+" out of the target for classification ..."); + Log.log(LogLevel.Debug, "["+this.getName()+"] Slicing slot at pixels: "+gridX+"x"+gridY+" out of the target with "+slotWidth+"x"+slotHeight+"for classification ..."); BufferedImage subImage = target.getSubimage(gridX, gridY, slotWidth, slotHeight); ColorModel cm = ColorModel.getRGBdefault(); float red = 0, green = 0, blue = 0; @@ -106,7 +107,7 @@ public class TargetImageAnalyzerWorker extends ImageAnalyzerWorker{ green = green/pixels; blue = blue/pixels; int rgb = new Color((int)red, (int)green, (int)blue).getRGB(); - Log.log(LogLevel.Debug, "["+this.getName()+"] Classified slot "+gridX+"x"+gridY+" with following rgb result: value:"+rgb+ + Log.log(LogLevel.Debug, "["+this.getName()+"] Classified slot at pixels: "+gridX+"x"+gridY+" with following rgb result: value:"+rgb+ " red:"+red+", green:"+green+", blue:"+blue); return rgb; }