前言:
先来看看下面这张图,我把一张图进行了二等份了,左边是经过高斯模糊过的,右边是原图。
图-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.卷积图像处理