FPGA实现图像中值滤波

1. 图像中值滤波

中值滤波是一种统计排序(非线性)的信号处理方法,最初由 J.W.Tukey 于1971年提出,并应用于一维信号处理,后来被应用于二维图像处理领域。

复杂背景下,红外目标进行精确有效的识别和跟踪是一个难题,这种情况下,由于目标与背景的对比度较小、信噪比较低。若直接进行识别和跟踪往往比较困难,因此会对图像信号进行滤波预处理。滤波可以有效的抑制背景噪声,增加目标强度,从而提高图像信噪比。

实时图像处理中,实时预处理包括对图像的各种滤波、直方图统计及均衡、图像增强、灰度变换等,他们共同的特点就是处理的数据量特别大

1.1 中值滤波基本思想

用局部领域像素灰度值排序后的中值代替当前像素的灰度值:

其中:

1.2 中值滤波算例

例如取 方型滤波窗口

Missing \left or extra \right\left[\begin{matrix}212&200&198\206&202&201\208&205&207\\end{matrix} \right]

从小到大排列,取中间值:198, 200, 201, 202, 205, 206, 207, 208, 212,取205代替中间值202之后的矩阵为:

Missing \left or extra \right\left[\begin{matrix}212&200&198\206&205&201\208&205&207\\end{matrix}\right]

滤波窗口有多种形状:

image-20220706014750018

1.3 中值滤波实例

image-20220706014849391 image-20220706014916992

2.4 中值滤波特性总结

  • 对离散阶跃信号、斜声信号不产生作用,对点状噪声和干扰脉冲有良好的抑制作用。

  • 能保持图像边缘,使原始图像不产生模糊。

  • 缺点

    • 对高斯噪声无能为力
    • 计算比较费时

2.5 FPGA 实现图像中值滤波

中值滤波实现的核心就是对数组进行排序,Verilog实现三个数的排序代码如下:

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
module mid_filter_sort(
input clk,
input rst_n,
input [7:0] data1, data2, data3,
output reg [7:0] max, mid, min
);
always @(posedge clk, negedge rst_n) begin
if(!rst_n) begin
max <= 0;
mid <= 0;
min <= 0;
end else begin
// max
if(data1 >= data2 && data1 >= data3)
max <= data1;
else if(data2 >= data1 && data2 >= data3)
max <= data2;
else
max <= data3;

// mid
if((data1 >= data2 && data1 <= data3) || (data1 >= data3 && data1 <= data2))
mid <= data1;
else if((data2 >= data1 && data2 <= data3) || (data2 >= data3 && data2 <= data1))
mid <= data2;
else
mid <= data3;

// min
if(data1 <= data2 && data1 <= data3)
min <= data1;
else if(data2 <= data1 && data2 <= data3)
min <= data2;
else
min <= data3;
end
end
endmodule

中值滤波是对滤波矩阵进行排序,获取排序矩阵的Verilog代码为:

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
module filter_3x3(
input clk,
input rst_n,

input i_Valid,
input [7:0] i_Data,

output o_Valid,
output reg [7:0] o_Data_11, o_Data_12, o_Data_13,
output reg [7:0] o_Data_21, o_Data_22, o_Data_23,
output reg [7:0] o_Data_31, o_Data_32, o_Data_33
);
reg [7:0] row3;
wire [7:0] row2;
wire [7:0] row1;
reg [1:0] Valid_shift;

line_shift_ram inst_line_shift_ram(
.clock (clk),
.clken (iValid),
.shiftin (row3_data),
.taps0x (row2_data),
.taps1x (row1_data),
.shiftout ()
);

// 将输入数据寄存作为窗口第三行起始数据,同时也作为行缓存的输入
always @(posedge clk, negedge rst_n) begin
if(!rst_n)
row3_data <= 0;
else if(iValid)
row3_data <= iData;
end

