FPGA图像处理----卷积窗口求累加和

求解累加和的意义

  在对图像进行卷积处理的时候,经常会使用到卷积操作,对应到FPGA中就是乘加操作。其中在相加这个阶段,对于某些算子,需要将卷积窗口中的数据全部相加,例如Sobel,均值滤波等。若是3x3的卷积运算,那么在求和阶段是比较简单的,但是,若卷积算子是5x5,7x7乃至更大的时候,为了满足FPGA处理的时序的要求,不要在一个时钟周期执行过多的加法运算。因此,考虑一种更加通用且更容易满足时序要求的方法来求累加和是有极大的益处的。

累加和求解过程

  若要求解下面的7x7的矩阵的累加和,那么一共需要完成49个数据的加法,若直接在一个时钟周期完成49个数据的加法是不太现实的,最理想的情况下是每个加法器,在每个时钟周期,只进行两个数的加法运算,这样更容易满足时序要求。在处理组合逻辑延时过大的问题时,采用插入寄存器形成流水线的这种方式,是比较常用的。
FPGA图像处理----卷积窗口求累加和_第1张图片
  在求解以7x7的矩阵内部的和时,可以进行的步骤如下图所示,相邻两数,两个一组,进行相加,相加的数为奇数个时,需要将最后一个数缓存,这样进行第一级加法运算。完成第一步加法运算后,下一步的操作采用的是和上一级同样的操作方式。
  在第一个时钟周期执行24次加法,将最后一个数缓存,在第二个时钟周期,进行12次加法,将最后一个数缓存,在第三个时钟周期,进行6次加法,以此缓存,知道最后一次,只有两个数进行相加,那么本次累加和求解完成。
FPGA图像处理----卷积窗口求累加和_第2张图片
  通过上面的推导可以得到如下结论:

假设:本次待相加的数目为n
(1) 本次需要进行的加法运算共有[n/2]组,也就是需要[n/2]的加法器,需要的寄存器的数目为[n/2] + n%2(其中[n/2]表示取整,n%2表示取余)
(2) 下一次运算需要相加的数目为[n/2] + n%2
(3)每一级的运算需要一个时钟周期的开销
  因此采用递归的方式,来完成这个运算是再方便不过了。但是在C/C++或其他高级语言中使用递归是在方便不过的事情了,但是在FPGA中想要使用递归还是有点麻烦的,这时候就需要灵活的使用generate语句,来在每一级生成不同的电路。

程序设计

