altera公司FPGA的视频流帧抓取

Posted qseng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了altera公司FPGA的视频流帧抓取相关的知识,希望对你有一定的参考价值。

我们在用FPGA对视频进行处理时,常常会遇到:有时候图像中的某些文字显示模糊----这有可能是缩放导致;有时可能是AD/DA模块采用了不同厂家的芯片----导致转换后的效果不同;有可能在图像YUV422与YUV420互相转换----算法间接导致图像效果变差。林林总总,当然还有其它不同的图像处理而有可能降低画质的场景。在针对图像质量跟踪定位时,最直观的,莫过于直接抓取各图像处理功能模块的输入输出,取出前后图像直接对比,就能立竿见影的诊断出该模块是否异常。所以:如何在FPGA中抓取视频流中的一帧图像呢?本文将记录图像质量定位的实践中,如何对altera公司的fpga在高清视频流中,抓取图像。
在使用alera公司的fpga时,SignalTab是一个很方便的诊断工具。而用SignalTab进行图像抓取的时候,它的抓取深度最大为128K。而在实际的工程应用中,往往难以预留出128K的RAM,大多时候8K已是极限。更不用说,对于CycloneII/III系列,只能腾挪出2K也是常事。因此,想要一次性的将高清视频流的帧图像抓取出来,这点RAM是无法做到的。通过不断的逐次次抓取1到4行,然后将其拼接成一帧完整的图像成为一个可行的方法。

应用于还发现SignalTab存储的日志频率与深度有极大的相关性:深度为8K的时候,1秒只它只能记录1个日志;而深度为2K的时候,1秒它能记录4个日志。所以深度为2K的抓取效率(1次1行,1秒4行)与8K的抓取效率(1次3行,1秒3行)相近,并且从通信性考虑深度以2K为宜。从而做到让SignalTab在1秒内抓取4条日志,1条日志对应视频流帧图像中的1行有效数据。

对于1080p的高清视频,一行的有效数据是1920,如果现一行的有效数据就立即进行抓取,会因为SignalTab在抓取触发前截取1/8预留数据的这一特性,而无法得到1920的足量有效数据(此时只能抓取到1792个图像据)。基于SignalTab的这一特性,需要加一逻辑:在找到有效数据后,再偏移160个拍子(1792+160=1952大于1920),就能抓取成功啦。如下:基于YUV422视频流的抓取模块:

module vi_sync_gen_xy(
    // input //
    input sys_rst_n,
    input vp_clk_in,
    input [15:0]	vp_data_in,
    //	output	//
    output reg [10:0] y,
    output this2out
    output [15:0]	video_data,
);        

 

输入:复位,时钟,YUV422数据;输出:列号y,有效数据位标志this2out,采集到的数据;

对于1080p/30的视频1秒30帧,SignalTab是1秒记录4个日志,所以可以近似的每8帧取1行视频数据,以便SignalTab平滑的记录每行的数据。以下是verilog实现模块代码:

1,解析EAV和SAV,感知有效行数据

//SAV和EAV为四个
	 reg    [15 : 0]   pdata_t1;
	 reg    [15 : 0]   pdata_t2;
	 reg    [15 : 0]   pdata_t3;
	 reg    [15 : 0]   pdata_t4;
	//SAV和EAV的XY字节
	 wire 	            xy_b7;
	 wire 	            xy_f;
	 wire 	            xy_v;
	 wire 	            xy_h;
	 wire 	            xy_p3;
	 wire 	            xy_p2;
	 wire 	            xy_p1;
	 wire 	            xy_p0;   
	 wire 	            is_xy;
//------ Extract the synchronization from video stream in BT1120 format------//	
   assign 	xy_b7 = pdata_t1[7];
   assign 	xy_f  = pdata_t1[6];
   assign 	xy_v  = pdata_t1[5];
   assign 	xy_h  = pdata_t1[4];
   assign 	xy_p3 = pdata_t1[3];
   assign 	xy_p2 = pdata_t1[2];
   assign 	xy_p1 = pdata_t1[1];
   assign 	xy_p0 = pdata_t1[0];
 	//确定SAV和EAV的XY
assign 	is_xy = (xy_b7==1‘b1) & (xy_p3==xy_v^xy_h) & (xy_p2==xy_f^xy_h) & (xy_p1==xy_f^xy_v) & (xy_p0==xy_f^xy_v^xy_h);