// 获取3x3卷积模板
always @(posedge clk, negedge rst_n) begin
if(!rst_n) begin
{oData_11, oData_12, oData_13} <= 3'b000;
{oData_21, oData_22, oData_23} <= 3'b000;
{oData_31, oData_32, oData_33} <= 3'b000;
end else if(iValid) begin
{oData_11, oData_12, oData_13} <= {oData_12, oData_13, row1_data};
{oData_21, oData_22, oData_23} <= {oData_22, oData_23, row2_data};
{oData_31, oData_32, oData_33} <= {oData_32, oData_33, row3_data};
end
end

// iValid打两拍输出
always @(posedge clk, negedge rst_n) begin
if(!rst_n)
Valid_shift <= 2'b00;
else
Valid_shift <= {Valid_shift[0], iValid};
end

assign oValid = Valid_shift[1];
endmodule

项目中值滤波实现:

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
308
309
/*
Y方向延时2行 X方向延时5个CLK,
即H_Cnt=100,P_Cnt=100的数据要在H_Cnt=102,P_Cnt=105时才能得到结果
*/
module MEDIAN_FILTER(
input ClkIn,
input [15:0] DataIn,
output[15:0] DataO,
input [9:0] P_Cnt,
input [9:0] H_Cnt,
input VREF,
input HREF
);

wire [15:0] DataIn02,DataIn03;
wire [15:0] a00,a01,a02,a10,a11,a12,a20,a21,a22;
wire [15:0] b00,b01,b02,b10,b11,b12,b20,b21,b22;

D_Array D_ROW01_1(.ClkIn(ClkIn),.ImgIn(DataIn),.a0(b00),.a1(b01),.a2(b02));
D_Array D_ROW02_2(.ClkIn(ClkIn),.ImgIn(DataIn02),.a0(b10),.a1(b11),.a2(b12));
D_Array D_ROW03_3(.ClkIn(ClkIn),.ImgIn(DataIn03),.a0(b20),.a1(b21),.a2(b22));

assign a00='d65535;
assign a01=b01;
assign a02='d0;
assign a10=b10;
assign a11=b11;
assign a12=b12;
assign a20='d0;
assign a21=b21;
assign a22='d65535;

parameter [7:0] n=2;

wire rdclk,wrclk;
assign rdclk=ClkIn;
assign wrclk=ClkIn;

wire HREF_RD,HREF_WR;
reg HREF_TMP1,HREF_TMP2;
assign HREF_RD=HREF_TMP1;
assign HREF_WR=HREF_TMP2;

always@(posedge ClkIn)
begin
HREF_TMP1<=HREF;
HREF_TMP2<=HREF_TMP1;
end

wire rdreq01,wrreq01;
FIFO720 ROW01_1(
.aclr(!VREF),
.data(DataIn),
.rdclk(rdclk),
.rdreq(rdreq01),
.wrclk(wrclk),
.wrreq(wrreq01),
.q(DataIn02)
);
assign rdreq01=(H_Cnt>=n+1)?HREF_RD:1'b0;
assign wrreq01=(H_Cnt>=n)?HREF_WR:1'b0;

wire rdreq02,wrreq02;
FIFO720 ROW02_2(
.aclr(!VREF),
.data(DataIn02),
.rdclk(rdclk),
.rdreq(rdreq02),
.wrclk(wrclk),
.wrreq(wrreq02),
.q(DataIn03)
);
assign rdreq02=(H_Cnt>=n+2)?HREF_RD:1'b0;
assign wrreq02=(H_Cnt>=n+1)?HREF_WR:1'b0;


//=================================

//----------------------分三组排序--------------------------
//--------------------------------
wire ageb00_01;
COMPARE COMP_INT0(
.dataa(a00),
.datab(a01),
.ageb(ageb00_01));
wire ageb00_02;
COMPARE COMP_INT1(
.dataa(a00),
.datab(a02),
.ageb(ageb00_02));
wire ageb01_02;
COMPARE COMP_INT2(
.dataa(a01),
.datab(a02),
.ageb(ageb01_02));
//--------------------------------
wire ageb10_11;
COMPARE COMP_INT3(
.dataa(a10),
.datab(a11),
.ageb(ageb10_11));
wire ageb10_12;
COMPARE COMP_INT4(
.dataa(a10),
.datab(a12),
.ageb(ageb10_12));
wire ageb11_12;
COMPARE COMP_INT5(
.dataa(a11),
.datab(a12),
.ageb(ageb11_12));
//--------------------------------
wire ageb20_21;
COMPARE COMP_INT6(
.dataa(a20),
.datab(a21),
.ageb(ageb20_21));
wire ageb20_22;
COMPARE COMP_INT7(
.dataa(a20),
.datab(a22),
.ageb(ageb20_22));
wire ageb21_22;
COMPARE COMP_INT8(
.dataa(a21),
.datab(a22),
.ageb(ageb21_22));

reg [15:0] max0,med0,min0;
reg [15:0] max1,med1,min1;
reg [15:0] max2,med2,min2;
always@(posedge ClkIn)
begin
case({ageb00_01,ageb00_02,ageb01_02})
3'b000 : begin max0<=a02; med0<=a01; min0<=a00; end//a00<a01 a00<a02 a01<a02 <==> a00<a01<a02
3'b001 : begin max0<=a01; med0<=a02; min0<=a00; end//a00<a01 a00<a02 a01>=a02 <==> a00<a02<=a01
3'b010 : begin max0<=max0; med0<=med0; min0<=min0; end//a00<a01 a00>=a02 a01<a02 <==> impossible
3'b011 : begin max0<=a01; med0<=a00; min0<=a02; end//a00<a01 a00>=a02 a01>=a02 <==> a02<=a00<a01
3'b100 : begin max0<=a02; med0<=a00; min0<=a01; end//a00>=a01 a00<a02 a01<a02 <==> a01<=a00<a02
3'b101 : begin max0<=max0; med0<=med0; min0<=min0; end//a00>=a01 a00<a02 a01>=a02 <==> impossible
3'b110 : begin max0<=a00; med0<=a02; min0<=a01; end//a00>=a01 a00>=a02 a01<a02 <==> a01<a02<=a00
3'b111 : begin max0<=a00; med0<=a01; min0<=a02; end//a00>=a01 a00>=a02 a01>=a02 <==> a02<=a01<=a00
default: begin max0<=max0; med0<=med0; min0<=min0; end
endcase
case({ageb10_11,ageb10_12,ageb11_12})
3'b000 : begin max1<=a12; med1<=a11; min1<=a10; end//a10<a11 a10<a12 a11<a12 <==> a10<a11<a12
3'b001 : begin max1<=a11; med1<=a12; min1<=a10; end//a10<a11 a10<a12 a11>=a12 <==> a10<a12<=a11
3'b010 : begin max1<=max1; med1<=med1; min1<=min1; end//a10<a11 a10>=a12 a11<a12 <==> impossible
3'b011 : begin max1<=a11; med1<=a10; min1<=a12; end//a10<a11 a10>=a12 a11>=a12 <==> a12<=a10<a11
3'b100 : begin max1<=a12; med1<=a10; min1<=a11; end//a10>=a11 a10<a12 a11<a12 <==> a11<=a10<a12
3'b101 : begin max1<=max1; med1<=med1; min1<=min1; end//a10>=a11 a10<a12 a11>=a12 <==> impossible
3'b110 : begin max1<=a10; med1<=a12; min1<=a11; end//a10>=a11 a10>=a12 a11<a12 <==> a11<a12<=a10
3'b111 : begin max1<=a10; med1<=a11; min1<=a12; end//a10>=a11 a10>=a12 a11>=a12 <==> a12<=a11<=a10
default: begin max1<=max1; med1<=med1; min1<=min1; end
endcase
case({ageb20_21,ageb20_22,ageb21_22})
3'b000 : begin max2<=a22; med2<=a21; min2<=a20; end//a20<a21 a20<a22 a21<a22 <==> a20<a21<a22
3'b001 : begin max2<=a21; med2<=a22; min2<=a20; end//a20<a21 a20<a22 a21>=a22 <==> a20<a22<=a21
3'b010 : begin max2<=max2; med2<=med2; min2<=min2; end//a20<a21 a20>=a22 a21<a22 <==> impossible
3'b011 : begin max2<=a21; med2<=a20; min2<=a22; end//a20<a21 a20>=a22 a21>=a22 <==> a22<=a20<a21
3'b100 : begin max2<=a22; med2<=a20; min2<=a21; end//a20>=a21 a20<a22 a21<a22 <==> a21<=a20<a22
3'b101 : begin max2<=max2; med2<=med2; min2<=min2; end//a20>=a21 a20<a22 a21>=a22 <==> impossible
3'b110 : begin max2<=a20; med2<=a22; min2<=a21; end//a20>=a21 a20>=a22 a21<a22 <==> a21<a22<=a20
3'b111 : begin max2<=a20; med2<=a21; min2<=a22; end//a20>=a21 a20>=a22 a21>=a22 <==> a22<=a21<=a20
default: begin max2<=max2; med2<=med2; min2<=min2; end
endcase
end
//----------find out max_min med_med min_max---------
//--------------------------------
wire ageb_max_0_1;
COMPARE COMP_INT10(
.dataa(max0),
.datab(max1),
.ageb(ageb_max_0_1));
wire ageb_max_0_2;
COMPARE COMP_INT11(
.dataa(max0),
.datab(max2),
.ageb(ageb_max_0_2));
wire ageb_max_1_2;
COMPARE COMP_INT12(
.dataa(max1),
.datab(max2),
.ageb(ageb_max_1_2));
//--------------------------------
wire ageb_med_0_1;
COMPARE COMP_INT13(
.dataa(med0),
.datab(med1),
.ageb(ageb_med_0_1));
wire ageb_med_0_2;
COMPARE COMP_INT14(
.dataa(med0),
.datab(med2),
.ageb(ageb_med_0_2));
wire ageb_med_1_2;
COMPARE COMP_INT15(
.dataa(med1),
.datab(med2),
.ageb(ageb_med_1_2));
//--------------------------------
wire ageb_min_0_1;
COMPARE COMP_INT16(
.dataa(min0),
.datab(min1),
.ageb(ageb_min_0_1));
wire ageb_min_0_2;
COMPARE COMP_INT17(
.dataa(min0),
.datab(min2),
.ageb(ageb_min_0_2));
wire ageb_min_1_2;
COMPARE COMP_INT18(
.dataa(min1),
.datab(min2),
.ageb(ageb_min_1_2));

