1 简介
在第七章介绍了基于三种卷积前的图像填充方式,并生成了3X3的图像卷积模板,第八章运用这种卷积模板进行了均值滤波的FPGA实现与MATLAB实现,验证了卷积模板生成的正确性和均值滤波算法的MATLAB算法实现。
由于均值滤波、中值滤波、腐蚀、膨胀都依赖与卷积核且实现较为简单,这里将对剩下的三种图像处理方式一起介绍。
2 处理前图像的生成。
由于在第八章的中值滤波的试验需要放大观察,且去噪效果几乎看不到,这里我们先对灰度图添加随机的椒盐噪声,并利用添加椒盐噪声后的图像做数字图像处理的FPGA算法实现以及MATLAB算法验证。这样可以方便观察图像处理前后的差异。
对图像随机添加椒盐噪声的MATLAB代码如下:
clear;
clc;
close all;
a = imread('../img/1920X1080_gray.bmp');
subplot(1,2,1),imshow(a),title('原图');
b=imnoise(a,'salt & pepper',0.01);
subplot(1,2,2),imshow(b),title('加入噪声密度:0.01的椒盐噪声');
imwrite(b,'../img/gray.bmp');
添加前后的图像对比如下:
可以看到图像现在有了一定数量的噪点。后续的图像处理均以添加椒盐噪声后的图像作为原始图像来处理。
3 三种算法的FPGA实现与MATLAB验证
3.1 中值滤波
中值滤波是一种非线性的滤波方式,是一种排序滤波,它将图像的某一个像素点的灰度值设置成该点某一邻域窗口内所有像素点灰度值排序后的中值。
中值滤波对孤立的噪点像素即椒盐噪声具有良好的滤波效果,且不同于均值滤波对图像边缘性的破坏,中值滤波不会显著性的使图像边缘变得模糊。
1.用一个模板来移动窗口,并将模板中心与图中的某个像素位置重合;
2.读取模板的像素值并按照大小进行排序;
3.选取灰度序列中,最中间的一个灰度值;
4.将灰度值赋值给模板中间的像素;
3.2 3X3模板的中值滤波的FPGA算法实现简介
在3X3的卷积模板中,找出中值共需要经历七次排序。
1.前三次排序分别对3X3模板的三行进行排序,得到min、mid、max。
2.后三次排序在三个最小值中找出一个最大值,三个中值中找出一个中值,三个最大值中找出一个最小值。
3.在步骤二找出的三个值中找出中值,这个值即为最终需要赋值的像数值。
3.2.1 sort_3模块
由于3X3卷积模板中值滤波共需要经过七次三个数的排序,因此首先需要创建一个排序模块供中值滤波模块调用。三个数的排序模块代码如下:
`timescale 1ns / 1ps
module sort_3 #(
parameter DW = 8
)(
input wire clk ,
input wire rst_n ,
input wire data_de ,
input wire [DW-1:0] data1 ,
input wire [DW-1:0] data2 ,
input wire [DW-1:0] data3 ,
output reg data_out_de ,
output reg [DW-1:0] min_data ,
output reg [DW-1:0] mid_data ,
output reg [DW-1:0] max_data
);
always @(posedge clk)
if(rst_n == 0)
min_data <= 0;
else if(data1 <= data2 && data1 <= data3)
min_data <= data1 ;
else if(data2 <= data1 && data2 <= data3)
min_data <= data2 ;
else
min_data <= data3 ;
always @(posedge clk)
if(rst_n == 0)
mid_data <= 0;
else if((data1 <= data2 && data1 >= data3) || (data1 >= data2 && data1 <= data3))
mid_data <= data1 ;
else if((data2 <= data1 && data2 >= data3) || (data2 >= data1 && data2 <= data3))
mid_data <= data2 ;
else
mid_data <= data3 ;
always @(posedge clk)
if(rst_n == 0)
max_data <= 0;
else if(data1 >= data2 && data1 >= data3)
max_data <= data1 ;
else if(data2 >= data1 && data2 >= data3)
max_data <= data2 ;
else
max_data <= data3 ;
always @(posedge clk)
if(rst_n == 0)
data_out_de <= 0;
else
data_out_de <= data_de;
endmodule
3.2.2 中值滤波模块
中值滤波本身就是一种排序算法,只需要多次三个数的排序即可完成,因此此算法模块内部就是sort_3模块的例化与连接,代码如下:
`timescale 1ns / 1ps
module mid_filter#(
parameter DW = 8
)(
input wire clk ,
input wire rst_n ,
input wire matrix_de ,
input wire [DW-1:0] matrix11 ,
input wire [DW-1:0] matrix12 ,
input wire [DW-1:0] matrix13 ,
input wire [DW-1:0] matrix21 ,
input wire [DW-1:0] matrix22 ,
input wire [DW-1:0] matrix23 ,
input wire [DW-1:0] matrix31 ,
input wire [DW-1:0] matrix32 ,
input wire [DW-1:0] matrix33 ,
output wire mid_data_de ,
output wire [DW-1:0] mid_data
);
wire matrix1_de ;
wire [DW-1:0] min_data1 ;
wire [DW-1:0] mid_data1 ;
wire [DW-1:0] max_data1 ;
wire matrix2_de ;
wire [DW-1:0] min_data2 ;
wire [DW-1:0] mid_data2 ;
wire [DW-1:0] max_data2 ;
wire matrix3_de ;
wire [DW-1:0] min_data3 ;
wire [DW-1:0] mid_data3 ;
wire [DW-1:0] max_data3 ;
wire matrix4_de ;
wire [DW-1:0] max_min_data ;
wire matrix5_de ;
wire [DW-1:0] mid_mid_data ;
wire matrix6_de ;
wire [DW-1:0] min_max_data ;
wire matrix7_de ;
wire [DW-1:0] mid_filter_data ;
sort_3 #(
.DW (DW)
)u1_sort_3(
.clk (clk ),
.rst_n (rst_n ),
.data_de (matrix_de ),
.data1 (matrix11 ),
.data2 (matrix12 ),
.data3 (matrix13 ),
.data_out_de (matrix1_de ),
.min_data (min_data1 ),
.mid_data (mid_data1 ),
.max_data (max_data1 )
);
sort_3 #(
.DW (DW)
)u2_sort_3(
.clk (clk ),
.rst_n (rst_n ),
.data_de (matrix_de ),
.data1 (matrix21 ),
.data2 (matrix22 ),
.data3 (matrix23 ),
.data_out_de (matrix2_de ),
.min_data (min_data2 ),
.mid_data (mid_data2 ),
.max_data (max_data2 )
);
sort_3 #(
.DW (DW)
)u3_sort_3(
.clk (clk ),
.rst_n (rst_n ),
.data_de (matrix_de ),
.data1 (matrix31 ),
.data2 (matrix32 ),
.data3 (matrix33 ),
.data_out_de (matrix3_de ),
.min_data (min_data3 ),
.mid_data (mid_data3 ),
.max_data (max_data3 )
);
sort_3 #(
.DW (DW)
)u4_sort_3(
.clk (clk ),
.rst_n (rst_n ),
.data_de (matrix1_de ),
.data1 (max_data1 ),
.data2 (max_data2 ),
.data3 (max_data3 ),
.data_out_de (matrix4_de ),
.min_data (max_min_data ),
.mid_data ( ),
.max_data ( )
);
sort_3 #(
.DW (DW)
)u5_sort_3(
.clk (clk ),
.rst_n (rst_n ),
.data_de (matrix1_de ),
.data1 (mid_data1 ),
.data2 (mid_data2 ),
.data3 (mid_data3 ),
.data_out_de (matrix5_de ),
.min_data ( ),
.mid_data (mid_mid_data ),
.max_data ( )
);
sort_3 #(
.DW (DW)
)u6_sort_3(
.clk (clk ),
.rst_n (rst_n ),
.data_de (matrix1_de ),
.data1 (min_data1 ),
.data2 (min_data2 ),
.data3 (min_data3 ),
.data_out_de (matrix6_de ),
.min_data ( ),
.mid_data (min_max_data ),
.max_data ( )
);
sort_3 #(
.DW (DW)
)u7_sort_3(
.clk (clk ),
.rst_n (rst_n ),
.data_de (matrix4_de ),
.data1 (max_min_data ),
.data2 (mid_mid_data ),
.data3 (min_max_data ),
.data_out_de (matrix7_de ),
.min_data ( ),
.mid_data (mid_filter_data),
.max_data ( )
);
assign mid_data = mid_filter_data ;
assign mid_data_de = matrix7_de ;
endmodule
例化后用vivado查看连线有
3.2.3 中值滤波的顶层连接
`timescale 1ns / 1ps
module top_mid_filter#(
parameter COL = 1920 ,
parameter ROW = 1080 ,
parameter PADDING = 2 ,
parameter DW = 8
)(
input wire clk ,
input wire rst_n ,
input wire data_de ,
input wire [DW-1:0] data ,
output wire mid_data_de ,
output wire [DW-1:0] mid_data
);
wire [DW-1:0] matrix11 ;
wire [DW-1:0] matrix12 ;
wire [DW-1:0] matrix13 ;
wire [DW-1:0] matrix21 ;
wire [DW-1:0] matrix22 ;
wire [DW-1:0] matrix23 ;
wire [DW-1:0] matrix31 ;
wire [DW-1:0] matrix32 ;
wire [DW-1:0] matrix33 ;
padding_matrix #(
.COL (COL ),
.ROW (ROW ),
.PADDING (PADDING )
)u_padding_matrix(
.clk (clk ),
.rst_n (rst_n ),
.data (data ),
.data_de (data_de ),
.matrix_de (matrix_de ),
.matrix11 (matrix11 ),
.matrix12 (matrix12 ),
.matrix13 (matrix13 ),
.matrix21 (matrix21 ),
.matrix22 (matrix22 ),
.matrix23 (matrix23 ),
.matrix31 (matrix31 ),
.matrix32 (matrix32 ),
.matrix33 (matrix33 )
);
mid_filter #(
.DW (DW)
)u_mid_filter(
.clk (clk ),
.rst_n (rst_n ),
.matrix_de (matrix_de ),
.matrix11 (matrix11 ),
.matrix12 (matrix12 ),
.matrix13 (matrix13 ),
.matrix21 (matrix21 ),
.matrix22 (matrix22 ),
.matrix23 (matrix23 ),
.matrix31 (matrix31 ),
.matrix32 (matrix32 ),
.matrix33 (matrix33 ),
.mid_data_de (mid_data_de ),
.mid_data (mid_data )
);
endmodule
例化后用vivado查看连线有
3.3 中值滤波的FPGA仿真
仿真依旧是img_gen模块读取pre.txt的像素数据,tb顶层写回仿真后的像素数据到mid_filter.txt文本。
`timescale 1ns / 1ps
module img_gen
#(
parameter ACTIVE_IW = 1920 ,
parameter ACTIVE_IH = 1080 ,
parameter TOTAL_IW = 2200 ,
parameter TOTAL_IH = 1100 ,
parameter H_START = 100 ,
parameter V_START = 4
)(
input wire clk ,
input wire rst_n ,
output reg vs ,
output reg de ,
output reg [7:0] data
);
reg [7:0] raw_array [ACTIVE_IW*ACTIVE_IH-1:0];
integer i;
initial begin
for (i=0 ;i<ACTIVE_IH*ACTIVE_IW ; i=i+1) begin
raw_array[i] = 0;
end
end
initial begin
$readmemh("H:/picture/Z7/lesson8/data/pre.txt",raw_array);
end
reg [15:0] hcnt ;
reg [15:0] vcnt ;
reg h_de ;
reg v_de ;
reg index_de ;
reg [31:0] index ;
always @(posedge clk or negedge rst_n)
if(!rst_n)
hcnt <= 'd0;
else if(hcnt == TOTAL_IW - 1)
hcnt <= 'd0;
else
hcnt <= hcnt + 1'b1;
always @(posedge clk or negedge rst_n)
if(!rst_n)
vcnt <= 'd0;
else if(hcnt == TOTAL_IW - 1 && vcnt == TOTAL_IH - 1)
vcnt <= 'd0;
else if(hcnt == TOTAL_IW - 1)
vcnt <= vcnt + 1'b1;
else
vcnt <= vcnt;
always @(posedge clk or negedge rst_n)
if(!rst_n)
vs <= 'd0;
else if(vcnt>=2)
vs <= 1'b1;
else
vs <= 1'b0;
always @(posedge clk or negedge rst_n)
if(!rst_n)
h_de <= 'd0;
else if(hcnt >= H_START && hcnt < H_START + ACTIVE_IW)
h_de <= 1'b1;
else
h_de <= 1'b0;
always @(posedge clk or negedge rst_n)
if(!rst_n)
v_de <= 'd0;
else if(vcnt >= V_START && vcnt < V_START + ACTIVE_IH)
v_de <= 1'b1;
else
v_de <= 1'b0;
always @(posedge clk or negedge rst_n)
if(!rst_n)
index_de <= 'd0;
else if(h_de == 1'b1 && v_de == 1'b1)
index_de <= 1'b1;
else
index_de <= 1'b0;
always @(posedge clk or negedge rst_n)
if(!rst_n)
index <= 'd0;
else if(index == ACTIVE_IW * ACTIVE_IH-1)
index <= 0;
else if(index_de == 1'b1)
index <= index + 1;
else
index <= index;
always @(posedge clk or negedge rst_n)
if(!rst_n)
de <= 'd0;
else
de <= index_de;
always @(posedge clk or negedge rst_n)begin
if(index_de == 1'b1)
data <= raw_array[index];
else
data <= 0;
end
endmodule
`timescale 1ns / 1ps
module tb_top_mid_filter(
);
reg clk ;
reg rst_n ;
wire [7:0] data ;
wire de ;
wire vs ;
wire [7:0] mid_data;
wire mid_data_de;
integer outfile;
always #5 clk <= ~clk;
initial begin
clk <= 0;
rst_n = 0;
#100
rst_n = 1;
outfile = $fopen("H:/picture/Z7/lesson8/data/mid_filter.txt","w");
end
img_gen
#(
.ACTIVE_IW (1920 ),
.ACTIVE_IH (1080 ),
.TOTAL_IW (2200 ),
.TOTAL_IH (1100 ),
.H_START (4 ),
.V_START (4 )
)u_img_gen(
.clk (clk ),
.rst_n (rst_n ),
.vs (vs ),
.de (de ),
.data (data )
);
top_mid_filter#(
.COL ( 1920 ),
.ROW ( 1080 ),
.PADDING ( 2 ),
.DW ( 8 )
)u_top_mid_filter(
.clk (clk ),
.rst_n (rst_n ),
.data_de (de ),
.data (data ),
.mid_data_de (mid_data_de ),
.mid_data (mid_data )
);
reg vs_r ;
always @(posedge clk)
if(rst_n == 0)
vs_r <= 1'b0;
else
vs_r <= vs;
always @(posedge clk)
if(~vs&&vs_r)
$stop;
else if(mid_data_de)
$fdisplay(outfile,"%h\t",mid_data);
endmodule
3.3 中值滤波的MATLAB算法实现与验证
中值滤波的MATLAB算法实现如下:
clc;
clear all;
GRAY = imread('../img/gray.bmp');
[row,col] = size(GRAY);
GRAY = double(GRAY);
mid_filter_padding = zeros(row+2,col+2);
mid_filter_result = zeros(row,col);
for i = 1:row
for j = 1:col
mid_filter_padding(i+1,j+1) = GRAY(i,j);
end
end
for i = 1:row+2
mid_filter_padding(i,1) = mid_filter_padding(i,2);
mid_filter_padding(i,col+2) = mid_filter_padding(i,col+1);
end
for i = 1:col+2
mid_filter_padding(1,i) = mid_filter_padding(2,i);
mid_filter_padding(row+2,i) = mid_filter_padding(row+1,i);
end
for i = 2:row+1
for j = 2:col+1
matrix11 = mid_filter_padding(i-1,j-1);
matrix12 = mid_filter_padding(i-1,j);
matrix13 = mid_filter_padding(i-1,j+1);
matrix21 = mid_filter_padding(i,j-1);
matrix22 = mid_filter_padding(i,j);
matrix23 = mid_filter_padding(i,j+1);
matrix31 = mid_filter_padding(i+1,j-1);
matrix32 = mid_filter_padding(i+1,j);
matrix33 = mid_filter_padding(i+1,j+1);
sort_buf = sort([matrix11,matrix12,matrix13,matrix21,matrix22,matrix23,matrix31,matrix32,matrix33],'ascend');
mid_filter_result(i-1,j-1) = sort_buf(1,5);
end
end
matlab_Y = uint8(floor(mid_filter_result));
a = textread('../data/mid_filter.txt','%s');
IMdec1 = hex2dec(a);
col = 1920;
row = 1080;
IM1 = reshape(IMdec1,col,row);
fpga_Y = uint8(IM1)';
b = textread('../data/pre.txt','%s');
IMdec2 = hex2dec(b);
col = 1920;
row = 1080;
IM2 = reshape(IMdec2,col,row);
gray = uint8(IM2)';
subplot(1,3,1)
imshow(gray),title('原始图像');
subplot(1,3,2)
imshow(matlab_Y),title('MATLAB中值滤波算法图像');
subplot(1,3,3)
imshow(fpga_Y),title('FPGA中值滤波算法图像');
sub = matlab_Y - fpga_Y;
min_sub = min(min(sub));
max_sub = max(max(sub));
MATLAB算法实现与FPGA仿真结果完全一致,验证通过。由于在添加椒盐噪声的时候,添加的是孤立的噪声点,完美符合中值滤波算法,所以还原效果最好,但是当椒盐噪声的像素点过多的时候,中值滤波需要更大的卷积模板,且也会一定程度的使边缘模糊。
3.2 腐蚀与膨胀滤波
中值滤波是取卷积模板的中位数做滤波算法,但是当单个椒盐噪点的面积过大时,此算法并不适用,腐蚀算法就是取卷积模板的最小值,膨胀就是取卷积模板的最大值。因此只需要在中值滤波的基础上改变卷积的连线即可。腐蚀和膨胀经常成对的使用,具体使用方式可以参考图像的膨胀与腐蚀这篇文章。
这里只分别贴出对图像进行腐蚀和膨胀的处理前后效果图。