实验五
一、实验目的和要求
- 目的
- 了解连续和离散的函数卷积的原理
- 了解噪声对图片的影响,并掌握简单的为图像增加噪声的方法。
- 了解滤波函数,并掌握均值滤波和中值滤波的方法。
- 掌握拉普拉斯算子增强图像的方法。
- 要求
- 自己写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)$
卷积等价于计算区域内像素点值的加权和
滤波
根据公式 $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
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
9void 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
31void 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
38void 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
41void 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);
}实验结果展示
盐噪声

盐噪声均值滤波

均值滤波的拉普拉斯增强
中值滤波

中值滤波的拉普拉斯增强
四、心得体会
本次实验主要学习了函数卷积、均值滤波和中值滤波的方法。并且了解了拉普拉斯算子对图像的增强。
这次实验的代码相较上次作业比较简单,在写代码过程中没有出现严重的bug。值得注意的一个点是,在每次计算得到像素点的颜色的值的时候,如果其值不在[0,255]时,应将其值限制在[0,255]上。
另外,中值滤波和均值滤波对于盐椒噪声消除的效果是不同的。由于盐椒噪声的值相对极端,其对均值的影响较大,故采用中值滤波的话,可以良好的消除极端点的影响。
拉普拉斯算子能够锐化图像,增强图像效果十分明显。其对于极端数据也非常敏感。如果滤波效果很差的话,噪声也会得到明显加强