Advertisement
jules0707

SeamCarver.java

Mar 25th, 2021
980
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 12.63 KB | None | 0 0
  1.  
  2. /*
  3. Aggregate score: 91.18%
  4. Correctness:  34/34 tests passed
  5. Memory:       0/6 tests passed
  6. Timing:       18/17 tests passed
  7.  
  8.  
  9. maximum allowed memory is ~ 12 n^2 bytes.
  10.  
  11.                  n       student (bytes)
  12. -------------------------------------------
  13. => FAILED       16        18112
  14. => FAILED       32        77264
  15. => FAILED       64       319984
  16. => FAILED      128      1303088
  17. => FAILED      256      5125512
  18. => FAILED      512      9031208
  19. ==> 0/6 tests passed
  20.  
  21. Total: 0/6 tests passed!
  22.  
  23. Estimated student memory (bytes) = -5.39 n^2 + 22039.01 n - 742984.03   (R^2 = 0.984)
  24. */
  25.  
  26.  
  27.  
  28. import edu.princeton.cs.algs4.Bag;
  29. import edu.princeton.cs.algs4.Picture;
  30. import edu.princeton.cs.algs4.Stack;
  31. import edu.princeton.cs.algs4.StdOut;
  32.  
  33. import java.awt.Color;
  34. import java.util.ArrayList;
  35. import java.util.Arrays;
  36.  
  37. public class SeamCarver {
  38.  
  39.     private double[][] energies;
  40.     private int[][] colors;
  41.     private int[] pixelTo;
  42.     private double[] disTo;
  43.     private boolean[] marked;
  44.     private int width;
  45.     private int height;
  46.     private final int source;
  47.     private int sink;
  48.     private Stack<Integer> topologicalOrder;
  49.  
  50.     // create a seam carver object based on the given picture
  51.     public SeamCarver(Picture picture) {
  52.         if (picture == null) throw new IllegalArgumentException();
  53.         height = picture.height();
  54.         width = picture.width();
  55.         int n = width * height + 2; // w * h pixels plus a source and a sink
  56.         energies = new double[width][height];
  57.         colors = new int[width][height];
  58.         source = 0;
  59.         sink = n - 1;
  60.         buildColors(picture);
  61.         buildEnergies();
  62.     }
  63.  
  64.     // current picture
  65.     public Picture picture() {
  66.         int w = width();
  67.         int h = height();
  68.         Picture resizedPicture = new Picture(w, h);
  69.         for (int x = 0; x < w; x++)
  70.             for (int y = 0; y < h; y++) resizedPicture.set(x, y, new Color(colors[x][y]));
  71.         return resizedPicture;
  72.     }
  73.  
  74.     // width of current picture
  75.     public int width() {
  76.         return colors.length;
  77.     }
  78.  
  79.     // height of current picture
  80.     public int height() {
  81.         return colors[0].length;
  82.     }
  83.  
  84.     // energy of pixel at column x and row y
  85.     public double energy(int x, int y) {
  86.         if (!validate(x, y)) throw new IllegalArgumentException();
  87.         if (x == 0 || y == 0 || x == width() - 1 || y == height() - 1)
  88.             return 1000; // energy of border's pixels is 1000
  89.         else
  90.             return Math.sqrt(deltaX(x, y) + deltaY(x, y));
  91.     }
  92.  
  93.     // sequence of indices for horizontal seam
  94.     public int[] findHorizontalSeam() {
  95.         transposePicture();
  96.         int[] seams = findVerticalSeam();
  97.         transposePicture();
  98.         return seams;
  99.     }
  100.  
  101.     // sequence of indices for vertical seam
  102.     public int[] findVerticalSeam() {
  103.         int h = height();
  104.         int[] seam = new int[h];
  105.         runDFS();
  106.         ArrayList<Integer> path = pathTo(sink);
  107.         for (int i = 0; i < path.size(); i++) {
  108.             seam[i] = x(path.get(path.size() - 1 - i));
  109.         }
  110.         return seam;
  111.     }
  112.  
  113.     // remove horizontal seam from current picture
  114.     public void removeHorizontalSeam(int[] seam) {
  115.         validate(seam, false);
  116.         if (seam.length != width()) throw new IllegalArgumentException();
  117.         shift(seam, width(), height());
  118.         height--;
  119.         sink -= seam.length;
  120.         recomputeEnergies(seam, false);
  121.     }
  122.  
  123.     // remove vertical seam from current picture
  124.     public void removeVerticalSeam(int[] seam) {
  125.         validate(seam, true);
  126.         if (seam.length != height()) throw new IllegalArgumentException();
  127.         transposePicture();
  128.         shift(seam, width(), height());
  129.         transposePicture();
  130.         width--;
  131.         sink -= seam.length;
  132.         recomputeEnergies(seam, true);
  133.     }
  134.  
  135.     //////////////////////////////////////////////////////////
  136.     ////................... UTILITIES ....................////
  137.     //////////////////////////////////////////////////////////
  138.  
  139.     private void runDFS() {
  140.         int n = width() * height();
  141.         disTo = new double[n + 2]; // the source and sink extra
  142.         pixelTo = new int[n + 2];
  143.         for (int p = 1; p <= sink; p++)
  144.             disTo[p] = Double.POSITIVE_INFINITY; // all pixels are at an infinite distance from source
  145.         Iterable<Integer> topOrder = topologicalOrder(); // build topological order
  146.         for (int i : topOrder)
  147.             if (i < sink) relax(i);
  148.     }
  149.  
  150.     private Iterable<Integer> topologicalOrder() {
  151.         topologicalOrder = new Stack<>();
  152.         int size = width() * height() + 2;
  153.         marked = new boolean[size];
  154.         for (int p = 0; p < size; p++) if (!marked[p]) dfs(p);
  155.         return topologicalOrder;
  156.     }
  157.  
  158.     private void dfs(int p) {
  159.         marked[p] = true;
  160.         if (p != sink) // if sink hole reached just add it to stack. Ends recursive call.
  161.             for (int i : adj(p)) if (!marked[i]) dfs(i);
  162.         topologicalOrder.push(p);
  163.     }
  164.  
  165.     private void relax(int i) {
  166.         for (int p : adj(i)) {
  167.             int[] xy = xy(p);
  168.             int x = xy[0];
  169.             int y = xy[1];
  170.             double e = p == sink ? 0 : energies[x][y];
  171.             if (disTo[p] > disTo[i] + e) {
  172.                 disTo[p] = disTo[i] + e;
  173.                 pixelTo[p] = i;
  174.             }
  175.         }
  176.     }
  177.  
  178.     private ArrayList<Integer> pathTo(int pixel) {
  179.         ArrayList<Integer> path = new ArrayList<>();
  180.         for (int p = pixelTo[pixel]; p != source; p = pixelTo[p]) // starting from sink we go back until we reach source
  181.             path.add(p);
  182.         return path;
  183.     }
  184.  
  185.     private Bag<Integer> adj(int pixel) {
  186.         Bag<Integer> bag = new Bag<>();
  187.         int w = width();
  188.         int h = height();
  189.  
  190.         if (pixel == source)  //  all first row pixels are adjacent to source
  191.             for (int p = 1; p <= w; p++) bag.add(p);
  192.         else if (pixel > w * h - w) bag.add(sink); // bottom row
  193.         else {
  194.             int belowLeft = pixel + w - 1;
  195.             int belowCenter = pixel + w;
  196.             int belowRight = pixel + w + 1;
  197.             if (pixel % w == 1) { // first column
  198.                 bag.add(belowCenter);
  199.                 bag.add(belowRight);
  200.             } else if (pixel % w == 0) { // last column
  201.                 bag.add(belowLeft);
  202.                 bag.add(belowCenter);
  203.             } else {
  204.                 bag.add(belowLeft);
  205.                 bag.add(belowCenter);
  206.                 bag.add(belowRight);
  207.             }
  208.         }
  209.         return bag;
  210.     }
  211.  
  212.     // used to remove a seam that is horizontal
  213.     private void shift(int[] seam, int w, int h) {
  214.         for (int x = 0; x < w; x++) { // for each column shift color by removing pixel located at position (x,seam[x])
  215.             int[] dest1 = new int[h - 1];
  216.             double[] dest2 = new double[h - 1];
  217.             int srcPos = seam[x] + 1;
  218.             int destPos = seam[x];
  219.             int length1 = dest1.length - seam[x];
  220.             int length2 = seam[x];
  221.  
  222.             System.arraycopy(colors[x], srcPos, dest1, destPos, length1); // copies from seam[x]+1 to end
  223.             System.arraycopy(colors[x], 0, dest1, 0, length2); // copies from 0 to seam[x]-1
  224.             colors[x] = dest1; // merge the result and in place update colors
  225.  
  226.             System.arraycopy(energies[x], srcPos, dest2, destPos, length1); // same for energies
  227.             System.arraycopy(energies[x], 0, dest2, 0, length2);
  228.             energies[x] = dest2;
  229.         }
  230.     }
  231.  
  232.     private void recomputeEnergies(int[] seam, boolean isVertical) {
  233.         int y = 0;
  234.         if (isVertical) {
  235.             for (int s : seam) {
  236.                 if (validate(s - 1, y)) // does it have a neighbour on the left?
  237.                     energies[s - 1][y] = energy(s - 1, y); // recompute its energy
  238.  
  239.                 if (validate(s, y)) // same for right neighbour if exists
  240.                     energies[s][y] = energy(s, y);
  241.                 y++; // and we go down to the next row
  242.             }
  243.         } else {
  244.             for (int s : seam) {
  245.                 if (validate(y, s - 1)) { // does it have a top neighbour?
  246.                     energies[y][s - 1] = energy(y, s - 1);
  247.                 }
  248.                 if (validate(y, s)) { // and a bottom one
  249.                     energies[y][s] = energy(y, s);
  250.                 }
  251.                 y++;
  252.             }
  253.         }
  254.     }
  255.  
  256.     private int x(int pixel) {
  257.         return xy(pixel)[0];
  258.     }
  259.  
  260.     private int[] xy(int pixel) {
  261.         int w = width(); //energies.length;
  262.         return new int[]{(pixel - 1) % w, (pixel - 1) / w};
  263.     }
  264.  
  265.     private void transposePicture() {
  266.         int w = width();
  267.         int h = height();
  268.         int[][] tColors = new int[h][w];
  269.         double[][] tEnergies = new double[h][w];
  270.         for (int x = 0; x < h; x++)
  271.             for (int y = 0; y < w; y++) {
  272.                 tColors[x][y] = colors[y][x];
  273.                 tEnergies[x][y] = energies[y][x];
  274.             }
  275.         colors = tColors;
  276.         energies = tEnergies;
  277.     }
  278.  
  279.     private void buildColors(Picture picture) {
  280.         for (int x = 0; x < width; x++)
  281.             for (int y = 0; y < height; y++) colors[x][y] = picture.getRGB(x, y);
  282.     }
  283.  
  284.     private void buildEnergies() {
  285.         for (int x = 0; x < width; x++)
  286.             for (int y = 0; y < height; y++) energies[x][y] = energy(x, y);
  287.     }
  288.  
  289.     private double deltaX(int x, int y) {
  290.         Color colorXPlusOne = new Color(colors[x + 1][y]);
  291.         Color colorXMinusOne = new Color(colors[x - 1][y]);
  292.         return Math.pow(colorXPlusOne.getRed() - colorXMinusOne.getRed(), 2)
  293.                 + Math.pow(colorXPlusOne.getGreen() - colorXMinusOne.getGreen(), 2)
  294.                 + Math.pow(colorXPlusOne.getBlue() - colorXMinusOne.getBlue(), 2);
  295.     }
  296.  
  297.     private double deltaY(int x, int y) {
  298.         Color colorYPlusOne = new Color(colors[x][y + 1]);
  299.         Color colorYMinusOne = new Color(colors[x][y - 1]);
  300.         return Math.pow(colorYPlusOne.getRed() - colorYMinusOne.getRed(), 2)
  301.                 + Math.pow(colorYPlusOne.getGreen() - colorYMinusOne.getGreen(), 2)
  302.                 + Math.pow(colorYPlusOne.getBlue() - colorYMinusOne.getBlue(), 2);
  303.     }
  304.  
  305.     private void validate(int[] seam, boolean isVertical) {
  306.         if (seam == null || seam.length < 1) throw new IllegalArgumentException();
  307.         int size;
  308.         if (isVertical) {
  309.             size = width();
  310.         } else {
  311.             size = height();
  312.         }
  313.         for (int i = 0; i < seam.length; i++) { // seam within bounds
  314.             if (seam[i] >= size || seam[i] < 0) throw new IllegalArgumentException();
  315.             if (i < seam.length - 1) { // and gap no greater than 1
  316.                 if (Math.abs(seam[i] - seam[i + 1]) > 1) throw new IllegalArgumentException();
  317.             }
  318.         }
  319.     }
  320.  
  321.     private boolean validate(int x, int y) {
  322.         return (x >= 0 && x < width() && y >= 0 && y < height());
  323.     }
  324.  
  325.     // unit testing (optional)
  326.     public static void main(String[] args) {
  327.         Picture picture = new Picture(6, 6);
  328.  
  329.         String[][] s1 = {
  330.                 {"#050208", "#070304", "#050809", "#070000", "#020309", "#010706"},
  331.                 {"#050204", "#080102", "#060000", "#010708", "#020907", "#080408"},
  332.                 {"#010502", "#080307", "#000200", "#090804", "#090106", "#060106"},
  333.                 {"#000403", "#070607", "#020306", "#070808", "#000205", "#060307"},
  334.                 {"#030703", "#010504", "#080608", "#050808", "#010907", "#050101"},
  335.                 {"#050808", "#080201", "#020008", "#080603", "#060709", "#050108"},
  336.         };
  337.  
  338.         String[][] s2 = {
  339.                 {"#090503", "#090307", "#080003", "#020602", "#000304", "#000906"},
  340.                 {"#000702", "#040800", "#090809", "#070101", "#080307", "#000402"},
  341.                 {"#020904", "#030600", "#090000", "#040807", "#010702", "#090407"},
  342.                 {"#020403", "#010206", "#080703", "#060002", "#010107", "#090204"},
  343.                 {"#020202", "#070906", "#050509", "#030105", "#000507", "#020004"},
  344.                 {"#060109", "#090305", "#080309", "#070507", "#070707", "#090900"},
  345.         };
  346.  
  347.         int w = picture.width();
  348.         int h = picture.height();
  349.  
  350.         for (int i = 0; i < w; i++) {
  351.             for (int j = 0; j < h; j++)
  352.                 picture.set(i, j, Color.decode(s2[i][j]));
  353.         }
  354.  
  355.         SeamCarver carver = new SeamCarver(picture);
  356.  
  357.         int[] seam1 = carver.findVerticalSeam();
  358.         StdOut.println("1st Seam = " + Arrays.toString(seam1));
  359.         carver.removeVerticalSeam(seam1);
  360.         carver.picture();
  361.  
  362.     }
  363. }
  364.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement