Search This Blog

08 February 2015

Java Box Blur (code for Android)

We will quickly cover how to do box blur in Java.
To create a blur effect on an image we have to average a pixel with the data from its neighboring pixels. How much pixels we have to average depends on how big of a blur we want. Most of the time the size of the blur is called a radius, because we think of the area of pixels we want to cover as a circle.
For example if we want a blur with size 1:

1 1 1
1 2 1
1 1 1

"2" marks the location of the current pixel and "1"s mark the position of its neighbors.
One way to get the average of all these pixels is to add their values and then divide that value to the count of the pixels:

(1 + 1 + 1 + 1 + 2 + 1 + 1 + 1 + 1) / 9 = averageValue

Then we set pixel "2" to the "averageValue".

Another way is to use what is called a convolution kernel. For our blur, the kernel would be:

1/9 1/9 1/9
1/9 1/9 1/9
1/9 1/9 1/9

It is a matrix with the size of our blur, and we can imagine we are moving it over every pixel of our image and multiplying the pixel color values with the corresponding kernel value (here they are all the same, and this is called a box blur. In Gaussian blur, the center of the kernel has more weight than the other parts).
For our example we will multiply all the pixels with 1/9 and will assign as color value for pixel at position marked with "2":
1*1/9 + 1*1/9 + 1*1/9 + 1*1/9 + 2*1/9 + 1*1/9 + 1*1/9 + 1*1/9 + 1*1/9

If we use a bigger radius for the blur we need a bigger kernel. What stays the same is that when we add all values of the kernel together they must add up to 1.
1/9 + 1/9 + 1/9 + 1/9 + 1/9 + 1/9 + 1/9 + 1/9 + 1/9 = 9/9 = 1

Kernel with radius 3, which gives us a 7x7 matrix:
1/49 + 1/49 ... = 49/49 = 1

Instead of doing a 2D calculation we can split the kernel into two 1D kernels
1/9 1/9 1/9 and 1/9 and multiply each pixel with these kernels separately.
                        1/9
                        1/9










This way we would gain a little bit of speed.
Another way of speeding up is scaling down the image before we apply blur. And then scale it back up or just draw it in a bigger rectangle.
If we are blurring a grey scale image we don't need to multiply each color channel, because they all are the same. Just multiply the alpha and one of the color channels. (we have a note on that in the code).

And here is our code:
1:  public static Bitmap boxBlur(Bitmap aBitmap, int aSize, boolean aCreateNewBitmap) 
2:       { 
3:            float kernel = 1.0f / ((aSize * 2) + 1); 
4:            Bitmap bitmap = null; 
5:            if(aCreateNewBitmap) { 
6:                 bitmap = Bitmap.createBitmap(aBitmap.getWidth(), aBitmap.getHeight(), Config.ARGB_8888); 
7:            } else { 
8:                 bitmap = aBitmap; 
9:            } 
10:             
11:            Rect rectSrc = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); 
12:            Rect rectDest = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); 
13:             
14:            Canvas canvas = new Canvas(bitmap); 
15:             
16:            //box blur 
17:            int colsCount = bitmap.getWidth(); 
18:            int rowsCount = bitmap.getHeight(); 
19:             
20:            for(int row = 0; row < rowsCount; row++) { 
21:                 for(int col = 0; col < colsCount; col++) { 
22:                      float sumAlpha = 0; 
23:                      float sumRed = 0; 
24:                      float sumGreen = 0; 
25:                      float sumBlue = 0; 
26:                      for(int k = col - aSize; k <= col + aSize; k++) { 
27:                           int x = k; 
28:                           if(x < 0) {x = Math.abs(x);} 
29:                           if(x >= colsCount) {x = (colsCount - 1) - (x - colsCount);} 
30:                            
31:                           int pixel = bitmap.getPixel(x, row); 
32:                            
33:                           int alpha = Color.alpha(pixel); 
34:                           int red = Color.red(pixel); 
35:                           int green = Color.green(pixel); 
36:                           int blue = Color.blue(pixel); 
37:                                 
38:                           float resultAlpha = (float)alpha * kernel; 
39:                           float resultRed = (float)red * kernel; 
40:                           float resultGreen = (float)green * kernel; 
41:                           float resultBlue = (float)blue * kernel; 
42:                                 
43:                           sumAlpha += resultAlpha; 
44:                           sumRed += resultRed; 
45:                           sumGreen += resultGreen; 
46:                           sumBlue += resultBlue; 
47:                      } 
48:                      bitmap.setPixel(col, row, Color.argb((int)sumAlpha, (int)sumRed, (int)sumGreen, (int)sumBlue)); 
49:                 } 
50:            } 
51:             
52:            for(int col = 0; col < colsCount; col++) { 
53:                 for(int row = 0; row < rowsCount; row++) { 
54:                      float sumAlpha = 0; 
55:                      float sumRed = 0; 
56:                      float sumGreen = 0; 
57:                      float sumBlue = 0; 
58:                      for(int k = row - aSize; k <= row + aSize; k++) { 
59:                           int y = k; 
60:                           if(y < 0) {y = Math.abs(y);} 
61:                           if(y >= rowsCount) {y = (rowsCount - 1) - (y - rowsCount);} 
62:                            
63:                           int pixel = bitmap.getPixel(col, y); 
64:                            
65:                           int alpha = Color.alpha(pixel); 
66:                           int red = Color.red(pixel); 
67:                           int green = Color.green(pixel); 
68:                           int blue = Color.blue(pixel); 
69:                                 
70:                           float resultAlpha = (float)alpha * kernel; 
71:                           float resultRed = (float)red * kernel; 
72:                           float resultGreen = (float)green * kernel; 
73:                           float resultBlue = (float)blue * kernel; 
74:                                 
75:                           sumAlpha += resultAlpha; 
76:                           sumRed += resultRed; 
77:                           sumGreen += resultGreen; 
78:                           sumBlue += resultBlue; 
79:                      } 
80:                      bitmap.setPixel(col, row, Color.argb((int)sumAlpha, (int)sumRed, (int)sumGreen, (int)sumBlue)); 
81:                 } 
82:            } 
83:                       
84:            canvas.drawBitmap(bitmap, rectSrc, rectDest, null); 
85:            return bitmap; 
86:       } 


No comments:

Post a Comment