一、功能原理描述
前面我们成功找到了3x3的矩阵模板c1~c9,在这一章我们接着需要实现的是midfilter模块,其功能就是通过比较的方式寻找矩阵的中值,用它来代替图像的每一个像素点。如何寻找矩阵的中值呢?分为三步:
第一步:将矩阵的三行的每一行都按照{大、中、小}的位置顺序排序;
第二步:比较矩阵第一列3个数的大小,取出最小值;比较第二列的大小取出中值,比较第三列的大小取出最大值;
第三步:将第二步取出的大、中、小三个值作比较,比较出中值即为我们寻找的矩阵的中值。
二、端口描述和设计
老规矩看图:
输入信号:输入的信号都比较熟悉了,c1~c9是矩阵数据,全局信号clk、rst_n,以及前一个模块的完成标志信号mid3x3_done。
输出信号:输出的信号data_out[23:0]代表计算出的当前矩阵的中值,flag代表着矩阵的中值求取完成,通知前面的模块可以获取下一个矩阵了。
通过Verilog设计的端口和常量如下:
module midfilter#(
parameter picture_size =71200,
data_width =24
)(
input clk,rst_n,
input mid3x3_done,
input [data_width-1:0] c1,c2,c3,c4,c5,c6,c7,c8,c9,模板矩阵
output reg flag,
output [data_width-1:0]data_out ///滤波后输出数据
);
三、逻辑功能设计
在开始的时候,我们需要明确的是输入的矩阵数据位宽为24bit,表示了我们输入的其实是一个三层的3x3矩阵。逻辑功能设计可以分为三部分代码段:图层分离、中值求取、标志生成。
首先是图层分离,通过内部线型wire的定义如下:
wire [7:0] A [8:0];//第一图层矩阵
wire [7:0] B [8:0];//第二图层矩阵
wire [7:0] C [8:0];//第三图层矩阵
wire [7:0] A_sort [8:0];//大小排序后第一图层矩阵
wire [7:0] B_sort [8:0];//大小排序后第二图层矩阵
wire [7:0] C_sort [8:0];//大小排序后第三图层矩阵
wire [7:0] A_min ;//第一层模板第一列最小值
wire [7:0] B_min ;//第二层模板第一列最小值
wire [7:0] C_min ;//第三层模板第一列最小值
wire [7:0] A_mid ;//第一层模板第二列中间值
wire [7:0] B_mid ;//第二层模板第二列中间值
wire [7:0] C_mid ;//第三层模板第二列中间值
wire [7:0] A_max ;//第一层模板第三列最大值
wire [7:0] B_max ;//第二层模板第三列最大值
wire [7:0] C_max ;//第三层模板第三列最大值
wire [data_width-1:0]mid_data;//输出中间值
/*************************转换为矩阵*************************//
assign A[0]= c1[7:0];
assign A[1]= c2[7:0];
assign A[2]= c3[7:0];
assign A[3]= c4[7:0];
assign A[4]= c5[7:0];
assign A[5]= c6[7:0];
assign A[6]= c7[7:0];
assign A[7]= c8[7:0];
assign A[8]= c9[7:0];
assign B[0]= c1[15:8];
assign B[1]= c2[15:8];
assign B[2]= c3[15:8];
assign B[3]= c4[15:8];
assign B[4]= c5[15:8];
assign B[5]= c6[15:8];
assign B[6]= c7[15:8];
assign B[7]= c8[15:8];
assign B[8]= c9[15:8];
assign C[0]= c1[23:16];
assign C[1]= c2[23:16];
assign C[2]= c3[23:16];
assign C[3]= c4[23:16];
assign C[4]= c5[23:16];
assign C[5]= c6[23:16];
assign C[6]= c7[23:16];
assign C[7]= c8[23:16];
assign C[8]= c9[23:16];
每个内部连线的定义后面都有注释就不做解释了,需要讲一下的是assign这里就是将我们的三层图层的矩阵分A、B、C存放,后面每一层的算法处理方式是一模一样的直接复用就行了。
接下来就是中值求取:midfilter模块的核心:如何比较三个数的大小?当比较过后我们就能轻易的对这三个数据进行排序和找出后面需要的最大值、最小值、中值。
具体的比较模块的verilog实现程序如下:
module sort_data(
input clk ,rst_n,
input [7:0] a,b,c,
output reg [23:0] data_out{大,中,小}
);
wire A,B,C;
wire [2:0] state;
assign A=(a>b)?1:0;
assign B=(b>c)?1:0;
assign C=(c>a)?1:0;
assign state={A,B,C};
always@(posedge clk)
if(!rst_n)
begin
data_out<=24'b0;
end
else case(state)
3'b110:begin data_out<={a,b,c}; end
3'b100:begin data_out<={a,c,b}; end
3'b010:begin data_out<={b,a,c}; end
3'b011:begin data_out<={b,c,a}; end
3'b101:begin data_out<={c,a,b}; end
3'b001:begin data_out<={c,b,a}; end
default: begin data_out<={a,b,c}; end
endcase
endmodule
这个例程十分的简单,直接根据组合逻辑将输入的三个数中的两个两两比较,然后映射成3bit数据的某一bit,再根据这个3bit数据的数值唯一性利用简单的状态机输出大小顺序。
举个例子,如果输入的三个数:b>c>a,那么通过assign实现的三态门,计算出的state的二进制值就等于011,然后根据状态机3‘b011的输出data_out={b,c,a},默认顺序是大中小,结果表示没有问题。依次类推当我们要求取最大值时,只需要将这里的b值给到data_out,要求取中值时,只需要将这里的c值给到输出data_out,包括最小值的求取都可以这样操作。
所以通过简单的修改例化,就可以完成我们第一节当中所说的求中值的三步:
/*************************处理第一图层*************************//
//模板每一行为单位排序
genvar i;
generate
for(i=0;i<=2;i=i+1)
begin:Asort
sort_data A_array(
.clk(clk),
.rst_n(rst_n),
.a(A[0+i*3]),
.b(A[1+i*3]),
.c(A[2+i*3]),
.data_out({A_sort[0+i*3],A_sort[1+i*3],A_sort[2+i*3]})/{大,中,小}
);
end
endgenerate
//排序后取第一列最小值
min_data A1(
.clk(clk),
.rst_n(rst_n),
.a(A_sort[0]),
.b(A_sort[3]),
.c(A_sort[6]),
.data_out(A_min)
);
//排序后取第二列中间值
mid_data A2(
.clk(clk),
.rst_n(rst_n),
.a(A_sort[1]),
.b(A_sort[4]),
.c(A_sort[7]),
.data_out(A_mid)
);
//排序后取第三列最大值
max_data A3(
.clk(clk),
.rst_n(rst_n),
.a(A_sort[2]),
.b(A_sort[5]),
.c(A_sort[8]),
.data_out(A_max)
);
//排序后取中间值输出
mid_data A4(
.clk(clk),
.rst_n(rst_n),
.a(A_min),
.b(A_mid),
.c(A_max),
.data_out(mid_data[7:0])
);
首先复用sort_data模块对第一个图层的A矩阵的三行进行排序,然后利用min_data、mid_data、max_data三个模块分别求取排序过后矩阵的第一列的最小值、第二列的中值、第三列的最大值,最后将求取三个值的中值。
剩下的两个图层只需要将这里的A矩阵改成B、C矩阵复用操作即可。
最后一部分程序就是标志生成:主要是通过计数的方式完成功能
/*****************判定模板生成然后计数等待找中值完成********************
reg [1:0]cnt;
reg [2:0] state;
reg [data_width-1:0]data_out_reg;
always@(posedge clk)
begin
if(!rst_n)
begin
flag<=0;
cnt<=0;
state<=0;
data_out_reg<=0;
end
else case(state)
0:begin
flag<=0;
if(mid3x3_done) begin state<=1;cnt<=0;end
else begin state<=0; data_out_reg<=data_out_reg;end
end
1:begin if(cnt==2)begin cnt<=0; state<=2;flag<=1; data_out_reg<=mid_data;end
else begin cnt<=cnt+1; state<=1;end
end
2:begin
flag<=0;
data_out_reg<=data_out_reg;
state<=0;
end
default: state<=0;
endcase
end
实现的功能作用就是当mid3x3_done标志信号来之后开始对计数器cnt进行计数,计数到什么时候呢?需要计数到中值data_out求取完成,大概就是三个时钟周期也就是当cnt=2的时候产生一个中值完成标志,将flag拉高。文章来源:https://www.toymoban.com/news/detail-679770.html
四、总结
到这里基于FPGA的中值滤波的所有模块都设计实现完了,整体的难度都在中等偏下范围,但对于新入门的伙伴们来说应该还是十分的有用。整个的工程和所有的.v文件目前还没有找到较好的方式上传,大家如果需要的话可以留言。文章来源地址https://www.toymoban.com/news/detail-679770.html
到了这里,关于基于FPGA的中值滤波设计————(4)矩阵求取中值算法模块的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!