简体   繁体   中英

Java blur Image

I am trying to blur the image

   int radius = 11;
    int size = radius * 2 + 1;
    float weight = 1.0f / (size * size);
    float[] data = new float[size * size];

    for (int i = 0; i < data.length; i++) {
        data[i] = weight;
    }

    Kernel kernel = new Kernel(size, size, data);
    ConvolveOp op = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
    //tbi is BufferedImage
    BufferedImage i = op.filter(tbi, null);

It will blur the image but not all portion of the image.

在此处输入图片说明

Where I am missing so that it will blur complete image. Without any path .

The standard Java ConvolveOp only has the two options EDGE_ZERO_FILL and EDGE_NO_OP . What you want is the options from the JAI equivalent ( ConvolveDescriptor ), which is EDGE_REFLECT (or EDGE_WRAP , if you want repeating patterns).

If you don't want to use JAI, you can implement this yourself, by copying your image to a larger image, stretching or wrapping the edges, apply the convolve op, then cut off the edges (similar to the technique described in the "Working on the Edge" section of the article posted by @halex in the comments section, but according to that article, you can also just leave the edges transparent).

For simplicity, you can just use my implementation called ConvolveWithEdgeOp which does the above (BSD license).

The code will be similar to what you had originally:

// ...kernel setup as before...
Kernel kernel = new Kernel(size, size, data);
BufferedImageOp op = new ConvolveWithEdgeOp(kernel, ConvolveOp.EDGE_REFLECT, null);

BufferedImage blurred = op.filter(original, null);

The filter should work like any other BufferedImageOp , and should work with any BufferedImage .

That is because you are using ConvolveOp.EDGE_NO_OP in this line:

ConvolveOp op = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);

The API documentation says:

Pixels at the edge of the source image are copied to the corresponding pixels in the destination without modification.

Try EDGE_ZERO_FILL - that will give you black borders.

You can also try to cut off the edges after blurring.

The reason for why it can't do the edges has to do with how the algorithm works.

您可以使用opencv库使完整的图像模糊。

You can't blur one pixel. This sounds obvious, but when you think about it, what is the minimum? To blur a pixel, you need neighboring pixels.

The problem here is that at the edges and corners, the pixels have too few neighbours – the blur algorith has too few pixels to use. It has no pixels "outside" the image with which to blur, so it will just leave those as-is.

The solution is either to extend the picture somehow (do you have a larger source image available?), or to cut off the non-blurred bits when you are done. Both are essentially the same.

I found this once when I was looking for a blur ability for documents when the ConvolveOp class didn't cut it (for the reasons you are running into). It does a Gaussian blur which is the most natural blur imho... Hopefully it will help you. I retrieved it from this webpage: Java Image Processing ...

/*
** Copyright 2005 Huxtable.com. All rights reserved.
*/

package com.jhlabs.image;

import java.awt.image.*;

/**
 * A filter which applies Gaussian blur to an image. This is a subclass of ConvolveFilter
 * which simply creates a kernel with a Gaussian distribution for blurring.
 * @author Jerry Huxtable
 */
public class GaussianFilter extends ConvolveFilter {

    static final long serialVersionUID = 5377089073023183684L;

    protected float radius;
    protected Kernel kernel;

    /**
     * Construct a Gaussian filter
     */
    public GaussianFilter() {
        this(2);
    }

    /**
     * Construct a Gaussian filter
     * @param radius blur radius in pixels
     */
    public GaussianFilter(float radius) {
        setRadius(radius);
    }

    /**
     * Set the radius of the kernel, and hence the amount of blur. The bigger the radius, the longer this filter will take.
     * @param radius the radius of the blur in pixels.
     */
    public void setRadius(float radius) {
        this.radius = radius;
        kernel = makeKernel(radius);
    }

    /**
     * Get the radius of the kernel.
     * @return the radius
     */
    public float getRadius() {
        return radius;
    }