always @(posedge vp_clk_in or negedge sys_rst_n)
     begin
	    if(!sys_rst_n)
	      begin
	        pdata_t1	<=   8‘h00;
	        pdata_t2	<=   8‘h00;
	        pdata_t3	<=   8‘h00;
	        pdata_t4	<=   8‘h00;
	      end
	    else 
	      begin
	        pdata_t1	<=   vp_data_in;
	        pdata_t2	<=   pdata_t1;
	        pdata_t3	<=   pdata_t2;
	        pdata_t4	<=   pdata_t3;
	      end
     end 
//produce sorts of EAV and SAV according to the protocol of SMPTE294
   assign eav_sav_pulse = (&pdata_t4[7:0]) & (!(|pdata_t3[7:0])) & (!(pdata_t2[7:0])) & is_xy;
   assign valid_sav = eav_sav_pulse & (~xy_f) & (~xy_v) & (~xy_h);//8080
   assign valid_eav = eav_sav_pulse & (~xy_f) & (~xy_v) & (xy_h); //9D9D
   assign blank_sav = eav_sav_pulse & (~xy_f) & (xy_v) & (~xy_h); //ABAB
   assign blank_eav = eav_sav_pulse & (~xy_f) & (xy_v) & (xy_h);  //B6B6

  

2,场同步和行同步

// produce hsync_flag,行同步,有效行
   always @ ( posedge vp_clk_in  or negedge sys_rst_n)
     if ( !sys_rst_n )
       hsync_flag  <=  1‘b0;
     else  if ( valid_sav )
       hsync_flag  <=  1‘b1;
     else  if ( valid_eav | blank_eav )
       hsync_flag  <=  1‘b0;
     else
       hsync_flag  <= hsync_flag;
//有效行的开始,和有效行结束的信号
   always @ ( posedge vp_clk_in  or negedge sys_rst_n)
     if ( !sys_rst_n )
	      hsync_flag_buf  <=  1‘b0;
     else
	      hsync_flag_buf  <=  hsync_flag;
	
   assign  hsync_ad  =  hsync_flag_buf && (!hsync_flag);	//有效行结束
	assign  hsync_ad_pos = hsync_flag && (!hsync_flag_buf); //有效行开始

// produce vsync_flag	场同步,一帧图像有效数据标志
   always @ ( posedge vp_clk_in  or negedge sys_rst_n)
     if ( !sys_rst_n )
       vsync_flag  <=  1‘b0;
     else  if ( valid_sav | valid_eav )
       vsync_flag  <=  1‘b1;
     else  if ( blank_eav )
       vsync_flag  <=  1‘b0;
     else 
       vsync_flag  <= vsync_flag;

   //有效数据开始信号
   always @ ( posedge vp_clk_in  or negedge sys_rst_n)
     if ( !sys_rst_n )
       begin
         vsync_flag_buf    <=  1‘b0;
       end                 
     else
       begin
         vsync_flag_buf    <= vsync_flag;
       end
                
   assign  vsync_ad     =  vsync_flag && (!vsync_flag_buf);   //positive edge of vsynn_flag 有效数据开始信号
	assign  vsync_ad_neg =  (!vsync_flag) && vsync_flag_buf;	//negedge edge of vsync_flah 有效数据结束信号

3,有效数据抓取逻辑

//y,图像中行号逻辑
reg [10:0] max_y;	//需要知道场扫描中,宽度
always @ ( posedge vp_clk_in  or negedge sys_rst_n)
     if ( !sys_rst_n )
       begin
         y    <=  1‘b0;
	     max_y <=  1‘b0;
       end                 
     else
       begin
			if (vsync_ad)
				y <= 1‘b0;
			else begin
				if (vsync_flag) //在读取有效数据时					
						if ( hsync_ad)	//每读完一行 y自增,其它时间维持原值
							y <= y + 1‘b1;
						else
							y <= y;
				else if(vsync_ad_neg) begin
					max_y <= y;
					y <= 1‘b0;
				end
				else
					y <= 1‘b0;
			end
       end
//x,图像中每行中x逻辑
reg [10:0] x;
reg state_x;
	always @ ( posedge vp_clk_in  or negedge sys_rst_n)
     if ( !sys_rst_n )
       begin
         x    <=  1‘b0;
			state_x <= 1‘b0;
       end                 
     else
       begin
			if (vsync_ad)
				x <= 1‘b0;
			else begin
				if (vsync_flag) //在读取有效数据时						
						if ( hsync_ad) begin	//每读完一行有效数据后, x归0,直到出现下行一的有效数据时,此间一直维持为0
							state_x <= 1‘b0;
							x <= 1‘b0;
						end
						else if (hsync_ad_pos) begin //找到SAV后,即为有效数据的读取,x开始自增,直到离开有效数据时归0
							state_x <= 1‘b1;
							x <= 1‘b0;
						end
						else begin
							if ( state_x)		//读取有效数据,则自增
								x <= x + 1‘b1;
							else			//非有效数据,持续为0
								x <= 1‘b0;
						end
				else
					x <= 1‘b0;
			end
       end
