From e214ed157bbf5da6bb20b430d2a747e6b5851067 Mon Sep 17 00:00:00 2001 From: charlesbvll Date: Tue, 1 Oct 2019 01:22:32 +0200 Subject: [PATCH] Add files via upload --- Collector.java | 104 ++++++++++++++ DistanceBasedSearch.java | 71 ++++++++++ Helper.java | 180 ++++++++++++++++++++++++ ImageProcessing.java | 155 +++++++++++++++++++++ Main.java | 271 +++++++++++++++++++++++++++++++++++++ Program.java | 16 +++ SignatureChecks.java | 57 ++++++++ SimilarityBasedSearch.java | 116 ++++++++++++++++ 8 files changed, 970 insertions(+) create mode 100644 Collector.java create mode 100644 DistanceBasedSearch.java create mode 100644 Helper.java create mode 100644 ImageProcessing.java create mode 100644 Main.java create mode 100644 Program.java create mode 100644 SignatureChecks.java create mode 100644 SimilarityBasedSearch.java 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 quicksortPixelCoordinates(double[][] matrix) { + + // TODO implement me correctly for "underpriced" bonus! + return new ArrayList(); + } + + + /** + * BONUS + * Notice : Bonus points are underpriced ! + *

+ * 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 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) { + 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 + *

+ * 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 ; i