diff --git a/Collector.java b/Collector.java new file mode 100644 index 0000000..768d71a --- /dev/null +++ b/Collector.java @@ -0,0 +1,104 @@ +package main; + +import java.util.ArrayList; + +public class Collector { + + /** + * Find the row, column coordinates of the best element (biggest or smallest) for the given matrix + * + * @param matrix : an 2D array of doubles + * @param smallestFirst : a boolean, indicates if the smallest element is the best or not (biggest is then the best) + * @return an array of two integer coordinates, row first and then column + */ + public static int[] findBest(double[][] matrix, boolean smallestFirst) { + int tempi = 0; + int tempj = 0; + if (smallestFirst) { + double temp = 255; + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix[0].length; j++) { + if (matrix[i][j] < temp) { + temp = matrix[i][j]; + tempi = i; + tempj = j; + } + } + } + } else { + double temp = 0; + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix[0].length; j++) { + if (matrix[i][j] > temp) { + temp = matrix[i][j]; + tempi = i; + tempj = j; + } + } + } + } + int best[] = new int[]{tempi, tempj}; + return best; + } + + + /** + * Find the row, column coordinate-pairs of the n best (biggest or smallest) elements of the given matrix + * + * @param n : an integer, the number of best elements we want to find + * @param matrix : an 2D array of doubles + * @param smallestFirst : a boolean, indicates if the smallest element is the best or not (biggest is the best) + * @return an array of size n containing row, column-coordinate pairs + */ + public static int[][] findNBest(int n, double[][] matrix, boolean smallestFirst) { + double temp[][] = matrix; + int array[][] = new int[n][2]; + for (int i = 0; i < n; i++) { + int[] best = findBest(temp, smallestFirst); + for (int j = 0; j < 2; j++) + array[i][j] = best[j]; + if (smallestFirst) { + temp[best[0]][best[1]] = Double.POSITIVE_INFINITY; + } else { + temp[best[0]][best[1]] = Double.NEGATIVE_INFINITY; + } + } + return array; + } + + + /** + * BONUS + * Notice : Bonus points are underpriced ! + *
+ * Sorts all the row, column coordinates based on their pixel value
+ * Hint : Use recursion !
+ *
+ * @param matrix : an 2D array of doubles
+ * @return A list of points, each point is an array of length 2.
+ */
+ public static ArrayList
+ * Use a quick sort to find the row, column coordinate-pairs of the n best (biggest or smallest) elements of the given matrix
+ * Hint : return the n first or n last elements of a sorted ArrayList
+ *
+ * @param n : an integer, the number of best elements we want to find
+ * @param matrix : an 2D array of doubles
+ * @param smallestFirst : a boolean, indicate if the smallest element is the best or not (biggest is the best)
+ * @return an array of size n containing row, column-coordinate pairs
+ */
+ public static int[][] findNBestQuickSort(int n, double[][] matrix, boolean smallestFirst) {
+
+ // TODO implement me correctly for underpriced bonus!
+ return new int[][]{};
+ }
+}
diff --git a/DistanceBasedSearch.java b/DistanceBasedSearch.java
new file mode 100644
index 0000000..075ac43
--- /dev/null
+++ b/DistanceBasedSearch.java
@@ -0,0 +1,71 @@
+package main;
+
+public class DistanceBasedSearch {
+
+ /**
+ * Computes the mean absolute error between two RGB pixels, channel by channel.
+ *
+ * @param patternPixel : a integer, the second RGB pixel.
+ * @param imagePixel : a integer, the first RGB pixel.
+ * @return a double, the value of the error for the RGB pixel pair. (an integer in [0, 255])
+ */
+ public static double pixelAbsoluteError(int patternPixel, int imagePixel) {
+ double sum = Math.abs(ImageProcessing.getRed(patternPixel) - ImageProcessing.getRed(imagePixel)) +
+ Math.abs(ImageProcessing.getGreen(patternPixel) - ImageProcessing.getGreen(imagePixel)) +
+ Math.abs(ImageProcessing.getBlue(patternPixel) - ImageProcessing.getBlue(imagePixel));
+ return sum / 3.0;
+ }
+
+ /**
+ * Computes the mean absolute error loss of a RGB pattern if positioned
+ * at the provided row, column-coordinates in a RGB image
+ *
+ * @param row : a integer, the row-coordinate of the upper left corner of the pattern in the image.
+ * @param col : a integer, the column-coordinate of the upper left corner of the pattern in the image.
+ * @param pattern : an 2D array of integers, the RGB pattern to find
+ * @param image : an 2D array of integers, the RGB image where to look for the pattern
+ * @return a double, mean absolute error value at position (row, col) between the pattern and the part of
+ * the base image that is covered by the pattern, if the pattern is shifted by x and y.
+ * should return -1 if the denominator is -1
+ */
+ public static double meanAbsoluteError(int row, int col, int[][] pattern, int[][] image) {
+ assert pattern.length != 0 : "pattern contains no pixel";
+ assert image.length != 0 : "image contains no pixel";
+ assert row < image.length - pattern.length : "Motif non contenu entierement";
+ assert col < image[0].length - pattern[0].length : "Motif non contenu entierement";
+ double sum = 0;
+ for (int i = 0; i < pattern.length; i++) {
+ for (int j = 0; j < pattern[0].length; j++) {
+ sum += pixelAbsoluteError(pattern[i][j], image[row + i][col + j]);
+ }
+ }
+ double size = pattern.length * pattern[0].length;
+ double eam = sum / size;
+ return eam;
+ }
+
+ /**
+ * Compute the distanceMatrix between a RGB image and a RGB pattern
+ *
+ * @param pattern : an 2D array of integers, the RGB pattern to find
+ * @param image : an 2D array of integers, the RGB image where to look for the pattern
+ * @return a 2D array of doubles, containing for each pixel of a original RGB image,
+ * the distance (meanAbsoluteError) between the image's window and the pattern
+ * placed over this pixel (upper-left corner)
+ */
+ public static double[][] distanceMatrix(int[][] pattern, int[][] image) {
+ assert pattern.length != 0 : "pattern contains no pixel";
+ assert image.length != 0 : "image contains no pixel";
+ int W = image[0].length;
+ int w = pattern[0].length;
+ int H = image.length;
+ int h = pattern.length;
+ double[][] matrix = new double[H - h][W - w];
+ for (int i = 0; i < H - h; i++) {
+ for (int j = 0; j < W - w; j++) {
+ matrix[i][j] = meanAbsoluteError(i, j, pattern, image);
+ }
+ }
+ return matrix;
+ }
+}
diff --git a/Helper.java b/Helper.java
new file mode 100644
index 0000000..a33f7f7
--- /dev/null
+++ b/Helper.java
@@ -0,0 +1,180 @@
+package main;
+
+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 {
+
+ /**
+ * Draws a rectangle over a RGB image
+ *
+ * @param r : an integer, the vertical coordinate (col) of the upper left corner.
+ * @param c : an integer, the horizontal coordinate (row) of the upper left corner.
+ * @param w : an integer, the width of the rectangle.
+ * @param h : an integer, the height of the rectangle.
+ * @param dst : a 2D integer array, the RGB image on which to draw the rectangle.
+ * @param color: an integer representing the RBG value of the line color of the rectangle
+ * @param strokeWidth: width of pencil stroke
+ */
+ public static void drawBox(int r, int c, int w, int h, int[][] dst, int strokeWidth, int color) {
+ if (strokeWidth < 1) strokeWidth = 1;
+ for (int row = r; row < r + h && row < dst.length; ++row) {
+ for (int col = c; col < c + w && col < dst[0].length; ++col) {
+ if (row < r + strokeWidth || row >= r + h - strokeWidth ||
+ col < c + strokeWidth || col >= c + w - strokeWidth) {
+ dst[row][col] = color;
+ }
+ }
+ }
+ }
+
+ /**
+ * Draws a red rectangle over a RGB image
+ *
+ * @param r : an integer, the vertical coordinate (col) of the upper left corner.
+ * @param c : an integer, the horizontal coordinate (row) of the upper left corner.
+ * @param w : an integer, the width of the rectangle.
+ * @param h : an integer, the height of the rectangle.
+ * @param dst : a 2D integer array, the RGB image on which to draw the rectangle.
+ */
+ public static void drawBox(int r, int c, int w, int h, int[][] dst) {
+ drawBox(r, c, w, h, dst, w / 15, 255 << 16);
+ }
+
+ // 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) & 0xffffffff;
+ }
+ }
+ return array;
+ }
+
+ // Convert specified array into 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] | 0xff000000);
+ }
+ }
+ return image;
+ }
+
+ /**
+ * Reads specified image from disk.
+ *
+ * @param path : a String, the Input file path
+ * @return HxW integer array of packed RGB colors, or
+ * Where is Charlie Project
+ */
+public final class Main {
+
+ public static void main(String[] args) {
+ testGetRed();
+ testGrayscale();
+ testGetGreen();
+ testGetBlue();
+ testGetGray();
+ testGetRGB();
+ testFindBest();
+ testFindNBest();
+ pixelAbsoluteError();
+ testToGray();
+ testToRGB();
+ testDistanceBasedSearch();
+ testSimilarityBasedSearch();
+ testNCCPatternEqualImage();
+ testSimilarityPatternEqualImage();
+ testSimilaritySimple();
+ findCharlie();
+ }
+
+ /*
+ * Tests for Class ImageProcessing
+ */
+ public static void testGetRed() {
+ int color = 0b11110000_00001111_01010101;
+ int ref = 0b11110000;
+ int red = ImageProcessing.getRed(color);
+ if (red == ref) {
+ System.out.println("Test red passed");
+ } else {
+ System.out.println("Test red failed. Returned value = " + red + " Expected value = " + ref);
+ }
+ }
+
+ public static void testGrayscale() {
+ System.out.println("Test Grayscale");
+ int[][] image = Helper.read("images/food.png");
+ double[][] gray = ImageProcessing.toGray(image);
+ Helper.show(ImageProcessing.toRGB(gray), "test bw");
+ }
+
+ public static void testGetGreen() {
+ int color = 0b11110000_00001111_01010101;
+ int ref = 0b00001111;
+ int green = ImageProcessing.getGreen(color);
+ if (green == ref) {
+ System.out.println("Test green passed");
+ } else {
+ System.out.println("Test green failed. Returned value = " + green + " Expected value = " + ref);
+ }
+ }
+
+ public static void testGetBlue() {
+ int color = 0b11110000_00001111_01010101;
+ int ref = 0b01010101;
+ int blue = ImageProcessing.getBlue(color);
+ if (blue == ref) {
+ System.out.println("Test blue passed");
+ } else {
+ System.out.println("Test blue failed. Returned value = " + blue + " Expected value = " + ref);
+ }
+ }
+
+ public static void testGetGray() {
+ int color = 0b11110000_00001111_01010101;
+ int ref = 0b01110001;
+ double gray = ImageProcessing.getGray(color);
+ if (Math.round(gray) == ref) {
+ System.out.println("Test gray passed");
+ } else {
+ System.out.println("Test gray failed. Returned value = " + gray + " Expected value = " + ref);
+ }
+ }
+
+ public static void testGetRGB() {
+ int ref = 0b11110000_00001111_01010101;
+ int red = 0b11110000;
+ int green = 0b00001111;
+ int blue = 0b01010101;
+ int RGB = ImageProcessing.getRGB(red, green, blue);
+ if (RGB == ref) {
+ System.out.println("Test RGB 1 passed");
+ } else {
+ System.out.println("Test RGB 1 failed. Returned value = " + RGB + " Expected value = " + ref);
+ }
+ int rgb = ImageProcessing.getRGB(127.0);
+ int ref2 = 0x7f7f7f;
+ if (rgb == ref2) {
+ System.out.println("Test RGB 2 passed");
+ } else {
+ System.out.println("Test RGB 2 failed. Returned value = " + rgb + " Expected value = " + ref2);
+ }
+ }
+ public static void testToGray() {
+ System.out.println("Test toGray");
+ double[][] ref = new double[][] {{0b01110001}, {0b01110001},{0b01110001}};
+ int [][] image = new int[][] {{0b01110001_01110001_01110001}, {0b01110001_01110001_01110001},{0b01110001_01110001_01110001}};
+ double[][] toGray = ImageProcessing.toGray(image);
+ for(int i=0 ; inull on failure
+ * @see #write
+ */
+ public static int[][] read(String path) {
+ try {
+ BufferedImage image = ImageIO.read(new File(path));
+ return fromBufferedImage(image);
+ } catch (IOException e) {
+ System.out.println(e);
+ System.out.println("Path: " + path);
+ System.exit(1);
+ return null;
+ }
+ }
+
+ /**
+ * Writes specified image to disk.
+ *
+ * @param path : a String, the 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 : a HxW integer array of packed RGB colors
+ * @param title : a String, the 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, Math.max(getWidth(), 100), Math.max(getHeight(), 100), null, null);
+ }
+ };
+
+ // Create a frame to hold this panel
+ final JFrame frame = new JFrame(title);
+ frame.add(panel);
+ frame.getContentPane().setPreferredSize(new Dimension(Math.max(image.getWidth(), 300), Math.max(image.getHeight(), 300)));
+ frame.pack();
+
+ // 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/ImageProcessing.java b/ImageProcessing.java
new file mode 100644
index 0000000..747ebff
--- /dev/null
+++ b/ImageProcessing.java
@@ -0,0 +1,155 @@
+package main;
+
+public final class ImageProcessing {
+
+ /**
+ * Checks wether or not a RGB component is between 0 and 255 and returns the right value.
+ *
+ * @param value : an integer.
+ * @return an integer, between 0 and 255
+ */
+ public static int checkInt(int value) {
+ if (value < 0)
+ value = 0;
+ if (value > 255)
+ value = 255;
+ return value;
+ }
+
+ /**
+ * Returns red component from given packed color.
+ *
+ * @param rgb : a 32-bits RGB color
+ * @return an integer, between 0 and 255
+ * @see #getGreen
+ * @see #getBlue
+ * @see #getRGB(int, int, int)
+ */
+ public static int getRed(int rgb) {
+ return (rgb >> 16) & 0xff;
+ }
+
+ /**
+ * Returns green component from given packed color.
+ *
+ * @param rgb : a 32-bits RGB color
+ * @return an integer between 0 and 255
+ * @see #getRed
+ * @see #getBlue
+ * @see #getRGB(int, int, int)
+ */
+ public static int getGreen(int rgb) {
+ return (rgb >> 8) & 0xff;
+ }
+
+ /**
+ * Returns blue component from given packed color.
+ *
+ * @param rgb : a 32-bits RGB color
+ * @return an integer between 0 and 255
+ * @see #getRed
+ * @see #getGreen
+ * @see #getRGB(int, int, int)
+ */
+ public static int getBlue(int rgb) {
+ return rgb & 0xff;
+ }
+
+
+ /**
+ * Returns the average of red, green and blue components from given packed color.
+ *
+ * @param rgb : 32-bits RGB color
+ * @return a double between 0 and 255
+ * @see #getRed
+ * @see #getGreen
+ * @see #getBlue
+ * @see #getRGB
+ */
+ public static double getGray(int rgb) {
+ return ((getRed(rgb) + getBlue(rgb) + getGreen(rgb)) / 3.0);
+ }
+
+ /**
+ * Returns packed RGB components from given red, green and blue components.
+ *
+ * @param red : an integer
+ * @param green : an integer
+ * @param blue : an integer
+ * @return a 32-bits RGB color
+ * @see #getRed
+ * @see #getGreen
+ * @see #getBlue
+ */
+ public static int getRGB(int red, int green, int blue) {
+ return checkInt(red) << 16 | checkInt(green) << 8 | checkInt(blue);
+ }
+
+ /**
+ * Returns packed RGB components from given gray-scale value.
+ *
+ * @param gray : an integer
+ * @return a 32-bits RGB color
+ * @see #getGray
+ */
+ public static int getRGB(double gray) {
+ int g = (int) Math.round(gray);
+ return checkInt(g) << 16 | checkInt(g) << 8 | checkInt(g);
+ }
+
+ /**
+ * Converts packed RGB image to gray-scale image.
+ *
+ * @param image : a HxW integer array
+ * @return a HxW double array
+ * @see #encode
+ * @see #getGray
+ */
+ public static double[][] toGray(int[][] image) {
+ double[][] img = new double[image.length][image[0].length];
+ for (int i = 0; i < image.length; i++) {
+ for (int j = 0; j < image[0].length; j++) {
+ img[i][j] = getGray(image[i][j]);
+ }
+ }
+ return img;
+ }
+
+ /**
+ * Converts gray-scale image to packed RGB image.
+ *
+ * @param channels : a HxW double array
+ * @return a HxW integer array
+ * @see #decode
+ * @see #getRGB(double)
+ */
+ public static int[][] toRGB(double[][] gray) {
+
+ int[][] img = new int[gray.length][gray[0].length];
+ for (int i = 0; i < gray.length; i++) {
+ for (int j = 0; j < gray[0].length; j++) {
+ img[i][j] = getRGB(gray[i][j]);
+ }
+ }
+ return img;
+ }
+
+ /**
+ * Convert an arbitrary 2D double matrix into a 2D integer matrix
+ * which can be used as RGB image
+ *
+ * @param matrix : the arbitrary 2D double array to convert into integer
+ * @param min : a double, the minimum value the matrix could theoretically contains
+ * @param max : a double, the maximum value the matrix could theoretically contains
+ * @return an 2D integer array, containing a RGB mapping of the matrix
+ */
+ public static int[][] matrixToRGBImage(double[][] matrix, double min, double max) {
+ int[][] imageRGB = new int[matrix.length][matrix[0].length];
+ for (int i = 0; i < matrix.length; i++) {
+ for (int j = 0; j < matrix[0].length; j++) {
+ imageRGB[i][j] = getRGB(255.0 * ((matrix[i][j] - min) / (max - min)));
+ }
+ }
+ return imageRGB;
+ }
+}
diff --git a/Main.java b/Main.java
new file mode 100644
index 0000000..a6d8841
--- /dev/null
+++ b/Main.java
@@ -0,0 +1,271 @@
+package main;
+
+/**
+ * @author Charles BEAUVILLE and Mike Sinsoillier
+ *