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