• 基于正态分布的图片高斯模糊算法


    前言:

      先来看看下面这张图,我把一张图进行了二等份了,左边是经过高斯模糊过的,右边是原图。

      

    图-1 高斯模糊效果对比图


    概述:

      高斯模糊也叫做高斯平滑,是一种图像平滑处理的技术。高斯模糊算法的原理是选取一个中心点,以及一个半径周围的所有点,再计算这些像素点的平均值,这个值就是目前这个中心点的值了。这样实现的效果是可以降低图像中的噪音干扰,以达到忽略图像中的细节的目的。


    本文链接:http://blog.csdn.net/lemon_tree12138/article/details/50425793 -- Coding-Naga
                                                                     --转载请注明出处

    原理说明:

      上面说到高斯模糊是计算某些像素点的平均值,那么究竟是什么样的呢?看图-2。


    图-2 像素点均值计算

      现在我们假设我们就是按照上面的图形进行选取一些像素点的,可是我们的实际图像的像素可不止这些。所以,这里就涉及到另一个知识点了:卷积。不要被卷积这个字面上的词语所惊吓,如果你觉得书面上卷积晦涩难懂,那么你可以看看下面这幅图,它大致描绘了在本文中使用到的卷积基础。

      从下面的图解中,我们可以看到,就像是有一块固定大小的区域在沿着横向或是纵向滑动一样。是的,这里我们是以亮绿色为中心点,包含一个半径为1的周围点进行向右和向下的平移。这个很像在计算机网络中学到的滑动窗口一样。


    图-3 卷积概念图解(部分)

    逻辑实现:

    1.朴素的高斯模糊算法

      如上面的图-2和图-3,我们知道模糊一张图片的做是可以卷积计算每个点的平均值。那么现在我们就选取以9个点为一个单位模块进行卷积计算,再求其平均值。效果如下图-4.


    图-4 朴素高斯模糊效果对比图

      从效果图中我们的确是可以看出有一些模糊的效果,不过感觉上像是图片进行了一定像素上的滑移。而且,图像很暗。结论:算法很糟糕。

    2.基于正态分布的高斯模糊算法

      上面朴素的做法是让中心点和它周围的点具有相同数值的权重。这样是不合理的,为什么?

      我们知道图片是连续的,距离越近的像素点,数值应该是更相近的。那么,朴素高斯模糊中简单平均的做法肯定就不合理了。

      所以这里我们是使用正态分布来分配权重。正态分布的分布图如下图-5所示:


    图-5 一维正态分布图

      这幅图相信大家都并不陌生,高中时的课本上就有的。不过因为我们的图片是一个二维的图像,那么单纯的一维还是解决不了问题,下面看看一下二维的高斯分布图吧。


    图-6 二维高斯分布图

      图-6中的说明之所以修改成了高斯分布图,是因为高斯模糊中使用的正态分布,跟我们在高中时期学习的正态分布公式有一些不一样。

      一维高斯函数:


      二维高斯函数:


      二维高斯函数的实现如下:

    /**
         * 二维高斯函数
         * 
         * @param n
         *      二维高斯的范围[-n, n]
         * @param σ
         *      标准方差
         * @return
         *      范围[-n, n]之内的高斯函数值
         */
        public static float[][] getTwoDimenGaussianFunction(int n, float σ) {        
            int size = 2 * n + 1;
            float σ22 = 2 * σ * σ;
            float σ22PI = (float)Math.PI * σ22;
            float[][] kernalData = new float[size][size];
            int row = 0;
            for(int i = -n; i <= n; i++) {
                int column = 0;
                for(int j = -n; j <= n; j++) {
                    float xDistance = i * i;
                    float yDistance = j * j;
                    kernalData[row][column] = (float)Math.exp(-(xDistance + yDistance) / σ22) / σ22PI;
                    column++;
                }
                row++;
            }
            return kernalData;
        }
      根据以上内容我们可以编写以下高斯模糊核心算法的Java代码:
    public class GaussianBlur implements ImageInterface {
        
        private int radius;
        private int round;
        
        // 高斯函数的权重矩阵
        private float[][] normal_distribution = null;
        
        public GaussianBlur(int _round, int _radius) {
            ... ...
            initEvent(radius, 1.5f);
        }
        
        public static void main(String[] args) {
            GaussianBlur blur = new GaussianBlur(5, 1);
            try {
                blur.gaussianBlur("F:\Wall Paper\9.jpg", "F:\Wall Paper\9-gb.jpg");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
        /**
         * 基于正态分布的图片高斯模糊
         */
        public void gaussianBlur(String sourcePath, String targetPath) throws IOException {
            gaussianBlur(sourcePath, targetPath, round, radius);
        }
        
        /**
         * 基于正态分布的图片高斯模糊
         */
        public void gaussianBlur(String sourcePath, String targetPath, int round, int radius) throws IOException {
            BufferedImage bufferedImage = ImageIO.read(new File(sourcePath));
            int height = bufferedImage.getHeight();
            int width = bufferedImage.getWidth();
            
            int matrixLength = 2 * radius + 1;
            int[][] matrix = new int[matrixLength][matrixLength];
            int[] values = new int[matrixLength * matrixLength];
            
            for (int r = 0; r < round; r++) {
                for (int i = 0; i < width / 2; i++) {
                    for (int j = 0; j < height; j++) {
                        readPixel(bufferedImage, i, j, values);
                        fillMatrix(matrix, values);
                        bufferedImage.setRGB(i, j, avgMatrix(matrix));
                    }
                }
            }
            
            ImageIO.write(bufferedImage, "jpg", new File(targetPath));
        }
        
        /*
         * 初始化任务
         */
        private void initEvent(int n, float σ) {
            normal_distribution = MathUtils.getTwoDimenGaussianSumOne(n, σ);
        }
        
        /*
         * 读取图片上的像素点
         */
        private void readPixel(BufferedImage img, int x, int y, int[] pixels) {
            int radius = (int) ((Math.sqrt(pixels.length) - 1) / 2);
            int raw = 2 * radius + 1;
            int clo = 2 * radius + 1;
            
            int xStart = x - radius;
            int yStart = y - radius;
            int current = 0;
            for (int i = xStart; i < clo + xStart; i++) {
                for (int j = yStart; j < raw + yStart; j++) {
                    int tx = i;
                    // 边界处理
                    if (tx < 0) {
                        tx = -tx;
                    } else if (tx >= img.getWidth()) {
                        tx = x;
                    }
    
                    int ty = j;
                    // 边界处理
                    if (ty < 0) {
                        ty = -ty;
                    } else if (ty >= img.getHeight()) {
                        ty = y;
                    }
                    pixels[current++] = img.getRGB(tx, ty);
                }
            }
        }
    
        /*
         * 将读取的像素RGB值保存到二维数组中
         */
        private void fillMatrix(int[][] matrix, int... values) {
            int filled = 0;
            for (int i = 0; i < matrix.length; i++) {
                int[] x = matrix[i];
                for (int j = 0; j < x.length; j++) {
                    x[j] = values[filled++];
                }
            }
        }
    
        /*
         * 计算平均值重新写入图片
         */
        private int avgMatrix(int[][] matrix) {
            int red = 0;
            int green = 0;
            int blue = 0;
            for (int i = 0; i < matrix.length; i++) {
                for (int j = 0; j < matrix[i].length; j++) {
                    Color color = new Color(matrix[i][j]);
                    red += (normal_distribution[i][j] * color.getRed());
                    green += (normal_distribution[i][j] * color.getGreen());
                    blue += (normal_distribution[i][j] * color.getBlue());
                }
            }
            return new Color(red, green, blue).getRGB();
        }
    }

    效果图:


    图-7 一轮高斯模糊


    图-8 三轮高斯模糊


    图-9 五轮高斯模糊


    图-10 十轮高斯模糊

    Ref:

    高斯模糊的算法:http://www.ruanyifeng.com/blog/2012/11/gaussian_blur.html

    图像处理之高斯模糊:http://blog.csdn.net/jia20003/article/details/7234741


    注:本文部分图片也是来源于上面的两个博客,在此特作说明。


    关联知识点:

    1.正态分布

    2.卷积图像处理


  • 相关阅读:
    WPF 插件开发(.NET Framework 3.5 System.Addin)
    如何:创建返回 UI 的外接程序
    WPF分析工具
    可视化的工具
    .NET Core入门程序
    我是如何同时拿到阿里和腾讯offer的 【转载】
    我的求职之路:9个offer,12家公司,35场面试,最终谷歌【转载】
    Mysql 配置文件和目录的合理分配
    修改socket文件, MySQL启动报错
    confluence + 禅道安装教程
  • 原文地址:https://www.cnblogs.com/fengju/p/6336021.html
Copyright © 2020-2023  润新知