DIP-HW5

实验五

一、实验目的和要求

  • 目的
    • 了解连续和离散的函数卷积的原理
    • 了解噪声对图片的影响,并掌握简单的为图像增加噪声的方法。
    • 了解滤波函数,并掌握均值滤波和中值滤波的方法。
    • 掌握拉普拉斯算子增强图像的方法。
  • 要求
    • 自己写C语言程序,实现所有操作,不调用其他库。

二、实验内容和原理

  • 内容:

    • 对图像添加盐椒噪声。
    • 对图像进行滤波处理,去除噪声。
    • 利用拉普拉斯算子对图像进行增强。
  • 原理:

    • 连续函数的卷积

      $g(x)=f(x)*h(x)=\int_{-\infty}^{\infty} f(x)*h(t-x)dt$

    • 卷积的性质

      $f(x)*h(x)=h(x)*f(x)$

      $f*(g+h)=fg+fh$

      $(fg)h=f(gh)$

    • 离散函数的卷积

      $g(x)=f(x)*h(x)=\frac{1}{M}\sum_{t=0}^{M-1}f(t)*h(x-t)$

    • 卷积等价于计算区域内像素点值的加权和

      image-20191201001543946

    • 滤波

      根据公式 $g(x,y)=\sum_{s=-a}^{a}\sum_{t=-b}^{b}w(s,t)*f(x+s,x+t)$

      得到新的图像 g

      不同的滤波方法的区别就在于w(s,t)的形式,可以用一个矩阵来表示。

      如均值滤波 $\frac{1}{9}\begin{bmatrix}1,1,1\1,1,1\1,1,1\end{bmatrix}$

    • 拉普拉斯算子增强

      $\frac{\part^2f}{\part x^2} = f(x+1,y) + f(x-1,y)-2*f(x,y)$

      $\frac{\part^2f}{\part y^2} = f(x,y+1) + f(x,y-1)-2*f(x,y)$

      $\bigtriangledown ^2f = [ f(x+1,y) + f(x-1,y)+ f(x,y+1) + f(x,y-1)]-4*f(x,y)$

      用矩阵表示:$\begin{bmatrix}0,1,0\1,-4,1\0,1,0\end{bmatrix}$

      将对角线的元素也考虑进去: $\begin{bmatrix}1,1,1\1,-8,1\1,1,1\end{bmatrix}$

      根据公式 $g(x,y)=f(x,y)-\bigtriangledown ^2f(x,y)$ 合并得到增强后的图像

    • 椒盐噪声

      椒盐噪声的原理是在图像中随机选取一些像素点,将其变成白色或者黑色,对应的分别是盐噪声和椒噪声。

