package collage.utils;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import collage.grid.Cell;
import collage.image.FeatureSet;
//import scala.rectangle.Rectangle;
import scala.tree.*;
public class SimulatedAnnealing {
private double T;
private double minT;
private double coolingRate;
public SimulatedAnnealing() {
this.T = 100000;
this.minT = 0.00001;
this.coolingRate = 0.99999;
}
public SimulatedAnnealing(double temperature, double minTemp, double coolingRate) {
this.T = temperature;
this.minT = minTemp;
this.coolingRate = coolingRate;
}
/**
* Performs simulated annealing.
*
* @param img - Image A
* @param cellDim - Dimension of each subimage
* @param candidates - List of image Bi features
* @return List of Cells, representing the best partition of A into images Bi.
*/
public List<Cell> perform(BufferedImage img, Dimension cellDim, List<FeatureSet> candidates) {
int imgHeight = img.getHeight();
int imgWidth = img.getWidth();
TwoDTree tree = TwoDTree.generateTwoDTree(imgWidth,6,imgHeight,4,90);
NeighborGen gen = NeighborGen.apply(6,4,80,(int)this.T);
List<Rectangle> currentSolution = tree.getRectanglesJava(imgWidth,imgHeight);
List<Cell> currentCelled = getCells(img, currentSolution, cellDim);
double currentFitness = calculateFitness(currentCelled, candidates, imgWidth, imgHeight);
List<Rectangle> bestSolution = currentSolution;
List<Cell> bestCelled = currentCelled;
double bestFitness = currentFitness;
List<Rectangle> newSolution;
List<Cell> newCelled;
double newFitness;
double fitness_change;
double counter = 1;
while (T > minT) {
//tree = TwoDTree.generateTwoDTree(imgWidth,10,imgHeight,5,80);
//newSolution = getNeighbour(currentSolution);
tree = gen.generate(tree,imgWidth,imgHeight,(int)T);
newSolution = tree.getRectanglesJava(imgWidth,imgHeight);
newCelled = getCells(img, newSolution, cellDim);
newFitness = calculateFitness(newCelled, candidates, imgWidth, imgHeight);
fitness_change = currentFitness - newFitness;
if (fitness_change > 0 || Math.random() < Math.pow(Math.E,fitness_change/T)) {
currentSolution = newSolution;
currentCelled = newCelled;
currentFitness = newFitness;
}
if (currentFitness < bestFitness) {
bestSolution = currentSolution;
bestCelled = currentCelled;
bestFitness = currentFitness;
}
T *= coolingRate;
counter += 1;
}
return bestCelled;
}
/**
* Turns a list of rectangles into a list of Cells on image img.
* Calculates FeatureSet for each Cell.
*
* @param img - Image A (background image)
* @param rectangles - List of rectangles specifying how to partition the image
* @param cellDim - Dimension of each subimage
* @return List of Cells corresponding to background image and its partitioning.
*/
private List<Cell> getCells(BufferedImage img, List<Rectangle> rectangles,
Dimension cellDim) {
List<Cell> cells = new ArrayList<Cell>();
FeatureCollector fc = new FeatureCollector();
FeatureSet fs;
for (Rectangle rect : rectangles) {
//System.out.format("%d %d %d %d\n", rect.x, rect.y, rect.width, rect.height);
fs = fc.gather(img.getSubimage(rect.x, rect.y, rect.width, rect.height),
"",cellDim);
cells.add(new Cell(rect,fs));
}
return cells;
}
/**
* Calculates the fitness of partitioning, using naive search to find each
* nearest neighbour Bi for image A's Cell.
*
* @param sources - Image A Cells
* @param candidates - Image Bi FeatureSets
* @param imgWidth - Width of image A
* @param imgHeight - Height of image A
* @return Fitness of the partitioning, the smaller the better.
*/
private double calculateFitness(List<Cell> sources, List<FeatureSet> candidates,
int imgWidth, int imgHeight) {
NearestNeighbour nn = new NearestNeighbour();
Rectangle bounds;
double imgArea = imgWidth*imgHeight;
double coef;
double result = 0.0;
for (Cell source : sources) {
bounds = source.getBounds();
coef = bounds.getHeight()*bounds.getWidth()/imgArea;
result += coef*nn.getNearestDistance(source.getFeatures(), candidates);
}
return result;
}
}