讲到图像处理的滤波器,就不得不先提图像中的低频分量和高频分量:
图像的频率代表了,图像颜色变化的剧烈程度。
低频分量:一幅图中,颜色变化缓慢的部分就叫做低频部分。通常低频是描述图像的主要部分,就像人的脸。
高频分量:一幅图中,颜色变化剧烈的部分就叫做高频部分。通常高频是描述图像的边缘、细节或者是噪声,就像人脸的轮廓,还有脸上的痘。
均值滤波器(Averaging Filter): 均值滤波器也是低通滤波器,主要是对图像进行模糊处理和降噪(noise reduction)处理,还起到光滑轮廓(模糊)的作用。原理: 取遮罩(mask),可以是3x3、5x5、7x7...自定义方形,然后对遮罩中的pixel值进行求和平均,遮罩最中间那一个pixel的pixel值用平均值替代。
中值滤波器(Median Filter): 跟均值滤波器相似,但是在降低噪声(noise reduction)方面优于均值滤波器,特别是对斑点噪声(speckle noise)和椒盐噪声(salt-and-pepper noise)尤其有用,同时还有保留边缘的特性也不会使轮廓模糊。原理: 取遮罩(mask),这儿不同的是,中值滤波器通常采用“十字”而不用“方形”框。可以是3x3、5x5、7x7...自定义十字,然后对遮罩中的pixel值按升序排序,遮罩最中间那一个pixel的pixel值用中值替代。
Outlier Filter:类似于均值滤波器。原理:取3x3的遮罩,然后将除最中间的Pixel外的8个Pixel取平均值,均值与最中间的pixel值差值若大于某一个阀值(threshold),那么就用平均值替代最中间值的pixel。
先看下图:假设取范围为5(单位Pixel)的遮罩,如红色框框,一次取5个Pixel每次移动1Pixel,下面分别是均值和中值滤波器对原图的过滤结果。
我们可以看出:
C#代码实现:
//Outlier滤波器
public Bitmap OutlierFilter(Bitmap oriImage)
{
int threshold = 30;
int width = oriImage.Width;
int height = oriImage.Height;
BitmapData oriImageData = oriImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
//创建新的bitmap存放滤波后的图
Bitmap newImage = new Bitmap(width, height, PixelFormat.Format24bppRgb);
BitmapData newImageData = newImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
IntPtr intPtrN = newImageData.Scan0;
IntPtr intPtr = oriImageData.Scan0;
int size = oriImageData.Stride * height;
byte[] oriBytes = new byte[size];
byte[] newBytes = new byte[size];
Marshal.Copy(intPtr, oriBytes, 0, size);
//先复制一份一模一样的数据到滤波数组中
Marshal.Copy(intPtr, newBytes, 0, size);
int[] mask = new int[9];
int k = 3;
for (int y = 0; y < height-2; y++)
{
for(int x = 0; x < width-2; x++)
{
//因为是灰阶图RGB相同,bitmapdata数组中就以每3个pixel为间隔。
mask[0] = oriBytes[y * oriImageData.Stride + x * k];
mask[1] = oriBytes[y * oriImageData.Stride + x * k+3];
mask[2] = oriBytes[y * oriImageData.Stride + x * k+6];
mask[3] = oriBytes[(y + 1) * oriImageData.Stride + x * k];
mask[4] = oriBytes[(y + 1) * oriImageData.Stride + x * k+3];
mask[5] = oriBytes[(y + 1) * oriImageData.Stride + x * k+6];
mask[6] = oriBytes[(y + 2) * oriImageData.Stride + x * k];
mask[7] = oriBytes[(y + 2) * oriImageData.Stride + x * k+3];
mask[8] = oriBytes[(y + 2) * oriImageData.Stride + x * k+6];
int mean = (mask[0] + mask[1] + mask[2] + mask[3] + mask[5] + mask[6] + mask[7] + mask[8]) / 8;
//绝对值很重要,不要忘
if(Math.Abs(mask[4]-mean) > threshold)
{
//newImageData.Stride 是等于 oriImageData.Stride 的
newBytes[(y + 1) * oriImageData.Stride + x * k + 3] = (byte)mean;
newBytes[(y + 1) * oriImageData.Stride + x * k + 4] = (byte)mean;
newBytes[(y + 1) * oriImageData.Stride + x * k + 5] = (byte)mean;
}
}
}
Marshal.Copy(newBytes,0,intPtrN,size);
oriImage.UnlockBits(oriImageData);
newImage.UnlockBits(newImageData);
return newImage;
}
//均值滤波器
public Bitmap AverageFilter(Bitmap oriImage)
{
int width = oriImage.Width;
int height = oriImage.Height;
BitmapData oriImageData = oriImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
//创建新的bitmap存放滤波后的图
Bitmap newImage = new Bitmap(width, height, PixelFormat.Format24bppRgb);
BitmapData newImageData = newImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
IntPtr intPtrN = newImageData.Scan0;
IntPtr intPtr = oriImageData.Scan0;
int size = oriImageData.Stride * height;
byte[] oriBytes = new byte[size];
byte[] newBytes = new byte[size];
Marshal.Copy(intPtr, oriBytes, 0, size);
//先复制一份原图的数据到滤波数组中
Marshal.Copy(intPtr, newBytes, 0, size);
int[] mask = new int[9];
int k = 3;
for (int y = 0; y < height - 2; y++)
{
for (int x = 0; x < width - 2; x++)
{
mask[0] = oriBytes[y * oriImageData.Stride + x * k];
mask[1] = oriBytes[y * oriImageData.Stride + x * k + 3];
mask[2] = oriBytes[y * oriImageData.Stride + x * k + 6];
mask[3] = oriBytes[(y + 1) * oriImageData.Stride + x * k];
mask[4] = oriBytes[(y + 1) * oriImageData.Stride + x * k + 3];
mask[5] = oriBytes[(y + 1) * oriImageData.Stride + x * k + 6];
mask[6] = oriBytes[(y + 2) * oriImageData.Stride + x * k];
mask[7] = oriBytes[(y + 2) * oriImageData.Stride + x * k + 3];
mask[8] = oriBytes[(y + 2) * oriImageData.Stride + x * k + 6];
int mean = (mask[0] + mask[1] + mask[2] + mask[3] + mask[4] + mask[5] + mask[6] + mask[7] + mask[8]) / 9;
//newImageData.Stride 是等于 oriImageData.Stride 的
newBytes[(y + 1) * oriImageData.Stride + x * k + 3] = (byte)mean;
newBytes[(y + 1) * oriImageData.Stride + x * k + 4] = (byte)mean;
newBytes[(y + 1) * oriImageData.Stride + x * k + 5] = (byte)mean;
}
}
Marshal.Copy(newBytes, 0, intPtrN, size);
oriImage.UnlockBits(oriImageData);
newImage.UnlockBits(newImageData);
return newImage;
}
//中值滤波器
public Bitmap MedianFilter(Bitmap oriImage)
{
int width = oriImage.Width;
int height = oriImage.Height;
BitmapData oriImageData = oriImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
//创建新的bitmap存放滤波后的图
Bitmap newImage = new Bitmap(width, height, PixelFormat.Format24bppRgb);
BitmapData newImageData = newImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
IntPtr intPtrN = newImageData.Scan0;
IntPtr intPtr = oriImageData.Scan0;
int size = oriImageData.Stride * height;
byte[] oriBytes = new byte[size];
byte[] newBytes = new byte[size];
Marshal.Copy(intPtr, oriBytes, 0, size);
//先复制一份原图的数据到滤波数组中
Marshal.Copy(intPtr, newBytes, 0, size);
int[] mask = new int[9];
int k = 3;
for (int y = 0; y < height - 2; y++)
{
for (int x = 0; x < width - 2; x++)
{
mask[0] = oriBytes[y * oriImageData.Stride + x * k];
mask[1] = oriBytes[y * oriImageData.Stride + x * k + 3];
mask[2] = oriBytes[y * oriImageData.Stride + x * k + 6];
mask[3] = oriBytes[(y + 1) * oriImageData.Stride + x * k];
mask[4] = oriBytes[(y + 1) * oriImageData.Stride + x * k + 3];
mask[5] = oriBytes[(y + 1) * oriImageData.Stride + x * k + 6];
mask[6] = oriBytes[(y + 2) * oriImageData.Stride + x * k];
mask[7] = oriBytes[(y + 2) * oriImageData.Stride + x * k + 3];
mask[8] = oriBytes[(y + 2) * oriImageData.Stride + x * k + 6];
Array.Sort(mask);
int median = mask[4];
//newImageData.Stride 是等于 oriImageData.Stride 的
newBytes[(y + 1) * oriImageData.Stride + x * k + 3] = (byte)median;
newBytes[(y + 1) * oriImageData.Stride + x * k + 4] = (byte)median;
newBytes[(y + 1) * oriImageData.Stride + x * k + 5] = (byte)median;
}
}
Marshal.Copy(newBytes, 0, intPtrN, size);
oriImage.UnlockBits(oriImageData);
newImage.UnlockBits(newImageData);
return newImage;
}
仅为个人理解,如有不足,请指教。 https://blog.csdn.net/weixin_35811044