• 图像滤波之高斯滤波介绍


    1 高斯滤波简介

      了解高斯滤波之前,我们首先熟悉一下高斯噪声。高斯噪声是指它的概率密度函数服从高斯分布(即正态分布)的一类噪声。如果一个噪声,它的幅度分布服从高斯分布,而它的功率谱密度又是均匀分布的,则称它为高斯白噪声。高斯白噪声的二阶矩不相关,一阶矩为常数,是指先后信号在时间上的相关性,高斯白噪声包括热噪声散粒噪声

      高斯滤波器是一类根据高斯函数的形状来选择权值的线性平滑滤波器。高斯平滑滤波器对于抑制服从正态分布的噪声非常有效。一维零均值高斯函数为:

                                 g(x)=exp( -x^2/(2 sigma^2) 

      其中,高斯分布参数Sigma决定了高斯函数的宽度。对于图像处理来说,常用二维零均值离散高斯函数作平滑滤波器,高斯函数的图形: 

                      

    2 高斯滤波函数

      对于图像来说,高斯滤波器是利用高斯核的一个2维的卷积算子,用于图像模糊化(去除细节和噪声)。

      1) 高斯分布

      一维高斯分布:

              

      二维高斯分布:

        

      2) 高斯核

      理论上,高斯分布在所有定义域上都有非负值,这就需要一个无限大的卷积核。实际上,仅需要取均值周围3倍标准差内的值,以外部份直接去掉即可。 如下图为一个标准差为1.0的整数值高斯核。

                            

    3 高斯滤波性质

      高斯函数具有五个重要的性质,这些性质使得它在早期图像处理中特别有用.这些性质表明,高斯平滑滤波器无论在空间域还是在频率域都是十分有效的低通滤波器,且在实际图像处理中得到了工程人员的有效使用.高斯函数具有五个十分重要的性质,它们是: 

      (1)二维高斯函数具有旋转对称性,即滤波器在各个方向上的平滑程度是相同的.一般来说,一幅图像的边缘方向是事先不知道的,因此,在滤波前是无法确定一个方向上比另一方向上需要更多的平滑.旋转对称性意味着高斯平滑滤波器在后续边缘检测中不会偏向任一方向. 

      (2)高斯函数是单值函数.这表明,高斯滤波器用像素邻域的加权均值来代替该点的像素值,而每一邻域像素点权值是随该点与中心点的距离单调增减的.这一性质是很重要的,因为边缘是一种图像局部特征,如果平滑运算对离算子中心很远的像素点仍然有很大作用,则平滑运算会使图像失真. 

      (3)高斯函数的傅立叶变换频谱是单瓣的.正如下面所示,这一性质是高斯函数付立叶变换等于高斯函数本身这一事实的直接推论.图像常被不希望的高频信号所污染(噪声和细纹理).而所希望的图像特征(如边缘),既含有低频分量,又含有高频分量.高斯函数付立叶变换的单瓣意味着平滑图像不会被不需要的高频信号所污染,同时保留了大部分所需信号. 

      (4)高斯滤波器宽度(决定着平滑程度)是由参数σ表征的,而且σ和平滑程度的关系是非常简单的.σ越大,高斯滤波器的频带就越宽,平滑程度就越好.通过调节平滑程度参数σ,可在图像特征过分模糊(过平滑)与平滑图像中由于噪声和细纹理所引起的过多的不希望突变量(欠平滑)之间取得折衷. 

      (5)由于高斯函数的可分离性,较大尺寸的高斯滤波器可以得以有效地实现.二维高斯函数卷积可以分两步来进行,首先将图像与一维高斯函数进行卷积,然后将卷积结果与方向垂直的相同一维高斯函数卷积.因此,二维高斯滤波的计算量随滤波模板宽度成线性增长而不是成平方增长.

    4 高斯滤波应用

      高斯滤波后图像被平滑的程度取决于标准差。它的输出是领域像素的加权平均,同时离中心越近的像素权重越高。因此,相对于均值滤波(mean filter)它的平滑效果更柔和,而且边缘保留的也更好。

      高斯滤波被用作为平滑滤波器的本质原因是因为它是一个低通滤波器,见下图。而且,大部份基于卷积平滑滤波器都是低通滤波器。

                            

                          图.高斯滤波器(标准差=3像素)的频率响应。The spatial frequency axis is marked 

                              in cycles per pixel, and hence no value above 0.5 has a real meaning。

    5 高斯滤波步骤

     (1)移动相关核的中心元素,使它位于输入图像待处理像素的正上方 

     (2)将输入图像的像素值作为权重,乘以相关核 

     (3)将上面各步得到的结果相加做为输出 

    6 高斯滤波源码(C语言版)

    复制代码
      1 // gaosilvbo.cpp : 定义控制台应用程序的入口点。
      2 //
      3 
      4 #include "stdafx.h"
      5 #include <stdlib.h>
      6 #include <math.h>
      7 
      8 typedef unsigned long DWORD;
      9 typedef long LONG;
     10 typedef unsigned short WORD;
     11 typedef unsigned char BYTE;
     12 
     13 typedef struct tagRGBQUAD {
     14     BYTE    rgbBlue;
     15     BYTE    rgbGreen;
     16     BYTE    rgbRed;
     17     BYTE    rgbReserved;
     18 } RGBQUAD;
     19 
     20 #pragma pack (2)     /*指定按字节对齐*/  
     21 typedef struct tagBITMAPFILEHEADER {
     22     WORD    bfType;
     23     DWORD   bfSize;
     24     WORD    bfReserved1;
     25     WORD    bfReserved2;
     26     DWORD   bfOffBits;
     27 } BITMAPFILEHEADER;
     28 
     29 //恢复对齐状态
     30 typedef struct tagBITMAPINFOHEADER{
     31     DWORD      biSize;
     32     LONG       biWidth;
     33     LONG       biHeight;
     34     WORD       biPlanes;
     35     WORD       biBitCount;
     36     DWORD      biCompression;
     37     DWORD      biSizeImage;
     38     LONG       biXPelsPerMeter;
     39     LONG       biYPelsPerMeter;
     40     DWORD      biClrUsed;
     41     DWORD      biClrImportant;
     42 } BITMAPINFOHEADER;
     43 
     44 unsigned char *pTempBmpBuf;    //读入图像数据的指针
     45 
     46 unsigned char *pBmpBuf;    //读入图像数据的指针
     47 
     48 
     49 int bmpWidth;              //图像的宽
     50 int bmpHeight;             //图像的高
     51 RGBQUAD *pColorTable;       //颜色表指针
     52 int biBitCount;            //图像类型,每像素位数
     53 
     54 bool readBmp(char *bmpName)
     55 {
     56     //二进制读方式打开指定的图像文件
     57 
     58     FILE *fp=fopen(bmpName,"rb");
     59     if(fp==0) return 0;
     60 
     61 
     62     //跳过位图文件头结构BITMAPFILEHEADER
     63     fseek(fp, sizeof(BITMAPFILEHEADER),0);
     64 
     65     //定义位图信息头结构变量,读取位图信息头进内存,存放在变量head中
     66     BITMAPINFOHEADER head;  
     67     fread(&head, sizeof(BITMAPINFOHEADER), 1,fp); 
     68 
     69     //获取图像宽、高、每像素所占位数等信息
     70     bmpWidth = head.biWidth;
     71     bmpHeight = head.biHeight;
     72     biBitCount = head.biBitCount;
     73 
     74 
     75     //定义变量,计算图像每行像素所占的字节数(必须是的倍数)
     76     int lineByte=(bmpWidth * biBitCount/8+3)/4*4;
     77 
     78     //灰度图像有颜色表,且颜色表表项为
     79     if(biBitCount==8){
     80         //申请颜色表所需要的空间,读颜色表进内存
     81         pColorTable=new RGBQUAD[256];
     82         fread(pColorTable,sizeof(RGBQUAD),256,fp);
     83     }
     84 
     85 
     86     //申请位图数据所需要的空间,读位图数据进内存
     87     pTempBmpBuf=new unsigned char[lineByte * bmpHeight];
     88 
     89     pBmpBuf=new unsigned char[lineByte * bmpHeight];
     90     fread(pTempBmpBuf,1,lineByte * bmpHeight,fp);
     91     fseek(fp, 1078,0);
     92     fread(pBmpBuf,1,lineByte * bmpHeight,fp);
     93 
     94     //关闭文件
     95     fclose(fp);
     96     return 1;
     97 }
     98 
     99 bool saveBmp(char *bmpName, unsigned char *imgBuf, int width, int height,int biBitCount, RGBQUAD *pColorTable)
    100 {
    101     
    102     if(!imgBuf)
    103         return 0;
    104 
    105     //颜色表大小,以字节为单位,灰度图像颜色表为字节,彩色图像颜色表大小为
    106     int colorTablesize=0;
    107     if(biBitCount==8)
    108         colorTablesize=1024;
    109 
    110     //待存储图像数据每行字节数为的倍数
    111     int lineByte=(width * biBitCount/8+3)/4*4;
    112 
    113     //以二进制写的方式打开文件
    114     FILE *fp=fopen(bmpName,"wb");
    115     if(fp==0) return 0;
    116 
    117     //申请位图文件头结构变量,填写文件头信息
    118     BITMAPFILEHEADER fileHead;
    119     fileHead.bfType = 0x4D42;//bmp类型
    120 
    121     //bfSize是图像文件个组成部分之和
    122     fileHead.bfSize= sizeof(BITMAPFILEHEADER)
    123         + sizeof(BITMAPINFOHEADER)
    124         + colorTablesize + lineByte*height;
    125 
    126     fileHead.bfReserved1 = 0;
    127     fileHead.bfReserved2 = 0;
    128 
    129     //bfOffBits是图像文件前个部分所需空间之和
    130     fileHead.bfOffBits=54+colorTablesize;
    131 
    132     //写文件头进文件
    133     fwrite(&fileHead, sizeof(BITMAPFILEHEADER),1, fp);
    134 
    135     //申请位图信息头结构变量,填写信息头信息
    136     BITMAPINFOHEADER head; 
    137     head.biBitCount=biBitCount;
    138     head.biClrImportant=0;
    139     head.biClrUsed=0;
    140     head.biCompression=0;
    141     head.biHeight=height;
    142     head.biPlanes=1;
    143     head.biSize=40;
    144     head.biSizeImage=lineByte*height;
    145     head.biWidth=width;
    146     head.biXPelsPerMeter=0;
    147     head.biYPelsPerMeter=0;
    148 
    149     //写位图信息头进内存
    150     fwrite(&head, sizeof(BITMAPINFOHEADER),1, fp);
    151 
    152     //如果灰度图像,有颜色表,写入文件
    153     if(biBitCount==8)
    154         fwrite(pColorTable, sizeof(RGBQUAD),256, fp);
    155 
    156     //写位图数据进文件
    157     fwrite(imgBuf, height*lineByte, 1, fp);
    158 
    159     //关闭文件
    160     fclose(fp);
    161     return 1;
    162 }
    163 
    164 int _tmain(int argc, _TCHAR* argv[])
    165 {
    166         //读入指定BMP文件进内存
    167     char readPath[]="guass_test.bmp";
    168     readBmp(readPath);
    169     //输出图像的信息
    170     printf("width=%d,height=%d,biBitCount=%d
    ",
    171         bmpWidth,bmpHeight,biBitCount);
    172 
    173     //每行字节数
    174     int lineByte=(bmpWidth*biBitCount/8+3)/4*4;
    175     //定义最终写入的数据体
    176     //pBmpBuf=new unsigned char[lineByte * bmpHeight];
    177     //循环变量,图像的坐标
    178     int y,x;
    179 
    180     //循环变量,针对彩色图像,遍历每像素的三个分量
    181     int k;
    182     //单精度变量暂存计算后的灰度值(针对灰度图像)
    183     float       TempNum;
    184     //指向TempBmpbuf的指针
    185     unsigned char *TemPtr;
    186     //指向BmpBuf的指针
    187     unsigned char *Ptr;
    188     //定义*3的模板(拉普拉斯)
    189     float CoefArray[9]={1.0f,2.0f,1.0f,2.0f,4.0f,2.0f,1.0f,2.0f,1.0f};
    190     //定义模板前乘的系数(拉普拉斯)
    191     float coef=(float)(1.0/16.0);;  
    192 
    193     //lapulas滤波
    194     if(biBitCount==8){//对于灰度图像
    195         for(y=1;y<bmpHeight-1;y++){
    196             for(x=0;x<bmpWidth-1;x++){    
    197 
    198                 TemPtr=pTempBmpBuf+y*lineByte+x;
    199                 Ptr=pBmpBuf+y*lineByte+x;
    200 
    201                 TempNum=(float)((unsigned char)*(TemPtr+lineByte-1))*CoefArray[0];
    202                 TempNum+=(float)((unsigned char)*(TemPtr+lineByte))*CoefArray[1];
    203                 TempNum+=(float)((unsigned char)*(TemPtr+lineByte+1))*CoefArray[2];
    204                 TempNum+=(float)((unsigned char)*(TemPtr-1))*CoefArray[3];
    205                 TempNum+=(float)((unsigned char)*TemPtr)*CoefArray[4];
    206                 TempNum+=(float)((unsigned char)*(TemPtr+1))*CoefArray[5];
    207                 TempNum+=(float)((unsigned char)*(TemPtr-lineByte-1))*CoefArray[6];
    208                 TempNum+=(float)((unsigned char)*(TemPtr-lineByte))*CoefArray[7];
    209                 TempNum+=(float)((unsigned char)*(TemPtr-lineByte+1))*CoefArray[8];
    210 
    211                 TempNum*=coef;
    212 
    213                 if(TempNum>255.0) *Ptr =(BYTE)255;
    214                 else if(TempNum<0.0) 
    215                     *Ptr =(unsigned char)fabs(TempNum);
    216                 //用到了fabs函数,需要添加math.h头文件
    217                 else *Ptr=(char)TempNum;
    218             }
    219         }
    220     }
    221 
    222     else if(biBitCount==24){//彩色图像
    223         for(y=0;y<bmpHeight/2;y++){
    224             for(x=0;x<bmpWidth/2;x++){ 
    225                 for(k=0;k<3;k++)//每像素RGB三个分量分别置才变成黑色
    226                     *(pBmpBuf+y*lineByte+x*3+k)=0;
    227             }
    228         }
    229     }
    230     //将图像数据存盘
    231     char writePath[]="gauss_result.BMP";
    232     saveBmp(writePath, pBmpBuf, bmpWidth, 
    233         bmpHeight, biBitCount, pColorTable);
    234     //清除缓冲区,pBmpBuf和pColorTable是全局变量,在文件读入时申请的空间
    235     delete []pBmpBuf;
    236     delete []pTempBmpBuf;
    237     if(biBitCount==8)
    238         delete []pColorTable;
    239     return 0;
    240 }
    复制代码

    高斯滤波处理之后:                                                                   高斯滤波处理之前:

        

  • 相关阅读:
    【算法】HashMap相关要点记录
    【算法】二叉树、N叉树先序、中序、后序、BFS、DFS遍历的递归和迭代实现记录(Java版)
    SpringCloud Openfeign Get请求服务传递对象的报400 Post not support的错误解决办法
    掌握 Promise 的逻辑方法
    JavaScript的执行上下文,真没你想的那么难
    一套标准的ASP.NET Core容器化应用日志收集分析方案
    在IIS中部署前后端应用,多么痛的领悟!
    吐槽一下Abp的用户和租户管理模块
    ant-design-vue中tree增删改
    微服务下的注册中心如何选择
  • 原文地址:https://www.cnblogs.com/yzl050819/p/7514819.html
Copyright © 2020-2023  润新知