图像处理(Image Processing) ---------- 图像滤波器 (上) (C#实现)

讲到图像处理的滤波器,就不得不先提图像中的低频分量和高频分量:

图像的频率代表了,图像颜色变化的剧烈程度。

低频分量:一幅图中,颜色变化缓慢的部分就叫做低频部分。通常低频是描述图像的主要部分,就像人的脸。

高频分量:一幅图中,颜色变化剧烈的部分就叫做高频部分。通常高频是描述图像的边缘、细节或者是噪声,就像人脸的轮廓,还有脸上的痘。

 

平滑空间滤波器:

均值滤波器(Averaging Filter): 均值滤波器也是低通滤波器,主要是对图像进行模糊处理和降噪(noise reduction)处理,还起到光滑轮廓(模糊)的作用。原理: 取遮罩(mask),可以是3x3、5x5、7x7...自定义方形,然后对遮罩中的pixel值进行求和平均,遮罩最中间那一个pixel的pixel值用平均值替代。

          均值滤波器mask示意图:   图像处理(Image Processing) ---------- 图像滤波器 (上) (C#实现)_第1张图片         图像处理(Image Processing) ---------- 图像滤波器 (上) (C#实现)_第2张图片         图像处理(Image Processing) ---------- 图像滤波器 (上) (C#实现)_第3张图片

 

中值滤波器(Median Filter): 跟均值滤波器相似,但是在降低噪声(noise reduction)方面优于均值滤波器,特别是对斑点噪声(speckle noise)和椒盐噪声(salt-and-pepper noise)尤其有用,同时还有保留边缘的特性也不会使轮廓模糊。原理: 取遮罩(mask),这儿不同的是,中值滤波器通常采用“十字”而不用“方形”框。可以是3x3、5x5、7x7...自定义十字,然后对遮罩中的pixel值按升序排序,遮罩最中间那一个pixel的pixel值用中值替代。

中值滤波器mask示意图:图像处理(Image Processing) ---------- 图像滤波器 (上) (C#实现)_第4张图片                                      图像处理(Image Processing) ---------- 图像滤波器 (上) (C#实现)_第5张图片

 

 

Outlier Filter:类似于均值滤波器。原理:取3x3的遮罩,然后将除最中间的Pixel外的8个Pixel取平均值,均值与最中间的pixel值差值若大于某一个阀值(threshold),那么就用平均值替代最中间值的pixel。

                                             Outlier Filter mask 示意图: 图像处理(Image Processing) ---------- 图像滤波器 (上) (C#实现)_第6张图片

 

均值滤波器 vs 中值滤波器:

先看下图:假设取范围为5(单位Pixel)的遮罩,如红色框框,一次取5个Pixel每次移动1Pixel,下面分别是均值和中值滤波器对原图的过滤结果。

图像处理(Image Processing) ---------- 图像滤波器 (上) (C#实现)_第7张图片

我们可以看出:

  1. 原图可看成是边缘,因为两侧Pixel值突变,属于高频。均值滤波器使其变化变得平缓(边缘模糊),而中值滤波器保留了高频特性(边缘保留)。
  2. 原图变化平缓,属于低频。均值滤波器和中值滤波器都保持原状。
  3. 原图小部分突变可看出杂讯,属于高频。均值滤波器是将其分散到周围Pixel统一拉高,而中值滤波器直接去除了杂讯。
  • 结论:细微变化 mean = median ; 高频 mean 不保留,median保留 ; 处理杂讯 median 强于 mean 。

 

 

 

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

你可能感兴趣的:(图像处理)