    public BufferedImage filter( BufferedImage src, BufferedImage dst ) {
        int width = src.getWidth();
        int height = src.getHeight();

        if ( dst == null )
            dst = createCompatibleDestImage( src, null );

        int[] inPixels = new int[width*height];
        int[] outPixels = new int[width*height];
        src.getRGB( 0, 0, width, height, inPixels, 0, width );

        convolveAndTranspose(kernel, inPixels, outPixels, width, height, alpha, CLAMP_EDGES);
        convolveAndTranspose(kernel, outPixels, inPixels, height, width, alpha, CLAMP_EDGES);

        dst.setRGB( 0, 0, width, height, inPixels, 0, width );
        return dst;
    }

    public static void convolveAndTranspose(Kernel kernel, int[] inPixels, int[] outPixels, int width, int height, boolean alpha, int edgeAction) {
        float[] matrix = kernel.getKernelData( null );
        int cols = kernel.getWidth();
        int cols2 = cols/2;

        for (int y = 0; y < height; y++) {
            int index = y;
            int ioffset = y*width;
            for (int x = 0; x < width; x++) {
                float r = 0, g = 0, b = 0, a = 0;
                int moffset = cols2;
                for (int col = -cols2; col <= cols2; col++) {
                    float f = matrix[moffset+col];

                    if (f != 0) {
                        int ix = x+col;
                        if ( ix < 0 ) {
                            if ( edgeAction == CLAMP_EDGES )
                                ix = 0;
                            else if ( edgeAction == WRAP_EDGES )
                                ix = (x+width) % width;
                        } else if ( ix >= width) {
                            if ( edgeAction == CLAMP_EDGES )
                                ix = width-1;
                            else if ( edgeAction == WRAP_EDGES )
                                ix = (x+width) % width;
                        }
                        int rgb = inPixels[ioffset+ix];
                        a += f * ((rgb >> 24) & 0xff);
                        r += f * ((rgb >> 16) & 0xff);
                        g += f * ((rgb >> 8) & 0xff);
                        b += f * (rgb & 0xff);
                    }
                }
                int ia = alpha ? PixelUtils.clamp((int)(a+0.5)) : 0xff;
                int ir = PixelUtils.clamp((int)(r+0.5));
                int ig = PixelUtils.clamp((int)(g+0.5));
                int ib = PixelUtils.clamp((int)(b+0.5));
                outPixels[index] = (ia << 24) | (ir << 16) | (ig << 8) | ib;
                index += height;
            }
        }
    }

    /**
     * Make a Gaussian blur kernel.
     */
    public static Kernel makeKernel(float radius) {
        int r = (int)Math.ceil(radius);
        int rows = r*2+1;
        float[] matrix = new float[rows];
        float sigma = radius/3;
        float sigma22 = 2*sigma*sigma;
        float sigmaPi2 = 2*ImageMath.PI*sigma;
        float sqrtSigmaPi2 = (float)Math.sqrt(sigmaPi2);
        float radius2 = radius*radius;
        float total = 0;
        int index = 0;
        for (int row = -r; row <= r; row++) {
            float distance = row*row;
            if (distance > radius2)
                matrix[index] = 0;
            else
                matrix[index] = (float)Math.exp(-(distance)/sigma22) / sqrtSigmaPi2;
            total += matrix[index];
            index++;
        }
        for (int i = 0; i < rows; i++)
            matrix[i] /= total;

        return new Kernel(rows, 1, matrix);
    }

    public String toString() {
        return "Blur/Gaussian Blur...";
    }
}

If you frequently deal with pictures in your application, you may want to consider using the ImageJ API: It packs functionality on quite a lot of image processing tasks, including blurring.

Their Gaussian Blur filter will blur right to the edge of the picture, by making the following assumption

{...} it assumes that out-of-image pixels have a value equal to the nearest edge pixel {...}

Even if you don't want to change your code to use the ImageJ API, you may still find the above assumption useful to solving your problem.

For more info, check out the GaussianBlur filter in the API documentation: http://rsb.info.nih.gov/ij/developer/api/ij/plugin/filter/GaussianBlur.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM