FPGA series # 双线性插值的图像缩放【雏形】

写了将近一个月的代码,写写删删。一开始花了几天时间撸清了思路,画好了图,下手的时候发现很多地方还是考虑不周。今天好不容易写出点样子来,暂时做个总结。

正文:
缩放倍数:(分三步)
step1:预设好的,比如3、4倍;
step2:还是预设好的,缩放因子是一个parameter。可以通过修改代码改变;
step3:缩放因子是个变量,不用改代码,可以改变的。
从一开始的思路说起吧,之前的博客有撸过算法原理,这里不赘述。上图:0
理想很美好,现实很焦灼啊。。。理论上来说,模块是这样安排的:1
先说插值系数生成模块,两个计数器,一个行计数cnt_i,一个列计数cnt_j,考虑到计算结果有小数,且要用到小数,遂用定点数的计算方式。Q14,例:row_map = 4*i*(2^14)/3 = i* 21844。算出的结果取

row_int <= row_map[25 : 14];
row_dec	<= row_map[13 : 0 ];

row_int为x,row_dec为u,代入后面的模块。计算还都不算麻烦,麻烦的是数据提取,就是上图的数据缓存单元。因为考虑到数据提取时,是以(x,y)做地址提取,而RAM的写端口写入的行数据为一行1280个数据,读端口需要读出的行数据只需要960个数据,为避免产生不必要的冲突,比如读比写快(可使用异步时钟,但尽量使用同步时钟为好),读地址不好控这种问题,所以在计数时,暂时以3/4这个倍数为准,每隔三个数计一个延迟。(这一点其实一开始并没有考虑到,后来写数据提取模块的时候代码写了删删了写,考虑很多方法都还是有哪里不对,所以后来在师父的指点下,干脆从源头下手,后面看起来就好很多了)。
接着是数据提取模块,个人认为最麻烦的模块,天知道在这里死了我多少脑细胞,甚至一度以为我是不是智商不够,撸不清逻辑。大脑还一片混乱,主要,是不知道怎么控读地址。取simple dual ram,缓存四行数据,因为四个坐标分别来自上下两行,想要在同一时刻读出四个坐标,就得有一个ram输出第一行数据,一个ram输出第二行数据。输出第一行数据的ram,写端口从复位开始就可以写,且写地址以1递加,读端口从第三行数据写完开始读,控读地址,实在懒得描述,后续可补,这里就直接放代码:

//RAM0
		reg			wea0 = 0	;
		reg			enb0 = 0	;
		reg			[12 : 0] addra0	= 13'b0000000000001	;
		reg			[12 : 0] addrb0	;	
		reg			[12 : 0] cnt_rnum = 0	;
		reg			[8  : 0] cnt_ram0 = 4'b0100	;
		reg			[3  : 0] z0	;	
		always@(posedge clk) begin			//wea0
			if(!resetn) begin
				wea0 <= 1	;
			end
		end
		always@(posedge clk) begin			//addra0
			if(!resetn) begin
			 if(s_axis_df_tdata && m_axis_df_tready)
			 	addra0 <= addra0 + 1	;
			 if(addra0 == 5120)											//1280*4
			 	addra0 <= 13'b0000000000001	;
			end
		end	
		always@(posedge clk) begin			//enb0
			if(addra0 == 3840)											//5120-1280
				enb0 <= 1	;
		end
		always@(posedge clk) begin			//cnt_rnum			
			if(x > 4) begin
				if(cnt_rnum == 5119)
					cnt_rnum <= 0	;
				else
					cnt_rnum <= cnt_rnum + 1	;
			end	
			else
				cnt_rnum <= 0	;			
		end
		always@(posedge clk) begin			//cnt_ram0
			if(cnt_ram0 == 180)												//720/4
				cnt_ram0 <= 4'b0100	;
			if(cnt_rnum == 5119)											
				cnt_ram0 <= cnt_ram0 + 4'b0100	;
		end
		always@(posedge clk) begin			//z0
			if(x < 4)
				z0 <= x	;
			else
				z0 <= x - cnt_ram0	;
		end	
		always@(posedge clk) begin			//addrb0
			case(z0)
						4'd1		: addrb0 <= y	;
						4'd2		: addrb0 <= y + 11'b101_0000_0000	;
						4'd3		: addrb0 <= y + 12'b1010_0000_0000	;
						4'd4		: addrb0 <= y + 12'b1111_0000_0000	;
						default : addrb0 <= y	;
			endcase
		end	

输出第二行数据的ram,写端口从第一行数据走过了之后才可写入,读端口和前一个ram差不多。

