|
|
@ -7,41 +7,297 @@ import java.awt.image.BufferedImage;
|
|
|
|
import java.awt.image.ColorModel;
|
|
|
|
import java.awt.image.ColorModel;
|
|
|
|
import java.io.File;
|
|
|
|
import java.io.File;
|
|
|
|
import java.text.DecimalFormat;
|
|
|
|
import java.text.DecimalFormat;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
|
|
|
|
|
|
|
import peery.file.FileHandler;
|
|
|
|
import peery.file.FileHandler;
|
|
|
|
import peery.log.Log;
|
|
|
|
import peery.log.Log;
|
|
|
|
import peery.log.LogLevel;
|
|
|
|
import peery.log.LogLevel;
|
|
|
|
|
|
|
|
import peery.picture.worker.PlacementWorker;
|
|
|
|
|
|
|
|
import peery.picture.worker.PlainImageAnalyzerWorker;
|
|
|
|
|
|
|
|
import peery.picture.worker.MatchWorker;
|
|
|
|
|
|
|
|
import peery.picture.worker.TargetImageAnalyzerWorker;
|
|
|
|
|
|
|
|
|
|
|
|
public class ImageAnalyzer {
|
|
|
|
public class ImageAnalyzer {
|
|
|
|
|
|
|
|
|
|
|
|
public BufferedImage target;
|
|
|
|
public BufferedImage target;
|
|
|
|
private FileHandler fh;
|
|
|
|
public FileHandler fh;
|
|
|
|
|
|
|
|
|
|
|
|
private Dimension /*biggestInputSize,*/ targetSize;
|
|
|
|
private Dimension /*biggestInputSize,*/ targetSize;
|
|
|
|
public int slotWidth, slotHeight, slotX, slotY;
|
|
|
|
public int preSlotWidth, preSlotHeight, postSlotWidth, postSlotHeight, slotX, slotY, slotCount;
|
|
|
|
public BufferedImage canvas;
|
|
|
|
public BufferedImage canvas;
|
|
|
|
|
|
|
|
private HashMap<String, Integer> index;
|
|
|
|
|
|
|
|
|
|
|
|
public ImageAnalyzer(FileHandler fh){
|
|
|
|
private int alphaThreshhold;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Input Classification Worker
|
|
|
|
|
|
|
|
private int inputWorkersLimit;
|
|
|
|
|
|
|
|
private static final String inputWorkerName = "inputWorker";
|
|
|
|
|
|
|
|
private PlainImageAnalyzerWorker[] inputWorkers;
|
|
|
|
|
|
|
|
private ArrayList<File> inputFiles;
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Target Classification Worker
|
|
|
|
|
|
|
|
private int targetWorkersLimit;
|
|
|
|
|
|
|
|
private static final String targetWorkerName = "targetWorker";
|
|
|
|
|
|
|
|
private TargetImageAnalyzerWorker[] targetWorkers;
|
|
|
|
|
|
|
|
public HashMap<String, Integer> slotClassifications; //TODO change back private
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Matching Worker
|
|
|
|
|
|
|
|
private int matchWorkersLimit;
|
|
|
|
|
|
|
|
private static final String matchWorkerName = "matchWorker";
|
|
|
|
|
|
|
|
private MatchWorker[] matchWorkers;
|
|
|
|
|
|
|
|
private HashMap<int[], File[]> fileMap;
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//Placement Worker
|
|
|
|
|
|
|
|
private static final String placeWorkerName = "placeWorker";
|
|
|
|
|
|
|
|
private PlacementWorker placeWorker;
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public ImageAnalyzer(FileHandler fh, int inputWorkersLimit, int targetWorkersLimit,
|
|
|
|
|
|
|
|
int matchWorkersLimit, int placeWorkersLimit, int alphaThreshhold){
|
|
|
|
this.fh = fh;
|
|
|
|
this.fh = fh;
|
|
|
|
this.target = fh.loadImage(fh.TargetImageFile);
|
|
|
|
this.target = fh.loadImage(fh.TargetImageFile);
|
|
|
|
|
|
|
|
this.alphaThreshhold = alphaThreshhold;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.inputWorkersLimit = inputWorkersLimit;
|
|
|
|
|
|
|
|
this.targetWorkersLimit = targetWorkersLimit;
|
|
|
|
|
|
|
|
this.matchWorkersLimit = matchWorkersLimit;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.slotClassifications = new HashMap<String, Integer>();
|
|
|
|
|
|
|
|
this.fileMap = new HashMap<int[], File[]>();
|
|
|
|
|
|
|
|
this.inputFiles = new ArrayList<File>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.inputWorkers = new PlainImageAnalyzerWorker[inputWorkersLimit];
|
|
|
|
|
|
|
|
this.targetWorkers = new TargetImageAnalyzerWorker[targetWorkersLimit];
|
|
|
|
|
|
|
|
this.matchWorkers = new MatchWorker[matchWorkersLimit];
|
|
|
|
|
|
|
|
this.index = fh.loadIndex();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void rasterizeTarget(int gridX, int gridY, float targetSizeMultiplier, double gridErrorThresh, int adaptionThreshhold, double adaptionStep){
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Checks if any of the Worker's are still out there doing stuff.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* @return true if at least one worker of any kind is still alive!
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public boolean isPrepInProgress(){
|
|
|
|
|
|
|
|
for(PlainImageAnalyzerWorker pWorker: inputWorkers){
|
|
|
|
|
|
|
|
if(pWorker != null && pWorker.isAlive()){
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for(TargetImageAnalyzerWorker tWorker: targetWorkers){
|
|
|
|
|
|
|
|
if(tWorker != null && tWorker.isAlive()){
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean isMatchInProgress(){
|
|
|
|
|
|
|
|
for(MatchWorker mWorker: matchWorkers){
|
|
|
|
|
|
|
|
if(mWorker != null && mWorker.isAlive()){
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public boolean isPlaceInProgress(){
|
|
|
|
|
|
|
|
if(this.placeWorker != null && this.placeWorker.isAlive()){
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public synchronized void reloadIndex(){
|
|
|
|
|
|
|
|
Log.log(LogLevel.Debug, "Reloading index!");
|
|
|
|
|
|
|
|
this.index = fh.loadIndex();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Updates the index by spawning worker threads to classify new input Images.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* Checks for already indexed images before adding them to the workload.
|
|
|
|
|
|
|
|
* To check if workers are still running check worker.getRunning() or worker.isAlive() (latter preferred)
|
|
|
|
|
|
|
|
* Worker instances are found in this.inputWorkers
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public void updateIndex(){
|
|
|
|
|
|
|
|
Log.log(LogLevel.Info, "Starting to update Index.");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Log.log(LogLevel.Debug, "Filling work list with (unread) input files ...");
|
|
|
|
|
|
|
|
for(File f: fh.InputImagesFolder.listFiles()){
|
|
|
|
|
|
|
|
if(this.fh.loadIndexEntry(index, f) == null){
|
|
|
|
|
|
|
|
Log.log(LogLevel.Debug, "Unindexed input file "+f.getName()+" found! Adding to worklist ...");
|
|
|
|
|
|
|
|
inputFiles.add(f);
|
|
|
|
|
|
|
|
}else{
|
|
|
|
|
|
|
|
Log.log(LogLevel.Debug, "Already indexed input file "+f.getName()+" found!");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Log.log(LogLevel.Info, "Spawning input workers ...");
|
|
|
|
|
|
|
|
inputWorkers = new PlainImageAnalyzerWorker[inputWorkersLimit];
|
|
|
|
|
|
|
|
int count = 0;
|
|
|
|
|
|
|
|
for(int i = 0; i < inputWorkersLimit; i++){
|
|
|
|
|
|
|
|
if(inputFiles.size() <= i){
|
|
|
|
|
|
|
|
Log.log(LogLevel.Debug, "Allowed more workers than ther was work. Stopped spawning on "+i+" worker(s)!");
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
inputWorkers[i] = new PlainImageAnalyzerWorker(this, inputWorkerName+Integer.toString(i),
|
|
|
|
|
|
|
|
inputFiles.get(i), this.alphaThreshhold);
|
|
|
|
|
|
|
|
count++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Log.log(LogLevel.Info, count+" worker(s) spawned!");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Securely retrieves new workload for PlainImageAnalyzerWorker instances.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* Returns null when the list is empty.
|
|
|
|
|
|
|
|
* @return File for PlainImageAnalyzerWorker to work with.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public synchronized File getNewInputFile(){
|
|
|
|
|
|
|
|
if(inputFiles.size() == 0){
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
File file = inputFiles.get(0);
|
|
|
|
|
|
|
|
inputFiles.remove(0);
|
|
|
|
|
|
|
|
return file;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Starts classification of the target image's slots via spawning threaded workers.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* To check if workers are still running check worker.getRunning() or worker.isAlive() (latter preferred)
|
|
|
|
|
|
|
|
* Worker instances are found in this.targetWorkers
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
int initialWork = this.slotCount % workload;
|
|
|
|
|
|
|
|
int currWorker = 0;
|
|
|
|
|
|
|
|
if(initialWork != 0){
|
|
|
|
|
|
|
|
this.targetWorkers[0] = new TargetImageAnalyzerWorker(this, this.targetWorkerName+"0", 0, initialWork);
|
|
|
|
|
|
|
|
currWorker++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int currWork = initialWork;
|
|
|
|
|
|
|
|
for(int i = currWorker; i < targetWorkersLimit; i++){
|
|
|
|
|
|
|
|
targetWorkers[i] = new TargetImageAnalyzerWorker(this, this.targetWorkerName+Integer.toString(i), currWork, currWork+workload);
|
|
|
|
|
|
|
|
currWork += workload;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Log.log(LogLevel.Info, "Spawned "+(currWorker+1)+" target worker(s)");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Adds the given classification map to the classification map of the TargetImage.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* (invoked by target worker instances to deliver finished workloads)
|
|
|
|
|
|
|
|
* @param clFragment HashMap with classifications and coordinates (slot) as key.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public synchronized void addSlotClassifications(HashMap<int[], Integer> clFragment){
|
|
|
|
|
|
|
|
for(int[] key: clFragment.keySet()){
|
|
|
|
|
|
|
|
if(!slotClassifications.containsKey(ImageUtils.parseCoord(key))){
|
|
|
|
|
|
|
|
this.slotClassifications.put(ImageUtils.parseCoord(key), clFragment.get(key));
|
|
|
|
|
|
|
|
/*if(key[0] == 199 && key[1] == 0){
|
|
|
|
|
|
|
|
Log.log(LogLevel.Error, "ImageAnalyzer.addSlotClassifications() - key[0]==30 && key[1]==0");
|
|
|
|
|
|
|
|
Log.log(LogLevel.Error, "Brrrriiiing!");
|
|
|
|
|
|
|
|
Log.log(LogLevel.Error, "parseCoord: "+ImageUtils.parseCoord(key));
|
|
|
|
|
|
|
|
Log.log(LogLevel.Error, "deliveredFrag: "+clFragment.get(key));
|
|
|
|
|
|
|
|
Log.log(LogLevel.Error, "result: "+this.slotClassifications.get(ImageUtils.parseCoord(key)));
|
|
|
|
|
|
|
|
Log.log(LogLevel.Error, "");
|
|
|
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
}else{
|
|
|
|
|
|
|
|
Log.log(LogLevel.Error, "Multiple classifcation of target slot detected! Workloads were not sliced correctly or coordinates are screwed up!");
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void calculateMatches(){
|
|
|
|
|
|
|
|
Log.log(LogLevel.Info, "Starting to match slots to indexed Images. Spawning workers ...");
|
|
|
|
|
|
|
|
this.matchWorkers = new MatchWorker[matchWorkersLimit];
|
|
|
|
|
|
|
|
int workload = this.slotCount / this.matchWorkersLimit;
|
|
|
|
|
|
|
|
int initialWork = this.slotCount % workload;
|
|
|
|
|
|
|
|
int currWorker = 0;
|
|
|
|
|
|
|
|
int[] lel = {62, 68};
|
|
|
|
|
|
|
|
if(initialWork != 0){
|
|
|
|
|
|
|
|
this.matchWorkers[0] = new MatchWorker(this, matchWorkerName+"0", 0, initialWork, 0,
|
|
|
|
|
|
|
|
slotClassifications, index);
|
|
|
|
|
|
|
|
currWorker++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int currWork = initialWork;
|
|
|
|
|
|
|
|
for(int i = currWorker; i < matchWorkersLimit; i++){
|
|
|
|
|
|
|
|
matchWorkers[i] = new MatchWorker(this, matchWorkerName+"0", currWork, currWork+workload, 0,
|
|
|
|
|
|
|
|
slotClassifications, index);;
|
|
|
|
|
|
|
|
currWork += workload;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Log.log(LogLevel.Info, "Spawned "+(currWorker+1)+" match worker(s)");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Merges the given fileMapFragment with this instance's one.
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
* (invoked by MatchWorker instances)
|
|
|
|
|
|
|
|
* @param fileMapFragment
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public synchronized void addFileMaps(HashMap<int[], File[]> fileMapFragment){
|
|
|
|
|
|
|
|
for(int[] key: fileMapFragment.keySet()){
|
|
|
|
|
|
|
|
if(!fileMap.containsKey(key)){
|
|
|
|
|
|
|
|
this.fileMap.put(key, fileMapFragment.get(key));
|
|
|
|
|
|
|
|
}else{
|
|
|
|
|
|
|
|
Log.log(LogLevel.Error, "Multiple fileMap fragments for "+key.toString()+" available! Something went wrong ...");
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Starts processing the fileMap and queues workloads for PlacementWorker instances and spawns them.
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public void placeFragments(){
|
|
|
|
|
|
|
|
Log.log(LogLevel.Info, "Starting to gather Coordinates for each Image.");
|
|
|
|
|
|
|
|
HashMap<File, ArrayList<int[]>> coordMap = new HashMap<File, ArrayList<int[]>>();
|
|
|
|
|
|
|
|
for(int[] coord: fileMap.keySet()){
|
|
|
|
|
|
|
|
File file = fileMap.get(coord)[0];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(!coordMap.containsKey(file)){
|
|
|
|
|
|
|
|
coordMap.put(file, new ArrayList<int[]>());
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
coordMap.get(file).add(coord);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Log.log(LogLevel.Info, "Start placing fragments on the target.");
|
|
|
|
|
|
|
|
this.placeWorker = new PlacementWorker(this, this.placeWorkerName, coordMap);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void rasterizeTarget(int gridX, int gridY, float targetSizeMultiplier,
|
|
|
|
|
|
|
|
double gridErrorThresh, int adaptionCount, double adaptionStep){
|
|
|
|
Log.log(LogLevel.Info, "Rasterizing target image ...");
|
|
|
|
Log.log(LogLevel.Info, "Rasterizing target image ...");
|
|
|
|
Log.log(LogLevel.Info, "Aiming for "+gridX+"*"+gridY+"="+(gridX*gridY)+" tiles ...");
|
|
|
|
Log.log(LogLevel.Info, "Aiming for "+gridX+"*"+gridY+"="+(gridX*gridY)+" tiles ...");
|
|
|
|
|
|
|
|
Log.log(LogLevel.Debug, "Target is "+target.getWidth()+"x"+target.getHeight()+" big!");
|
|
|
|
//this.biggestInputSize = this.fh.loadBiggestDimension();
|
|
|
|
//this.biggestInputSize = this.fh.loadBiggestDimension();
|
|
|
|
this.targetSize = new Dimension(this.target.getWidth(), this.target.getHeight());
|
|
|
|
this.targetSize = new Dimension(this.target.getWidth(), this.target.getHeight());
|
|
|
|
|
|
|
|
|
|
|
|
//TMP
|
|
|
|
|
|
|
|
int count = 0;
|
|
|
|
int count = 0;
|
|
|
|
DecimalFormat df = new DecimalFormat("#");
|
|
|
|
DecimalFormat df = new DecimalFormat("#");
|
|
|
|
while(true){
|
|
|
|
while(true){
|
|
|
|
double realSlotWidth = ((targetSize.width*targetSizeMultiplier)/gridX);
|
|
|
|
double resultSlotWidth = ((targetSize.width*targetSizeMultiplier)/gridX);
|
|
|
|
double realSlotHeight = ((targetSize.height*targetSizeMultiplier)/gridY);
|
|
|
|
double resultSlotHeight = ((targetSize.height*targetSizeMultiplier)/gridY);
|
|
|
|
Log.log(LogLevel.Debug, "Perfect slot Size would be "+realSlotWidth+"x"+realSlotHeight);
|
|
|
|
postSlotWidth = (int)resultSlotWidth;
|
|
|
|
double widthLoss = Math.abs(realSlotWidth - Double.parseDouble(df.format(realSlotWidth)));
|
|
|
|
postSlotHeight = (int)resultSlotHeight;
|
|
|
|
double heightLoss = Math.abs(realSlotHeight - Double.parseDouble(df.format(realSlotHeight)));
|
|
|
|
|
|
|
|
|
|
|
|
Log.log(LogLevel.Debug, "Perfect slot Size would be "+resultSlotWidth+"x"+resultSlotHeight);
|
|
|
|
|
|
|
|
double widthLoss = Math.abs(resultSlotWidth - Double.parseDouble(df.format(resultSlotWidth)));
|
|
|
|
|
|
|
|
double heightLoss = Math.abs(resultSlotHeight - Double.parseDouble(df.format(resultSlotHeight)));
|
|
|
|
if(widthLoss > gridErrorThresh || heightLoss > gridErrorThresh){
|
|
|
|
if(widthLoss > gridErrorThresh || heightLoss > gridErrorThresh){
|
|
|
|
Log.log(LogLevel.Critical, "Grid misplacement error exceeded threshhold! Error is width:"+widthLoss+" height:"+heightLoss);
|
|
|
|
Log.log(LogLevel.Critical, "Grid misplacement error exceeded threshhold! Error is width:"+widthLoss+" height:"+heightLoss);
|
|
|
|
if(widthLoss > gridErrorThresh){
|
|
|
|
if(widthLoss > gridErrorThresh){
|
|
|
@ -52,7 +308,7 @@ public class ImageAnalyzer {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Log.log(LogLevel.Info, "This is the "+(++count)+". adaption to ease the error.");
|
|
|
|
Log.log(LogLevel.Info, "This is the "+(++count)+". adaption to ease the error.");
|
|
|
|
Log.log(LogLevel.Info, "Aiming for "+gridX+"*"+gridY+"="+(gridX*gridY)+" tiles ...");
|
|
|
|
Log.log(LogLevel.Info, "Aiming for "+gridX+"*"+gridY+"="+(gridX*gridY)+" tiles ...");
|
|
|
|
if(count > adaptionThreshhold){
|
|
|
|
if(count > adaptionCount){
|
|
|
|
Log.log(LogLevel.Critical, "Could not adapt to grid misplacement error! The result might be cut off or missing parts!");
|
|
|
|
Log.log(LogLevel.Critical, "Could not adapt to grid misplacement error! The result might be cut off or missing parts!");
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -63,74 +319,25 @@ public class ImageAnalyzer {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
slotWidth = (int) ((targetSize.width*targetSizeMultiplier)/gridX);
|
|
|
|
preSlotWidth = (int) ((targetSize.width)/gridX);
|
|
|
|
slotHeight = (int) ((targetSize.height*targetSizeMultiplier)/gridY);
|
|
|
|
preSlotHeight = (int) ((targetSize.height)/gridY);
|
|
|
|
//
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Log.log(LogLevel.Debug, "Target will be "+(int)(targetSize.width*targetSizeMultiplier)
|
|
|
|
Log.log(LogLevel.Debug, "Target will be "+(int)(targetSize.width*targetSizeMultiplier)
|
|
|
|
+"x"+(int)(targetSize.height*targetSizeMultiplier)+" big.");
|
|
|
|
+"x"+(int)(targetSize.height*targetSizeMultiplier)+" big.");
|
|
|
|
Log.log(LogLevel.Debug, "Slots are "+slotWidth+"x"+slotHeight+" big.");
|
|
|
|
Log.log(LogLevel.Debug, "Slots are "+preSlotWidth+"x"+preSlotHeight+" big.");
|
|
|
|
slotX = gridX;
|
|
|
|
slotX = gridX;
|
|
|
|
slotY = gridY;
|
|
|
|
slotY = gridY;
|
|
|
|
|
|
|
|
slotCount = gridX*gridY;
|
|
|
|
//ia.slotWidth*gridX+"x"+ia.slotHeight*gridY
|
|
|
|
//ia.slotWidth*gridX+"x"+ia.slotHeight*gridY
|
|
|
|
//canvas = new BufferedImage((int)(targetSize.getWidth()*targetSizeMultiplier), (int)(targetSize.getHeight()*targetSizeMultiplier), BufferedImage.TYPE_INT_ARGB);
|
|
|
|
//canvas = new BufferedImage((int)(targetSize.getWidth()*targetSizeMultiplier), (int)(targetSize.getHeight()*targetSizeMultiplier), BufferedImage.TYPE_INT_ARGB);
|
|
|
|
canvas = new BufferedImage(slotWidth*gridX, slotHeight*gridY, BufferedImage.TYPE_INT_ARGB);
|
|
|
|
canvas = new BufferedImage(postSlotWidth*gridX, postSlotHeight*gridY, BufferedImage.TYPE_INT_ARGB);
|
|
|
|
Graphics2D g2 = (Graphics2D) canvas.getGraphics();
|
|
|
|
Graphics2D g2 = (Graphics2D) canvas.getGraphics();
|
|
|
|
g2.setColor(Color.BLACK);
|
|
|
|
g2.setColor(Color.BLACK);
|
|
|
|
g2.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
|
|
|
|
g2.fillRect(0, 0, canvas.getWidth(), canvas.getHeight());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Compares the given rgb value with the index and returns a HashMap of the closest hits.
|
|
|
|
|
|
|
|
* Unlikely to yield more than 1 result without deviationPercentage
|
|
|
|
|
|
|
|
* @param index
|
|
|
|
|
|
|
|
* @param rgb
|
|
|
|
|
|
|
|
* @param deviation How much the matches can be different from the perfect match, and still be taken.
|
|
|
|
|
|
|
|
* @return
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public HashMap<String, Integer> matchClosestIndex(HashMap<String, Integer> index, int rgb, int deviation){
|
|
|
|
|
|
|
|
Log.log(LogLevel.Debug, "Searching for closest match for rgb:"+rgb+" in the index with deviation of "+deviation+".");
|
|
|
|
|
|
|
|
assert(deviation < 100 && 0 <= deviation);
|
|
|
|
|
|
|
|
if(index == null){
|
|
|
|
|
|
|
|
Log.log(LogLevel.Critical, "No Index was given for rgb matching. Loading it from file ...");
|
|
|
|
|
|
|
|
index = this.fh.loadIndex();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
HashMap<String, Double> metrics = new HashMap<String, Double>();
|
|
|
|
|
|
|
|
double currBest = -1;
|
|
|
|
|
|
|
|
for(String key: index.keySet()){
|
|
|
|
|
|
|
|
double metric = getMetric(index.get(key), rgb);
|
|
|
|
|
|
|
|
metrics.put(key, metric);
|
|
|
|
|
|
|
|
if(currBest > metric || currBest == -1){
|
|
|
|
|
|
|
|
currBest = metric;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Log.log(LogLevel.Debug, "Calculated all metrics for rgb:"+rgb+" !");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
HashMap<String, Integer> matches = new HashMap<String, Integer>();
|
|
|
|
|
|
|
|
for(String key: metrics.keySet()){
|
|
|
|
|
|
|
|
if(metrics.get(key) == currBest || metrics.get(key) < currBest+(currBest*(deviation/(double)100))){
|
|
|
|
|
|
|
|
matches.put(key, index.get(key));
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Log.log(LogLevel.Debug, "Grabbed all good matches for rgb:"+rgb+" ! Got "+matches.size()+" ...");
|
|
|
|
|
|
|
|
return matches;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Calculates the euclidian metric of two given rgb values.
|
|
|
|
|
|
|
|
* @param rgb1
|
|
|
|
|
|
|
|
* @param rgb2
|
|
|
|
|
|
|
|
* @return
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private double getMetric(int rgb1, int rgb2){
|
|
|
|
|
|
|
|
ColorModel cm = ColorModel.getRGBdefault();
|
|
|
|
|
|
|
|
double result = Math.sqrt(Math.pow((cm.getRed(rgb1)-cm.getRed(rgb2)), 2)+
|
|
|
|
|
|
|
|
Math.pow(cm.getGreen(rgb1)-cm.getGreen(rgb2), 2)+
|
|
|
|
|
|
|
|
Math.pow(cm.getBlue(rgb1)-cm.getBlue(rgb2), 2));
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Places the given input in its specified slot (gridX, gridY) on the canvas.
|
|
|
|
* Places the given input in its specified slot (gridX, gridY) on the canvas.
|
|
|
|
* Assumes and checks boundaries calculated by rasterizeTarget().
|
|
|
|
* Assumes and checks boundaries calculated by rasterizeTarget().
|
|
|
@ -140,75 +347,19 @@ public class ImageAnalyzer {
|
|
|
|
* @param canvas
|
|
|
|
* @param canvas
|
|
|
|
* @return
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
public void placeImage(int gridX, int gridY, BufferedImage input){
|
|
|
|
public synchronized void placeImage(int gridX, int gridY, BufferedImage input){
|
|
|
|
assert(gridX < slotX && gridY < slotY);
|
|
|
|
assert(gridX < slotX && gridY < slotY);
|
|
|
|
assert(input.getWidth() < slotWidth && input.getHeight() < slotHeight);
|
|
|
|
assert(input.getWidth() < postSlotWidth && input.getHeight() < postSlotHeight);
|
|
|
|
|
|
|
|
|
|
|
|
Graphics2D g2 = (Graphics2D)canvas.getGraphics();
|
|
|
|
Graphics2D g2 = (Graphics2D)canvas.getGraphics();
|
|
|
|
g2.drawImage(input, gridX*slotWidth, gridY*slotHeight, slotWidth, slotHeight, null);
|
|
|
|
g2.drawImage(input, gridX*postSlotWidth, gridY*postSlotHeight, postSlotWidth, postSlotHeight, null);
|
|
|
|
g2.dispose();
|
|
|
|
g2.dispose();
|
|
|
|
|
|
|
|
//Log.log(LogLevel.Error, "Drawn picture at "+gridX*postSlotWidth+" "+gridY*postSlotHeight+" with "+input.getWidth()+"x"+input.getHeight());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Plain calculation of average Color of an Image via RGB.
|
|
|
|
|
|
|
|
* Takes average of RGB values of each pixel.
|
|
|
|
|
|
|
|
* Does not account for alpha.
|
|
|
|
|
|
|
|
* @param img Image to process
|
|
|
|
|
|
|
|
* @param alphaThreshhold value under which pixels are discarded
|
|
|
|
|
|
|
|
* @return RGB value of the average color
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
public int classifyImage(BufferedImage img, File file, int alphaTreshhold){
|
|
|
|
|
|
|
|
int width = img.getWidth();
|
|
|
|
|
|
|
|
int height = img.getHeight();
|
|
|
|
|
|
|
|
Log.log(LogLevel.Debug, "Starting classification of "+file.getName()+" with width:"+
|
|
|
|
|
|
|
|
width+" and height:"+height+" ...");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ColorModel cm = ColorModel.getRGBdefault();
|
|
|
|
|
|
|
|
float red = 0, green = 0, blue = 0;
|
|
|
|
|
|
|
|
int pixels = 0;
|
|
|
|
|
|
|
|
for(int x = 0; x < img.getWidth(); x++){
|
|
|
|
|
|
|
|
for(int y = 0; y < img.getHeight(); y++){
|
|
|
|
|
|
|
|
int rgb = img.getRGB(x, y);
|
|
|
|
|
|
|
|
red += cm.getRed(rgb);
|
|
|
|
|
|
|
|
blue += cm.getBlue(rgb);
|
|
|
|
|
|
|
|
green += cm.getGreen(rgb);
|
|
|
|
|
|
|
|
pixels++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
red = red/pixels;
|
|
|
|
|
|
|
|
green = green/pixels;
|
|
|
|
|
|
|
|
blue = blue/pixels;
|
|
|
|
|
|
|
|
int rgb = new Color((int)red, (int)green, (int)blue).getRGB();
|
|
|
|
|
|
|
|
Log.log(LogLevel.Debug, "Classified "+file.getPath()+" with following rgb result: value:"+rgb+
|
|
|
|
|
|
|
|
" red:"+red+", green:"+green+", blue:"+blue);
|
|
|
|
|
|
|
|
return rgb;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public int classifySlot(int gridX, int gridY, BufferedImage target){
|
|
|
|
|
|
|
|
int slotWidth = target.getWidth()/slotX,
|
|
|
|
|
|
|
|
slotHeight = target.getHeight()/slotY;
|
|
|
|
|
|
|
|
BufferedImage subImage = target.getSubimage(gridX*slotWidth, gridY*slotHeight, slotWidth, slotHeight);
|
|
|
|
|
|
|
|
Log.log(LogLevel.Debug, "Slicing slot "+gridX+"x"+gridY+" out of the target for classification ...");
|
|
|
|
|
|
|
|
ColorModel cm = ColorModel.getRGBdefault();
|
|
|
|
|
|
|
|
float red = 0, green = 0, blue = 0;
|
|
|
|
|
|
|
|
int pixels = 0;
|
|
|
|
|
|
|
|
for(int x = 0; x < subImage.getWidth(); x++){
|
|
|
|
|
|
|
|
for(int y = 0; y < subImage.getHeight(); y++){
|
|
|
|
|
|
|
|
int rgb = subImage.getRGB(x, y);
|
|
|
|
|
|
|
|
red += cm.getRed(rgb);
|
|
|
|
|
|
|
|
green += cm.getGreen(rgb);
|
|
|
|
|
|
|
|
blue += cm.getBlue(rgb);
|
|
|
|
|
|
|
|
pixels++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
red = red/pixels;
|
|
|
|
|
|
|
|
green = green/pixels;
|
|
|
|
|
|
|
|
blue = blue/pixels;
|
|
|
|
|
|
|
|
int rgb = new Color((int)red, (int)green, (int)blue).getRGB();
|
|
|
|
|
|
|
|
Log.log(LogLevel.Debug, "Classified slot "+gridX+"x"+gridY+" with following rgb result: value:"+rgb+
|
|
|
|
|
|
|
|
" red:"+red+", green:"+green+", blue:"+blue);
|
|
|
|
|
|
|
|
return rgb;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
public static void main(String[] args){
|
|
|
|
public static void main(String[] args){
|
|
|
|
System.out.println("ZHE DEBUG");
|
|
|
|
System.out.println("ZHE DEBUG");
|
|
|
|