c#实现高斯模糊
说说高斯模糊
高斯模糊的理论我这里就不太多费话了,百度下太多,都是抄来抄去。
主要用到二个函数“高斯函数”
一维形式为:
二维形式为:
X,Y对应的一维二维坐标,σ表示模糊半径(半径* 2 + 1) / 2)
根据这二个公式获取对应的权重。
先看二维
假设我们现在图片中的像素点位置为(0,0)
假设我们设置的模糊半径为1,那么对应的坐标为如下图
它是以(0,0)这个坐标为标记,向外扩展1个像素。
接下来就是计算各个坐标的权重值,我们采用二维的高斯函数来计算,计算的代码如下:
/// <summary> /// 获取权重 /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> private double GetWeighing(int x, int y) { double q = (this.BlurRadius * 2 + 1) / 2; return 1 / (2 * Math.PI * Math.Pow(q, 2)) * Math.Exp(-(x * x + y * y) / (2 * q * q)); }
this.BlurRadius 为我们设置的模糊半径
上图是我们计算的结果,这9个值的结果的总和为:0.779483679709388,该值不能大于1。这个时候我们要将上面的9个值 除以0.779483679709388,使他们的和为1.
除以0.779483679709388之后为:
假设这9个点上的RGB颜色值中的R值乘以上图矩阵中的值,如下图
计算之后的颜色值
求和为:112.14236039551
所以(0,0)坐标的RGB颜色值中的R为112.14236039551
然后我们获取这9个点的坐标RGB值,让后将RGB值分别乘以权重值,然和将这9个值相加得到最后的颜色值。
using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text; namespace NetShadow { /// <summary> /// 高斯模糊 /// </summary> public class GaussianBlur { /// <summary> /// 模糊半径 /// </summary> public int BlurRadius { get; private set; } private Bitmap SourceImage { get; set; } private List<double> BlurArray { get; set; } private int MaxWidth { get; set; } private int MaxHeight { get; set; } public GaussianBlur(int blurRadius) { BlurArray = new List<double>(); this.BlurRadius = blurRadius; this.SetBlurArray(); } /// <summary> /// 设置需要模糊的图片 /// </summary> /// <param name="img"></param> public void SetSourceImage(Image img) { this.SourceImage = (Bitmap)img; this.MaxWidth = this.SourceImage.Width - 1; this.MaxHeight = this.SourceImage.Height - 1; } /// <summary> /// 获取模糊之后的图片 /// </summary> /// <returns></returns> public Bitmap GetBlurImage() { if (this.SourceImage == null) return null; Bitmap newImage = new Bitmap(SourceImage.Width, SourceImage.Height); for (int y = 0; y < this.SourceImage.Height; y++) { for (int x = 0; x < this.SourceImage.Width; x++) { var nC = GetBlurColor(x, y); //return null; newImage.SetPixel(x, y, nC); } } return newImage; } /// <summary> /// 获取高斯模糊的颜色值 /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> private Color GetBlurColor(int x, int y) { double r = 0, g = 0 , b = 0; int index = 0; for (var t = y - this.BlurRadius; t <= y + this.BlurRadius; t++) { for (var l = x - this.BlurRadius; l <= x + this.BlurRadius; l++) { var color = GetDefautColor(l, t); var weighValue = BlurArray[index]; r += color.R * weighValue; g += color.G * weighValue; b += color.B * weighValue; index++; } } return Color.FromArgb((byte)r, (byte)g, (byte)b); } private Color GetDefautColor(int x, int y) { if (x < 0 && y < 0) return this.SourceImage.GetPixel(0, 0); else if (x < 0) return this.SourceImage.GetPixel(0, Math.Min(MaxHeight, y)); else if (y < 0) return this.SourceImage.GetPixel(Math.Min(MaxWidth, x), 0); else return this.SourceImage.GetPixel(Math.Min(MaxWidth, x), Math.Min(MaxHeight, y)); } private void SetBlurArray() { int blur = this.BlurRadius; double sum = 0; for (var y = blur; y >= blur * -1; y--) { for (var x = blur * -1; x <= blur; x++) { var d = GetWeighing(x, y); this.BlurArray.Add(d); sum += d; } } for (var i = 0; i < this.BlurArray.Count; i++) this.BlurArray[i] = this.BlurArray[i] / sum; //sum = 0; //foreach (var item in this.BlurArray) // sum += item; } /// <summary> /// 获取权重 /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns></returns> private double GetWeighing(int x, int y) { double q = (this.BlurRadius * 2 + 1) / 2; return 1 / (2 * Math.PI * Math.Pow(q, 2)) * Math.Exp(-(x * x + y * y) / (2 * q * q)); } } }
这种效率其实很地下,所以网上的解决办法是将二维高斯改为一维高斯来计算,也就是先横向模糊,然后再纵向模糊。
另外获取图片的RGB颜色值采用的是GetPixel 和SetPixel,这二个函数的效率是很低下的,大家可以到网上找下相关的解决办法。