Размытие по Гауссу Более высокий радиус затемняет изображение - PullRequest
4 голосов
/ 06 января 2020

Я реализовал Размытие по Гауссу в Java, похоже, он работает на меньших радиусах, но не на больших. Я не уверен, почему на больших радиусах изображение темнеет, я следовал той же формуле и шагам, чтобы применить размытие. Генерация матрицы размытия и ее применение к исходному изображению и установка значения пикселя в качестве суммы результата умножения матрицы изображения на матрицу размытия. Я добавил код, который написал ниже:

public class GaussianBlur extends ImageFilter {
    private int radius;
    private double sigma;

    public GaussianBlur(String imageFilename, int radius) {
        super(imageFilename);
        this.radius = radius;
        this.sigma = ((2.0 * radius) + 1.0) / 2.0;
    }

    @Override
    public void applyFilter() throws IOException {
        init();

        Matrix<Double> gaussianMatrix = getGaussianMatrix();

        Matrix<Color> imageMatrix, weightedImageMatrix;
        Color weightedPixelSum;
        for(int i = 0; i < getWidth(); i++) {
            for(int j = 0; j < getHeight(); j++) {
                imageMatrix = getImageMatrix(i, j);
                weightedImageMatrix = multiplyImageMatrixWithWeight(imageMatrix, gaussianMatrix);
                weightedPixelSum = getWeightedGaussianBlurValue(weightedImageMatrix);

                getFilter().setRGB(i, j, weightedPixelSum.getRGB());
            }
        }
    }

    private Matrix<Double> getGaussianMatrix() {
        Matrix<Double> gaussianMatrix = new Matrix<>(Double.class, radius);
        double weightedSum = 0.0;

        int matrixI = 0, matrixJ;
        double gaussianValue;
        for(int i = -radius; i <= radius; i++) {
            matrixJ = 0;

            for(int j = -radius; j <= radius; j++) {
                gaussianValue = getGaussianValue(i, j);
                weightedSum += gaussianValue;
                gaussianMatrix.setValue(matrixI, matrixJ, gaussianValue);
                matrixJ++;
            }

            matrixI++;
        }

        for(int i = 0; i < gaussianMatrix.getMatrix().length; i++) {
            for(int j = 0; j < gaussianMatrix.getMatrix()[i].length; j++) {
                gaussianMatrix.setValue(i, j, gaussianMatrix.getValue(i, j) / weightedSum);
            }
        }

        return gaussianMatrix;
    }

    private double getGaussianValue(int x, int y) {
        return 1.0 / (2.0 * Math.PI * sigma * sigma) * Math.pow(Math.E, -((x * x) + (y * y)) / (2.0 * (sigma * sigma)));
    }

    private Color getWeightedGaussianBlurValue(Matrix<Color> weightedImageMatrix) {
        int r = 0, g = 0, b = 0;

        for(int i = 0; i < weightedImageMatrix.getMatrix().length; i++) {
            for(int j = 0; j < weightedImageMatrix.getMatrix()[i].length; j++) {
                if(weightedImageMatrix.getValue(i, j) != null) {
                    r += weightedImageMatrix.getValue(i, j).getRed();
                    g += weightedImageMatrix.getValue(i, j).getGreen();
                    b += weightedImageMatrix.getValue(i, j).getBlue();
                }
            }
        }

        return new Color(r, g, b);
    }

    /*
     * Multiply each image pixel with its matrix value to get a new matrix with the weighted pixel values.
     */
    private Matrix<Color> multiplyImageMatrixWithWeight(Matrix<Color> imageMatrix, Matrix<Double> gaussianMatrix) {
        Matrix<Color> weightedImageMatrix = new Matrix<>(Color.class, this.radius);

        Color weightedValue;
        for(int i = 0; i < weightedImageMatrix.getMatrix().length; i++) {
            for(int j = 0; j < weightedImageMatrix.getMatrix()[i].length; j++) {
                if(imageMatrix.getValue(i, j) != null) {
                    weightedValue = new Color(
                        (int) ((double) imageMatrix.getValue(i, j).getRed() * gaussianMatrix.getValue(i, j)),
                        (int) ((double) imageMatrix.getValue(i, j).getGreen() * gaussianMatrix.getValue(i, j)),
                        (int) ((double) imageMatrix.getValue(i, j).getBlue() * gaussianMatrix.getValue(i, j))
                    );

                    weightedImageMatrix.setValue(i, j, weightedValue);
                } else {
                    weightedImageMatrix.setValue(i, j, null);
                }
            }
        }

        return weightedImageMatrix;
    }

    /*
     * Given the center points (i, j), construct a matrix from the image to blur.
     */
    private Matrix<Color> getImageMatrix(int i, int j) {
        Matrix<Color> imageMatrix = new Matrix<>(Color.class, radius);

        int matrixI = 0, matrixJ;
        for(int x = i - radius; x <= i + radius; x++) {
            matrixJ = 0;

            for(int y = j - radius; y <= j + radius; y++) {
                if(x > -1 && y > -1 && x < getOriginal().getWidth() && y < getOriginal().getHeight()) {
                    imageMatrix.setValue(matrixI, matrixJ, new Color(getOriginal().getRGB(x, y)));
                } else {
                    imageMatrix.setValue(matrixI, matrixJ, null);
                }

                matrixJ++;
            }

            matrixI++;
        }

        return imageMatrix;
    }

    private class Color {
        private int r, g, b;

        public Color(int r, int g, int b) {
            this.r = r;
            this.g = g;
            this.b = b;
        }

        public Color(int rgb) {
            this((rgb >> 16) & 0xff, (rgb >> 8) & 0xff, rgb & 0xff);
        }

        public int getRed() {
            return r;
        }

        public int getGreen() {
            return g;
        }

        public int getBlue() {
            return b;
        }

        public int getRGB() {
            return (r << 16) | (g << 8) | b;
        }

        @Override
        public String toString() {
            return "(" + r + "," + g + "," + b + ")";
        }
    }

    private class Matrix<T> {
        private T[][] matrix;

        public Matrix(Class<T> clazz, int radius) {
            int length = (2 * radius) + 1;
            matrix = (T[][]) Array.newInstance(clazz, length, length);
        }

        public T getValue(int i, int j) {
            return matrix[i][j];
        }

        public void setValue(int i, int j, T value) {
            matrix[i][j] = value;
        }

        public T[][] getMatrix() {
            return matrix;
        }
    }
}

Класс ImageFilter - это просто абстрактный класс с двумя экземплярами BufferedImage (один для исходного изображения и один для размытого изображения) и функцией displayImage просто отображает изображение в диалоговом окне сообщения.

Основной метод, использующий этот класс:

public static void main(String[] args) throws IOException {
    String filename = "res" + File.separator + "TajMahal.jpeg";

    GaussianBlur gaussianBlur = new GaussianBlur(filename, 2);
    gaussianBlur.applyFilter();
    gaussianBlur.displayImage();
}

А ниже приведены полученные изображения

Исходное изображение: Original Image

Размыто с радиусом 2: Blur Radius 2

Размыто с радиусом 7: Blur Radius 7

Почему размытие с радиусом 7 затемняет изображение? Чего-то не хватает в формуле или чего-то, что я пропустил?

...