图像高斯模糊算法的原理及实现

图像高斯模糊算法的原理及实现

如果经常使用Photoshop等修图软件,那么对高斯模糊滤镜肯定不会陌生,通过调整模糊半径,图像会变得模糊,半径越大越模糊,这篇文章将会讲解高斯模糊算法的原理以及其Java实现。

上图是Photoshop的高斯模糊滤镜。

什么是高斯模糊

高斯模糊(英语:Gaussian Blur),也叫高斯平滑,通常用及降低图片细节层次。

基本方法

说道图像模糊算法,实现原理也并不是很难懂,要设计一个程序来模糊处理一个图像的话,对于正在处理的每一个像素,取周围若干个像素的RGB值并且平均,然后这个平均值就是模糊处理过的像素,如果对图片中的所有像素都这么处理的话,处理完成的图片就会变得模糊。但是这样做的效果并不是,如果图片颜色变化频繁而且单位面积里面颜色反差很大,并且模糊半径很大的话,那么结果就是:模糊范围的最外层的像素和中心像素周围的像素的权重是一样的,这样处理的图片可能过渡并不是很平滑。

所以,需要有一个算法,来为这些在模糊范围中的像素来分别计算权重,这样的话越在模糊半径外围的像素权重越低,造成的影响就越小,越在内侧的像素得到的权重最高,因为内侧像素更加重要,他们的颜色应该与我们要处理的中心像素更接近,更密切。这个时候就需要用到高斯模糊算法了。

正态分布类型的权重

看到上面一段,思考一下,显然正态分布是一个理想的权重分配方法。

正态分布一种概率分布,也称“常态分布”。正态分布具有两个参数μ和σ^2的连续型随机变量的分布,第一参数μ是服从正态分布的随机变量的均值,第二个参数σ^2是此随机变量的方差,所以正态分布记作N(μ,σ^2)。服从正态分布的随机变量的概率规律为取与μ邻近的值的概率大,而取离μ越远的值的概率越小;σ越小,分布越集中在μ附近,σ越大,分布越分散。

上面就是一个一维上高斯分布的图,μ是中心点,离中心点越远的位置权重越小,在3σ的时候只有0.1%的权重。

可是,我们需要用到的高斯分布应该是二维的,因为我们选择一个中心像素,之后来平均那个像素周边的像素来得到模糊像素的值。而不仅仅是左边的和右边的像素。所以需要用到二维的正态分布。

正态分布的密度函数叫做”高斯函数”(Gaussian function)他的公式是:

一般情况下μ就是中心点,所以是0;

通过一维公式,可以推导出二维公式。

可能看了上面的公式并不是很懂,我来解释一下,在上面的公式中σ就是我们的模糊半径,而x和y就是我们周边像素对于中心像素的相对坐标。

好了有了上面的知识,可以开始编写程序了。

首先我们要设计一个类,这个类叫做BlurEffect,初始化的时候,用户传入一张图片(Java中是BufferredImage)和模糊半径,这个类将会对图片做出模糊处理。

public class BlurEffect {
 
     private int blurRadius = 1 ;
     private BufferedImage image;
     private double [][] weightArr;
 
