• 图像处理算法的仿真平台之VGA时序


    一  概述

            图像处理算法一般是用matla或OpenCV实现的,若是用FPGA实现,设计思路差别极大。matlab和opencv的优势:这些工具的优势在于可以方便地载入图像文件,或输出数据到图像文件,同时提供了大量的API函数,便于使用者快速实现想要的功能,同时又能通过查看图像文件直观地看到预想结果。将算法直接在FPGA实现是有难度和漫长的,在matlab中,一个直方图处理和双边滤波器,引导图像滤波,仅仅一行代码即可,有现成的函数调用,十分简单。而在FPGA实现则需要考虑帧缓存,算法的设计结构与硬件相符合,时序等问题。很有必要对FPGA实现图像处理算法的基本思路和方法进行学习!

    二  FPGA实现图像处理算法的基本思路

        (一)  需求分析及问题描述

    问题描述应该清楚地描述问题而不是解决方法。为使描述更具体,至少需要讨论三个方面。第一是系统功能,也就是系统需要做什么。在一个图像处理应用中,需要详细说明图像处理后的预期结果。第二,必须讨论系统的性能,即说明系统完成这些功能的指标是什么。对 千实时图像处理来说,允许的最大延时和每秒需要处理的帧数是两个很重要的指标。第三个需要考虑的方面是系统将要运行的环境。应用图像处理不仅仅包含图像处理算法,它是一个需要对整个系统进行考虑和说明的系统工程问题。其他需要 考虑的重要方面包括照明、光学及所支持的硬件和机械接口。图像处理系统之间及其与整个工程系统其他部分之间的联系也需要认真地说明和定义。比如,要做一个分辨率为640*480@60Hz的视频处理系统,要求提升每一帧图像的亮度和对比度。这就是明确的需求:即明确输入图像或视频的要求和最终的评价指标。

    (二)软件算法设计及验证

    软件开发及验证会适当地在硬件设计之前进行,比如在用matlab或OpenCV等工具验证算法的视觉效果和客观指标,这主要是由千在FPGA上调试算法周期过长,即使仅仅做仿真工作所消耗的时间也远远比软件多。如果在硬件上进行映射,其综合、编译和布局布线的时间花费更是无法令人接受。大部分情况下,FPGA更多的是仅仅作为一个映射工具。一般实现某个算法如红外图像细节增强,都是先花两周看经典的SCI论文,选两三篇经典的(一是权威的作者:如知名企业三星,学术机构IEEE的院士,知名大学中科院教授;二是看被引量,如60次以上,这是很多相关算法的基础,很完善成熟。三是发表时间五年之前的比较靠谱的,新的论文很少有人验证。四是看论文的实验仿真,设计是否严谨和多种指标衡量。),在对这两三篇经典的论文算法理解透了,用matlab复现。通过自己matlab仿真不同算法,从实验结果选择一种最佳的作为硬件实现。算法的精度,涉及到浮点转换为定点运算,FPGA不支持小数运算。

    (三)  硬件平台设计

    硬件平台的设计往往会和软件开发同时进行。通常 个算法的测试及改进是 个周期很长的任务。硬件平台的设计在算法开 发基本功能验证之后就可以对其进行整体评估。

    1. 软件与硬件的划分

    硬件平台设计的第 步是合理地划分硬件和软件。 这里的硬件是指算法由 FPGA逻辑实现,软件是指算法由 DSP、ARM 或MCU软件编程实现。规则的底层图像处理操作(如形态学滤波、 Sobel 算子、均值滤波等)具有计算数据量大、 结构规则并行等特点,非常适合千用 FPGA 硬件实现。 不规则的底层图像处理操作(如具有动态可变长度循环的算法) 和串行顶层图像处理操作(如弹道计算、任务判决融合等)用 FPGA实现会非常繁琐且效率较低, 此类操作用软件实现效率较高, 开发难度较低。无论怎么划分层级,清楚地定义软件与硬件之间的接口与通信机制是基本的要求。尤其有必要设计同步和数据交换机制来促进数据流的平滑。如常见的LCD屏显示,通常是FPGA处理像素数据后通过SPI总线发送给MCU驱动显示。

           2. 资源评估与FPGA选型

    在硬件方案确定之后, 在确定具体的FPGA型号之前, 对整个系统所消耗的资源 进行预估是十分必要的。对千图像处理系统来讲, 比较敏感的资源是存储器资源。此外,FPGA 所拥有的些高速接口资源也是重要的考虑因素, 这主要考虑到视频处理的高带宽特点。如系统需求为2560*1600,帧率为90Hz的RGB888视频流输入,则帧缓存的带宽为2560*1600*90*24/8=1.105GB/s,而一般DDR3的带宽为800M,需要两片DDR3才行。

    (四)   FPGA映射

    FPGA映射是将软件算法转换为FPGA设计的过程。这个在书籍<<基于FPGA的数字图像处理原理及应用>>的第四章,有讲解到映射技术,如下图所示,需要时可以看书籍进一步了解

    (五)  仿真验证

    在FPGA映射之后,接下来的重点工作是对设计的系统进行仿真和验证。在FPGA代码撰写完毕时对其进行功能测试是十分有必要的。一般是搭建视频的modelsim仿真平台,即编写一个Verilog文件模拟符合标准视频时序的视频输入源,提供给我们的设计模块,仿真观察波形,将算法处理的结果用txt文本存储,再用matlab观察对比效果。本文的后面会介绍一个vga的模拟输入。在硬件中的在线调试也是十分必要的。最简单的方法是将主信号布线到不用的IO,口上, 使得它们从FPGA外部是可观测的, 在外部使用 个示波器或是逻辑分析仪来监控信号。 此外, Xlinx和Altera厂商提供的IDE中也提供了虚拟的逻辑分析仪来辅助调试。 不过, 辅助调试手段需要占用片内的存储器资源。

    三  VGA

         上面介绍图像处理算法的fpga实现的基本思路,现在通过搭建一个vga时序来实现搭建图像仿真平台的第一步。vga时序模拟图像算法的视频输入,第二步就是设计图像处理算法,如直方图统计。第三步把算法对图像数据的处理结果用matlab直观显示,也可以和matlab实现算法的处理结果对比。通过这三步就很直观显示图像输入输出的效果,对于验证图像算法的有效性很方便。

         (一) 外部接口

    1.VGA原理图及端口

    上面是vga接口的电路图,可以看出总共五个信号,分成两类,一是控制VGA驱动的行同步信号VGA_HS(HSync) 和场同步信号(VSync);二是控制像素数据输出的RGB信号,根据vga接口和RGB的组合,常见RGB格式有RGB888和RGB565.

    2. VGA扫描方式

    显示是用逐行扫描的方式解决,阴极射线枪发出电子束打在涂有荧光粉的荧光屏上,产生RGB 三基色,合成一个彩色像素。扫描从屏幕的左上方开始,从左到右,从上到下,进行扫描,每扫完一行,电子束回到屏幕的左边下一行的起始位置,在这其间CRT 对电子束进行消隐。每行结束时,用行同步信号进行同步;扫描完所有行,用场同步信号进行同步,并使扫描回到屏幕左上方,同时进行场消隐,预备下一场的扫描。

    (二)  640*480@60Hz的VGA实现

         1.VESA显示标准

    下面我们以实现640*480@60Hz的vga驱动来掌握VGA的实现参数和驱动。对于vga的驱动,首先有一个官方的标准,VESA视频标准显示手册,这是做视频显示的权威指南,下面我们给出这个分辨率的vesa标准:

    首先是红色方框的图像有效显示的分辨率和帧数(一秒内显示的图像数),即640*480为有效区域和屏幕的总分辨率800*525区别,大致理解有效区域为总屏幕的一部分,这是理解重点,牵涉到后面的像素有效信号test_dvalid(de)的理解。像素的时钟由分辨率和帧率决定,pixel clock = (1/帧数)/屏幕分辨率(800*525)=25M左右

    2. VGA屏幕一帧的时序参数图

    其他的时序参数如行场同步,场前肩,场后肩,行左边界,行右边界。要结合下面的两张图才能深刻理解,光看上面的vesa标准还是很困惑这些时序参数的含义。

    上面的第二张为从<VESA Display Monitor Timing Standard>手册中截取的屏幕一帧的时序参数图,从图中可知红框表示图像的有效区域640*480对应着它上面图片的图像显示区。绿框为整个显示屏幕,分辨率大小800*525为标准手册的Hor Total Time=800 pixels和Ver Total Time = 525 lines,分别表示总行时间,总列时间。分别以像素时钟的周期和行的时间为单位。这个分辨率对应着它上面图2的整个屏幕的大小800*525.故后面代码中的像素有效标志信号test_dvalid为图像像素在红框的有效区域时,行同步信号有效的值,表示输出的像素有效。

     时序参数可以参考上面的解释。

    3. VGA 显示的时序图

    (三) VGA时序模拟的设计思路及代码

    1.设计思路

     上面是vga行场扫描的时序图,通过这个时序图结合我们上面的分析,就能编写Verilog代码驱动vga显示。行同步信号(Hsync)与场同步信号都可以每行/帧一开始拉高/低,场同步信号为每一帧的起始点。行同步信号则为每一行像素的起始点。设计思路,结合手册,先编写行计数器cnt_h和垂直计数器cnt_v,分别是计数到800-1,525-1,垂直计数器cnt_v是以每行计数完加1的。接着是像素有效信号test_dvalid,它是在有效区域640*480内,在像素时钟的驱动下拉高。场同步信号test_vsync_temp根据时序参数知为2lines的高电平,故在垂直计数器cnt_v在0-2范围则拉高,其余时间为零。故场同步信号表示每一帧的开始,即新帧的到来,它满足每一秒60帧,即场同步信号的周期为1/60秒,约为16.6毫秒,故可判读时序是否正确。

    2. 代码

      1 `timescale 1 ns/1 ns
      2 
      3 `define SEEK_SET 0
      4 `define SEEK_CUR 1
      5 `define SEEK_END 2
      6 //the simulation of image conform to the VESA stand
      7 //so modified the parameters
      8 //Timing Name = 640 x 480 @ 60Hz;
      9 //acquired from vesa
     10 module  ima_src(
     11         reset_l,
     12         clk,
     13         src_sel,
     14         test_vsync,
     15         test_dvalid,//pixel valid
     16         test_data,
     17         clk_out
     18     );
     19 
     20   parameter iw = 640;//640*2 2 bytes per pixels
     21   parameter ih = 480;//512 plus one command line
     22   parameter dw = 8;   
     23   
     24   parameter h_total = 800;//Hor Total Time = 800 pixel
     25   parameter v_total = 525 ;//Ver Total Time = 16.683; // (msec) = 525 lines
     26   
     27   parameter sync_b = 2;//V Front Porch/ Ver Sync Time=2 lines
     28   parameter sync_e = 2;//Ver Sync 
     29   parameter vld_b  = (2+25+8);//V Back Porch=35
     30    //Ver Addr Time =480 lines
     31     parameter h_b  = (96+40+8);//Hor Sync Time(96)+H Back Porch(40)+H Left Border(8) =144
     32   input reset_l,clk;//clock,reset
     33   input [3:0]src_sel;//to select the input file  
     34   output test_vsync,test_dvalid,clk_out;
     35   output [dw-1:0]test_data;
     36   
     37   reg [dw-1:0]test_data_reg;
     38   reg test_vsync_temp;
     39   reg test_dvalid_tmp;
     40   reg [1:0]test_dvalid_r;
     41   
     42   reg [10:0]       h_cnt;
     43   reg [10:0]       v_cnt;
     44   
     45   integer fp_r;
     46   integer cnt=0;
     47     
     48     assign clk_out = clk;//output the dv clk
     49     
     50     assign test_data = test_data_reg;//test data output 
     51     
     52     //read data from file
     53 
     54     always @(posedge clk or  posedge test_vsync_temp )
     55     
     56     if ((((~test_vsync_temp))) == 1'b0)
     57         cnt<=0;//clear file pointer when a new frame comes
     58     else 
     59     begin
     60         if (test_dvalid_tmp == 1'b1) 
     61         begin
     62             case (src_sel)  
     63                 4'b0000 :fp_r  = $fopen("../poc/ln.txt","r");
     64                 4'b0001 :fp_r  = $fopen("../poc/ln.txt","r");//very error
     65                 4'b0010 :fp_r  = $fopen("../poc/recovery/e_640x480_hex.txt","r");
     66                 4'b0011 :fp_r  = $fopen("txt_source/test_src3.txt",  "r");
     67                 4'b0100 :fp_r  = $fopen("txt_source/test_src4.txt",  "r");
     68                 4'b0101 :fp_r  = $fopen("txt_source/test_src5.txt",  "r");
     69                 4'b0110 :fp_r  = $fopen("txt_source/test_src6.txt",  "r");
     70                 4'b0111 :fp_r  = $fopen("txt_source/test_src7.txt",  "r");
     71                 4'b1000 :fp_r  = $fopen("txt_source/test_src8.txt",  "r");
     72                 4'b1001 :fp_r  = $fopen("txt_source/test_src9.txt",  "r");
     73                 4'b1010 :fp_r  = $fopen("txt_source/test_src10.txt", "r");
     74                 4'b1011 :fp_r  = $fopen("txt_source/test_src11.txt", "r");
     75                 4'b1100 :fp_r  = $fopen("txt_source/test_src12.txt", "r");
     76                 4'b1101 :fp_r  = $fopen("txt_source/test_src13.txt", "r");
     77                 4'b1110 :fp_r  = $fopen("txt_source/test_src14.txt", "r");
     78                 4'b1111 :fp_r  = $fopen("txt_source/test_src15.txt", "r");
     79                 default   :fp_r  = $fopen("../poc/ln.txt","r");
     80             endcase 
     81             
     82             $fseek(fp_r,cnt,0);
     83             $fscanf(fp_r, "%02x\n", test_data_reg);
     84             cnt <= cnt + 4 ;
     85             $fclose(fp_r);
     86             //$display("%02x",test_data_reg);  //for debug use
     87         end
     88     end
     89 
     90 //horizon counter
     91     always @(posedge clk or posedge reset_l)
     92   if (((~(reset_l))) == 1'b1)
     93      h_cnt <= #1 {11{1'b0}};
     94   else 
     95   begin
     96      if (h_cnt == ((h_total - 1)))
     97         h_cnt <= #1 {11{1'b0}};
     98      else
     99         h_cnt <= #1 h_cnt + 11'b00000000001;
    100   end
    101 
    102 //vertical counter
    103   always @(posedge clk or posedge reset_l)
    104   if (((~(reset_l))) == 1'b1)
    105      v_cnt <= #1 {11{1'b0}};
    106   else 
    107   begin
    108      if (h_cnt == ((h_total - 1)))
    109      begin
    110         if (v_cnt == ((v_total - 1)))
    111            v_cnt <= #1 {11{1'b0}};
    112         else
    113            v_cnt <= #1 v_cnt + 11'b00000000001;
    114      end
    115   end
    116 
    117 //field sync
    118   always   @(posedge clk or posedge reset_l)
    119   if (((~(reset_l))) == 1'b1)
    120      test_vsync_temp <= #1 1'b1;
    121   else 
    122   begin
    123      if (v_cnt >= 0 & v_cnt < (sync_b ))
    124         test_vsync_temp <= #1 1'b1;
    125      else
    126         test_vsync_temp <= #1 1'b0;
    127   end
    128 
    129   assign test_vsync = (test_vsync_temp);
    130 
    131 //horizon sync
    132   always @(posedge clk or posedge reset_l)
    133   if (((~(reset_l))) == 1'b1)
    134      test_dvalid_tmp <= #1 1'b0;
    135   else 
    136   begin
    137      if (v_cnt >= vld_b & v_cnt < ((vld_b + ih)-1))
    138      begin
    139         if (h_cnt >= h_b & h_cnt < ((h_b + iw)-1))
    140            test_dvalid_tmp <= #1 1'b1;
    141      else
    142         test_dvalid_tmp <= #1 1'b0;
    143   end
    144 end
    145   assign test_dvalid = test_dvalid_tmp;
    146 
    147   always @(posedge clk or posedge reset_l)
    148   if (((~(reset_l))) == 1'b1)
    149      test_dvalid_r <= #1 2'b00;
    150   else 
    151      test_dvalid_r <= #1 ({test_dvalid_r[0], test_dvalid_tmp});
    152 
    153 endmodule

    明显的差别是数据的输出,这次的设计,通过文件操作来获得图像的数据,为了和matlab联合仿真。在硬件描述语言仿真平台中简单地载入图像文件和输出图像文件,那么对图像类处理的仿真将会带来极大的方便。方式通过用matlab把图像转换为txt文本,用Verilog的$fopen,$fclose,$fscanf,$fread,$fwrite等进行文件操作,注意点是只能用来仿真,不可综合。后面Verilog算法处理后存储为txt文本还能用来作为matlab的图像输入进行显示。这样很方便能验证算法的处理效果,不要很麻烦到硬件平台去观察。

     3.MATLAB代码

           通过matlab代码将图像转换为8比特的16进制形式作为设计的输入数据。MATLAB处理结果如下:

     matlab功能:把640*480的lean原图转换640*480=307200个8比特的像素数据,存储为txt文本。后面modelsim仿真,打开这个文件作为vga模拟视频源的输入数据。

    clc;
    clear;
    
    %% 数据获取
    RGB = imread('lean640_480.bmp'); %rgb原始图像
    GRAY = rgb2gray(RGB); %Matlab变换灰度图像
    
    fid = fopen('./lean640_480_hex.txt','wt');
     for i = 1:size(RGB,1)
        for j = 1:size(RGB,2)
             fprintf(fid,'%2x\n',RGB(i,j));%每个数据之间用空格分开
        end
     end
    
    
    %% 画图显示
    figure(1);
    subplot(1,3,1);
    imshow(RGB);
    title('lena原始图像');
    
    subplot(1,3,2);
    imshow(GRAY);
    title('Matlab变换灰度图像');
    

      再附上lean原图,可以调整为640*480,供图像处理算法使用,这是图像领域的经典,好好收藏!

    (四) 仿真

    1.testbench代码设计

     1 `timescale 1ns/1ps
     2 
     3 module tb_top;
     4 
     5 //========================================================
     6 //parameters
     7 parameter CLK_FREQ   = 25.200;//ddr reference clock frequency, unit: MHz
     8 parameter CLK_PERIOD = 1000.0/CLK_FREQ; //unit: ns 
     9 //pixel_total=h_total*v_total*60=25_200_000
    10  // parameter  FREQ = 100_000_000 ;
    11   parameter  sim_num = 5_000_000 ;
    12  // parameter  BAUDRATE    = 115200  ;
    13  //ima parameter
    14   parameter iw = 640;//640*2 2 bytes per pixels
    15   parameter ih = 480;//512 plus one command line
    16   parameter dw = 8;   
    17   
    18   parameter h_total = 800;//Hor Total Time = 800 pixel
    19   parameter v_total = 525 ;//Ver Total Time = 16.683; // (msec) = 525 lines
    20   
    21   parameter sync_b = 2;//V Front Porch
    22   parameter sync_e = 2;//Ver Sync 
    23   parameter vld_b  = 25+8;//V Back Porch+V top Borch
    24    
    25 //=======================================================
    26 reg          clk;            // 50M
    27 reg          rst_n         ;
    28 reg [3:0]       de      ;
    29 
    30 
    31 wire test_vsync;
    32 wire      test_dvalid;
    33 wire[7:0] test_data;
    34 wire      clk_out;
    35 
    36 
    37 //========================================================
    38 GSR GSR(.GSRI(1'b1));
    39 
    40 
    41 initial 
    42 begin
    43     rst_n = 1'b0;
    44     de =4'd0;
    45 #200;
    46  de = 4'd1;
    47  rst_n = 1'b1;
    48 
    49 
    50 
    51 end
    52 
    53 //----------------------------------------------------
    54 //ref clk
    55 initial 
    56 begin
    57     clk = 1'b0;
    58 end
    59 
    60 always  #(CLK_PERIOD/2.0) clk = ~clk;
    61 
    62 //==================================================
    63 //ima_src
    64 //parameter must be connected to constant
    65     ima_src  inst_ima_src (
    66             .reset_l     (rst_n),
    67             .clk         (clk),
    68             .src_sel     (de),
    69             .test_vsync  (test_vsync),
    70             .test_dvalid (test_dvalid),
    71             .test_data   (test_data),
    72             .clk_out     (clk_out)
    73         );
    74 
    75 endmodule
    test Code

    2.波形

     从仿真波形看出一帧的时间为16.6ms左右,即场同步信号的周期, 符合每秒60帧的时序。并且像素数据在像素有效信号拉高是输出。

    四  总结

         一是至少看三种以上的参考资料。如本次学习搭建图像仿真平台,就是<<基于FPGA的数字图像处理原理及应用>>,然后是网上博客如咸鱼FPGA;V3学院就业办的基础课程第17讲VGA,OpensLee, 开源骚客,vesa标准手册等。

      二是,循序渐进搭仿真。如刚开始是直接给时钟复位,让仿真跑起来。其次是理解别人的设计逻辑。这时候通过上面的各种资料,这一步可能花两三天,最后就是修改逻辑和不断仿真调试,直到符合vesa时序图。
      三是,在实践过程中记录。遇到问题及解决过程。

    参考资料:

    1.<<基于FPGA的数字图像处理原理及应用>>

     2.V3学院--第十七讲、VGA 接口驱动

    3. vesa标准手册

    4.https://www.cnblogs.com/huangwei0521/

  • 相关阅读:
    js拷贝
    git
    移动端网页电话号码的拨打
    es6 promise对象
    h5调用qq客户端
    ActiveMQ中的Destination高级特性(一)
    ActiveMQ集群下的消息回流功能
    ActiveMQ的静态网络链接
    深入理解IoC/DI
    ActiveMQ启动多个broker
  • 原文地址:https://www.cnblogs.com/cutewei/p/15057522.html
Copyright © 2020-2023  润新知