Apply blur effect to image programmatically

120 Views Asked by At

I am using a background segmentation model and it provides a probability of each pixel belonging to foreground or background. I have this function that fills the background pixels with a solid color. But how can I make make them blur instead? Like what those video conferencing apps perform background blur effects.

  private int[] maskBackgroundColors(ByteBuffer byteBuffer) {
    @ColorInt int[] colors = new int[maskWidth * maskHeight];
    for (int i = 0; i < maskWidth * maskHeight; i++) {
      float backgroundLikelihood = 1 - byteBuffer.getFloat();
      if (backgroundLikelihood > 0.9) {
        
        //belonging to background pixels. Fill with a solid color.
        //But how to do blur instead?!
        colors[i] = Color.argb(128, 0, 0, 255);
        //colors[i] = applyBlurEffectToColor(colors[i]);
      }
    }
    return colors;
  }

I came up with this function that gets colors[i] pixel. But I don't see any changes, so it doesn't work.

private int applyBlurEffectToColor(@ColorInt int color) {
    // Apply blur effect algorithm here
    // You can use libraries like RenderScript, OpenCV, or custom algorithms to achieve the blur effect.
    // The implementation details depend on the specific blur effect you want to achieve.

    // Example: Apply Gaussian blur effect
    // You can replace this example with your own blur effect implementation
    float radius = 10.0f;
    float sigma = radius / 3.0f;
    float[] matrix = new float[]{
            1.0f, 2.0f, 1.0f,
            2.0f, 4.0f, 2.0f,
            1.0f, 2.0f, 1.0f
    };
    int width = (int) Math.round(Math.sqrt(matrix.length));
    int height = width;
    float sum = 0;
    for (float value : matrix) {
      sum += value;
    }
    for (int i = 0; i < matrix.length; i++) {
      matrix[i] /= sum;
    }

    int redSum = 0;
    int greenSum = 0;
    int blueSum = 0;

    for (int x = 0; x < width; x++) {
      for (int y = 0; y < height; y++) {
        int offsetX = x - width / 2;
        int offsetY = y - height / 2;
        float coefficient = (float) (Math.exp(-(offsetX * offsetX + offsetY * offsetY) / (2 * sigma * sigma))
                / (2 * Math.PI * sigma * sigma));

        int alpha = Color.alpha(color);
        int red = Color.red(color);
        int green = Color.green(color);
        int blue = Color.blue(color);

        redSum += (int) (coefficient * red);
        greenSum += (int) (coefficient * green);
        blueSum += (int) (coefficient * blue);
      }
    }

    int blurredColor = Color.argb(Color.alpha(color), redSum, greenSum, blueSum);
    return blurredColor;
  }
1

There are 1 best solutions below

11
Dennis On

To kick things off with a blur effect, you'll first need your desired source image. Once you've got that, a Gaussian blur is a fantastic option to consider.

Here's a quick rundown: A Gaussian blur smooths an image by placing a grid (called a kernel) over each pixel. This kernel has values based on the Gaussian function, which looks like a bell curve. The pixel under the kernel's center and its neighbors are averaged together, with the kernel values determining how much weight each pixel gets in the average. The outcome is a softer, blurred image.

For a hands-on approach, your solution might look something like this:

private int[] maskBackgroundColors(ByteBuffer byteBuffer, int[] sourceImage) {
    @ColorInt int[] colors = new int[maskWidth * maskHeight];
    
    // Define a 3x3 Gaussian blur kernel
    float[][] kernel = {
        {1/16f, 2/16f, 1/16f},
        {2/16f, 4/16f, 2/16f},
        {1/16f, 2/16f, 1/16f}
    };

    int kH = kernel[0].length / 2;
    int kW = kernel.length / 2;

    for (int y = 0; y < maskHeight; y++) {
        for (int x = 0; x < maskWidth; x++) {
            int i = y * maskWidth + x;
            float backgroundLikelihood = 1 - byteBuffer.getFloat();
            if (backgroundLikelihood > 0.9) {
                // Apply Gaussian blur
                float sumR = 0, sumG = 0, sumB = 0;
                for (int ky = -kH; ky <= kH; ky++) {
                    for (int kx = -kW; kx <= kW; kx++) {
                        int nx = x + kx;
                        int ny = y + ky;
                        if (nx >= 0 && nx < maskWidth && ny >= 0 && ny < maskHeight) {
                            int neighborIndex = ny * maskWidth + nx;
                            int neighborColor = sourceImage[neighborIndex];
                            float weight = kernel[ky + kH][kx + kW];
                            sumR += weight * Color.red(neighborColor);
                            sumG += weight * Color.green(neighborColor);
                            sumB += weight * Color.blue(neighborColor);
                        }
                    }
                }
                colors[i] = Color.rgb((int) sumR, (int) sumG, (int) sumB);
            }
        }
    }
    return colors;
}

This implementation uses a 3x3 kernel, which provides a mild blur. For a stronger blur, you can use a larger kernel (e.g., 5x5, 7x7) with appropriate weights.

I hope this provides some clarity! Best of luck with your coding adventures!