DIP-HW6

实验六

一、实验目的和要求

  • 目的
    • 了解双边滤波的方法
  • 要求
    • 自己写C语言程序,实现所有操作,不调用其他库。

二、实验内容和原理

  • 内容

    • 读入图像
    • 给图像施加椒盐噪声
    • 对含有噪声的图像进行双边滤波
    • 输出图像
  • 原理

    • $I_p^{bf} = \frac{1}{W_p^{bf}}\sum_{q\in S} G_{\sigma_s}(|p-q|)G_{\sigma_r}(|Ip-Iq|Iq)$

      $W_P^{bf} = \sum_{q\in S} G_{\sigma_s}(|p-q|)G_{\sigma_r}(|I_p-I_q|)$

    • $w(i,j,k,l) = e^{(-\frac{(i-k)^2+(j-l)^2}{2\sigma_d^2}-\frac{|I(i,j)-I(k,l)|^2}{2\sigma_r^2})}$

      $I_D(i,j) = \frac{\sum_{k,l} I(k,l)w(i,j,k,l)}{\sum_{k,l}w(i,j,k,l)}$

    • 根据上述公式即可计算得到新的图像

三、源代码和分析

  • 源代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    #include<stdio.h>
    #include<stdlib.h>
    #include<math.h>
    #include<string.h>
    #include<time.h>
    #define WORD unsigned short
    #define DWORD unsigned int
    #define BYTE unsigned char
    int ph,pw,psize,lsize;
    double const sigma_r=100,sigma_d=100;
    typedef struct BMP_FILE_HEADER
    {
    WORD bfType;
    DWORD bfSize;
    WORD bfReserved1;
    WORD bfReserved2;
    DWORD bfOffBits;
    }BMPFILEHEADER;
    /* 位图信息头 */
    typedef struct BMP_INFO
    {
    DWORD biSize; // 信息头的大小
    DWORD biWidth; // 图像的宽度
    DWORD biHeight; // 图像的高度
    WORD biPlanes; // 图像的位面数
    WORD biBitCount; // 每个像素的位数
    DWORD biCompression; // 压缩类型
    DWORD biSizeImage; // 图像的大小,以字节为单位
    DWORD biXPelsPerMeter; // 水平分辨率
    DWORD biYPelsPerMeter; // 垂直分辨率
    DWORD biClrUsed; // 使用的色彩数
    DWORD biClrImportant; // 重要的颜色数
    } BMPINF; // 40 字节
    BMPFILEHEADER thisBmpFileHeader; // 定义一个 BMP 文件头的结构体
    BMPINF thisBmpInfo; // 定义一个 BMP 文件信息结构体
    void readFileHead(FILE *fp); //fp指向的文件的文件头读入到thisBMPFileHeader中
    void writeFileHead(FILE *fp); //thisBMPFileHeader写入到fp指向的文件的文件头中
    void BilateralFilter(BYTE *p,BYTE* pOUT,char *name);//均值滤波,结果存在pOUT中
    void OutPutImg(BYTE *pOUT,char *name);//输出图像
    void AddNoise_salt(BYTE* p, BYTE *pNoise);//增加盐噪声
    void AddNoise_pepper(BYTE* p, BYTE *pNoise);//增加椒噪声
    double getWeight(int i,int j,int tmpi,int tmpj,int I0,int I2);//计算权重
    int main()
    {
    FILE *fp; // 定义一个文件指针
    if((fp=fopen("in.bmp","rb"))==NULL)
    {
    printf("Can't open the file\n");
    return 0;
    }
    printf("open the file\n");
    readFileHead(fp);
    ph=thisBmpInfo.biHeight;//高度
    pw=thisBmpInfo.biWidth;//宽度
    psize=pw*ph*3;//图片信息字节总数
    lsize=pw*3;//每行信息字节总数
    BYTE* p=(BYTE*)malloc(psize);
    fread(p,psize,1,fp);
    fclose(fp);
    BYTE* pOUT=(BYTE*)malloc(psize);
    BYTE* pNoise=(BYTE*)malloc(psize);
    BilateralFilter(p,pOUT,"BilateralFiler_of_origin");
    AddNoise_salt(p,pNoise);
    BilateralFilter(pNoise,pOUT,"BilateralFiler_of_salt");
    AddNoise_pepper(p,pNoise);
    BilateralFilter(pNoise,pOUT,"BilateralFiler_of_pepper");
    return 0;
    }
    void OutPutImg(BYTE *pOUT,char *name)
    {
    char postfix[10]=".bmp",tmp[100];
    strcpy(tmp,name);
    strcat(tmp,postfix);
    FILE *fp=fopen(tmp,"wb");
    writeFileHead(fp);
    fwrite(pOUT,psize,1,fp);
    fclose(fp);
    printf("%s",name);
    printf(" finised!\n");
    }
    void readFileHead(FILE *fp)
    {
    fread(&thisBmpFileHeader.bfType,sizeof(thisBmpFileHeader.bfType),1,fp);
    fread(&thisBmpFileHeader.bfSize,sizeof(thisBmpFileHeader.bfSize),1,fp);
    fread(&thisBmpFileHeader.bfReserved1,sizeof(thisBmpFileHeader.bfReserved1),1,fp);
    fread(&thisBmpFileHeader.bfReserved2,sizeof(thisBmpFileHeader.bfReserved2),1,fp);
    fread(&thisBmpFileHeader.bfOffBits,sizeof(thisBmpFileHeader.bfOffBits),1,fp);
    fread(&thisBmpInfo.biSize, sizeof(thisBmpInfo.biSize), 1, fp);
    fread(&thisBmpInfo.biWidth, sizeof(thisBmpInfo.biWidth), 1, fp);
    fread(&thisBmpInfo.biHeight, sizeof(thisBmpInfo.biHeight), 1, fp);
    fread(&thisBmpInfo.biPlanes, sizeof(thisBmpInfo.biPlanes), 1, fp);
    fread(&thisBmpInfo.biBitCount, sizeof(thisBmpInfo.biBitCount), 1, fp);
    fread(&thisBmpInfo.biCompression, sizeof(thisBmpInfo.biCompression), 1, fp);
    fread(&thisBmpInfo.biSizeImage, sizeof(thisBmpInfo.biSizeImage), 1, fp);
    fread(&thisBmpInfo.biXPelsPerMeter, sizeof(thisBmpInfo.biXPelsPerMeter), 1, fp);
    fread(&thisBmpInfo.biYPelsPerMeter, sizeof(thisBmpInfo.biYPelsPerMeter), 1, fp);
    fread(&thisBmpInfo.biClrUsed, sizeof(thisBmpInfo.biClrUsed), 1, fp);
    fread(&thisBmpInfo.biClrImportant, sizeof(thisBmpInfo.biClrImportant), 1, fp);
    }
    void writeFileHead(FILE *fp)
    {
    // 写入位图文件头
    fwrite(&thisBmpFileHeader.bfType,sizeof(thisBmpFileHeader.bfType),1,fp);
    fwrite(&thisBmpFileHeader.bfSize,sizeof(thisBmpFileHeader.bfSize),1,fp);
    fwrite(&thisBmpFileHeader.bfReserved1,sizeof(thisBmpFileHeader.bfReserved1),1,fp);
    fwrite(&thisBmpFileHeader.bfReserved2,sizeof(thisBmpFileHeader.bfReserved2),1,fp);
    fwrite(&thisBmpFileHeader.bfOffBits,sizeof(thisBmpFileHeader.bfOffBits),1,fp);
    // 写入位图信息头
    fwrite(&thisBmpInfo.biSize, sizeof(thisBmpInfo.biSize), 1, fp);
    fwrite(&thisBmpInfo.biWidth, sizeof(thisBmpInfo.biWidth), 1, fp);
    fwrite(&thisBmpInfo.biHeight, sizeof(thisBmpInfo.biHeight), 1, fp);
    fwrite(&thisBmpInfo.biPlanes, sizeof(thisBmpInfo.biPlanes), 1, fp);
    fwrite(&thisBmpInfo.biBitCount, sizeof(thisBmpInfo.biBitCount), 1, fp);
    fwrite(&thisBmpInfo.biCompression, sizeof(thisBmpInfo.biCompression), 1, fp);
    fwrite(&thisBmpInfo.biSizeImage, sizeof(thisBmpInfo.biSizeImage), 1, fp);
    fwrite(&thisBmpInfo.biXPelsPerMeter, sizeof(thisBmpInfo.biXPelsPerMeter), 1, fp);
    fwrite(&thisBmpInfo.biYPelsPerMeter, sizeof(thisBmpInfo.biYPelsPerMeter), 1, fp);
    fwrite(&thisBmpInfo.biClrUsed, sizeof(thisBmpInfo.biClrUsed), 1, fp);
    fwrite(&thisBmpInfo.biClrImportant, sizeof(thisBmpInfo.biClrImportant), 1, fp);
    }
    void AddNoise_salt(BYTE* p, BYTE *pNoise)
    {
    srand(time(0));
    int *flag=(int *)malloc(ph*pw*sizeof(int));
    memset(flag,0,sizeof(int)*ph*pw);
    int num=ph*pw*0.01;
    int i,j,tmp,_;
    for(i=0;i<ph;i++)
    for(j=0;j<pw;j++)
    {
    tmp=i*lsize+j*3;
    for(_=0;_<3;_++)
    pNoise[tmp+_]=p[tmp+_];
    }
    for(i=0;i<num;i++)
    {
    int tmpi=rand()%ph,
    tmpj=rand()%pw;
    while(flag[tmpi*pw+tmpj])
    {
    tmpi=rand()%ph;
    tmpj=rand()%pw;
    }
    flag[tmpi*pw+tmpj]=1;
    for(_=0;_<3;_++)
    {
    pNoise[tmpi*lsize+tmpj*3+_]=255;
    }
    }
    OutPutImg(pNoise,"Salt_Noise");
    }
    void AddNoise_pepper(BYTE* p, BYTE *pNoise)
    {
    srand(time(0));
    int *flag=(int *)malloc(ph*pw*sizeof(int));
    memset(flag,0,sizeof(int)*ph*pw);
    int num=ph*pw*0.01;
    int i,j,tmp,_;
    for(i=0;i<ph;i++)
    for(j=0;j<pw;j++)
    {
    tmp=i*lsize+j*3;
    for(_=0;_<3;_++)
    pNoise[tmp+_]=p[tmp+_];
    }
    for(i=0;i<num;i++)
    {
    int tmpi=rand()%ph,
    tmpj=rand()%pw;
    while(flag[tmpi*pw+tmpj])
    {
    tmpi=rand()%ph;
    tmpj=rand()%pw;
    }
    flag[tmpi*pw+tmpj]=1;
    for(_=0;_<3;_++)
    {
    pNoise[tmpi*lsize+tmpj*3+_]=0;
    }
    }
    OutPutImg(pNoise,"Pepper_Noise");
    }
    void BilateralFilter(BYTE *p,BYTE* pOUT,char *name)
    {
    int i,j,tmp,_,o;
    int fx[49]={0},fy[49]={0};
    for(i=0,_=0;i<7;i++)
    for(j=0;j<7;j++,_++)
    {
    fx[_]=i-3;
    fy[_]=j-3;
    }
    for(i=0;i<ph;i++)
    {
    for(j=0;j<pw;j++)
    {
    tmp=i*lsize+j*3;
    for(_=0;_<3;_++)
    {
    int flag=0;
    double G=0,W=0;
    for(o=0;o<49;o++)
    {
    int tmpi=i+fx[o],tmpj=j+fy[o];
    if(tmpi>=0&&tmpi<ph&&tmpj>=0&&tmpj<pw)
    {
    int t=tmpi*lsize+tmpj*3;
    double w=getWeight(i,j,tmpi,tmpj,p[tmp+_],p[t+_]);
    G+=p[t+_]*w;
    W+=w;
    }
    else flag=1;
    }
    if(!flag)
    pOUT[tmp+_]=(G/W > 255) ? 255 : G/W;
    else
    pOUT[tmp+_]=p[tmp+_];
    }
    }
    }
    OutPutImg(pOUT,name);
    }
    double getWeight(i,j,tmpi,tmpj,I0,I2)
    {
    double d=((double)((i-tmpi)*(i-tmpi)+(j-tmpj)*(j-tmpj)))/(2*sigma_d*sigma_d);
    double r=((double)((I0-I2)*(I0-I2)))/(2*sigma_r*sigma_r);
    return exp(-1*d-r);
    }
  • 分析

    • 双边滤波函数

      利用fx,fy两个数组作为坐标偏移函数

      首先通过一个循环为坐标偏移函数赋值

      然后枚举$77$方格中的像素点,利用getWeight* 函数计算得到相应的权重,并记录累加的值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    void BilateralFilter(BYTE *p,BYTE* pOUT,char *name)
    {
    int i,j,tmp,_,o;
    int fx[49]={0},fy[49]={0};
    for(i=0,_=0;i<7;i++)
    for(j=0;j<7;j++,_++)
    {
    fx[_]=i-3;
    fy[_]=j-3;
    }
    for(i=0;i<ph;i++)
    {
    for(j=0;j<pw;j++)
    {
    tmp=i*lsize+j*3;
    for(_=0;_<3;_++)
    {
    int flag=0;
    double G=0,W=0;
    for(o=0;o<49;o++)
    {
    int tmpi=i+fx[o],tmpj=j+fy[o];
    if(tmpi>=0&&tmpi<ph&&tmpj>=0&&tmpj<pw)
    {
    int t=tmpi*lsize+tmpj*3;
    double w=getWeight(i,j,tmpi,tmpj,p[tmp+_],p[t+_]);
    G+=p[t+_]*w;
    W+=w;
    }
    else flag=1;
    }
    if(!flag)
    pOUT[tmp+_]=(G/W > 255) ? 255 : G/W;
    else
    pOUT[tmp+_]=p[tmp+_];
    }
    }
    }
    OutPutImg(pOUT,name);
    }
    • 权重计算

      根据公式,直接计算各个像素点的权重

      1
      2
      3
      4
      5
      6
      double getWeight(i,j,tmpi,tmpj,I0,I2)
      {
      double d=((double)((i-tmpi)*(i-tmpi)+(j-tmpj)*(j-tmpj)))/(2*sigma_d*sigma_d);
      double r=((double)((I0-I2)*(I0-I2)))/(2*sigma_r*sigma_r);
      return exp(-1*d-r);
      }

心得体会

  • 实验结果

    对原图的双边滤波

    BilateralFiler_of_origin

    对原图施加盐噪声

    Salt_Noise

    对噪声干扰后的图像进行双边滤波

    BilateralFiler_of_salt

  • 心得

    本次实验的代码和上次实验大体相似,区别仅在于权重的计算变得复杂一些。继续沿用上次实验坐标偏移的做法,大大简化了代码的编写难度

    通过调整两个 $\sigma$ 的值,我们可以看到lena 的模糊程度会发生变化。

    通过这次实验,我对于高斯滤波、双边滤波有了更深的理解。双边滤波综合距离和像素变化两者影响的思想及方法值得我们深思。