reg [15:0] max_min;
reg [15:0] med_med;
reg [15:0] min_max;
always@(posedge ClkIn)
begin
case({ageb_max_0_1,ageb_max_0_2,ageb_max_1_2})
3'b000 : max_min <= max0; //a00<a01 a00<a02 a01<a02 <==> a00<a01<a02
3'b001 : max_min <= max0; //a00<a01 a00<a02 a01>=a02 <==> a00<a02<=a01
3'b010 : max_min <= max_min; //a00<a01 a00>=a02 a01<a02 <==> impossible
3'b011 : max_min <= max2; //a00<a01 a00>=a02 a01>=a02 <==> a02<=a00<a01
3'b100 : max_min <= max1; //a00>=a01 a00<a02 a01<a02 <==> a01<=a00<a02
3'b101 : max_min <= max_min; //a00>=a01 a00<a02 a01>=a02 <==> impossible
3'b110 : max_min <= max1; //a00>=a01 a00>=a02 a01<a02 <==> a01<a02<=a00
3'b111 : max_min <= max2; //a00>=a01 a00>=a02 a01>=a02 <==> a02<=a01<=a00
default: max_min <= max_min;
endcase
case({ageb_med_0_1,ageb_med_0_2,ageb_med_1_2})
3'b000 : med_med <= med1; //a00<a01 a00<a02 a01<a02 <==> a00<a01<a02
3'b001 : med_med <= med2; //a00<a01 a00<a02 a01>=a02 <==> a00<a02<=a01
3'b010 : med_med <= med_med; //a00<a01 a00>=a02 a01<a02 <==> impossible
3'b011 : med_med <= med0; //a00<a01 a00>=a02 a01>=a02 <==> a02<=a00<a01
3'b100 : med_med <= med0; //a00>=a01 a00<a02 a01<a02 <==> a01<=a00<a02
3'b101 : med_med <= med_med; //a00>=a01 a00<a02 a01>=a02 <==> impossible
3'b110 : med_med <= med2; //a00>=a01 a00>=a02 a01<a02 <==> a01<a02<=a00
3'b111 : med_med <= med1; //a00>=a01 a00>=a02 a01>=a02 <==> a02<=a01<=a00
default: med_med <= med_med;
endcase
case({ageb_min_0_1,ageb_min_0_2,ageb_min_1_2})
3'b000 : min_max <= min2; //a00<a01 a00<a02 a01<a02 <==> a00<a01<a02
3'b001 : min_max <= min1; //a00<a01 a00<a02 a01>=a02 <==> a00<a02<=a01
3'b010 : min_max <= min_max; //a00<a01 a00>=a02 a01<a02 <==> impossible
3'b011 : min_max <= min1; //a00<a01 a00>=a02 a01>=a02 <==> a02<=a00<a01
3'b100 : min_max <= min2; //a00>=a01 a00<a02 a01<a02 <==> a01<=a00<a02
3'b101 : min_max <= min_max; //a00>=a01 a00<a02 a01>=a02 <==> impossible
3'b110 : min_max <= min0; //a00>=a01 a00>=a02 a01<a02 <==> a01<a02<=a00
3'b111 : min_max <= min0; //a00>=a01 a00>=a02 a01>=a02 <==> a02<=a01<=a00
default: min_max <= min_max;
endcase
end

