/** * @author Pacien TRAN-GIRARD * @author Timothée FLOURE */ public final class Filter { public static final float[][] SMOOTH_CORE = new float[][]{ {0.1f, 0.1f, 0.1f}, {0.1f, 0.2f, 0.1f}, {0.1f, 0.1f, 0.1f} }; public static final float[][] SOBEL_X_CORE = new float[][]{ {-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1} }; public static final float[][] SOBEL_Y_CORE = new float[][]{ {-1, -2, -1}, {0, 0, 0}, {1, 2, 1} }; /** * Get a pixel without accessing out of bounds * * @param gray a HxW float array, H > 0, W > 0 * @param row Y coordinate * @param col X coordinate * @return nearest valid pixel color */ public static float at(float[][] gray, int row, int col) { int maxRow = gray.length - 1; int maxCol = gray[0].length - 1; if (row < 0) row = 0; if (col < 0) col = 0; if (row > maxRow) row = maxRow; if (col > maxCol) col = maxCol; return gray[row][col]; } /** * 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) { int width = gray[0].length; int height = gray.length; int kernelWidth = kernel[0].length; int kernelHeight = kernel.length; float pixelNeighbors[][] = new float[kernelHeight][kernelWidth]; float[][] filteredImage = new float[height][width]; for (int row = 0; row < height; ++row) { for (int col = 0; col < width; ++col) { int pixelRow = -kernelHeight / 2; int pixelCol = -kernelWidth / 2; for (int kernelRow = 0; kernelRow < kernelWidth; kernelRow++) { for (int kernelCol = 0; kernelCol < kernelHeight; kernelCol++) { pixelNeighbors[kernelRow][kernelCol] = Filter.at(gray, row + pixelRow, col + pixelCol); pixelCol += 1; if (pixelCol > kernelWidth / 2) { pixelCol = -kernelWidth / 2; } } pixelRow += 1; if (pixelRow > kernelHeight / 2) { pixelRow = -kernelHeight / 2; } } for (int i = 0; i < kernelWidth; i++) { for (int j = 0; j < kernelHeight; j++) { filteredImage[row][col] += kernel[i][j] * pixelNeighbors[i][j]; } } } } return filteredImage; } /** * Smooth a single-channel image * * @param gray a HxW float array * @return a HxW float array */ public static float[][] smooth(float[][] gray) { return Filter.filter(gray, SMOOTH_CORE); } /** * Compute horizontal Sobel filter * * @param gray a HxW float array * @return a HxW float array */ public static float[][] sobelX(float[][] gray) { return Filter.filter(gray, SOBEL_X_CORE); } /** * Compute vertical Sobel filter * * @param gray a HxW float array * @return a HxW float array */ public static float[][] sobelY(float[][] gray) { return Filter.filter(gray, SOBEL_Y_CORE); } /** * Compute the magnitude of combined Sobel filters * * @param gray a HxW float array * @return a HxW float array */ public static float[][] sobel(float[][] gray) { float[][] x = Filter.sobelX(gray); float[][] y = Filter.sobelY(gray); int width = gray[0].length; int height = gray.length; float[][] sobelImage = new float[height][width]; for (int row = 0; row < height; ++row) { for (int col = 0; col < width; ++col) { sobelImage[row][col] = (float) Math.sqrt(Math.pow(x[row][col], 2) + Math.pow(y[row][col], 2)); } } return sobelImage; } }