/*============================================
#
# Author: Wcc - [email protected]
#
# QQ : 1530604142
#
# Last modified: 2020-07-08 20:02
#
# Filename: add_tree.v
#
# Description: 
#
============================================*/
`timescale 1ns / 1ps

module add_tree #(
	//==========================================
	//parameter define
	//==========================================
	parameter	KSZ 		= 3*3					,//卷积核大小
	parameter	IMG_WIDTH 	= 128					,//图像宽度,每个Line_buffer需要缓存的数据量
	parameter	IMG_HEIGHT	= 128					,//图像高度
	parameter 	HALF_IDX	= KSZ >> 1				,//输入数据的一半位置处
	parameter	KSZ_NEW		= (KSZ >> 1) + (KSZ%2)	,//下次计算的大小
	parameter	DW 			= 8						,//数据位宽
	parameter	DW_NEW		= DW + 1 				 //下一级的数据位宽
	)(
	input 	wire 					clk 		,
	input	wire					rst 		,
	input	wire					pi_hs		,
	input	wire					pi_vs		,
	input	wire					pi_de		,	
	input	wire					pi_dv		,
	input	wire	[DW*KSZ -1: 0]	pi_data		,


	output 	wire 					po_dv		,
	output 	wire 					po_de		,
	output 	wire 					po_hs		,
	output 	wire 					po_vs		,
	output	wire	[2*(DW+1) - 1 : 0]	po_data
    );

generate
	genvar i;
	reg 	[KSZ_NEW*(DW_NEW) - 1 : 0]	sum_tmp	;
	reg 								tmp_dv	;
	reg 								tmp_de	;
	reg 								tmp_hs	;
	reg 								tmp_vs	;
	//当前不是最后一次加法
	if (KSZ != 2) begin
		for(i = 0; i < HALF_IDX; i = i + 1)
		begin: cal_sum
			always @(posedge clk) begin
				if (rst==1'b1) begin
					sum_tmp[(i+1)*DW_NEW - 1 : i*DW_NEW] <= 'd0;
					tmp_dv <= 1'b0;
					tmp_de <= 1'b0;
					tmp_hs <= 1'b0;
					tmp_vs <= 1'b0;
				end
				else begin
					//两数相加,位宽扩充一位
					//eg:sum_tmp[8:0] = pi_data[7:0] + pi_data[15:8];
					//eg:sum_tmp[17:9] = pi_data[23:16] + pi_data[31:24];
					//eg:sum_tmp[26:18] = pi_data[39:32] + pi_data[47:40];
					//eg:sum_tmp[35:27] = pi_data[55:48] + pi_data[63:56];
					sum_tmp[(i+1)*DW_NEW - 1 : i*DW_NEW] <= pi_data[(2*i + 1) * DW - 1 : (2*i) * DW] + pi_data[(2*i + 2) * DW - 1 : (2*i + 1) * DW];
					tmp_dv <= pi_dv;
					tmp_de <= pi_de;
					tmp_hs <= pi_hs;
					tmp_vs <= pi_vs;
				end
			end	
		end
		//递归调用求和模块,将本季求和计算得到的模块,传递到下一级
			add_tree #(
			.KSZ(KSZ_NEW),
			.DW(DW_NEW)
		) inst_add_tree (
			.clk     (clk),
			.rst     (rst),
			.pi_hs   (tmp_hs),
			.pi_vs   (tmp_vs),
			.pi_de   (tmp_de),
			.pi_dv   (tmp_dv),
			.pi_data (sum_tmp),
			.po_dv   (po_dv),
			.po_de   (po_de),
			.po_hs   (po_hs),
			.po_vs   (po_vs),
			.po_data (po_data)
		);
	end
	//==========================================
	//当前需要相加的个数为奇数个需要将最后一个数据缓存
	//==========================================
	if (KSZ%2 ==1) begin
		always @(posedge clk) begin
			if (rst==1'b1) begin
				sum_tmp[KSZ_NEW*(DW_NEW) - 1: (KSZ_NEW - 1)*DW_NEW] <= 'd0;
			end
			else begin
				sum_tmp[KSZ_NEW*(DW_NEW) - 1: (KSZ_NEW - 1)*DW_NEW] <= {1'b0,pi_data[KSZ*DW - 1: (KSZ-1)*DW]};
			end
		end
	end
endgenerate

generate
	//执行最后一次加法,只剩下两个数相加
	if (KSZ == 2) begin
		reg 								tmp_dv_final	;
		reg 								tmp_de_final	;
		reg 								tmp_hs_final	;
		reg 								tmp_vs_final	;
		reg 	[2*DW_NEW - 1 : 0]			sum_tmp_final	;

		always @(posedge clk) begin
			if (rst==1'b1) begin
				sum_tmp_final[2*DW_NEW - 1 :0] <= 'd0;
				tmp_dv_final <= 1'b0;
				tmp_de_final <= 1'b0;
				tmp_hs_final <= 1'b0;
				tmp_vs_final <= 1'b0;
		end
			else begin
				sum_tmp_final[2*DW_NEW - 1 : 0] <= pi_data[DW - 1 : 0] + pi_data[2* DW - 1 : DW];				
				tmp_dv_final <= pi_dv;
				tmp_de_final <= pi_de;
				tmp_hs_final <= pi_hs;
				tmp_vs_final <= pi_vs;			
			end
		end
		assign po_dv = tmp_dv_final;
		assign po_de = tmp_de_final;
		assign po_hs = tmp_hs_final;
		assign po_vs = tmp_vs_final;
		assign po_data = sum_tmp_final;
	end
endgenerate

endmodule

参考:《基于FPGA的数字图像处理原理及应用》牟新刚

你可能感兴趣的:(FPGA图像处理)