参考文献
[1] https://www.cnblogs.com/xianyuIC/p/12401085.html
前言
在数字图像处理中,很多图像算法的实现需要卷积操作,而图像卷积操作是以滑动窗为基础的,滑动窗大小可为1x1、3x3等。在实际应用中,图像数据基本上是实时输入到FPGA中,因此需要对数据进行行缓存才能同时输出3x3个并行数据。本文将介绍FPGA如何得到3x3数据核。
一、行缓存
矩形窗口最常用的窗口有3x3和5x5模型,下面介绍3x3窗口的硬件实现方法:
1)通过2个或3个FIFO来实现;
2)通过2个或3个RAM来实现;
3)通过2个或3个Shift_Ram通过移位来实现;
本设计主要用2个FIFO来实现3x3矩形窗口。3x3窗口本质上就是将窗口中的9个像素点在一个时钟周期下同时并行输出,然后再对9个数据进行相应的操作运算。但是图像数据在硬件中是按照时钟一个像素一个像素进行传输的,所以我们必须使用寄存器进行缓存相应行数的数据,再将输出同时输出。本设计采用的方法是,调用两次FIFOIP核,该FIFO的深度只要超过2行的数据个数就行,两个FIFO写数据相同,都是输入的像素数据,两个FIFO的读写信号通过行数的进行使能。其中一个FIFO从第1行开始读,不写最后一行,第二个FIFO从第2行开始读,不写最后2行。FIFO读写信号示意图如下:
如此,从第3行数据开始,两个FIFO输出的数据核输入的数据形成3行平行的数据。
二、代码实现
下面实现的是对二值图像进行行缓存并并行输出3x3的并行数据。可以通过简单的位宽修改成其他位宽的数据。需要注意的是FIFO的深度。
//耗时2clk
module Matrix_3X3
(
input clk,
input rst_n,
input vs,
input hs,
input de,
input data_in,//输入的一位数据,二值化的数据
output matrix_vs,
output matrix_hs,
output matrix_de,
output reg matrix_p11,//0或者1
output reg matrix_p12,
output reg matrix_p13,
output reg matrix_p21,
output reg matrix_p22,
output reg matrix_p23,
output reg matrix_p31,
output reg matrix_p32,
output reg matrix_p33
);
wire row1_data;
wire row2_data;
wire read_vs;
wire read_de;
//wire data_bw;
reg row3_data;
reg [1:0] vs_r;
reg [1:0] hs_r;
reg [1:0] de_r;
assign read_vs = hs_r[0];
assign read_de = de_r[0];
assign matrix_vs = vs_r[1];
assign matrix_hs = hs_r[1];
assign matrix_de = de_r[1];//延迟两个周期产生矩阵
always@(posedge clk or negedge rst_n) begin
if(!rst_n)
row3_data <= 0;
else begin
if(de)
row3_data <= data_in ;
else
row3_data <= row3_data ;
end
end
//产生平行数据,1clk
line_shift line_Shift_m0
(
.clk (clk),
.vs (vs),
.de (de),
.rst_n (rst_n),
.shiftin (data_in ), //二值化后的数据1bit,且取反,1代表白色,0代表黑色
.taps2x (row2_data),
.taps1x (row1_data)
);
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
vs_r <= 0;
hs_r <= 0;
de_r <= 0;
end
else begin
vs_r <= { vs_r[0],vs };
hs_r <= { hs_r[0],hs };
de_r <= { de_r[0],de };
end
end
always@(posedge clk or negedge rst_n) begin
if(!rst_n) begin
{matrix_p11, matrix_p12, matrix_p13} <= 3'b0;//初始化为黑色
{matrix_p21, matrix_p22, matrix_p23} <= 3'b0;
{matrix_p31, matrix_p32, matrix_p33} <= 3'b0;
end
else if(read_vs) begin //延迟一个clk与输出的平行数据同步
if(read_de) begin
{matrix_p11, matrix_p12, matrix_p13} <= {matrix_p12, matrix_p13, row1_data};
{matrix_p21, matrix_p22, matrix_p23} <= {matrix_p22, matrix_p23, row2_data};
{matrix_p31, matrix_p32, matrix_p33} <= {matrix_p32, matrix_p33, row3_data};
end
else begin
{matrix_p11, matrix_p12, matrix_p13} <= {matrix_p11, matrix_p12, matrix_p13};
{matrix_p21, matrix_p22, matrix_p23} <= {matrix_p21, matrix_p22, matrix_p23};
{matrix_p31, matrix_p32, matrix_p33} <= {matrix_p31, matrix_p32, matrix_p33};
end
end
else begin
{matrix_p11, matrix_p12, matrix_p13} <= 3'b0;//数据无效区域补零,边缘补0
{matrix_p21, matrix_p22, matrix_p23} <= 3'b0;
{matrix_p31, matrix_p32, matrix_p33} <= 3'b0;
end
end
endmodule
//延迟一个clk
module line_shift
#(
parameter H = 10'd768,//图像高
parameter W = 10'd1024//图像宽
)
(
input clk,
input vs,
input de,
input rst_n,
input shiftin, //输入的数据,延迟一个周期就是第三行数据,为什么?为了与第二第一行数据保持同步
output taps2x, //输出的数据,第二行数据
output taps1x //输出的数据,第一行数据
);
wire rd_data1 ;//移位寄存器宽度1,深度1024
wire rd_data2 ;//第二级移位寄存器
assign taps2x = rd_data1;
assign taps1x = rd_data2;
reg [9:0] cnt_row;
reg de_d,vs_d;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_row<=10'd0;
else if(!de && de_d)
cnt_row<=cnt_row+1;
else if(!vs && vs_d)
cnt_row<=10'd0;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
de_d<=1'b0;vs_d<=1'b0;
end
else begin
de_d<=de;vs_d<=vs;
end
end
wire wr_en1,rd_en1,wr_en2,rd_en2;
assign wr_en1 = (de && cnt_row < H-1)? 1'b1:1'b0;//不写最后一行,0~766
assign rd_en1 = (de && cnt_row > 0 )? 1'b1:1'b0;//从第一行开始读,1~767
assign wr_en2 = (de && cnt_row < H-2)? 1'b1:1'b0;//不写最后两行,0~765
assign rd_en2 = (de && cnt_row > 1 )? 1'b1:1'b0;//从第二行开始读,2~767
//fifo深度 >= 2*W
line_shift_fifo fifo_row2 (
.clk (clk ), // input
.rst (!rst_n ), // input
.wr_en (wr_en1 ), // input
.wr_data (shiftin ), // input
.wr_full ( ), // output
.almost_full ( ), // output
.rd_en (rd_en1 ), // input
.rd_data (rd_data1 ), // output
.rd_empty ( ), // output
.almost_empty ( ) // output
);
line_shift_fifo fifo_row1 (
.clk (clk ), // input
.rst (!rst_n ), // input
.wr_en (wr_en2 ), // input
.wr_data (shiftin ), // input
.wr_full ( ), // output
.almost_full ( ), // output
.rd_en (rd_en2 ), // input
.rd_data (rd_data2 ), // output
.rd_empty ( ), // output
.almost_empty ( ) // output
);
endmodule