import java.awt.image.*; import java.io.File; import java.io.IOException; import javax.imageio.*; public class ImageConvolution { public static class ArrayData { public final int[] dataArray; public final int width; public final int height; public ArrayData(int width, int height) { this(new int[width * height], width, height); } public ArrayData(int[] dataArray, int width, int height) { this.dataArray = dataArray; this.width = width; this.height = height; } public int get(int x, int y) { return dataArray[y * width + x]; } public void set(int x, int y, int value) { dataArray[y * width + x] = value; } } private static int bound(int value, int endIndex) { if (value < 0) return 0; if (value < endIndex) return value; return endIndex - 1; } public static ArrayData convolute(ArrayData inputData, ArrayData kernel, int kernelDivisor) { int inputWidth = inputData.width; int inputHeight = inputData.height; int kernelWidth = kernel.width; int kernelHeight = kernel.height; if ((kernelWidth <= 0) || ((kernelWidth & 1) != 1)) throw new IllegalArgumentException("Kernel must have odd width"); if ((kernelHeight <= 0) || ((kernelHeight & 1) != 1)) throw new IllegalArgumentException("Kernel must have odd height"); int kernelWidthRadius = kernelWidth >>> 1; int kernelHeightRadius = kernelHeight >>> 1; ArrayData outputData = new ArrayData(inputWidth, inputHeight); for (int i = inputWidth - 1; i >= 0; i--) { for (int j = inputHeight - 1; j >= 0; j--) { double newValue = 0.0; for (int kw = kernelWidth - 1; kw >= 0; kw--) for (int kh = kernelHeight - 1; kh >= 0; kh--) newValue += kernel.get(kw, kh) * inputData.get( bound(i + kw - kernelWidthRadius, inputWidth), bound(j + kh - kernelHeightRadius, inputHeight)); outputData.set(i, j, (int)Math.round(newValue / kernelDivisor)); } } return outputData; } public static ArrayData[] getArrayDatasFromImage(String filename) throws IOException { BufferedImage inputImage = ImageIO.read(new File(filename)); int width = inputImage.getWidth(); int height = inputImage.getHeight(); int[] rgbData = inputImage.getRGB(0, 0, width, height, null, 0, width); ArrayData reds = new ArrayData(width, height); ArrayData greens = new ArrayData(width, height); ArrayData blues = new ArrayData(width, height); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int rgbValue = rgbData[y * width + x]; reds.set(x, y, (rgbValue >>> 16) & 0xFF); greens.set(x, y, (rgbValue >>> 8) & 0xFF); blues.set(x, y, rgbValue & 0xFF); } } return new ArrayData[] { reds, greens, blues }; } public static void writeOutputImage(String filename, ArrayData[] redGreenBlue) throws IOException { ArrayData reds = redGreenBlue[0]; ArrayData greens = redGreenBlue[1]; ArrayData blues = redGreenBlue[2]; BufferedImage outputImage = new BufferedImage(reds.width, reds.height, BufferedImage.TYPE_INT_ARGB); for (int y = 0; y < reds.height; y++) { for (int x = 0; x < reds.width; x++) { int red = bound(reds.get(x, y), 256); int green = bound(greens.get(x, y), 256); int blue = bound(blues.get(x, y), 256); outputImage.setRGB(x, y, (red << 16) | (green << 8) | blue | -0x01000000); } } ImageIO.write(outputImage, "PNG", new File(filename)); return; } public static void main(String[] args) throws IOException { int kernelWidth = Integer.parseInt(args[2]); int kernelHeight = Integer.parseInt(args[3]); int kernelDivisor = Integer.parseInt(args[4]); System.out.println("Kernel size: " + kernelWidth + "x" + kernelHeight + ", divisor=" + kernelDivisor); int y = 5; ArrayData kernel = new ArrayData(kernelWidth, kernelHeight); for (int i = 0; i < kernelHeight; i++) { System.out.print("["); for (int j = 0; j < kernelWidth; j++) { kernel.set(j, i, Integer.parseInt(args[y++])); System.out.print(" " + kernel.get(j, i) + " "); } System.out.println("]"); } ArrayData[] dataArrays = getArrayDatasFromImage(args[0]); for (int i = 0; i < dataArrays.length; i++) dataArrays[i] = convolute(dataArrays[i], kernel, kernelDivisor); writeOutputImage(args[1], dataArrays); return; } }