//开始抓取this2out的输出逻辑
        reg [10:0] last_y;
	reg [15:0] ticks;
	reg [10:0] max_ticks;
	parameter  V1080P30_TICK  = 11‘d8;

	//隔一定的时间,取图像中一行
	always @ ( posedge vp_clk_in  or negedge sys_rst_n)
	  if ( !sys_rst_n )
		 begin
			last_y    <=  1‘b0;
			ticks		 <= 1‘b0;
			max_ticks <= V1080P30_TICK; 
		 end                 
	  else begin
			if (max_y>0)
				max_ticks <= (16‘d8640/max_y)+1‘d1;
			else
				max_ticks <= max_ticks;
			if ( ticks >= max_ticks)
				begin
					ticks <= 1‘b0;
					if ( last_y >= max_y )
						last_y <= 1‘b0;
					else
						last_y <= last_y + 3‘d1;
				end
			else
				if ( vsync_ad)
					ticks <= ticks + 1‘b1;
				else
					ticks <= ticks;
	  end
	
	//定义thisout的逻辑
	assign this2out = (last_y == y && vsync_flag && x > 11‘d160);
//定义video_data
assign  video_data = pdata_t1;        

因为抓取数据,需要SignalTab,都需要连接仿真器,所以对于选择的控制,可以添加一个ISSP的IP核,手动选取输出哪一路视频

技术图片

技术图片

之后,只需要将抓取模块添加到工程中,导入要监控的视频即可:

//qseng
 wire sv2st_rst,sv2st_clk;
 wire [15:0]sv2st_videodata;
 wire [5:0] sv2st_sel;
 isspv isspv_inst( .probe(), .source(sv2st_sel));
 select_video_to_signaltab sv2st_inst(
				.sel(sv2st_sel),
				.rst1(reset_n),.clk1(VP1_FP1_TO_FP2_CLK),.vdata1(VP1_FP1_TO_FP2),			//vp1
				.rst2(reset_n),.clk2(VP2_FP1_TO_FP2_CLK),.vdata2(VP2_FP1_TO_FP2),			//vp2
				.rst(sv2st_rst), .clk(sv2st_clk), .videoData(sv2st_videodata));
 vi_sync_gen_xy signaltab_catch_video(
					//           input           //
					.sys_rst_n(sv2st_rst),         // reset
					.vp_clk_in(sv2st_clk), 			 // clock
					.vp_data_in(sv2st_videodata)
			);

然后,添加stp文件,并创建需要监控的y,this2out和video_data

技术图片

注意:this2out需要置1,只有抓取逻辑成立时才取相应数据。右边的时钟,与Sample depth如下:

技术图片

在实际的工程应用中,抓取效果如下:

 

技术图片

需要记下第一次抓取时的y值,如上为156h,直到下一次再次重复为156h时,表示一帧抓取结束。点击右下方的保存日志,此时有1000多条日志。这时不要日志进行导出(1000多条,一个一个导出肯定手也会酸吧),我们改为存储stp文件的方式,一次性的导出所有已存储的数据:

技术图片

在出现的对话框中,新建一个名字,另存为一个新的stp文件

技术图片

接下来,需要进行windows编程,解析此stp文件,并显示图片,并可以将该图片,存储为BMP文件,以便后续的分析。

可以直接到git的widnwos_release目录下,运行YUVSee.exe来解析刚才保存的stp文件。

关于stp的文件解析,请参看:YUVSee解析SignalTab保存的日志文件。

所有FPGA和YUVSee的源码,在github下:

https://github.com/qseng/zieee/tree/master/SignalTab_Capture_VideoFrame

 


以上是关于altera公司FPGA的视频流帧抓取的主要内容,如果未能解决你的问题,请参考以下文章

FPGA设计——CMOS摄像与显示(DVP)

FPGA设计——CMOS图像采集与以太网传输显示

锆石科技开发板的简单介绍

FPGA设计中遇到的奇葩问题之“芯片也要看出身”

FPGA设计——CMOS图像采集与以太网传输显示(MT9V011)

lattice与altera的fpga有啥区别?譬如开发环境,配置等