高斯模糊是被广泛使用的图形算法之一,在实现高斯模糊之前,先要了解正态分布
正态分布
一维的正态分布为
f(x)=2πσ1e−2σ2(x−μ)2
直接让f(x)和f(y)相乘,就得到了二维的正态分布
f(x,y)=2πσ21e−2σ2(x2+y2)2
此处直接令μ=0,将会在下面解释.
权值矩阵
设有一个(2n+1)阶矩阵M,且有
∑Mi,j=1
,我们称这个矩阵为权值矩阵,称Mi,j为(i,j)点处的权.其中n是高斯模糊的半径.
离散卷积
离散卷积是卷积对离散量的特殊形式,假设现有原图矩阵A,权值矩阵B,则点(x,y)处的离散卷积为
G(x,y)=i∈[x±n]j∈[y±n]∑Ai,j⋅Bi,j
在更严格的定义中,A(i,j)应该与B(u−i,v−j)相乘,但是针对本文的高斯模糊而言,其效果是一样的,且上面给出的公式更为简洁.
现在举个例子,有一张尺寸为3*3的图片S,将其颜色转化成矩阵A,为
⎣⎢⎡147258369⎦⎥⎤
有权值矩阵B为
⎣⎢⎡0−10−15−10−10⎦⎥⎤
将A(i,j)与B(i,j)相乘,将结果相加
(−1)×2+(−1)×4+(−1)×6+(−1)×8+5×5=5
则以上两个矩阵的离散卷积结果为5,这就是矩阵A经过处理后得到的新矩阵M(2,2)的值.
在高斯模糊中,设模糊半径为n,则定义一个维数为2n+1的权值矩阵G,且G(i,j)=f(i−n−1,j−n−1),类似于将一个直角坐标系放在了G的中点处,这就是μ=0的原因.此处的f是二维正态分布函数.然后求和∑Gi,j,将矩阵的每个数除以这个和.求和的步骤是防止图片过亮或过暗.
将得到的矩阵G代替B计算,其结果就是高斯模糊的结果
优化
上述方法的效率较低,在介绍正态分布时,二维的正态分布函数是两个一维函数相乘得到的,这两个一维函数分别是f(x)和f(y),f(x)代表水平方向,f(y)代表垂直方向.对于一个n维权值矩阵,用它来处理a×b尺寸的图片,如果用二维正态分布函数来计算,总共需要计算a⋅b⋅n⋅n=abn2次,及其繁琐.这时我们可以使用一维的正态分布函数,得出一个“权值列向量”,这个向量的作用类似权值矩阵,用这个列向量把图片横向处理,相当于f(x),再用它把图片纵向处理,相当于f(y),此时图片经过两次处理,相当于f(x)⋅f(y),也可以达到二维正态分布的效果,而计算量仅仅是a⋅b⋅n+a⋅b⋅n=2abn,下降了一个数量级.该方法不详细介绍,将在代码中展示.
代码实现
GaussianBlur
类,算法的核心部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
| public final class GaussianBlur { private static final int precision = 10000; private static final double E = 2.718281828459045; private static final double PI = 3.141592653589793;
public static int[][][] GaussianBlur(int[][][] picture,int radius){ int i, j, x, R, G, B, proportion, subscript; int[] matrix = LinearNormalDistribution(radius,1.5); int width = picture[0].length, height = picture[0][0].length; int[][][] color_1 = new int[3][width][height]; int[][][] color_2 = new int[3][width][height]; for (i = 0; i < width; i++) { for (j = 0; j < height; j++) { R = G = B = 0; for (x = j - radius; x <= j + radius; x++) { proportion = matrix[x + radius - j]; subscript = (x >= 0 && x < height) ? x : 2 * j - x; R += picture[0][i][subscript] * proportion; G += picture[1][i][subscript] * proportion; B += picture[2][i][subscript] * proportion; } color_2[0][i][j] = R / precision; color_2[1][i][j] = G / precision; color_2[2][i][j] = B / precision; } } for (i = 0; i < height; i++) { for (j = 0; j < width; j++) { R = G = B = 0; for (x = j - radius; x <= j + radius; x++) { proportion = matrix[x + radius - j]; subscript = (x >= 0 && x < width) ? x : 2 * j - x; R += color_2[0][subscript][i] * proportion; G += color_2[1][subscript][i] * proportion; B += color_2[2][subscript][i] * proportion; } color_1[0][j][i] = R / precision; color_1[1][j][i] = G / precision; color_1[2][j][i] = B / precision; } } return color_1; }
public static int[][][] SlowGaussianBlur(int[][][] picture,int radius){ int[][] matrix = NormalDistribution(radius,1.5); int i, j, x, y, R, G, B, proportion, left, right, width = picture[0].length, height = picture[0][0].length; int[][][] color = new int[3][width][height]; for (i = 0; i < width; i++) { for (j = 0; j < height; j++) { R = G = B = 0; for (x = i - radius; x <= i + radius; x++) { for (y = j - radius; y <= j + radius; y++) { proportion = matrix[x + radius - i][y + radius - j]; left = (x >= 0 && x < width) ? x : 2 * i - x; right = (y >= 0 && y < height) ? y : 2 * j - y; R += picture[0][left][right] * proportion; G += picture[1][left][right] * proportion; B += picture[2][left][right] * proportion; } } color[0][i][j] = R / precision; color[1][i][j] = G / precision; color[2][i][j] = B / precision; } } return color; }
private static int[] LinearNormalDistribution(int radius,double SIGMA){ int[] matrix = new int[2 * radius + 1]; int sum, i; sum = matrix[radius] = (int) (precision / (2 * PI * SIGMA * SIGMA)); for (i = 1; i <= radius; i++) { matrix[radius-i] = matrix[radius+i] = (int) ((Math.pow(E, -i * i / (2 * SIGMA * SIGMA)) / (2 * PI * SIGMA * SIGMA)) * precision); sum += matrix[radius+i] * 2; } for (i = 0; i < 2 * radius + 1; i++) { matrix[i] = matrix[i] * precision / sum; } return matrix; }
private static int[][] NormalDistribution(int radius, double SIGMA) { int sum = 0, i, j; int[][] matrix = new int[2 * radius + 1][2 * radius + 1]; for (i = 0; i <= radius; i++) { for (j = 0; j <= radius; j++) { matrix[radius-i][radius-j] = matrix[radius-i][radius+j] = matrix[radius+i][radius-j] = matrix[radius+i][radius+j] = (int) (Math.pow(E, -(i * i + j * j) / (2 * SIGMA * SIGMA)) / (2 * PI * SIGMA * SIGMA) * precision); sum += 4 * matrix[radius+i][radius+j]; } } for (i = 0; i <= 2 * radius; i++) { for (j = 0; j <= 2 * radius; j++) { matrix[i][j] = matrix[i][j] * precision / sum; } } return matrix; } }
|
Filter
类,通过调用GaussianBlur类来处理图像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; public final class Filter { public static BufferedImage GaussianBlur(String path){ int pixel; try { BufferedImage image = ImageIO.read(new File(path)); int width = image.getWidth(),height = image.getHeight(); int[][][] picture = new int[3][width][height]; for(int i=image.getMinX();i<width;i++){ for(int j=image.getMinY();j<height;j++){ pixel = image.getRGB(i,j); picture[0][i][j] = (pixel & 0xff0000) >> 16; picture[1][i][j] = (pixel & 0xff00) >> 8; picture[2][i][j] = (pixel & 0xff); } } picture = GaussianBlur.GaussianBlur(picture,100); for(int i=image.getMinX();i<width;i++){ for(int j=image.getMinY();j<height;j++){ pixel = ((picture[0][i][j] & 0xff) << 16) + ((picture[1][i][j] & 0xff) << 8) + (picture[2][i][j] & 0xff); image.setRGB(i,j,pixel); } } return image; } catch (IOException e) { e.printStackTrace(); return null; } } }
|
Main
,打开1.jpg并高斯模糊,保存为2.jpg
1 2 3 4 5 6 7 8 9 10 11
| import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; public class Main { public static void main(String[] args) throws Exception{ BufferedImage image = Filter.GaussianBlur("D://1.jpg"); ImageIO.write(image,"jpg",new File("D://2.jpg")); } }
|
效果
原图
高斯模糊之后的图
(半径20,SIGMA1.5)