diff --git a/src/peery/Mosaic.java b/src/peery/Mosaic.java index 1b26efb..8b7ed1a 100644 --- a/src/peery/Mosaic.java +++ b/src/peery/Mosaic.java @@ -1,31 +1,37 @@ package peery; import java.io.File; +import java.util.HashMap; import peery.file.FileHandler; +import peery.file.SettingsHandler; import peery.log.Log; import peery.log.LogLevel; import peery.picture.ImageAnalyzer; +import peery.picture.ImageUtils; public class Mosaic extends Thread{ public static final String programmName = "Mosaic", - versionString = "Alpha-0.33"; + versionString = "Alpha-0.40"; - private static final String outputName = "Output"; - private static final int gridX = 100, gridY = 50, targetMulti = 2, + private static String outputName = "Output"; + private static int gridWidth = 100, gridHeight = 100, targetMulti = 2, alphaThreshhold = 30, adaptionCount = 300, inputWorkerLimit = 10, - targetWorkerLimit = 4, + targetWorkerLimit = 8, matchWorkerLimit = 10, - placeWorkerLimit = 2; - private static final double adaptionStep = 1.1, gridErrorThresh = 0.15; - private static boolean keepRatio = false; + placeWorkerLimit = 2; //get overwritten by file settings + private static double adaptionStep = 1.1, gridErrorThresh = 0.15; + private static boolean keepRatio = true, overlapImages = true; /* * - * Performance: + * TO DO: + * Write down stats what image was used (how often) + * Make settings save & load from a settings file * + * Performance: * * FIX: * rasterization doesn't cover everything! @@ -36,6 +42,7 @@ public class Mosaic extends Thread{ * explore guarantee of usage of at least once, each image. */ + public SettingsHandler sh; public FileHandler fh; public ImageAnalyzer ia; @@ -43,17 +50,26 @@ public class Mosaic extends Thread{ fh = new FileHandler("resources"); Log.log(LogLevel.Info, "Starting "+programmName+" "+versionString); ia = new ImageAnalyzer(fh, inputWorkerLimit, targetWorkerLimit, matchWorkerLimit, - placeWorkerLimit, alphaThreshhold, keepRatio); - + placeWorkerLimit, alphaThreshhold, keepRatio, overlapImages); + sh = new SettingsHandler(fh, "settings.txt"); this.start(); } public void run(){ Log.log.perfLog("Started "+programmName+" v."+versionString); + + Log.log(LogLevel.Info, "Checking for settings file ..."); + if(sh.settingsFile.exists()){ + Log.log(LogLevel.Info, "Importing settings from file ..."); + importSettings(sh.loadSettings()); + }else{ + Log.log(LogLevel.Info, "Couldn't find a settings file. Dumping hard-coded settings ..."); + sh.saveSettings(exportSettings()); + } + System.out.println("gridHeightxxx:"+gridHeight); + Log.log.perfLog("Starting indexing ..."); prepMatching(); - //Log.log(LogLevel.Error, ia.slotClassifications.toString()+" prep:"+ia.isPrepInProgress()); - //Log.log(LogLevel.Error, ia.slotClassifications.toString()+" prep:"+ia.isPrepInProgress()); while(ia.isPrepInProgress()){ try { @@ -82,13 +98,14 @@ public class Mosaic extends Thread{ e.printStackTrace(); } } - cutOutGrid(); + ImageUtils.cutOutGrid(ia); Log.log.perfLog("Finished Placement!"); Log.log(LogLevel.Info, "Finished placement. Output is done ..."); Log.log.perfLog("Saving output to file ..."); fh.saveImage(ia.canvas, new File(fh.OutputFolder+fh.fs+outputName+"-"+fh.OutputFolder.listFiles().length+".png")); Log.log.perfLog("Everything done! Exiting ..."); Log.log.finishPerfLog(); + Log.shutdownLog(); } /** @@ -97,7 +114,7 @@ public class Mosaic extends Thread{ * Starts threads to index all not indexed images and rasterizes and classifies the Target. */ public void prepMatching(){ - ia.rasterizeTarget(gridX, gridY, targetMulti, gridErrorThresh, adaptionCount, adaptionStep); + ia.rasterizeTarget(gridWidth, gridHeight, targetMulti, gridErrorThresh, adaptionCount, adaptionStep); ia.updateIndex(); ia.classifyTarget(); } @@ -112,12 +129,49 @@ public class Mosaic extends Thread{ 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 void importSettings(HashMap settings){ + if(0 < settings.get("version").compareTo(versionString)){ + Log.log(LogLevel.Error, "Won't import settings! Version String doesn't match!"); + Log.log(LogLevel.Error, "Read: "+versionString+" Expected: "+settings.get("version")); + return; + } + outputName = settings.get("outputName"); + gridWidth = Integer.parseInt(settings.get("gridWidth")); + gridHeight = Integer.parseInt(settings.get("gridHeight")); + targetMulti = Integer.parseInt(settings.get("targetMultiplier")); + alphaThreshhold = Integer.parseInt(settings.get("alphaThreshhold")); + adaptionCount = Integer.parseInt(settings.get("adaptionCount")); + inputWorkerLimit = Integer.parseInt(settings.get("inputWorkerLimit")); + targetWorkerLimit = Integer.parseInt(settings.get("targetWorkerLimit")); + matchWorkerLimit = Integer.parseInt(settings.get("matchWorkerLimit")); + placeWorkerLimit = Integer.parseInt(settings.get("placeWorkerLimit")); + adaptionStep = Double.parseDouble(settings.get("adaptionStep")); + gridErrorThresh = Double.parseDouble(settings.get("gridErrorThresh")); + keepRatio = Boolean.parseBoolean(settings.get("keepRatio")); + overlapImages = Boolean.parseBoolean(settings.get("overlapImages")); } - + + public HashMap exportSettings(){ + HashMap settings = new HashMap(); + settings.put("version", versionString); + settings.put("outputName", outputName); + settings.put("gridWidth", Integer.toString(gridWidth)); + settings.put("gridHeight", Integer.toString(gridHeight)); + settings.put("targetMultiplier", Integer.toString(targetMulti)); + settings.put("alphaThreshhold", Integer.toString(alphaThreshhold)); + settings.put("adaptionCount", Integer.toString(adaptionCount)); + settings.put("inputWorkerLimit", Integer.toString(inputWorkerLimit)); + settings.put("targetWorkerLimit", Integer.toString(targetWorkerLimit)); + settings.put("matchWorkerLimit", Integer.toString(matchWorkerLimit)); + settings.put("placeWorkerLimit", Integer.toString(placeWorkerLimit)); + settings.put("adaptionStep", Double.toString(adaptionStep)); + settings.put("gridErrorThresh", Double.toString(gridErrorThresh)); + settings.put("keepRatio", Boolean.toString(keepRatio)); + settings.put("overlapImages", Boolean.toString(overlapImages)); + + return settings; + } + public static void main(String[] args){ new Mosaic(); } diff --git a/src/peery/file/FileHandler.java b/src/peery/file/FileHandler.java index 379ca87..2bb9907 100644 --- a/src/peery/file/FileHandler.java +++ b/src/peery/file/FileHandler.java @@ -213,6 +213,23 @@ public class FileHandler { return indexData; } + public void overwriteIndex(HashMap index){ + try{ + BufferedWriter bw = new BufferedWriter(new FileWriter(indexFile, false)); + for(String key: index.keySet()){ + bw.write(index.get(key)+";"+key+"\n"); + } + bw.flush(); + bw.close(); + Log.log(LogLevel.Info, "Overwrote index file with new index!"); + } catch (IOException e) { + Log.log(LogLevel.Critical, "Couldn't create or write index file "+indexFile.getAbsolutePath()+" ." + + "Are write permissions missing?"); + e.printStackTrace(); + return; + } + } + /** * Parses a line of indexData into an ArrayList containing the rgb int and the name * @param indexData diff --git a/src/peery/file/SettingsHandler.java b/src/peery/file/SettingsHandler.java new file mode 100644 index 0000000..dccaa24 --- /dev/null +++ b/src/peery/file/SettingsHandler.java @@ -0,0 +1,70 @@ +package peery.file; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.HashMap; +import java.util.Scanner; + +import peery.log.Log; +import peery.log.LogLevel; + +public class SettingsHandler { + + public File settingsFile; + + public SettingsHandler(FileHandler fh, String settings){ + this.settingsFile = new File(fh.sourceFolder.getAbsolutePath()+fh.fs+settings);; + } + + /** + * Saves the settings to file. + * @param settings + */ + public void saveSettings(HashMap settings){ + try { + Log.log(LogLevel.Info, "Saving settings to file ..."); + BufferedWriter bw = new BufferedWriter(new FileWriter(settingsFile, false)); + for(String key: settings.keySet()){ + bw.write(key+"="+settings.get(key)+"\n"); + bw.flush(); + } + bw.close(); + } catch (IOException e) { + Log.log(LogLevel.Error, "Couldn't write settings file at "+settingsFile.getAbsolutePath()+" ! Are write permissions missing?"); + e.printStackTrace(); + } + } + + /** + * Loads all settings from file. + * @return + */ + public HashMap loadSettings(){ + HashMap settings; + try { + settings = new HashMap(); + Scanner sc = new Scanner(new BufferedReader(new FileReader(settingsFile))); + while(sc.hasNext()){ + String raw = sc.nextLine(); + if(!raw.matches(".*=.*")){ + continue; + } + String[] entry = raw.split("="); + //System.out.println(entry[0]+":"+entry[1]); + settings.put(entry[0], entry[1]); + } + sc.close(); + return settings; + } catch (FileNotFoundException e) { + Log.log(LogLevel.Error, "Couldn't load settings file at "+settingsFile.getAbsolutePath()+" ! Are read permissions missing?"); + e.printStackTrace(); + } + return null; + } + +} diff --git a/src/peery/log/Log.java b/src/peery/log/Log.java index 4db53ce..97fb1eb 100644 --- a/src/peery/log/Log.java +++ b/src/peery/log/Log.java @@ -11,7 +11,7 @@ import peery.file.FileHandler; public class Log { public static Log log; - public static final boolean silenceDebug = false, + public static final boolean silenceDebug = true, appendEvents = false, appendErrors = false; private final static String readmeText = @@ -73,6 +73,7 @@ public class Log { try { Log.log.errorWriter.close(); Log.log.eventWriter.close(); + Log.log.perfWriter.close(); Log.log = null; } catch (IOException e) { e.printStackTrace(); diff --git a/src/peery/picture/ImageAnalyzer.java b/src/peery/picture/ImageAnalyzer.java index 7ab4377..0039a88 100644 --- a/src/peery/picture/ImageAnalyzer.java +++ b/src/peery/picture/ImageAnalyzer.java @@ -29,7 +29,7 @@ public class ImageAnalyzer { private HashMap index; private int alphaThreshhold; - public final boolean keepRatio; + public final boolean keepRatio, overlapImages; public int[] gridEnd; //Input Classification Worker @@ -59,11 +59,13 @@ public class ImageAnalyzer { // public ImageAnalyzer(FileHandler fh, int inputWorkersLimit, int targetWorkersLimit, - int matchWorkersLimit, int placeWorkersLimit, int alphaThreshhold, boolean keepRatio){ + int matchWorkersLimit, int placeWorkersLimit, int alphaThreshhold, boolean keepRatio, + boolean overlapImages){ this.fh = fh; this.target = fh.loadImage(fh.TargetImageFile); this.alphaThreshhold = alphaThreshhold; this.keepRatio = keepRatio; + this.overlapImages = overlapImages; this.gridEnd = new int[2]; this.inputWorkersLimit = inputWorkersLimit; @@ -140,7 +142,7 @@ public class ImageAnalyzer { } } Log.log(LogLevel.Info, "Work list filled with "+inputFiles.size()+" file(s)!"); - + if(inputFiles.size() <= 0){ Log.log(LogLevel.Info, "No work then! Ending Index Update ..."); return; @@ -335,7 +337,8 @@ public class ImageAnalyzer { Log.log(LogLevel.Debug, "Target will be "+(int)(targetSize.width*targetSizeMultiplier) +"x"+(int)(targetSize.height*targetSizeMultiplier)+" big."); - Log.log(LogLevel.Debug, "Slots are "+preSlotWidth+"x"+preSlotHeight+" big."); + Log.log(LogLevel.Debug, "Slots on the Target are "+preSlotWidth+"x"+preSlotHeight+" big."); + Log.log(LogLevel.Debug, "Slots on the Output are "+postSlotWidth+"x"+postSlotHeight+" big."); slotX = gridX; slotY = gridY; slotCount = gridX*gridY; diff --git a/src/peery/picture/ImageUtils.java b/src/peery/picture/ImageUtils.java index 70a47b2..8d62d35 100644 --- a/src/peery/picture/ImageUtils.java +++ b/src/peery/picture/ImageUtils.java @@ -5,37 +5,63 @@ import java.awt.Graphics2D; import java.awt.Image; import java.awt.image.BufferedImage; +import peery.log.Log; +import peery.log.LogLevel; + public class ImageUtils { - public static BufferedImage resizeImage(BufferedImage input, Dimension targetSize, boolean keepRatio){ + private static final int scalingMethod = Image.SCALE_SMOOTH; + + /** + * TODO Needs cleanup + * @param input + * @param targetSize + * @param keepRatio + * @param overlapImages + * @return + */ + public static BufferedImage resizeImage(BufferedImage input, Dimension targetSize, boolean keepRatio, + boolean overlapImages){ Image tmp; BufferedImage img; + int imageWidth, imageHeight; if(!keepRatio){ - tmp = input.getScaledInstance((int)targetSize.getWidth(), (int)targetSize.getHeight(), Image.SCALE_SMOOTH); - img = new BufferedImage(tmp.getWidth(null), tmp.getHeight(null), BufferedImage.TYPE_INT_ARGB); - - Graphics2D g2 = (Graphics2D) img.getGraphics(); - g2.drawImage(tmp, 0, 0, null); - g2.dispose(); - return img; + imageWidth = (int)targetSize.getWidth(); + imageHeight = (int)targetSize.getHeight(); } - 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(); - g2.drawImage(tmp, 0, 0, null); - g2.dispose(); - return img; - }else{ - 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(); - g2.drawImage(tmp, 0, 0, null); - g2.dispose(); - return img; + else{ + if(!overlapImages){ + if(input.getWidth() > input.getHeight()){ + imageWidth = (int)targetSize.getWidth(); + imageHeight = -1; + }else{ + imageWidth = -1; + imageHeight = (int)targetSize.getHeight(); + } + }else{ + if(input.getWidth() < input.getHeight()){ + imageWidth = (int)targetSize.getWidth(); + imageHeight = -1; + }else{ + imageWidth = -1; + imageHeight = (int)targetSize.getHeight(); + } + } } + + tmp = input.getScaledInstance(imageWidth, imageHeight, scalingMethod); + img = new BufferedImage(tmp.getWidth(null), tmp.getHeight(null), BufferedImage.TYPE_INT_ARGB); + + Graphics2D g2 = (Graphics2D) img.getGraphics(); + g2.drawImage(tmp, 0, 0, null); + g2.dispose(); + return img; + } + + public static void cutOutGrid(ImageAnalyzer ia){ + 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]); } /** diff --git a/src/peery/picture/worker/PlacementWorker.java b/src/peery/picture/worker/PlacementWorker.java index 507147d..a63c2b6 100644 --- a/src/peery/picture/worker/PlacementWorker.java +++ b/src/peery/picture/worker/PlacementWorker.java @@ -26,12 +26,12 @@ public class PlacementWorker extends ImageAnalyzerWorker{ public void run() { Log.log(LogLevel.Info, "Worker "+this.getName()+" commencing work!"); for(File file: coordMap.keySet()){ - if(coordMap.get(file) == null){ + if(coordMap.get(file) == null){ //Check to avoid NullPointerException continue; } BufferedImage img = ia.fh.loadImage(file); ArrayList coords = coordMap.get(file); - img = ImageUtils.resizeImage(img, new Dimension(ia.postSlotWidth, ia.postSlotHeight), ia.keepRatio); + img = ImageUtils.resizeImage(img, new Dimension(ia.postSlotWidth, ia.postSlotHeight), ia.keepRatio, ia.overlapImages); 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)!");