Verilog实现图像处理的行缓存Line Buffer

在图像处理中,难免会遇到对图像进行卷积或者模板的局部处理,例如ISP中的一些算法,很大部分都需要一个窗口,在实时视频处理中,可以利用行缓存Line buffer可以暂存几行数据,然后同时输出每行中的对应列的像素。

下面以一个三行缓存进行解释

DVP像素流从Shif_in输入,像素不流不断在行缓存中移动,行缓存的最后一个像素使用line_out来保存,通过这种方式,lin_out输出部分就是每行对应列中的像素,在算法模块中,对line_out寄存三拍,就可以得到3x3的窗口大小。

暂存区的设计可以使用一个双口同步RAM来实现:

cpp 复制代码
module simple_dp_ram
#(
	parameter DW = 8,
	parameter AW = 4,
	parameter SZ = 2**AW
)
(
	input           clk,
	input           wen,
	input  [AW-1:0] waddr,
	input  [DW-1:0] wdata,
	input           ren,
	input  [AW-1:0] raddr,
	output [DW-1:0] rdata
);
	reg [DW-1:0] mem [SZ-1:0];
	always @ (posedge clk) begin
		if (wen) begin
			mem[waddr] <= wdata;
		end
	end
	reg [DW-1:0] q;
	always @ (posedge clk) begin
		if (ren) begin
			q <= mem[raddr];
		end
	end
    
	// always @(posedge clk) begin
	// 	// 先执行读操作
	// 	if (ren) begin
	// 		q <= mem[raddr];
	// 	end

	// 	// 后执行写操作
	// 	if (wen) begin
	// 		mem[waddr] <= wdata;
	// 	end
	// end

	assign rdata = q;
endmodule

这里涉及到一个问题,当读地址和写地址相同时,会导致冲突吗?

我这里找到的解释是,具体行为会依赖于底层的实现,在同一个时钟周期同时读写同一个地址在FPGA中通常是读优先的,即会先读取该地址的旧值,然后向该地址写如新值。(有大佬了解的可以帮忙解释以下:))。

行缓存LineBuffer源码如下所示:

cpp 复制代码
///基于卷积或者模板的算法需要使用该模块
module shift_register
#(
	parameter BITS = 8,         
	parameter WIDTH = 480,    //行缓存宽度
	parameter LINES = 3       //行缓存行数
)
(
	input                clock,    
	input                clken, 
	input  [BITS-1:0]    shiftin,        //输入数据流
	output [BITS-1:0]    shiftout,       //当前行最后一个像素的数据输出。
	output [BITS*LINES-1:0] tapsx        //行缓存的输出:包含每行最后一个像素的输出,拼接成一个长向量
);

	localparam RAM_SZ = WIDTH - 1;   //每行的实际大小
	localparam RAM_AW = clogb2(RAM_SZ);  //计算地址宽度


    ///地址指针逻辑
	reg [RAM_AW-1:0] pos_r;   //pos_r:记录当前写入的位置。
    ///(RAM_SZ[RAM_AW-1:0] - 1'b1)实际上就是WIDTH - 2
    //行缓存的最后一个像素使用line_out来保存,RAM实际上只保存了WDITH-1个像素(pos在0~WIDTH-2之间)
	wire [RAM_AW-1:0] pos = pos_r < RAM_SZ ? pos_r : (RAM_SZ[RAM_AW-1:0] - 1'b1);  ///当 pos_r 超过范围时限制其值,用于确保地址有效
	always @ (posedge clock) begin
		if (clken) begin     
			if (pos_r < RAM_SZ - 1)  
				pos_r <= pos_r + 1'b1;   //地址指针 pos_r 循环递增,直到到达最大地址后重置为 0
			else
				pos_r <= 0;
		end
	end

	reg [BITS-1:0] in_r;  
	always @ (posedge clock) begin
		if (clken) begin
			in_r <= shiftin;      //寄存输入数据 shiftin,用于提供给第 0 行的 RAM 写入。
		end
	end

	wire [BITS-1:0] line_out[LINES-1:0];    //记录每行的最后一个像素

    ///生成菊花链行缓存结构,在第二行开始才将上一行末尾连接到下一行的输入中,第一行输入in_r,每一行的最后一个像素寄存在line_out中
	generate          
		genvar i;
		for (i = 0; i < LINES; i = i + 1) begin : gen_ram_inst
            //在当前周期,line_out[i] 先从 pos 地址中读取之前存储的数据。随后将 (i > 0 ? line_out[i-1] : in_r) 写入 pos 地址。
			simple_dp_ram #(BITS, RAM_AW, RAM_SZ) u_ram(clock, clken, pos, (i > 0 ? line_out[i-1] : in_r), clken, pos, line_out[i]);
		end
	endgenerate


	assign shiftout = line_out[LINES-1];     //移位输出
	generate
		genvar j;
		for (j = 0; j < LINES; j = j + 1) begin : gen_taps_assign
			assign tapsx[(BITS*j)+:BITS] = line_out[j];
		end
	endgenerate


    //计算需要位宽
	function integer clogb2;    
	input integer depth;
	begin
		for (clogb2 = 0; depth > 0; clogb2 = clogb2 + 1)
			depth = depth >> 1;
	end
	endfunction
endmodule

这里使用了一个clogb2函数来获取配置的WIDTH需要的位宽。在代码中涉及了同一时钟周期对双口RAM中同一地址的读写问题,既然项目最后能跑起来,应该就是读优先的。

相关推荐
Tiandaren1 小时前
Python 参数配置使用 XML 文件的教程:轻松管理你的项目配置
xml·开发语言·图像处理·人工智能·python·深度学习
杨德杰19 小时前
ISP(Image Signal Processor)——HDR技术总结
图像处理·isp·图像信号处理器·isp pipeline
加德霍克1 天前
Opencv之识别图片颜色并绘制轮廓
图像处理·人工智能·opencv·学习·计算机视觉
想成为风筝2 天前
HALCON算子函数 Filter(过滤)(1)
图像处理·深度学习
CUGLin2 天前
遥感图像处理二(ENVI5.6 Classic)
图像处理·人工智能·计算机视觉
GOTXX2 天前
【自动驾驶】单目摄像头实现自动驾驶3D目标检测
图像处理·人工智能·python·目标检测·机器学习·3d·自动驾驶
小成晓程2 天前
opencv——( 二值化函数 、自适应二值化函数、腐蚀函数、膨胀函数、仿射变换函数、透视变换函数)
图像处理·opencv·计算机视觉
Learning改变世界3 天前
Matlab(三)——图像处理
开发语言·图像处理·matlab
liuming19923 天前
Halcon中lines_gauss(Operator)算子原理及应用详解
图像处理·人工智能·深度学习·计算机视觉·视觉检测