Issue
I have blured an image. But it is not smooth.
I heard that if I use the gaussian blur technique then I can remove the box effect.
But I don't know how to implement it with my code (I did some random technique but it messes with the color). Can you suggest me how to do gaussian blur with my code?
public class BlurImageDemo {
Color c[];
BlurImageDemo() throws IOException, InterruptedException {
File f = new File("D:\\x.jpg");
BufferedImage im = ImageIO.read(f);
BufferedImage bi = new BufferedImage(im.getWidth(), im.getHeight(), BufferedImage.TYPE_INT_RGB);
int i = 0;
int max = 400, radius = 10;
int a1 = 0, r1 = 0, g1 = 0, b1 = 0;
c = new Color[max];
int x = 1, y = 1, x1, y1, ex = 5, d = 0;
for (x = radius; x < im.getHeight() - radius; x++) {
for (y = radius; y < im.getWidth() - radius; y++) {
//20x20 matrix
for (x1 = x - radius; x1 < x + radius; x1++) {
for (y1 = y - radius; y1 < y + radius; y1++) {
c[i++] = new Color(im.getRGB(y1, x1));
//System.out.println(i);
}
}
i = 0;
for (d = 0; d < max; d++) {
a1 = a1 + c[d].getAlpha();
}
a1 = a1 / (max);
for (d = 0; d < max; d++) {
r1 = r1 + c[d].getRed();
}
r1 = r1 / (max);
for (d = 0; d < max; d++) {
g1 = g1 + c[d].getGreen();
}
g1 = g1 / (max);
for (d = 0; d < max; d++) {
b1 = b1 + c[d].getBlue();
}
b1 = b1 / (max);
int sum1 = (a1 << 24) + (r1 << 16) + (g1 << 8) + b1;
bi.setRGB(y, x, (int) (sum1));
}
}
ImageIO.write(bi, "jpg", new File("D:\\x1.jpg"));
}
public static void main(String[] args) throws IOException, InterruptedException {
new BlurImageDemo();
}
}
Solution
One really nice property of Gaussian blur is that it is separable, meaning that it can be expressed as the composition of a purely horizontal and a purely vertical blur. The advantage of doing that is that, per pixel, it them takes 2N multiplications (N is the size of the kernel), whereas the 2D non-separated version takes N2 multiplications. For N=7 (as you have), that's already a decent difference. It also has a small downside, the intermediate result is either rounded (losing some precision) or big (3 floats per pixel instead of 1 int), usually a little rounding is not a problem though.
An other thing, more of an implementation detail, is that the division by the total weight of the kernel can be put into the kernel itself, saving a whole bunch of (pretty slow) divisions.
Also your kernel doesn't actually look like a Gaussian, it's too "pointy". That's up to you, but the Gaussian kernel is the only circularly symmetric one that is also separable (if you only look at real-valued kernels) and generally has nice properties, so I would recommend only deviating from it if there is a good reason for it.
Anyway I'll write some example code now, not tested:
BufferedImage transposedHBlur(BufferedImage im) {
int height = im.getHeight();
int width = im.getWidth();
// result is transposed, so the width/height are swapped
BufferedImage temp = new BufferedImage(height, width, BufferedImage.TYPE_INT_RGB);
float[] k = new float[7] { 0.00598, 0.060626, 0.241843, 0.383103, 0.241843, 0.060626, 0.00598 };
// horizontal blur, transpose result
for (int y = 0; y < height; y++) {
for (int x = 3; x < width - 3; x++) {
float r = 0, g = 0, b = 0;
for (int i = 0; i < 7; i++) {
int pixel = im.getRGB(x + i - 3, y);
b += (pixel & 0xFF) * k[i];
g += ((pixel >> 8) & 0xFF) * k[i];
r += ((pixel >> 16) & 0xFF) * k[i];
}
int p = (int)b + ((int)g << 8) + ((int)r << 16);
// transpose result!
temp.setRGB(y, x, p);
}
}
return temp;
}
Since it also transposes, you can simply call it twice and the second time will effectively be a vertical blur that also restores the orientation:
temp = transposedHBlur(input);
result = transposedHBlur(temp);
Answered By - harold