Add files via upload
This commit is contained in:
parent
40f34b7d38
commit
e214ed157b
104
Collector.java
Normal file
104
Collector.java
Normal file
@ -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 !
|
||||
* <p>
|
||||
* 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<int[]> quicksortPixelCoordinates(double[][] matrix) {
|
||||
|
||||
// TODO implement me correctly for "underpriced" bonus!
|
||||
return new ArrayList<int[]>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* BONUS
|
||||
* Notice : Bonus points are underpriced !
|
||||
* <p>
|
||||
* 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[][]{};
|
||||
}
|
||||
}
|
||||
71
DistanceBasedSearch.java
Normal file
71
DistanceBasedSearch.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
180
Helper.java
Normal file
180
Helper.java
Normal file
@ -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 <code>null</code> 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();
|
||||
}
|
||||
|
||||
}
|
||||
155
ImageProcessing.java
Normal file
155
ImageProcessing.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
271
Main.java
Normal file
271
Main.java
Normal file
@ -0,0 +1,271 @@
|
||||
package main;
|
||||
|
||||
/**
|
||||
* @author Charles BEAUVILLE and Mike Sinsoillier
|
||||
* <p>
|
||||
* 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<toGray.length; i++) {
|
||||
for(int j =0 ; j<toGray[0].length ; j++) {
|
||||
if ((ref[i][j]==toGray[i][j])) {
|
||||
System.out.println("Test passed");
|
||||
} else {
|
||||
System.out.println("Test failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void testToRGB() {
|
||||
System.out.println("Test toRGB");
|
||||
int[][] ref = new int[][] {{0b01110001_01110001_01110001}, {0b01110001_01110001_01110001},{0b01110001_01110001_01110001}};
|
||||
double [][] image = new double[][] {{0b01110001}, {0b01110001},{0b01110001}};
|
||||
int[][] toRGB = ImageProcessing.toRGB(image);
|
||||
for(int i=0 ; i<toRGB.length; i++) {
|
||||
for(int j =0 ; j<toRGB[0].length ; j++) {
|
||||
if ((ref[i][j]==toRGB[i][j])) {
|
||||
System.out.println("Test passed");
|
||||
} else {
|
||||
System.out.println("Test failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests for Class Collector
|
||||
*/
|
||||
public static void testFindBest() {
|
||||
System.out.println("Test findBest");
|
||||
double[][] t = new double[][]{{20, 30, 10, 50, 32}, {28, 39, 51, 78, 91}};
|
||||
int[] coords = Collector.findBest(t, false);
|
||||
System.out.println("Coordonnes : " + coords);
|
||||
}
|
||||
|
||||
public static void testFindNBest() {
|
||||
System.out.println("Test findNBest");
|
||||
double[][] t = new double[][]{{20, 30, 10, 50, 32}, {28, 39, 51, 78, 91}};
|
||||
int[][] coords = Collector.findNBest(10, t, true);
|
||||
for (int[] a : coords) {
|
||||
int r = a[0];
|
||||
int c = a[1];
|
||||
System.out.println("Row=" + r + " Col=" + c + " Val=" + t[r][c]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests for Class DistanceBasedSearch
|
||||
*/
|
||||
|
||||
public static void pixelAbsoluteError() {
|
||||
int color = 0b11110000_00001111_01010101;
|
||||
int color2 = 0b11010000_00101011_01010001;
|
||||
int ref = 21;
|
||||
int AE = (int) DistanceBasedSearch.pixelAbsoluteError(color, color2);
|
||||
if (AE == ref) {
|
||||
System.out.println("Test AE passed");
|
||||
} else {
|
||||
System.out.println("Test AE failed. Returned value = " + AE + " Expected value = " + ref);
|
||||
}
|
||||
}
|
||||
|
||||
public static void testDistanceBasedSearch() {
|
||||
System.out.println("Test DistanceBasedSearch");
|
||||
int[][] food = Helper.read("images/food.png");
|
||||
int[][] onions = Helper.read("images/onions.png");
|
||||
double[][] distance = DistanceBasedSearch.distanceMatrix(onions, food);
|
||||
int[] p = Collector.findBest(distance, true);
|
||||
Helper.drawBox(p[0], p[1], onions[0].length, onions.length, food);
|
||||
Helper.show(food, "Found!");
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests for Class SimilarityBasedSearch
|
||||
*/
|
||||
|
||||
public static void testSimilarityBasedSearch() {
|
||||
System.out.println("Test SimilarityBasedSearch");
|
||||
int[][] food = Helper.read("images/food.png");
|
||||
int[][] onions = Helper.read("images/onions.png");
|
||||
double[][] foodGray = ImageProcessing.toGray(food);
|
||||
double[][] onionsGray = ImageProcessing.toGray(onions);
|
||||
double[][] similarity = SimilarityBasedSearch.similarityMatrix(onionsGray, foodGray);
|
||||
int[][] best = Collector.findNBest(8, similarity, false);
|
||||
for (int[] a : best) {
|
||||
int r = a[0];
|
||||
int c = a[1];
|
||||
Helper.drawBox(r, c, onions[0].length, onions.length, food);
|
||||
}
|
||||
Helper.show(food, "Found again!");
|
||||
}
|
||||
|
||||
public static void findCharlie() {
|
||||
System.out.println("Find Charlie");
|
||||
int[][] beach = Helper.read("images/charlie_beach.png");
|
||||
int[][] charlie = Helper.read("images/charlie.png");
|
||||
double[][] beachGray = ImageProcessing.toGray(beach);
|
||||
double[][] charlieGray = ImageProcessing.toGray(charlie);
|
||||
|
||||
System.out.println("Compute Similarity Matrix: expected time about 2 min");
|
||||
double[][] similarity = SimilarityBasedSearch.similarityMatrix(charlieGray, beachGray);
|
||||
|
||||
System.out.println("Find N Best");
|
||||
int[] best = Collector.findBest(similarity, false);
|
||||
double max = similarity[best[0]][best[1]];
|
||||
|
||||
Helper.show(ImageProcessing.matrixToRGBImage(similarity, -1, max), "Similarity");
|
||||
|
||||
Helper.drawBox(best[0], best[1], charlie[0].length, charlie.length, beach);
|
||||
System.out.println("drawBox at (" + best[0] + "," + best[1] + ")");
|
||||
Helper.show(beach, "Found again!");
|
||||
}
|
||||
|
||||
/*
|
||||
* Tests for extreme cases
|
||||
*/
|
||||
|
||||
public static void testNCCPatternEqualImage() {
|
||||
double[][] pattern = {{0, 0, 0},
|
||||
{0, 255, 0},
|
||||
{0, 0, 0}};
|
||||
double similarity = SimilarityBasedSearch.normalizedCrossCorrelation(0, 0, pattern, pattern);
|
||||
if (similarity == 1.0) {
|
||||
System.out.println("PASSED");
|
||||
} else {
|
||||
System.out.println("ERROR: expected value 1.0 but was " + similarity);
|
||||
}
|
||||
}
|
||||
|
||||
public static void testSimilarityPatternEqualImage() {
|
||||
double[][] pattern = {{0, 255}};
|
||||
double[][] similarity = SimilarityBasedSearch.similarityMatrix(pattern, pattern);
|
||||
if (similarity.length == 1) {
|
||||
if (similarity[0][0] == 1.0) {
|
||||
System.out.println("PASSED");
|
||||
} else {
|
||||
System.out.println("ERROR: expected value 1.0 but was " + similarity[0][0]);
|
||||
}
|
||||
} else {
|
||||
System.out.println("ERROR: expected length 1 but was " + similarity.length);
|
||||
}
|
||||
}
|
||||
|
||||
public static void testSimilaritySimple() {
|
||||
double[][] image = {{3, 2, 2, 2},
|
||||
{0, 3, 0, 0}};
|
||||
double[][] pattern = {{0, 3, 0}};
|
||||
double[][] similarity = SimilarityBasedSearch.similarityMatrix(pattern, image);
|
||||
|
||||
if (similarity.length == 2 && similarity[0].length == 2) {
|
||||
if (similarity[0][0] == -0.5 && similarity[0][1] == -1.0 &&
|
||||
similarity[1][0] == 1.0 && similarity[1][1] == -0.5) {
|
||||
System.out.println("PASSED");
|
||||
} else {
|
||||
System.out.println("ERROR: wrong values");
|
||||
}
|
||||
} else {
|
||||
System.out.println("ERROR: incorrect size");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
16
Program.java
Normal file
16
Program.java
Normal file
@ -0,0 +1,16 @@
|
||||
package main;
|
||||
|
||||
public class Program {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Loading files...");
|
||||
int[][] image = Helper.read("images/charlie_beach.png");
|
||||
int[][] pattern = Helper.read("images/charlie.png");
|
||||
System.out.println("Searching...");
|
||||
double[][] distance = DistanceBasedSearch.distanceMatrix(pattern, image);
|
||||
System.out.println("Processing...");
|
||||
int[] p = Collector.findBest(distance, true);
|
||||
Helper.drawBox(p[0], p[1], pattern[0].length, pattern.length, image);
|
||||
Helper.show(image, "Found!");
|
||||
}
|
||||
}
|
||||
57
SignatureChecks.java
Normal file
57
SignatureChecks.java
Normal file
@ -0,0 +1,57 @@
|
||||
package main;
|
||||
|
||||
/**
|
||||
* Check if the signatures of all required functions are correct
|
||||
* This file should complied but not run ! (i.e, not indicate any error)
|
||||
* For your own good, please do not change anything inside this file !
|
||||
* Check this file is complied before submit your project on moodle
|
||||
*/
|
||||
public class SignatureChecks {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static void main(String[] argv) {
|
||||
int[][] image = new int[0][0];
|
||||
double[][] grayImage = new double[0][0];
|
||||
|
||||
int rgb = 0;
|
||||
int red = 0;
|
||||
int green = 0;
|
||||
int blue = 0;
|
||||
double gray = 0;
|
||||
double factor = 0;
|
||||
|
||||
int intResult;
|
||||
double doubleResult;
|
||||
boolean boolResult;
|
||||
|
||||
//ImageProcessing
|
||||
intResult = ImageProcessing.getRed(rgb);
|
||||
intResult = ImageProcessing.getGreen(rgb);
|
||||
intResult = ImageProcessing.getBlue(rgb);
|
||||
doubleResult = ImageProcessing.getGray(rgb);
|
||||
intResult = ImageProcessing.getRGB(red, green, blue);
|
||||
intResult = ImageProcessing.getRGB(gray);
|
||||
|
||||
grayImage = ImageProcessing.toGray(image);
|
||||
image = ImageProcessing.toRGB(grayImage);
|
||||
|
||||
int[][] img = ImageProcessing.matrixToRGBImage(grayImage, 0, 255);
|
||||
|
||||
|
||||
//Collector
|
||||
int[] best = Collector.findBest(grayImage, false);
|
||||
int[][] nBests = Collector.findNBest(3, grayImage, false);
|
||||
|
||||
|
||||
//DistanceBasedSearch
|
||||
double error = DistanceBasedSearch.pixelAbsoluteError(0xffffff, 0x000000);
|
||||
error = DistanceBasedSearch.meanAbsoluteError(0, 0, image, image);
|
||||
double[][] res = DistanceBasedSearch.distanceMatrix(image, image);
|
||||
|
||||
|
||||
//CrossCorrelation
|
||||
double m = SimilarityBasedSearch.mean(res);
|
||||
SimilarityBasedSearch.normalizedCrossCorrelation(0, 0, grayImage, grayImage);
|
||||
SimilarityBasedSearch.similarityMatrix(grayImage, grayImage);
|
||||
}
|
||||
}
|
||||
116
SimilarityBasedSearch.java
Normal file
116
SimilarityBasedSearch.java
Normal file
@ -0,0 +1,116 @@
|
||||
package main;
|
||||
|
||||
public class SimilarityBasedSearch {
|
||||
|
||||
/**
|
||||
* Computes the mean value of a gray-scale image given as a 2D array
|
||||
*
|
||||
* @param image : a 2D double array, the gray-scale Image
|
||||
* @return a double value between 0 and 255 which is the mean value
|
||||
*/
|
||||
public static double mean(double[][] image) {
|
||||
|
||||
assert image.length != 0 : "image contains no pixel";
|
||||
double sum = 0;
|
||||
|
||||
for (int i = 0; i < image.length; i++) {
|
||||
for (int j = 0; j < image[0].length; j++) {
|
||||
sum += image[i][j];
|
||||
}
|
||||
}
|
||||
|
||||
return sum / ((double) image.length * image[0].length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the mean value of a part of an image
|
||||
*
|
||||
* @param matrix : a 2D array of double, the gray-scale image we want to calculate the mean value of a part
|
||||
* @param row : a integer, the row-coordinate of the upper left corner of the pattern in the image.
|
||||
* @param column : a integer, the column-coordinate of the upper left corner of the pattern in the image.
|
||||
* @param width : a integer, the width of the window.
|
||||
* @param height : a integer, the height of the window.
|
||||
* @return a double, the mean value of the specified part of the input matrix
|
||||
*/
|
||||
static double windowMean(double[][] matrix, int row, int col, int width, int height) {
|
||||
|
||||
double[][] temp = new double[width][height];
|
||||
|
||||
for (int i = 0; i < width; i++) {
|
||||
for (int j = 0; j < height; j++) {
|
||||
temp[i][j] = matrix[i + row][j + col];
|
||||
}
|
||||
}
|
||||
|
||||
return mean(temp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the Normalized Cross Correlation of a gray-scale pattern if positioned
|
||||
* at the provided row, column-coordinate in a gray-scale image
|
||||
*
|
||||
* @param row : a integer, the row-coordinate of the upper left corner of the pattern in the image.
|
||||
* @param column : a integer, the column-coordinate of the upper left corner of the pattern in the image.
|
||||
* @param pattern : an 2D array of doubles, the gray-scale pattern to find
|
||||
* @param image : an 2D array of double, the gray-scale image where to look for the pattern
|
||||
* @return a double, the Normalized Cross Correlation 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 0
|
||||
*/
|
||||
public static double normalizedCrossCorrelation(int row, int col, double[][] pattern, double[][] image) {
|
||||
|
||||
assert image.length != 0 : "image contains no pixel";
|
||||
assert pattern.length != 0 : "pattern contains no pixel";
|
||||
double wmean = windowMean(image, row, col, pattern.length, pattern[0].length);
|
||||
double m = mean(pattern);
|
||||
double sum1 = 0, sum2 = 0, sum3 = 0;
|
||||
|
||||
for (int i = 0; i < pattern.length; i++) {
|
||||
for (int j = 0; j < pattern[0].length; j++) {
|
||||
sum1 += (image[row + i][col + j] - wmean) * (pattern[i][j] - m);
|
||||
sum2 += (image[row + i][col + j] - wmean) * (image[row + i][col + j] - wmean);
|
||||
sum3 += (pattern[i][j] - m) * (pattern[i][j] - m);
|
||||
}
|
||||
}
|
||||
|
||||
double result;
|
||||
|
||||
if ((Math.sqrt(sum2 * sum3)) == 0)
|
||||
result = -1;
|
||||
else
|
||||
result = sum1 / (Math.sqrt(sum2 * sum3));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compute the similarityMatrix between a gray-scale image and a gray-scale pattern
|
||||
*
|
||||
* @param pattern : an 2D array of doubles, the gray-scale pattern to find
|
||||
* @param image : an 2D array of doubles, the gray-scale image where to look for the pattern
|
||||
* @return a 2D array of doubles, containing for each pixel of a original gray-scale image,
|
||||
* the similarity (normalized cross-correlation) between the image's window and the pattern
|
||||
* placed over this pixel (upper-left corner)
|
||||
*/
|
||||
public static double[][] similarityMatrix(double[][] pattern, double[][] 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 + 1][W - w + 1];
|
||||
|
||||
for (int i = 0; i < H - h + 1; i++) {
|
||||
for (int j = 0; j < W - w + 1; j++) {
|
||||
matrix[i][j] = normalizedCrossCorrelation(i, j, pattern, image);
|
||||
}
|
||||
}
|
||||
|
||||
return matrix;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user