From 4453b77e0f24afa2aa8ce4eab9aca8c7e158fc49 Mon Sep 17 00:00:00 2001 From: Pacien TRAN-GIRARD Date: Sat, 10 Oct 2015 10:39:03 +0200 Subject: Bootstrap project --- src/main/java/Color.java | 120 +++++++++++++++++++++++++++++++ src/main/java/Filter.java | 76 ++++++++++++++++++++ src/main/java/Helper.java | 140 +++++++++++++++++++++++++++++++++++++ src/main/java/Main.java | 51 ++++++++++++++ src/main/java/Seam.java | 75 ++++++++++++++++++++ src/main/java/SignatureChecks.java | 37 ++++++++++ 6 files changed, 499 insertions(+) create mode 100644 src/main/java/Color.java create mode 100644 src/main/java/Filter.java create mode 100644 src/main/java/Helper.java create mode 100644 src/main/java/Main.java create mode 100644 src/main/java/Seam.java create mode 100644 src/main/java/SignatureChecks.java (limited to 'src/main/java') diff --git a/src/main/java/Color.java b/src/main/java/Color.java new file mode 100644 index 0000000..4597b26 --- /dev/null +++ b/src/main/java/Color.java @@ -0,0 +1,120 @@ +/** + * @author Pacien TRAN-GIRARD + * @author Timothée FLOURE + */ +public final class Color { + + /** + * Returns red component from given packed color. + * + * @param rgb 32-bits RGB color + * @return a float between 0.0 and 1.0 + * @see #getGreen + * @see #getBlue + * @see #getRGB(float, float, float) + */ + public static float getRed(int rgb) { + // TODO getRed + return 0.0f; + } + + /** + * Returns green component from given packed color. + * + * @param rgb 32-bits RGB color + * @return a float between 0.0 and 1.0 + * @see #getRed + * @see #getBlue + * @see #getRGB(float, float, float) + */ + public static float getGreen(int rgb) { + // TODO getGreen + return 0.0f; + } + + /** + * Returns blue component from given packed color. + * + * @param rgb 32-bits RGB color + * @return a float between 0.0 and 1.0 + * @see #getRed + * @see #getGreen + * @see #getRGB(float, float, float) + */ + public static float getBlue(int rgb) { + // TODO getBlue + return 0.0f; + } + + /** + * Returns the average of red, green and blue components from given packed color. + * + * @param rgb 32-bits RGB color + * @return a float between 0.0 and 1.0 + * @see #getRed + * @see #getGreen + * @see #getBlue + * @see #getRGB(float) + */ + public static float getGray(int rgb) { + // TODO getGray + return 0.0f; + } + + /** + * Returns packed RGB components from given red, green and blue components. + * + * @param red a float between 0.0 and 1.0 + * @param green a float between 0.0 and 1.0 + * @param blue a float between 0.0 and 1.0 + * @return 32-bits RGB color + * @see #getRed + * @see #getGreen + * @see #getBlue + */ + public static int getRGB(float red, float green, float blue) { + // TODO getRGB + return 0; + } + + /** + * Returns packed RGB components from given grayscale value. + * + * @param red a float between 0.0 and 1.0 + * @param green a float between 0.0 and 1.0 + * @param blue a float between 0.0 and 1.0 + * @return 32-bits RGB color + * @see #getGray + */ + public static int getRGB(float gray) { + // TODO getRGB + return 0; + } + + /** + * Converts packed RGB image to grayscale float image. + * + * @param image a HxW int array + * @return a HxW float array + * @see #toRGB + * @see #getGray + */ + public static float[][] toGray(int[][] image) { + // TODO toGray + return null; + } + + /** + * Converts grayscale float image to packed RGB image. + * + * @param channels a HxW float array + * @return a HxW int array + * @see #toGray + * @see #getRGB(float) + */ + public static int[][] toRGB(float[][] gray) { + // TODO toRGB + return null; + } + +} diff --git a/src/main/java/Filter.java b/src/main/java/Filter.java new file mode 100644 index 0000000..ddc3e92 --- /dev/null +++ b/src/main/java/Filter.java @@ -0,0 +1,76 @@ +/** + * @author Pacien TRAN-GIRARD + * @author Timothée FLOURE + */ +public final class Filter { + + /** + * Get a pixel without accessing out of bounds + * + * @param gray a HxW float array + * @param row Y coordinate + * @param col X coordinate + * @return nearest valid pixel color + */ + public static float at(float[][] gray, int row, int col) { + // TODO at + return 0.0f; + } + + /** + * Convolve a single-channel image with specified kernel. + * + * @param gray a HxW float array + * @param kernel a MxN float array, with M and N odd + * @return a HxW float array + */ + public static float[][] filter(float[][] gray, float[][] kernel) { + // TODO filter + return null; + } + + /** + * Smooth a single-channel image + * + * @param gray a HxW float array + * @return a HxW float array + */ + public static float[][] smooth(float[][] gray) { + // TODO smooth + return null; + } + + /** + * Compute horizontal Sobel filter + * + * @param gray a HxW float array + * @return a HxW float array + */ + public static float[][] sobelX(float[][] gray) { + // TODO sobelX + return null; + } + + /** + * Compute vertical Sobel filter + * + * @param gray a HxW float array + * @return a HxW float array + */ + public static float[][] sobelY(float[][] gray) { + // TODO sobelY + return null; + } + + /** + * Compute the magnitude of combined Sobel filters + * + * @param gray a HxW float array + * @return a HxW float array + */ + public static float[][] sobel(float[][] gray) { + // TODO sobel + return null; + } + +} diff --git a/src/main/java/Helper.java b/src/main/java/Helper.java new file mode 100644 index 0000000..b7a139a --- /dev/null +++ b/src/main/java/Helper.java @@ -0,0 +1,140 @@ +import javax.imageio.ImageIO; +import javax.swing.*; +import java.awt.*; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; + +/** + * Provide simple tools to read, write and show pictures. + */ +public final class Helper { + + // Convert specified BufferedImage into an array + private static int[][] fromBufferedImage(BufferedImage image) { + int width = image.getWidth(); + int height = image.getHeight(); + int[][] array = new int[height][width]; + for (int row = 0; row < height; ++row) { + for (int col = 0; col < width; ++col) { + array[row][col] = image.getRGB(col, row); + } + } + return array; + } + + // Convert specified array int a BufferedImage + private static BufferedImage toBufferedImage(int[][] array) { + int width = array[0].length; + int height = array.length; + BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); + for (int row = 0; row < height; ++row) { + for (int col = 0; col < width; ++col) { + image.setRGB(col, row, array[row][col]); + } + } + return image; + } + + /** + * Reads specified image from disk. + * + * @param path Input file path + * @return HxW array of packed RGB colors, or null on failure + * @see #write + */ + public static int[][] read(String path) { + try { + BufferedImage image = ImageIO.read(new File(path)); + return fromBufferedImage(image); + } catch (IOException e) { + return null; + } + } + + /** + * Writes specified image to disk. + * + * @param path Output file path + * @param array HxW array of packed RGB colors + * @return {@code true} if write operation was successful, {@code false} otherwise + * @see #read + */ + public static boolean write(String path, int[][] array) { + + // Convert array to Java image + BufferedImage image = toBufferedImage(array); + + // Get desired file format + int index = path.lastIndexOf('.'); + if (index < 0) + return false; + String extension = path.substring(index + 1); + + // Export image + try { + return ImageIO.write(image, extension, new File(path)); + } catch (IOException e) { + return false; + } + + } + + /** + * Shows specified image in a window. + * + * @param array HxW array of packed RGB colors + * @param title title to be displayed + */ + public static void show(int[][] array, String title) { + + // Convert array to Java image + final BufferedImage image = toBufferedImage(array); + + // Create a panel to render this image + @SuppressWarnings("serial") + JPanel panel = new JPanel() { + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + g.drawImage(image, 0, 0, getWidth(), getHeight(), null, null); + } + }; + + // Create a frame to hold this panel + final JFrame frame = new JFrame(title); + frame.add(panel); + frame.pack(); + frame.setSize(image.getWidth(), image.getHeight()); + + // Register closing event + frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); + frame.addWindowListener(new WindowAdapter() { + @Override + public void windowClosing(WindowEvent e) { + frame.setVisible(false); + synchronized (frame) { + frame.notifyAll(); + } + } + }); + + // Show this frame + frame.setVisible(true); + + // Wait for close operation + try { + synchronized (frame) { + while (frame.isVisible()) + frame.wait(); + } + } catch (InterruptedException e) { + // Empty on purpose + } + frame.dispose(); + + } + +} diff --git a/src/main/java/Main.java b/src/main/java/Main.java new file mode 100644 index 0000000..43571bb --- /dev/null +++ b/src/main/java/Main.java @@ -0,0 +1,51 @@ +/** + * @author Pacien TRAN-GIRARD + * @author Timothée FLOURE + */ +public final class Main { + + public static void main(String[] args) { + + // Load image + System.out.println("Load image..."); + int[][] image = Helper.read("doves.jpg"); + Helper.show(image, "Original"); + + // Convert to grayscale + System.out.println("Convert to grayscale..."); + float[][] gray = Color.toGray(image); + Helper.show(Color.toRGB(gray), "Grayscale"); + + // Smooth it + System.out.println("Smooth image..."); + float[][] smooth = Filter.smooth(gray); + Helper.show(Color.toRGB(smooth), "Smooth"); + + // Apply Sobel + System.out.println("Compute Sobel filter..."); + float[][] sobel = Filter.sobel(smooth); + Helper.show(Color.toRGB(sobel), "Sobel"); + + // Find best seam + System.out.println("Find best seam..."); + int[] seam = Seam.find(sobel); + Helper.show(Seam.merge(image, seam), "Best seam"); + + // Shrink until it is a square + int count = image[0].length - image.length; + System.out.println("Shrink by removing " + count + " seams..."); + for (int i = 0; i < count; ++i) { + sobel = Filter.sobel(Filter.smooth(Color.toGray(image))); + seam = Seam.find(sobel); + image = Seam.shrink(image, seam); + System.out.println("Seam " + (i + 1) + "/" + count); + } + System.out.println("Done"); + Helper.show(image, "Shrink"); + + // Save result + Helper.write("doves.shrink.jpg", image); + + } + +} diff --git a/src/main/java/Seam.java b/src/main/java/Seam.java new file mode 100644 index 0000000..1cb4a0c --- /dev/null +++ b/src/main/java/Seam.java @@ -0,0 +1,75 @@ +/** + * @author Pacien TRAN-GIRARD + * @author Timothée FLOURE + */ +public final class Seam { + + /** + * Compute shortest path between {@code from} and {@code to} + * + * @param successors adjacency list for all vertices + * @param costs weight for all vertices + * @param from first vertex + * @param to last vertex + * @return a sequence of vertices, or {@code null} if no path exists + */ + public static int[] path(int[][] successors, float[] costs, int from, int to) { + // TODO path + return null; + } + + /** + * Find best seam + * + * @param energy weight for all pixels + * @return a sequence of x-coordinates (the y-coordinate is the index) + */ + public static int[] find(float[][] energy) { + // TODO find + return null; + } + + /** + * Draw a seam on an image + * + * @param image original image + * @param seam a seam on this image + * @return a new image with the seam in blue + */ + public static int[][] merge(int[][] image, int[] seam) { + // Copy image + int width = image[0].length; + int height = image.length; + int[][] copy = new int[height][width]; + for (int row = 0; row < height; ++row) + for (int col = 0; col < width; ++col) + copy[row][col] = image[row][col]; + + // Paint seam in blue + for (int row = 0; row < height; ++row) + copy[row][seam[row]] = 0x0000ff; + + return copy; + } + + /** + * Remove specified seam + * + * @param image original image + * @param seam a seam on this image + * @return the new image (width is decreased by 1) + */ + public static int[][] shrink(int[][] image, int[] seam) { + int width = image[0].length; + int height = image.length; + int[][] result = new int[height][width - 1]; + for (int row = 0; row < height; ++row) { + for (int col = 0; col < seam[row]; ++col) + result[row][col] = image[row][col]; + for (int col = seam[row] + 1; col < width; ++col) + result[row][col - 1] = image[row][col]; + } + return result; + } + +} diff --git a/src/main/java/SignatureChecks.java b/src/main/java/SignatureChecks.java new file mode 100644 index 0000000..f8a4355 --- /dev/null +++ b/src/main/java/SignatureChecks.java @@ -0,0 +1,37 @@ +public class SignatureChecks { + + @SuppressWarnings("unused") + public static void main(String[] argv) { + + // Check if the signatures of all required function in class Color are correct + float color; + int value; + float[][] gray = new float[4][4]; + int[][] image; + color = Color.getRed(0); + color = Color.getGreen(0); + color = Color.getBlue(0); + color = Color.getGray(0); + value = Color.getRGB(0.0f, 0.0f, 0.0f); + value = Color.getRGB(0.0f); + image = Color.toRGB(gray); + gray = Color.toGray(image); + + // Check if the signatures of all required function in class Filter are correct + float[][] kernel = new float[3][3]; + color = Filter.at(gray, -1, -1); + gray = Filter.filter(gray, kernel); + gray = Filter.smooth(gray); + gray = Filter.sobel(gray); + + // Check if the signatures of all required function in class Filter are correct + int[] path, seam; + float[] costs = new float[]{1, 1, 1}; + int[][] successors = new int[][]{{0, 1}, {}, {4}}; + float[][] energy = new float[4][4]; + path = Seam.path(successors, costs, 0, 2); + seam = Seam.find(energy); + image = Seam.merge(image, seam); + + } +} -- cgit v1.2.3