package fr.unistra.pelican.algorithms.geometric;
import fr.unistra.pelican.Algorithm;
import fr.unistra.pelican.BooleanImage;
import fr.unistra.pelican.Image;
import fr.unistra.pelican.IntegerImage;
import fr.unistra.pelican.algorithms.conversion.GrayToPseudoColors;
import fr.unistra.pelican.algorithms.conversion.RGBToGray;
import fr.unistra.pelican.algorithms.edge.Sobel;
import fr.unistra.pelican.algorithms.histogram.ContrastStretch;
import fr.unistra.pelican.algorithms.io.ImageLoader;
import fr.unistra.pelican.algorithms.visualisation.Viewer2D;
/**
* This class performs image resizing using the Seam Carving algorithm
* (doi:101145/1435417.1435437)
*
* @author Lefevre
*/
public class SeamCarving extends Algorithm {
/**
* The input image
*/
public Image input;
/**
* The new X dimension
*/
public int xDim;
/**
* The new Y dimension
*/
public int yDim;
/**
* The output image
*/
public Image output;
/**
* Default constructor
*/
public SeamCarving() {
super.inputs = "input,xDim,yDim";
super.outputs = "output";
}
/**
* Seam carving
*
* @param input
* The input image
* @param p1
* The top left point
* @param p2
* The bottom right point
* @return The output image
*/
@SuppressWarnings("unchecked")
public static <T extends Image> T exec(T input, int xDim, int yDim) {
return (T) new SeamCarving().process(input, xDim, yDim);
}
/*
* distance function used in the computation of the energy map
*/
private int distance(int x1, int y1, int x2, int y2) {
return Math.abs(input.getPixelXYByte(x1, y1)
- input.getPixelXYByte(x2, y2));
}
/*
* (non-Javadoc)
*
* @see fr.unistra.pelican.Algorithm#launch()
*/
public void launch() {
int xDelta = xDim - input.getXDim();
int yDelta = yDim - input.getYDim();
Image gradient = Sobel.exec(input);
// post-processing of the gradient to avoid problems with borders
// for(int x=0;x<gradient.getXDim();x++){
// gradient.setPixelXYDouble(x,0, Double.MAX_VALUE);
// gradient.setPixelXYDouble(x,gradient.getYDim()-1, Double.MAX_VALUE);
// }
for(int y=0;y<gradient.getYDim();y++){
gradient.setPixelXYDouble(0,y, 200);
gradient.setPixelXYDouble(gradient.getXDim()-1,y, 200);
}
Image energy = gradient.copyImage(true);
Image path = new IntegerImage(input, false);
double cost0, cost1, cost2, cost3;
if (true) {
// Removes one column (y--)
for (int y = 1; y < input.getYDim(); y++) {
// Case of x==0
cost0 = energy.getPixelXYDouble(0, y);
cost2 = energy.getPixelXYDouble(0, y - 1);
cost3 = energy.getPixelXYDouble(1, y - 1);
// Check for the minimum
if (cost2 < cost3) {
energy.setPixelXYDouble(0, y, cost2 + cost0);
path.setPixelXYInt(0, y, 0);
} else {
energy.setPixelXYDouble(0, y, cost3 + cost0);
path.setPixelXYInt(0, y, 1);
}
// Case of x==xDim-1
int x2 = input.getXDim() - 1;
cost0 = energy.getPixelXYDouble(x2, y);
cost1 = energy.getPixelXYDouble(x2 - 1, y - 1);
cost2 = energy.getPixelXYDouble(x2, y - 1);
// Check for the minimum
if (cost1 < cost2) {
energy.setPixelXYDouble(x2, y, cost1 + cost0);
path.setPixelXYInt(x2, y, -1);
} else {
energy.setPixelXYDouble(x2, y, cost2 + cost0);
path.setPixelXYInt(x2, y, 0);
}
// Case of other y values
for (int x = 1; x < input.getXDim() - 1; x++) {
// Cost computation
cost0 = energy.getPixelXYDouble(x, y);
cost1 = energy.getPixelXYDouble(x - 1, y - 1);
cost2 = energy.getPixelXYDouble(x, y-1);
cost3 = energy.getPixelXYDouble(x + 1, y - 1);
// Check for the minimum
if (cost1 < cost2) {
if (cost1 < cost3) {
energy.setPixelXYDouble(x, y, cost1 + cost0);
path.setPixelXYInt(x, y, -1);
} else {
energy.setPixelXYDouble(x, y, cost3 + cost0);
path.setPixelXYInt(x, y, 1);
}
} else {
if (cost2 < cost3) {
energy.setPixelXYDouble(x, y, cost2 + cost0);
path.setPixelXYInt(x, y, 0);
} else {
energy.setPixelXYDouble(x, y, cost3 + cost0);
path.setPixelXYInt(x, y, 1);
}
}
}
}
Viewer2D.exec(gradient,"sobel");
Viewer2D.exec(GrayToPseudoColors.exec(gradient,true),"gradient");
Viewer2D.exec(ContrastStretch.exec(energy));
Viewer2D.exec(GrayToPseudoColors.exec(energy,true));
// Draw the path
double min = Double.MAX_VALUE;
int y2 = input.getYDim() - 1;
int xmin = -1;
for (int x = 0; x < input.getXDim(); x++)
if (energy.getPixelXYDouble(x, y2) < min) {
min = energy.getPixelXYDouble(x, y2);
xmin = x;
}
BooleanImage seam=new BooleanImage(input,false);
System.out.println(xmin+" "+min);
for (int y=y2;y>=0;y--) {
System.out.println("Y="+y);
seam.setPixelXYBoolean(xmin,y,true);
xmin=xmin+path.getPixelXYInt(xmin,y);
System.out.println(xmin);
}
Viewer2D.exec(seam);
} else {
// Removes one line (x--)
for (int x = 1; x < input.getXDim(); x++) {
// Case of y==0
cost0 = energy.getPixelXYDouble(x, 0);
cost2 = energy.getPixelXYDouble(x - 1, 0);
cost3 = energy.getPixelXYDouble(x - 1, 1);
// Check for the minimum
if (cost2 < cost3) {
energy.setPixelXYDouble(x, 0, cost2 + cost0);
path.setPixelXYInt(x, 0, 0);
} else {
energy.setPixelXYDouble(x, 0, cost3 + cost0);
path.setPixelXYInt(x, 0, 1);
}
// Case of y==yDim-1
int y2 = input.getYDim() - 1;
cost0 = energy.getPixelXYDouble(x, y2);
cost1 = energy.getPixelXYDouble(x - 1, y2 - 1);
cost2 = energy.getPixelXYDouble(x - 1, y2);
// Check for the minimum
if (cost1 < cost2) {
energy.setPixelXYDouble(x, y2, cost1 + cost0);
path.setPixelXYInt(x, y2, -1);
} else {
energy.setPixelXYDouble(x, y2, cost2 + cost0);
path.setPixelXYInt(x, y2, 0);
}
// Case of other y values
for (int y = 1; y < input.getYDim() - 1; y++) {
// Cost computation
cost0 = energy.getPixelXYDouble(x, y);
cost1 = energy.getPixelXYDouble(x - 1, y - 1);
cost2 = energy.getPixelXYDouble(x - 1, y);
cost3 = energy.getPixelXYDouble(x - 1, y + 1);
// Check for the minimum
if (cost1 < cost2) {
if (cost1 < cost3) {
energy.setPixelXYDouble(x, y, cost1 + cost0);
path.setPixelXYInt(x, y, -1);
} else {
energy.setPixelXYDouble(x, y, cost3 + cost0);
path.setPixelXYInt(x, y, 1);
}
} else {
if (cost2 < cost3) {
energy.setPixelXYDouble(x, y, cost2 + cost0);
path.setPixelXYInt(x, y, 0);
} else {
energy.setPixelXYDouble(x, y, cost3 + cost0);
path.setPixelXYInt(x, y, 1);
}
}
}
}
Viewer2D.exec(ContrastStretch.exec(energy));
Viewer2D.exec(GrayToPseudoColors.exec(energy));
// Draw the path
double min = Double.MAX_VALUE;
int x2 = input.getXDim() - 1;
int ymin = -1;
for (int y = 0; y < input.getYDim(); y++)
if (energy.getPixelXYDouble(x2, y) < min) {
min = energy.getPixelXYDouble(x2, y);
ymin = y;
}
System.out.println(ymin);
energy.fill(0);
energy.setPixelXYDouble(x2, ymin, 1.0);
Viewer2D.exec(GrayToPseudoColors.exec(energy));
}
// for(int y=0;y<input.getYDim();y++)
// for (int x = 1; x < input.getXDim(); x++) {
// // Case of y==0
// cost2 = energy.getPixelXYByte(x - 1, 0);
// cost3 = energy.getPixelXYByte(x - 1, 1) + distance(x - 1, 0, x, 1);
// // Check for the minimum
// if (cost2 < cost3) {
// energy.setPixelXYByte(x, 0, cost2);
// } else {
// energy.setPixelXYByte(x, 0, cost3);
// }
//
// // Case of y==yDim-1
// int y2 = input.getYDim() - 1;
// cost1 = energy.getPixelXYByte(x - 1, y2 - 1)
// + distance(x - 1, y2, x, y2 - 1);
// cost2 = energy.getPixelXYByte(x - 1, y2);
// // Check for the minimum
// if (cost1 < cost2) {
// energy.setPixelXYByte(x, y2, cost1);
// } else {
// energy.setPixelXYByte(x, y2, cost2);
// }
//
// // Case of other y values
// for (int y = 1; y < input.getYDim() - 1; y++) {
// // Cost computation
// cost1 = energy.getPixelXYByte(x - 1, y - 1)
// + distance(x, y - 1, x, y + 1)
// + distance(x - 1, y, x, y - 1);
// cost2 = energy.getPixelXYByte(x - 1, y)
// + distance(x, y - 1, x, y + 1);
// cost3 = energy.getPixelXYByte(x - 1, y + 1)
// + distance(x, y - 1, x, y + 1)
// + distance(x - 1, y, x, y + 1);
// // Check for the minimum
// if (cost1 < cost2) {
// if (cost1 < cost3) {
// energy.setPixelXYByte(x, y, cost1);
// } else {
// energy.setPixelXYByte(x, y, cost3);
// }
// } else {
// if (cost2 < cost3) {
// energy.setPixelXYByte(x, y, cost2);
// } else {
// energy.setPixelXYByte(x, y, cost3);
// }
// }
// }
// }
// Viewer2D.exec(GrayToPseudoColors.exec(energy, true));
Image output = input.newInstance(xDim, yDim, input.getZDim(), input
.getTDim(), input.getBDim());
output.copyAttributes(input);
}
public static void main(String[] args) {
Image in = ImageLoader.exec("samples/queenstown.jpg");
in = RGBToGray.exec(in);
Viewer2D.exec(in, "input");
Image out = SeamCarving.exec(in, in.getXDim(), in.getYDim() - 1);
Viewer2D.exec(out, "output");
}
}