「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;
    }
}