//---------------------------------------------------
wire ageb0_1;
COMPARE COMP_INTA(
.dataa(max_min),
.datab(med_med),
.ageb(ageb0_1));
wire ageb0_2;
COMPARE COMP_INTB(
.dataa(max_min),
.datab(min_max),
.ageb(ageb0_2));
wire ageb1_2;
COMPARE COMP_INTC(
.dataa(med_med),
.datab(min_max),
.ageb(ageb1_2));

/* reg [15:0] DataO;
always@(posedge ClkIn)
begin
case({ageb0_1,ageb0_2,ageb1_2})
3'b000 : DataO<=med_med; //a00<a01 a00<a02 a01<a02 <==> a00<a01<a02
3'b001 : DataO<=min_max; //a00<a01 a00<a02 a01>=a02 <==> a00<a02<=a01
3'b010 : DataO<=DataO;//a00<a01 a00>=a02 a01<a02 <==> impossible
3'b011 : DataO<=max_min; //a00<a01 a00>=a02 a01>=a02 <==> a02<=a00<a01
3'b100 : DataO<=max_min; //a00>=a01 a00<a02 a01<a02 <==> a01<=a00<a02
3'b101 : DataO<=DataO;//a00>=a01 a00<a02 a01>=a02 <==> impossible
3'b110 : DataO<=min_max; //a00>=a01 a00>=a02 a01<a02 <==> a01<a02<=a00
3'b111 : DataO<=med_med; //a00>=a01 a00>=a02 a01>=a02 <==> a02<=a01<=a00
default: DataO<=DataO;
endcase
end */
reg [15:0] DataO_1;
reg [15:0] DataO_2;
reg [15:0] DataO_3;
reg [15:0] DataO_4;
reg [15:0] DataO_5;
reg [15:0] DataO_6;
reg [15:0] DataO;
always@(posedge ClkIn)
begin
DataO_1<=DataIn;
DataO_2<=DataO_1;
DataO_3<=DataO_2;
DataO_4<=DataO_3;
DataO_5<=DataO_4;
DataO_6<=DataO_5;

DataO<=DataO_6;
end

endmodule
0%