     public BlurEffect( int blurRadius,BufferedImage image){
         this .blurRadius = blurRadius;
         this .image = image;
         weightArr = new double [blurRadius* 2 + 1 ][blurRadius* 2 + 1 ];
     }

权重矩阵的计算

接下来就要构建权重矩阵了,举个例子,如果模糊半径是2的话,就要构建一个 (2*2+1)长宽的矩阵。

中心点的坐标为(0,0)其他点依次类推,之后就要写一个函数来根据模糊半径,x坐标,y坐标来计算权重。

private double getWeight( int x, int y){
 
     double sigma = (blurRadius* 2 + 1 )/ 2 ;
     double weight = ( 1 /( 2 *Math.PI*sigma*sigma))*Math.pow(Math.E,((-(x*x+y*y))/(( 2 *sigma)*( 2 *sigma))));
 
     return weight;
}

有了这个函数,再写一个函数来计算权重矩阵

private void calculateWeightMatrix(){
     for ( int i= 0 ;i < blurRadius* 2 + 1 ;i++){
         for ( int j= 0 ;j < blurRadius* 2 + 1 ;j++){
             weightArr[i][j] = getWeight(j-blurRadius,blurRadius-i);
         }
     }
}

之后通过调用这个函数就可以得到权重矩阵。

可是如果把这些权重加起来却只有 0.783118,总和不是1,要计算加权平均值的话,必须要让权重之和等于1.因此需要把上面各值在除以0.783118得到一个权重之和为1的矩阵。

通过编写以下函数来得到最终的权重矩阵:之后需要将这两个函数在构造函数中调用,因为只要模糊半径确定,那么权重矩阵将会是确定的。处理所有像素的权重矩阵都是一样的。

private void getFinalWeightMatrix(){
 
     int length = blurRadius* 2 + 1 ;
     double weightSum = 0 ;
 
     for ( int i = 0 ;i < length;i++){
         for ( int j= 0 ; j < length; j++ ){
             weightSum+=weightArr[i][j];
         }
     }
 
 
     for ( int i = 0 ;i < length;i++){
         for ( int j= 0 ; j < length; j++ ){
             weightArr[i][j] = weightArr[i][j]/weightSum;
         }
     }
 
}

之后我需要分别编写函数来取得一个像素的R,G,B值,并且还需要一个函数来生成色值矩阵,色值矩阵储存中心像素和周边像素的色值,一共有三个这样的矩阵,分别储存R,G,B值。

 
           private 
            double 
            getR( 
           int 
            x, 
           int 
            y){ 
          
     int rgb = image.getRGB(x, y);
     int r = (rgb & 0xff0000 ) >> 16 ;
     return r;
}
 
private double getG( int x, int y){
     int rgb = image.getRGB(x, y);
     int g = (rgb & 0xff00 ) >> 8 ;
     return g;
}
 
private double getB( int x, int y){
     int rgb = image.getRGB(x, y);
     int b = (rgb & 0xff );
     return b;
}
 
private double [][] getColorMatrix( int x, int y, int whichColor){
 
     int startX = x-blurRadius;
     int startY = y-blurRadius;
     int counter = 0 ;
 
     int length = blurRadius* 2 + 1 ;
     double [][] arr = new double [length][length];
 
     for ( int i=startX ; i
         for ( int j = startY; j < startY+length; j++){
             if (whichColor == 1 ){
                 arr[counter%length][counter/length] = getR(i,j);
             } else if (whichColor == 2 ){
                 arr[counter%length][counter/length] = getG(i,j);
             } else if (whichColor == 3 ){
                 arr[counter%length][counter/length] = getB(i,j);
             }
             counter++;
         }
     }
 
     return arr;
}

现在我们可以得到权重矩阵,可以得到RGB色值矩阵,再写一个函数来计算所有矩阵的平均值,把权重矩阵和色值矩阵相乘就可以得到权重处理过后的色值,之后相加,得到最终中心像素的色值。

double getBlurColor( int x, int y, int whichColor){
 
     double blurGray = 0 ;
     double [][] colorMat = getColorMatrix(x,y,whichColor);
 
     int length = blurRadius* 2 + 1 ;
     for ( int i = 0 ;i <; length;i++){
         for ( int j= 0 ; j < length; j++ ){
             blurGray += weightArr[i][j]*colorMat[i][j];
         }
     }
 
     return blurGray;
}

好了,现在万事具备了,可以调用上面的函数来计算指定像素模糊之后的R,G,B值了,可以生成新的图片文件了。

public BufferedImage getBluredImg(){
 
     BufferedImage bi = new BufferedImage(image.getWidth()-blurRadius* 2 , image.getHeight()-blurRadius* 2 , BufferedImage.TYPE_INT_RGB);
     for ( int x = 0 ; x < bi.getWidth(); x++) {
         for ( int y = 0 ; y < bi.getHeight(); y++) {
             int r = ( int )getBlurColor(blurRadius+x,blurRadius+y, 1 );
             int g = ( int )getBlurColor(blurRadius+x,blurRadius+y, 2 );
             int b = ( int )getBlurColor(blurRadius+x,blurRadius+y, 3 );
             Color color = new Color(r,g,b);
             bi.setRGB(x, y, color.getRGB());
         }
     }
 
     File file = new File( "C:\\Users\\Mike\\Desktop\\out.jpg" );
     try {
         ImageIO.write(bi, "jpg" ,file);
     } catch (IOException e) {
         e.printStackTrace();
     }
 
     return null ;
}

最后经过简单的调用,就可以生成一个经过高斯模糊模糊的图片了。

BufferedImage img = FileUtil.loadImg("1.jpg");
new BlurEffect(7,img).getFinalWeightMatrix();


大家可以在我的GitHub上下载项目源码

https://github.com/Yigang0622/GaussianBlur

MikeTech现已登陆iPhone和Android

iPhone版下载
Android版下载

你可能感兴趣的:(图像高斯模糊算法的原理及实现)