「CS61B」Note(10) HW5 LAB14
Seam-carving is a content-aware image resizing technique where the image is reduced in size by one pixel of height (or width) at a time. The detailed explanation and homework requirement is there.
Original photo: Seam Carved photo:
SeamCarver
energy()
: Computing the Energy of a Pixel
// energy of pixel at column x and row y
public double energy(int x, int y) {
if (x < 0 || x > width() - 1 || y < 0 || y > height() - 1) {
throw new java.lang.IndexOutOfBoundsException();
}
int xl, xr, yh, yl;
if (x == 0) { xl = width() - 1; } else { xl = x - 1; }
if (x == width() - 1) { xr = 0; } else { xr = x + 1; }
if (y == 0) { yh = height() - 1; } else { yh = y - 1; }
if (y == height() - 1) { yl = 0; } else { yl = y + 1; }
int energyHor = pow(getR(xl, y) - getR(xr, y)) +
pow(getG(xl, y) - getG(xr, y)) + pow(getB(xl, y) - getB(xr, y));
int energyVer = pow(getR(x, yh) - getR(x, yl)) +
pow(getG(x, yh) - getG(x, yl)) + pow(getB(x, yh) - getB(x, yl));
return energyHor + energyVer;
}
private int getR(int x, int y) { return (this.picture.getRGB(x,y) >> 16) & 0xFF; }
private int getG(int x, int y) { return (this.picture.getRGB(x,y) >> 8) & 0xFF; }
private int getB(int x, int y) { return this.picture.getRGB(x,y) & 0xFF; }
private int pow(int x) { return x * x; }
findVerticalSeam()
: Finding a Minimum Energy Path
// sequence of indices for vertical seam
public int[] findVerticalSeam() {
double[][] energyMat = new double[width()][height()];
for (int i = 0; i < width(); i++) {
for (int j = 0; j < height(); j++) {
energyMat[i][j] = energy(i, j);
}
}
// minCostMat is matrix of the cost of minimum cost path ending at (i, j)
// prePixelMat is matrix of the previous pixel in the minimum cost path ending at (i, j)
double[][] minCostMat = new double[width()][height()];
int[][] prePixelMat = new int[width()][height()];
for (int i = 0; i < width(); i++) {
minCostMat[i][0] = energyMat[i][0];
}
for (int j = 1; j < height(); j++) {
for (int i = 0; i < width(); i++) {
double minCost = Double.MAX_VALUE;
for (int n = -1; n < 2; n++) {
if (i + n < 0 || i + n > width() - 1) { continue; }
if (minCostMat[i + n][j - 1] < minCost) {
minCost = minCostMat[i + n][j - 1];
minCostMat[i][j] = minCost + energyMat[i][j];
prePixelMat[i][j] = i + n;
}
}
}
}
// find the min path energy pixel at bottom row
double minCostTotal = Double.MAX_VALUE;
int minEndIndex = 0;
for (int i = 0; i < width(); i++) {
if (minCostMat[i][height() - 1] < minCostTotal) {
minCostTotal = minCostMat[i][height() - 1];
minEndIndex = i;
}
}
int[] seam = new int[height()];
seam[height() - 1] = minEndIndex;
for (int j = height() - 1; j > 0; j--) {
minEndIndex = prePixelMat[minEndIndex][j];
seam[j - 1] = minEndIndex;
}
return seam;
}
findHorizontalSeam()
: Avoiding Redundancy The way to achieve it is transposing the image, running findVerticalSeam, and then transposing it back.
// sequence of indices for horizontal seam
public int[] findHorizontalSeam() {
Picture transpose = new Picture(height(), width());
Picture pictureCopy = new Picture(width(), height());
for (int i = 0; i < width(); i++) {
for (int j = 0; j < height(); j++) {
transpose.setRGB(j, i, this.picture.getRGB(i, j));
pictureCopy.setRGB(i, j, this.picture.getRGB(i, j));
}
}
this.picture = transpose;
int[] seam = findVerticalSeam();
this.picture = pictureCopy;
return seam;
}
removeHorizontalSeam()
and removeVerticalSeam()
:
// remove horizontal seam from picture
public void removeHorizontalSeam(int[] seam) {
if (seam.length != width() || !checkSeamValid(seam)) {
throw new java.lang.IllegalArgumentException();
}
this.picture = SeamRemover.removeHorizontalSeam(this.picture, seam);
}
// remove vertical seam from picture
public void removeVerticalSeam(int[] seam) {
if (seam.length != height() || !checkSeamValid(seam)) {
throw new java.lang.IllegalArgumentException();
}
this.picture = SeamRemover.removeVerticalSeam(this.picture, seam);
}
private boolean checkSeamValid(int[] seam) {
for (int i = 0; i < seam.length - 1; i++) {
if (seam[i + 1] > seam[i] + 1 || seam[i + 1] < seam[i] - 1) {
return false;
}
}
return true;
}
When I submitted my code to Autograder, it didn’t compile. It can’t resolvepicture.getRGB()
, which I think is a bug.
Fractal Sound
Building audio from scratch. The link is there.
Generating a SawTooth:
public class SawToothGenerator implements Generator {
private int period;
private int state;
public SawToothGenerator(int period) {
this.period = period;
state = 0;
}
public double next() {
state = (state + 1);
return normalize(state % period);
}
private double normalize(int v) {
return 2.0 * v / (period * 1.0 - 1) - 1;
}
}
Generating an AcceleratingSawTooth
public class AcceleratingSawToothGenerator implements Generator {
private int period;
private int state;
private double factor;
public AcceleratingSawToothGenerator(int period, double factor) {
this.period = period;
this.factor = factor;
state = 0;
}
public double next() {
state = (state + 1);
if (state > period - 1) {
period = (int)Math.floor(period * factor);
state = 0;
}
return normalize(state % period);
}
private double normalize(int v) {
return 2.0 * v / (period * 1.0 - 1) - 1;
}
}
Generating a Fractal Sound
public class StrangeBitwiseGenerator implements Generator {
private int period;
private int state;
public StrangeBitwiseGenerator(int period) {
this.period = period;
state = 0;
}
public double next() {
state = (state + 1);
// int weirdState = state & (state >>> 3) % period;
int weirdState = state & (state >> 3) & (state >> 8) % period;
return normalize(weirdState % period);
}
private double normalize(int v) {
return 2.0 * v / (period * 1.0 - 1) - 1;
}
}