三、源代码和分析

  • 源代码

    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
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    #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;
    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 meanFilter(BYTE *p,BYTE* pOUT,char *name);
    void medianFilter(BYTE *p,BYTE* pOUT,char *name);
    void Laplacian(BYTE *p,char *name);
    void OutPutImg(BYTE *pOUT,char *name);
    void AddNoise_salt(BYTE* p, BYTE *pNoise);
    void AddNoise_pepper(BYTE* p, BYTE *pNoise);
    int cmp_int(const void* _a , const void* _b);
    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);
    meanFilter(p,pOUT,"meanFiler_of_origin");
    Laplacian(p,"Laplacian_of_origin");
    BYTE* pNoise=(BYTE*)malloc(psize);
    AddNoise_salt(p,pNoise);
    meanFilter(pNoise,pOUT,"meanFiler_of_salt");
    Laplacian(pOUT,"Laplacian_of_meanFiler_of_salt");
    medianFilter(pNoise,pOUT,"medianFiler_of_salt");
    Laplacian(pOUT,"Laplacian_of_medianFilter_of_salt");
    AddNoise_pepper(p,pNoise);
    meanFilter(pNoise,pOUT,"meanFiler_of_pepper");
    Laplacian(pOUT,"Laplacian_of_meanFiler_of_pepper");
    medianFilter(pNoise,pOUT,"medianFiler_of_pepper");
    Laplacian(pOUT,"Laplacian_of_medianFilter_of_pepper");
    return 0;
    }
    void meanFilter(BYTE *p,BYTE* pOUT,char *name)
    {
    int i,j,tmp,_,o;
    int fx[9]={-1,-1,-1,0,0,0,1,1,1},fy[9]={-1,0,1,-1,0,1,-1,0,1};
    for(i=0;i<ph;i++)
    {
    for(j=0;j<pw;j++)
    {
    tmp=i*lsize+j*3;
    for(_=0;_<3;_++)
    {
    int flag=0,value=0;
    for(o=0;o<9;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;
    value+=p[t+_];
    }
    else flag=1;
    }
    if(!flag)
    pOUT[tmp+_]=value/9;
    else
    pOUT[tmp+_]=p[tmp+_];
    }
    }
    }
    OutPutImg(pOUT,name);
    }
    void Laplacian(BYTE *p,char *name)
    {
    BYTE* pOUT=(BYTE*)malloc(psize);
    int i,j,tmp,_,o;
    int fx[8]={-1,-1,-1,0,0,1,1,1},fy[8]={-1,0,1,-1,1,-1,0,1};
    for(i=0;i<ph;i++)
    {
    for(j=0;j<pw;j++)
    {
    tmp=i*lsize+j*3;
    for(_=0;_<3;_++)
    {
    int flag=0,value=0;
    for(o=0;o<8;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;
    value+=p[t+_];
    }
    else flag=1;
    }
    if(!flag)
    {
    int nV=p[tmp+_]-(value-8*p[tmp+_]);
    if(nV<0)
    nV=0;
    if(nV>255)
    nV=255;
    pOUT[tmp+_]=nV;

    }
    else
    pOUT[tmp+_]=p[tmp+_];

    }
    }
    }
    OutPutImg(pOUT,name);
    }
    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");
    }
    int cmp_int(const void* _a , const void* _b)
    {
    int* a = (int*)_a;
    int* b = (int*)_b;
    return *a - *b;
    }
    void medianFilter(BYTE *p,BYTE* pOUT,char *name)
    {
    int i,j,tmp,_,o;
    int fx[9]={-1,-1,-1,0,0,0,1,1,1},fy[9]={-1,0,1,-1,0,1,-1,0,1},value[9];
    for(i=0;i<ph;i++)
    {
    for(j=0;j<pw;j++)
    {
    tmp=i*lsize+j*3;
    for(_=0;_<3;_++)
    {
    int flag=0;
    for(o=0;o<9;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;
    value[o]=p[t+_];
    }
    else
    {
    flag=1;
    break;
    }
    }
    if(!flag)
    {
    qsort(value,9,sizeof(int),cmp_int);
    pOUT[tmp+_]=value[4];
    }
    else
    pOUT[tmp+_]=p[tmp+_];
    }
    }
    }
    OutPutImg(pOUT,name);
    }

  • 分析

    • 函数列表及功能
    1
    2
    3
    4
    5
    6
    7
    8
    9
    void readFileHead(FILE *fp);   //fp指向的文件的文件头读入到thisBMPFileHeader中
    void writeFileHead(FILE *fp); //thisBMPFileHeader写入到fp指向的文件的文件头中
    void meanFilter(BYTE *p,BYTE* pOUT,char *name);//均值滤波,结果存入pOUT中
    void medianFilter(BYTE *p,BYTE* pOUT,char *name);//中值滤波
    void Laplacian(BYTE *p,char *name);//拉普拉斯算子增强
    void OutPutImg(BYTE *pOUT,char *name);//输出图像
    void AddNoise_salt(BYTE* p, BYTE *pNoise);//增加盐噪声
    void AddNoise_pepper(BYTE* p, BYTE *pNoise);//增加椒噪声
    int cmp_int(const void* _a , const void* _b);//用于排序的比较函数
    • 均值滤波

      对于每一个像素点,计算其周围3*3网格中像素点颜色的均值,作为新图像的值。

      构造坐标偏移数组fx,fy。利用这两个数组进行像素点枚举。RGB三个轨道分别进行计算。

    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
    void meanFilter(BYTE *p,BYTE* pOUT,char *name)
    {
    int i,j,tmp,_,o;
    int fx[9]={-1,-1,-1,0,0,0,1,1,1},fy[9]={-1,0,1,-1,0,1,-1,0,1};
    for(i=0;i<ph;i++)
    {
    for(j=0;j<pw;j++)
    {
    tmp=i*lsize+j*3;
    for(_=0;_<3;_++)
    {
    int flag=0,value=0;
    for(o=0;o<9;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;
    value+=p[t+_];
    }
    else flag=1;
    }
    if(!flag)
    pOUT[tmp+_]=value/9;
    else
    pOUT[tmp+_]=p[tmp+_];
    }
    }
    }
    OutPutImg(pOUT,name);
    }
    • 中值滤波

      对于每一个像素点,找到其周围3*3网格中像素点颜色的中值,作为新图像的值。

      具体分析同上

    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
    void medianFilter(BYTE *p,BYTE* pOUT,char *name)
    {
    int i,j,tmp,_,o;
    int fx[9]={-1,-1,-1,0,0,0,1,1,1},fy[9]={-1,0,1,-1,0,1,-1,0,1},value[9];
    for(i=0;i<ph;i++)
    {
    for(j=0;j<pw;j++)
    {
    tmp=i*lsize+j*3;
    for(_=0;_<3;_++)
    {
    int flag=0;
    for(o=0;o<9;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;
    value[o]=p[t+_];
    }
    else
    {
    flag=1;
    break;
    }
    }
    if(!flag)
    {
    qsort(value,9,sizeof(int),cmp_int);
    pOUT[tmp+_]=value[4];
    }
    else
    pOUT[tmp+_]=p[tmp+_];
    }
    }
    }
    OutPutImg(pOUT,name);
    }
    • 拉普拉斯算子

      根据拉普拉斯算子,计算得到图片锐化的值,并将其和原图合并。这里一起进行了这布操作。

    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
    void Laplacian(BYTE *p,char *name)
    {
    BYTE* pOUT=(BYTE*)malloc(psize);
    int i,j,tmp,_,o;
    int fx[8]={-1,-1,-1,0,0,1,1,1},fy[8]={-1,0,1,-1,1,-1,0,1};
    for(i=0;i<ph;i++)
    {
    for(j=0;j<pw;j++)
    {
    tmp=i*lsize+j*3;
    for(_=0;_<3;_++)
    {
    int flag=0,value=0;
    for(o=0;o<8;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;
    value+=p[t+_];
    }
    else flag=1;
    }
    if(!flag)
    {
    int nV=p[tmp+_]-(value-8*p[tmp+_]);
    if(nV<0)
    nV=0;
    if(nV>255)
    nV=255;
    pOUT[tmp+_]=nV;

    }
    else
    pOUT[tmp+_]=p[tmp+_];

    }
    }
    }
    OutPutImg(pOUT,name);
    }
  • 实验结果展示

    盐噪声

Salt_Noise

盐噪声均值滤波

meanFiler_of_salt

均值滤波的拉普拉斯增强

Laplacian_of_meanFiler_of_salt

中值滤波

medianFiler_of_salt

中值滤波的拉普拉斯增强

Laplacian_of_medianFilter_of_salt

四、心得体会

本次实验主要学习了函数卷积、均值滤波和中值滤波的方法。并且了解了拉普拉斯算子对图像的增强。

这次实验的代码相较上次作业比较简单,在写代码过程中没有出现严重的bug。值得注意的一个点是,在每次计算得到像素点的颜色的值的时候,如果其值不在[0,255]时,应将其值限制在[0,255]上。

另外,中值滤波和均值滤波对于盐椒噪声消除的效果是不同的。由于盐椒噪声的值相对极端,其对均值的影响较大,故采用中值滤波的话,可以良好的消除极端点的影响。

拉普拉斯算子能够锐化图像,增强图像效果十分明显。其对于极端数据也非常敏感。如果滤波效果很差的话,噪声也会得到明显加强