//RAM1
		reg			wea1 = 0	;
		reg			enb1 = 0	;
		reg			[12 : 0] addra1	= 13'b0000000000001	;
		reg			[12 : 0] addrb1		;
		reg			[8  : 0] cnt_ram1 = 4'b0100	;
		reg			[3  : 0] z1	;			
		always@(posedge clk) begin			//wea1
			if(addra0 == 1280) 
				wea1 <= 1	;
		end
		always@(posedge clk) begin			//addra1
			if(!resetn) begin
			 if(wea1)
			 	addra1 <= addra1 + 1	;
			 if(addra1 == 5120)
			 	addra1 <= 13'b0000000000001	;
			end
		end	
		always@(posedge clk) begin			//enb1
			if(addra0 == 3840)											//5120-1280
				enb1 <= 1	;
		end
		always@(posedge clk) begin			//cnt_ram1
			if(cnt_ram1 == 179)												//720/4-1
				cnt_ram1 <= 4'b0100	;
			if(cnt_rnum == 5119)
				cnt_ram1 <= cnt_ram1 + 4'b0100	;
		end
		always@(posedge clk) begin			//z1
			if(x < 4)
				z1 <= x	;
			else
				z1 <= x - cnt_ram1	;
		end	
		always@(posedge clk) begin			//addrb1
			case(z1)
						4'd1		: addrb1 <= y	;
						4'd2		: addrb1 <= y + 11'b101_0000_0000	;
						4'd3		: addrb1 <= y + 12'b1010_0000_0000	;
						4'd4		: addrb1 <= y + 12'b1111_0000_0000	;
						default : addrb1 <= y	;
			endcase
		end

需要延迟的数据就用语句延迟。这里还要加上valid信号,以便后期做一个筛选,valid为0时是无效数据,valid为1时输出为有效数据。考虑和axis接口的s_axis_tvalid合并。
最后是双线性插值模块,就是以前两个模块的输出为输入做计算,乘法器用的比较多,大概后期还要有个类似优化的过程。。。
那顶层文件里就是调用这三个模块,再做一些需要的延迟即可:

