Java: 高斯模糊的实现

Java: implementation of Gaussian Blur

我需要在 Java 中为 3x3、5x5 和 7x7 矩阵实现高斯模糊。如果我错了你能纠正我吗:

  1. 我有一个矩阵(M) 3x3(中间值是 M(0, 0)):

    1 2 1  
    2 4 2  
    1 2 1  
    
  2. 我从图像中取出一个像素 (P),对于每个最近的像素:

    s = M(-1, -1) * P(-1, -1) + M(-1, 0) * P(-1, 0) + ... + M(1, 1) * P(1, 1)  
    
  3. 然后除以矩阵的总值:

    P'(i, j) = s / M(-1, -1) + M(-1, 0) + ... + M(1, 1)
    

这就是我的程序所做的全部。我保留极端像素不变。

我的程序:

for(int i = 1; i < height - 1; i++){  
    for(int j = 1; j < width - 1; j++){  
        int sum = 0, l = 0;
        for(int m = -1; m <= 1; m++){  
            for(int n = -1; n <= 1; n++){  
                try{  
                    System.out.print(l + " ");  
                    sum += mask3[l++] * Byte.toUnsignedInt((byte) source[(i + m) * height + j + n]);  
                } catch(ArrayIndexOutOfBoundsException e){  
                    int ii = (i + m) * height, jj = j + n;  
                    System.out.println("Pixels[" + ii + "][" + jj + "]    " + i + ", " + j);  
                    System.exit(0);  
                }  
            }  
            System.out.println();  
        }  
        System.out.println();  
        output[i * width + j] = sum / maskSum[0];  
    }  
}

我像这样从 BufferedImage 得到 source

int[] source = image.getRGB(0, 0, width, height, null, 0, width);

所以对于这张图片:

结果是这样的:

你能描述一下吗,我的程序有什么问题?

首先,你计算源数组中索引的公式是错误的。图像数据逐行存储在阵列中。因此给定 xy 的索引计算如下:

index = x + y * width

此外,颜色通道存储在 int 的不同位中,不能简单地对整个 int 进行计算,因为这允许通道影响其他通道。

以下解决方案应该有效(即使它只是让边界处的像素透明):

public static BufferedImage blur(BufferedImage image, int[] filter, int filterWidth) {
    if (filter.length % filterWidth != 0) {
        throw new IllegalArgumentException("filter contains a incomplete row");
    }

    final int width = image.getWidth();
    final int height = image.getHeight();
    final int sum = IntStream.of(filter).sum();

    int[] input = image.getRGB(0, 0, width, height, null, 0, width);

    int[] output = new int[input.length];

    final int pixelIndexOffset = width - filterWidth;
    final int centerOffsetX = filterWidth / 2;
    final int centerOffsetY = filter.length / filterWidth / 2;

    // apply filter
    for (int h = height - filter.length / filterWidth + 1, w = width - filterWidth + 1, y = 0; y < h; y++) {
        for (int x = 0; x < w; x++) {
            int r = 0;
            int g = 0;
            int b = 0;
            for (int filterIndex = 0, pixelIndex = y * width + x;
                    filterIndex < filter.length;
                    pixelIndex += pixelIndexOffset) {
                for (int fx = 0; fx < filterWidth; fx++, pixelIndex++, filterIndex++) {
                    int col = input[pixelIndex];
                    int factor = filter[filterIndex];

                    // sum up color channels seperately
                    r += ((col >>> 16) & 0xFF) * factor;
                    g += ((col >>> 8) & 0xFF) * factor;
                    b += (col & 0xFF) * factor;
                }
            }
            r /= sum;
            g /= sum;
            b /= sum;
            // combine channels with full opacity
            output[x + centerOffsetX + (y + centerOffsetY) * width] = (r << 16) | (g << 8) | b | 0xFF000000;
        }
    }

    BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
    result.setRGB(0, 0, width, height, output, 0, width);
    return result;
}
int[] filter = {1, 2, 1, 2, 4, 2, 1, 2, 1};
int filterWidth = 3;
BufferedImage blurred = blur(img, filter, filterWidth);