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的视频流帧抓取的主要内容,如果未能解决你的问题,请参考以下文章