//缩放因子:value = 3/4
//源图行列数据:row_src = 1280,col_src = 720
//目标图像行列数据:row_is = 960,col_is = 540   
//---------------------------------------------------------------------
`timescale 1 ns / 1 ps

module ImageScaling #(
	parameter WIDTH = 24 , CWIDTH = 11 ,dec_WIDTH = 14//数据位宽  坐标位宽	小数位宽
)
(
	input	  				      clk			    ,
	input	  				      resetn			,
	output	[CWIDTH - 1 : 0] 	 x	,
	output	[CWIDTH - 1 : 0] 	 y  ,			
	output	[9  : 0]	row_cnt	,
  	output	[10 : 0]	col_cnt	,
  	output	reg			valid	,	
  	output	[23 : 0]  data00	,
  	output	[23 : 0]  data01	,	
  	output	[23 : 0]  data10	,
  	output	[23 : 0]  data11	,
  	output	[13 : 0]	 u_in	,
  	output	[13 : 0]	 v_in	,
  	output	[13 : 0]	 u1_in  ,
  	output	[13 : 0]	 v1_in  ,	
	//slave
	input		[WIDTH-1 : 0]	s_axis_scaling_tdata	,
	input		wire					s_axis_scaling_tlast	,
	output	 							s_axis_scaling_tready	,
	input		wire					s_axis_scaling_tuser  ,
	input		wire	 				s_axis_scaling_tvalid	,
	//master                       
	output	[WIDTH-1 : 0]	m_axis_scaling_tdata	,
	output	 							m_axis_scaling_tlast	,
	input		wire					m_axis_scaling_tready	,
	output								m_axis_scaling_tuser	,
	output								m_axis_scaling_tvalid	
);
//延迟
			reg	[13 : 0]	u_r0	;
			reg	[13 : 0]	v_r0	;
			reg	[13 : 0]	u1_r0	;
			reg	[13 : 0]	v1_r0	; 
			reg	[13 : 0]	u_r1	;
			reg	[13 : 0]	v_r1	;
			reg	[13 : 0]	u1_r1	;
			reg	[13 : 0]	v1_r1	;
			reg	[13 : 0]	u_r2	;
			reg	[13 : 0]	v_r2	;
			reg	[13 : 0]	u1_r2	;
			reg	[13 : 0]	v1_r2	;
		  always@(posedge clk) begin
				u_r0	<= u	;
				v_r0	<= v	;
				u1_r0	<= u1	;
				v1_r0	<= v1	;
			end
			always@(posedge clk) begin
				u_r1	<= u_r0		;           
				v_r1	<= v_r0		;           
				u1_r1	<= u1_r0	;           
				v1_r1	<= v1_r0	;           
			end 
			always@(posedge clk) begin
				u_r2	<= u_r1	  ;           
				v_r2	<= v_r1	  ;           
				u1_r2	<= u1_r1	;           
				v1_r2	<= v1_r1	;           
			end                        
		  always@(posedge clk) begin
		  	u_in  <= u_r2	  ;
		  	v_in  <= v_r2	  ;
		  	u1_in <= u1_r2	;
		  	v1_in <= v1_r2	;
		  end
		  
		  reg		valid_0 ;
		  reg		valid_1 ;
		  reg		valid_2 ;
		  reg		valid_3 ;
		  reg		valid_4 ;
		  always@(posedge clk) begin
        valid_0	<= valid_in ;
				valid_1	<= valid_0  ;
				valid_2	<= valid_1  ;
				valid_3	<= valid_2  ;
				valid_4	<= valid_3  ;
				valid	<= valid_4  ;
			end
//系数生成
			wire	[CWIDTH - 1 : 0] 	 x			;
			wire	[CWIDTH - 1 : 0] 	 y			;
			wire	[dec_WIDTH-1 : 0]	 u			;				
			wire	[dec_WIDTH-1 : 0]	 v			;	
			wire	[dec_WIDTH-1 : 0]	 u1 		;
			wire	[dec_WIDTH-1 : 0]	 v1 		;	
			Coefficient_generation CG(	
			.clk	 (clk	  )	,
			.resetn	 (resetn  )	,		
			.start_cg(start_cg)	,	//DF in		
			.row_int (x       ) ,	//CG out to DF
			.col_int (y       ) ,		
			.u	     (u       ) ,	//CG out to BI
			.u1	     (u1      ) ,
			.v	     (v       ) ,
			.v1	     (v1      )       
			);
//数据提取
			wire	[WIDTH - 1 : 0]		 data00	;
			wire	[WIDTH - 1 : 0]		 data01	;
			wire	[WIDTH - 1 : 0]		 data10	;
			wire	[WIDTH - 1 : 0]		 data11	;
			data_fetch DF(							
			.clk	(clk		),
			.resetn	(resetn		),
			.s_axis_df_tdata		(s_axis_scaling_tdata ),	//in
			.s_axis_df_tlast		(s_axis_scaling_tlast ),
			.s_axis_df_tuser		(s_axis_scaling_tuser ), 
			.s_axis_df_tvalid	  	(s_axis_scaling_tvalid),	
			.s_axis_df_tready	    (s_axis_scaling_tready),	//out
			.start_cg(start_cg),													//DF out to CG
			.start_bi(start_bi),													//DF out to BI	
			.x(x),																				//CG in
			.y(y),				
			.row_cnt(row_cnt),
			.col_cnt(col_cnt),
			.valid(valid_in),
			.doutb00 (data00),														//DF out to BI
			.doutb01 (data01),
			.doutb10 (data10),
			.doutb11 (data11),
			.m_axis_df_tlast	(m_axis_scaling_tlast),		//out
			.m_axis_df_tready	(m_axis_scaling_tready),		//in
			.m_axis_df_tuser  	(m_axis_scaling_tuser ),			
			.m_axis_df_tvalid	(m_axis_scaling_tvalid)   		
			);
//双线性插值计算					
			reg 	[dec_WIDTH-1 : 0]  u_in 	;										
			reg 	[dec_WIDTH-1 : 0]  u1_in	;
			reg 	[dec_WIDTH-1 : 0]  v_in 	;
			reg 	[dec_WIDTH-1 : 0]  v1_in	;
			bilinear_interpolation BI(	
			.clk	(clk	),
			.resetn (resetn ),
			.start_bi(start_bi),								 //DF in
			.data00 (data00 ),									 //DF in
			.data01 (data01 ),    							 
			.data10 (data10 ),    							 
			.data11 (data11 ),									 
			.u_in	  (u_in	 ),									 //CG in
			.u1_in	  (u1_in ),
			.v_in	  (v_in	 ),
			.v1_in	  (v1_in ),		
			.data_o (m_axis_scaling_tdata )			 //OUT
			);
			
endmodule

值得一提的是,动手写之前考虑好所有是个很6的技能啊,还需慢慢掌握。尽管是个小模块,但也是成长的一步嘛。后面其实事情挺多的,要学的也越来越清晰。加油啊,要成为攻城狮的我们!

  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值