• 基于Modelsim的直方图均衡化算法仿真


    一、前言

      本篇主要针对牟新刚编著《基于FPGA的数字图像处理原理及应用》中关于直方图

    均衡化算法的功能仿真验证。2020-03-15 10:43:27

    二、FPGA直方图均衡化算法原理

      直方图均衡化又称为灰度均衡化,是指通过某种灰度映射使输入图像转换为在每一

    灰度级上都有近似相同的输出图像(即输出的直方图是均匀的)。在经过均衡化处理后

    的图像中,像素将占有尽可能多的灰度级并且分布均匀。因此,这样的图像将具有较高

    的对比度和较大的动态范围,直方图均衡可以很好地解决相机过曝光或曝光不足的问题。

      直方图均衡化计算步骤如下:

      (1)首先计算出当前图像的直方图;

      (2)其次计算像素直方图累计和;

           (3)将上式乘以灰度值的最大值;

           (4)将上式除以图像像素总数,我们也将后两步运算统称为归一化运算。

       1.直方图累计和

        直方图累加和的定义是小于指定像素的所有像素的统计值之和。

       2.归一化计算

        归一化计算的步骤是先乘以灰度最大值,然后再除以像素总数:

                 图像宽度x图像高度。

    三、代码实现

      代码包括直方图归一化计算文件hist_equalized、直方图累加和统计文件histogram_2d文件

    及顶层文件hist_equal。

      (1)histogram_2d.v文件中加入了累加和统计模块,主要加入了一个用于累加和统计的帧

    双端口RAM;

      1 `timescale 1ps/1ps
      2 
      3 `define Equalize 1
      4 //==============================================================================//
      5 //FileName: histogram_2d.v
      6 //Date: 2020-02-29
      7 //==============================================================================//
      8 
      9 module histogram_2d(
     10     rst_n,
     11     clk,
     12     din_valid,            //输入有效
     13     din,                //输入待统计的数据
     14     dout,                //统计输出
     15     vsync,                //输入场同步
     16     dout_valid,            //输出有效
     17     rdyOutput,            //数据读出请求
     18     //dout_clk            //数据输出时钟    
     19     `ifdef Equalize
     20         hist_cnt_addr,
     21         hist_cnt_out,
     22     `endif
     23     int_flag            //中断输出
     24     
     25 );
     26 
     27     //模块入口参数
     28     parameter DW = 14;        //数据位宽
     29     parameter IH = 512;        //图像高度
     30     parameter IW = 640;        //图像宽度
     31     parameter TW = 32;        //直方图统计数据位宽
     32     
     33     localparam TOTAL_CNT = IW * IH;        //像素总数
     34     localparam HALF_WIDTH = (TW >> 1);    //将32位的数据位宽拆分为高低16位
     35     
     36     
     37     //输入输出声明
     38     input rst_n;
     39     input clk;
     40     input din_valid;
     41     input [DW-1:0] din;
     42     input rdyOutput;
     43     
     44     output reg [HALF_WIDTH:0] dout;
     45     
     46     //output wire [TW-1:0] dout;
     47     
     48     input vsync;
     49     output reg dout_valid;
     50     output reg int_flag;
     51     //output dout_clk;
     52     
     53     `ifdef Equalize
     54         input [DW-1:0] hist_cnt_addr;
     55         output reg [TW-1:0] hist_cnt_out;
     56     `endif
     57     
     58     //变量声明
     59     reg vsync_r;
     60     reg dvalid_r;
     61     reg dvalid_r2;
     62     reg [DW-1:0] din_r;
     63     reg [DW-1:0] din_r2;
     64     
     65     wire hsync_fall;
     66     wire hsync_rise;
     67     
     68     reg [9:0] hsync_count;
     69     reg count_en;
     70     wire [DW-1:0] mux_addr_b;
     71     wire [DW-1:0] mux_addr_b2;
     72     
     73     wire [TW-1:0] q_a;
     74     wire [TW-1:0] q_b;
     75     reg [TW-1:0] counter;
     76     
     77     wire [TW-1:0] count_value;
     78     wire rst_cnt;            //统计计数器复位信号
     79     wire inc_en;            //递增使能信号
     80     
     81     //DPRAM 信号
     82     wire we_a;
     83     wire we_b;
     84     wire we_b_l;
     85     reg  we_b_h;
     86     
     87     wire [DW-1:0] addr_a;
     88     //中断寄存器
     89     reg int_r;
     90     wire [DW-1:0] clr_addr;            //清零地址
     91     reg [DW-1:0] clr_addr_r;
     92     reg [DW:0] out_pixel;            //输出计数
     93     
     94     reg count_all;                    //统计完成信号
     95     //reg count_en_r;
     96     reg count_en_r;
     97     
     98     reg [TW-1:0] hist_cnt;            //直方图统计累加寄存器
     99     wire rstOutput;                    //读出电路复位信号
    100     
    101     wire [TW-1:0] dataTmp2;
    102     wire clr_flag;                    //全局清零
    103     
    104     //将输入数据打两拍
    105     always@(posedge clk or negedge rst_n)begin
    106         if(((~(rst_n))) == 1'b1)
    107         begin
    108             vsync_r     <= #1 1'b0;
    109             dvalid_r     <= #1 1'b0;
    110             dvalid_r2     <= #1 1'b0;
    111             din_r        <= #1 {DW{1'b0}};
    112             din_r2        <= #1 {DW{1'b0}};
    113         end
    114         else
    115         begin
    116             vsync_r        <= #1 vsync;
    117             dvalid_r    <= #1 din_valid;
    118             dvalid_r2    <= #1 dvalid_r;
    119             din_r        <= #1 din;
    120             din_r2        <= #1 din_r;
    121         end    
    122     end
    123     
    124     //输入行同步计数,确定统计的开始和结束时刻
    125     assign #1 hsync_fall = dvalid_r & (~(din_valid));
    126     assign #1 hsync_rise = (~(dvalid_r)) & din_valid;
    127     
    128     always@(posedge clk or negedge rst_n)begin
    129         if(((~(rst_n))) == 1'b1)
    130             hsync_count <= #1 {10{1'b0}};
    131         else
    132         begin
    133             if(vsync_r == 1'b1)
    134                 hsync_count <= #1 {10{1'b0}};
    135             else if(hsync_fall == 1'b1)
    136                 hsync_count <= hsync_count + 10'b1;
    137             else
    138                 hsync_count <= hsync_count; 
    139         end
    140     end
    141     
    142     //一帧图像结束后停止统计 下一帧图像到来时开始计数
    143     always@(posedge clk or negedge rst_n)begin
    144         if(((~(rst_n))) == 1'b1)
    145             count_en <= #1 1'b0;
    146         else 
    147         begin
    148             if(hsync_count >= IH)
    149                 count_en <= #1 1'b0;
    150             else if(hsync_rise == 1'b1)
    151                 count_en <= #1 1'b1;
    152             else
    153                 count_en <= #1 count_en;
    154         end
    155     end
    156     
    157     assign mux_addr_b     = ((count_en == 1'b1)) ? din_r : clr_addr;
    158     assign mux_addr_b2     = ((count_en == 1'b1)) ? din_r : clr_addr_r;
    159     
    160     //统计递增计数器
    161     always@(posedge clk)begin
    162         if(rst_cnt == 1'b1)
    163             counter <= #1 {{TW-1{1'b0}},1'b1}; //复位值为1
    164         else if(inc_en == 1'b1)
    165             counter <= #1 counter + {{TW-1{1'b0}},1'b1};
    166         else
    167             counter <= #1 counter;
    168     end
    169     
    170     assign #1 rst_cnt = ((din_r != din_r2) | ((dvalid_r2 == 1'b1) & (dvalid_r == 1'b0))) ? 1'b1 : 1'b0;
    171     assign #1 inc_en = (((din_r == din_r2) & (dvalid_r2 == 1'b1))) ? 1'b1 : 1'b0;
    172     assign #1 we_a = ((((din_r != din_r2) & (dvalid_r2 == 1'b1)) |((dvalid_r2 == 1'b1) & (dvalid_r == 1'b0)))) ? 1'b1 : 1'b0;
    173     assign #1 count_value = ((count_en == 1'b1)) ? counter+q_b : {TW{1'b0}};
    174     assign #1 addr_a = din_r2;
    175     
    176     
    177     //直方图存储器 分高16位和低16位分别存储
    178     hist_buffer dpram_bin_l(
    179         .address_a(addr_a),                        //输入地址为像素灰度值
    180         .address_b(mux_addr_b),                    //读出和清零地址
    181         .clock(clk),                            //同步时钟
    182         .data_a(count_value[HALF_WIDTH-1:0]),    //当前计数值
    183         .data_b({HALF_WIDTH{1'b0}}),            //清零数据
    184         .wren_a(we_a),
    185         .wren_b(we_b_l),
    186         .q_a(q_a[HALF_WIDTH-1:0]),
    187         .q_b(q_b[HALF_WIDTH-1:0])    
    188     );
    189     
    190 
    191     hist_buffer dpram_bin_h(
    192         .address_a(addr_a),
    193         .address_b(mux_addr_b2),
    194         .clock(clk),
    195         .data_a(count_value[TW-1:HALF_WIDTH]),
    196         .data_b({HALF_WIDTH{1'b0}}),
    197         .wren_a(we_a),
    198         .wren_b(we_b_h),
    199         .q_a(q_a[TW-1:HALF_WIDTH]),
    200         .q_b(q_b[TW-1:HALF_WIDTH])
    201     );
    202     
    203     always@(posedge clk or negedge rst_n)begin
    204         if(((~(rst_n))) == 1'b1)
    205             count_en_r <= #1 1'b0;
    206         else 
    207             count_en_r <= #1 count_en;
    208     end
    209     
    210     //读出电路逻辑,计数时不能输出,读出请求时才输出
    211     assign rstOutput = count_en_r | (~(rdyOutput));
    212     
    213     //输出像素计数
    214     always@(posedge clk)begin
    215         if(rstOutput == 1'b1)
    216             out_pixel <= {DW+1{1'b0}};
    217         else begin
    218             if((~count_all) == 1'b1)
    219             begin
    220                 if(out_pixel == (((2 ** (DW + 1)) - 1)))
    221                     out_pixel <= #1 {DW+1{1'b0}}; //输出完毕
    222                 else
    223                     out_pixel <= #1 out_pixel + 1'b1;
    224             end
    225         end
    226     end
    227     
    228     //统计结束信号
    229     always@(posedge clk)begin
    230         //count_all_r <= (~rstOutput);
    231         we_b_h <= we_b_l;
    232         clr_addr_r <= clr_addr;
    233         if(out_pixel == (((2 ** (DW + 1)) - 1)))
    234             count_all <= #1 1'b1;
    235         else if(count_en == 1'b1)
    236             count_all <= #1 1'b0;
    237     end
    238     
    239     //全局清零信号
    240     assign clr_flag = vsync;
    241     
    242     //中断输出 信号读出操作完成
    243     always@(posedge clk or negedge rst_n)begin
    244         if((~(rst_n)) == 1'b1)
    245         begin
    246             int_flag     <= 1'b1;
    247             int_r         <= 1'b1;
    248         end
    249         else
    250         begin
    251             int_flag <= #1 int_r;
    252             if(clr_flag == 1'b1)
    253                 int_r <= #1 1'b1;
    254             else if(out_pixel >= (((2 ** (DW + 1)) - 1)))
    255                 int_r <= #1 1'b0;
    256         end
    257     end
    258     
    259     assign we_b_l = (((out_pixel[0] == 1'b1) & (count_all == 1'b0))) ? 1'b1 : 1'b0;
    260     
    261     //清零地址,与读出地址反相
    262     assign clr_addr = out_pixel[DW:1];
    263     
    264     wire dout_valid_temp;
    265     
    266     wire [HALF_WIDTH-1:0] dout_temp;
    267     
    268     always@(posedge clk or negedge rst_n)begin
    269         if((~(rst_n)) == 1'b1)
    270         begin
    271             dout <= {HALF_WIDTH{1'b0}};
    272             dout_valid <= 1'b0;
    273         end
    274         else
    275         begin
    276             dout <= #1 dout_temp;
    277             dout_valid <= #1 dout_valid_temp;
    278         end
    279     end
    280     
    281     assign dout_temp = (we_b_l == 1'b1) ? q_b[HALF_WIDTH-1:0] : q_b[TW-1:HALF_WIDTH];
    282     
    283     assign dout_valid_temp = we_b_h | we_b_l; //输出使能
    284     
    285     //assign dout_clk = (dout_valid) ? we_b_h : 1'b0;
    286     
    287     //assign dout = q_b;
    288     
    289     always@(posedge clk or negedge rst_n)begin
    290         if((~(rst_n)) == 1'b1)
    291             hist_cnt <= {TW{1'b0}};                    //复位清零
    292         else begin
    293             if(vsync_r == 1'b0 & vsync == 1'b1)     //新的一帧到来时清零
    294                 hist_cnt <= {TW{1'b0}};
    295             else if(out_pixel[0] == 1'b1)            //每个像素读出时刻
    296                 hist_cnt <= hist_cnt + q_b;            //将结果累加
    297             else
    298                 hist_cnt <= hist_cnt;
    299         end
    300     end
    301     
    302     reg [DW:0] out_pixel_r;
    303     reg [DW-1:0] out_pixel_r2;
    304     
    305     wire [TW-1:0] hist_cnt_temp;
    306     
    307     always@(posedge clk or negedge rst_n)begin
    308         if((~(rst_n)) == 1'b1)begin
    309             out_pixel_r     <= {DW+1{1'b0}};
    310             out_pixel_r2     <= {DW{1'b0}};
    311             hist_cnt_out    <= {TW{1'b0}};
    312         end
    313         else begin
    314             out_pixel_r     <= #1 out_pixel;
    315             out_pixel_r2     <= #1 out_pixel_r[DW:1];
    316             hist_cnt_out    <= #1 hist_cnt_temp;    //将数据打一拍后输出
    317         end
    318     end
    319     
    320     hist_buffer_cnt hist_cnt_buf(
    321         .address_a(out_pixel_r2),        //写入地址,直方图当前地址
    322         .address_b(hist_cnt_addr),        //读出地址
    323         .clock(clk),                    //同步时钟
    324         .data_a(hist_cnt),                //写入数据
    325         .data_b(),                        
    326         .wren_a(dout_valid),            //写入时刻:直方图数据有效
    327         .wren_b(1'b0),                    
    328         .q_a(),
    329         .q_b(hist_cnt_temp)                //输出数据    
    330     );
    331     
    332 endmodule

      (2) hist_equalized.v文件主要用于计算直方图均衡化后的像素值,其中计算开销6个时钟,咋来的?

      1 `timescale 1ps/1ps 
      2 
      3 //==========================================================================================================//
      4 //FileName: hist_equalized.v
      5 //Date: 2020-03-12
      6 //==========================================================================================================//
      7 
      8 module hist_equalized(
      9     rst_n,
     10     clk,
     11     din_valid,        //输入数据有效
     12     din,            //输入数据
     13     dout,            //输出数据
     14     vsync,            //输入场同步
     15     dout_valid,        //输出有效
     16     vsync_out        //输出场同步
     17 );
     18 
     19     parameter DW = 8;    //数据位宽
     20     parameter IH = 512;    //图像高度
     21     parameter IW = 640;    //图像宽度
     22     parameter TW = 32;    //直方图数据位宽
     23     
     24     localparam TOTAL_CNT = IW * IH;
     25     localparam HALF_WIDTH = (TW >> 1);
     26     
     27     //计算开销
     28     localparam latency = 6;
     29     
     30     input rst_n;
     31     input clk;
     32     input din_valid;
     33     input [DW-1:0] din;
     34     output [DW-1:0] dout;
     35     input vsync;
     36     output vsync_out;
     37     output dout_valid;
     38     
     39     reg [DW-1:0] hist_cnt_addr;
     40     wire [TW-1:0] hist_cnt_out;
     41     
     42     //首先需例化一个直方图统计模块对输入图像进行直方图统计
     43     //注意我们只需要得到直方图统计累加和信息
     44     
     45     histogram_2d hist(
     46         .rst_n(rst_n),
     47         .clk(clk),
     48         .din_valid(din_valid),
     49         .din(din),
     50         .vsync(vsync),
     51         .dout(),                        //直方图统计输出
     52         .dout_valid(),                    //输出有效
     53         .rdyOutput(),                    //数据读出请求
     54         .hist_cnt_addr(hist_cnt_addr),     //累加和输入地址
     55         .hist_cnt_out(hist_cnt_out),    //累加和输出
     56         .int_flag()
     57     );
     58     
     59     defparam hist.DW = DW;
     60     defparam hist.IH = IH;
     61     defparam hist.IW = IW;
     62     
     63     wire vsync_fall;
     64     wire valid;
     65     reg [1:0] frame_cnt;
     66     reg hist_valid_temp;
     67     reg vsync_r;
     68     
     69     //由于至少需要等到第一帧输出完毕之后才能完成第一帧数据的直方图统计信息,
     70     //因此有必要对图像帧进行计数
     71     always@(posedge clk or negedge rst_n)begin
     72         if((~(rst_n)) == 1'b1)begin
     73             vsync_r <= #1 1'b0;
     74             hist_valid_temp <= 1'b0;
     75             frame_cnt <= 2'b0;
     76         end
     77         else begin
     78             vsync_r <= #1 vsync;
     79             
     80             if(vsync_fall)
     81                 frame_cnt <= frame_cnt + 2'b01;    //每帧结束时帧计数加1
     82             else
     83                 frame_cnt <= frame_cnt;
     84                 
     85             if(frame_cnt >= 2'b10)    //第二帧开始输入均衡操作才开始有效
     86                 hist_valid_temp <= 1'b1;
     87         end
     88     end
     89     
     90     //场同步下降沿信号
     91     assign vsync_fall = (vsync & ~vsync_r);
     92     
     93     //全局有效信号
     94     assign valid = hist_valid_temp & din_valid;
     95     
     96     //缓存全局有效信号,完成时序对齐
     97     reg [latency:0] valid_r;
     98     
     99     always@(posedge clk or negedge rst_n)begin
    100         if((~(rst_n)) == 1'b1)begin
    101             valid_r[latency:0] <= {latency+1{1'b0}};
    102         end
    103         else begin
    104             valid_r <= #1 {valid_r[latency-1:0],valid};
    105         end
    106     end
    107     
    108     reg [DW-1:0] din_r;
    109     
    110     //缓存输入数据完成时序对齐
    111     always@(posedge clk or negedge rst_n)begin
    112         if((~(rst_n)) == 1'b1)begin
    113             din_r <= {DW{1'b0}};
    114         end
    115         else begin
    116             din_r <= #1 din;
    117         end
    118     end
    119     
    120     //查询当前像素的直方图统计累加和
    121     always@(posedge clk or negedge rst_n)begin
    122         if((~(rst_n)) == 1'b1)begin
    123             hist_cnt_addr <= {DW{1'b0}};
    124         end
    125         else begin
    126             if(valid_r[0])
    127                 hist_cnt_addr <= #1 din_r;
    128             else
    129                 hist_cnt_addr <= hist_cnt_addr;
    130         end
    131     end
    132     
    133     reg [2*TW-1:0] mul_temp[0:2];
    134     reg [DW-1:0] dout_temp;
    135     
    136     //对于分辨率512*512的图像而言
    137     generate
    138         if((IW == 512) & (IH == 512))begin : IW_512
    139             always@(posedge clk or negedge rst_n)
    140                 if((~(rst_n)) == 1'b1)begin
    141                     mul_temp[0] <= {2*TW{1'b0}};
    142                 end
    143                 else begin
    144                     if(valid_r[1])
    145                         //hist_cnt_out*255 - 1,
    146                         mul_temp[0] <= {{TW-DW{1'b0}},hist_cnt_out[TW-1:0],{DW{1'b0}}} - {{TW{1'b0}},hist_cnt_out};
    147                     if(valid_r[1])
    148                         //hist_cnt_out/(512*512) IW = IH =512
    149                         mul_temp[1] <= #1 {{18{1'b0}},mul_temp[0][2*TW-1:18]};
    150                     if(valid_r[2])
    151                         dout_temp <= #1 mul_temp[1][DW-1:0];    
    152                 end
    153         end
    154     endgenerate
    155     
    156     //对于分辨率为640*512的图像而言
    157     generate
    158         if(IW == 640 & IH == 512)begin : IW_640
    159             wire [2*TW-1:0] dout_temp_r/*synthesis keep*/;
    160             
    161             assign dout_temp_r = {{16{1'b0}},mul_temp[2][2*TW-1:16]};
    162             
    163             always@(posedge clk or negedge rst_n)
    164                 if((~(rst_n)) == 1'b1)begin
    165                     mul_temp[0] <= {2*TW{1'b0}};
    166                 end
    167                 else begin
    168                     if(valid_r[1])
    169                     //hist_cnt_out*51,DW must be 8
    170                     //hist_cnt_out*32 + hist_cnt_out*16
    171                     mul_temp[0] <= #1 {{TW-5{1'b0}},hist_cnt_out[TW-1:0],{5{1'b0}}} + {{TW-4{1'b0}},hist_cnt_out[TW-1:0],{4{1'b0}}};
    172                     //hist_cnt_out*1 + hist_cnt_out*2
    173                     mul_temp[1] <= #1 {{TW{1'b0}},hist_cnt_out[TW-1:0]} + {{TW-1{1'b0}},hist_cnt_out[TW-1:0],{1{1'b0}}};
    174                     
    175                     if(valid_r[1])
    176                         //hist_cnt_out/(64*2*512)
    177                         mul_temp[2] <= #1 mul_temp[0] + mul_temp[1];
    178             
    179                     if(valid_r[2])
    180                         dout_temp <= #1 dout_temp_r[DW-1:0];
    181                 end
    182         end
    183     endgenerate
    184     
    185     //完成数据输出对齐
    186     assign dout = dout_temp;
    187     assign dout_valid = valid_r[latency];
    188     assign vsync_out = vsync;
    189     
    190 endmodule

      (6)用于功能仿真的顶层文件,包含图像数据的捕获、灰度图像转换及图像均衡化模块;

      1 `timescale 1ps/1ps
      2 
      3 //=========================================================================================//
      4 //FileName: hist_equal.v TOP FILE
      5 //Date: 2020-03-12
      6 //=========================================================================================//
      7 
      8 module hist_equal(
      9     RSTn,                //全局复位
     10     CLOCK,                //系统时钟
     11     
     12     IMG_CLK,            //像素时钟
     13     IMG_DVD,            //像素值
     14     IMG_DVSYN,            //输入场信号
     15     IMG_DHSYN,            //输入数据有效信号
     16     HISTEQUAL_DAT,        //输出直方均衡化数据
     17     HISTEQUAL_VALID,    //输出直方均衡化有效信号
     18     HISTEQUAL_VSYNC        //输出直方图均衡化场有效信号
     19 );
     20 
     21     /*image parameter*/
     22     parameter iw             = 640;        //image width
     23     parameter ih            = 512;        //image height
     24     parameter trig_value    = 400;         //250
     25     parameter tw            = 32;        //直方图统计数据位宽
     26     
     27     localparam half_width    = (tw >> 1); //将32位的数据位宽拆分为高低16位
     28     
     29     /*data width*/
     30     parameter dvd_dw     = 8;    //image source data width
     31     parameter dvd_chn    = 3;    //channel of the dvd data: when 3 it's rgb or 4:4:YCbCr
     32     parameter local_dw    = dvd_dw * dvd_chn;    //local algorithem process data width
     33     parameter cmd_dw    = dvd_dw * dvd_chn;    //local algorithem process data width
     34 
     35     //Port Declared
     36     input RSTn;
     37     input CLOCK;
     38     input IMG_CLK;
     39     input [dvd_dw-1:0] IMG_DVD;
     40     input IMG_DVSYN;
     41     input IMG_DHSYN;
     42     output [dvd_dw-1:0] HISTEQUAL_DAT;
     43     output HISTEQUAL_VALID;
     44     output HISTEQUAL_VSYNC;
     45 
     46     
     47     //Variable Declared
     48     wire GRAY_CLK;
     49     wire GRAY_VSYNC;
     50     wire GRAY_DVALID;
     51     wire [dvd_dw-1:0] Y_DAT;
     52     wire [dvd_dw-1:0] Cb_DAT;
     53     wire [dvd_dw-1:0] Cr_DAT;
     54     
     55     wire [local_dw-1:0] RGB_DAT;
     56     wire RGB_DVALID;
     57     wire RGB_VSYNC;
     58     
     59     video_cap video_cap_inst(
     60             .reset_l(RSTn),                //异步复位信号
     61             .DVD(IMG_DVD),                //输入视频流
     62             .DVSYN(IMG_DVSYN),            //输入场同步信号
     63             .DHSYN(IMG_DHSYN),            //输入行同步
     64             .DVCLK(IMG_CLK),            //输入DV时钟
     65             .cap_dat(RGB_DAT),            //输出RGB通道像素流,24位
     66             .cap_dvalid(RGB_DVALID),    //输出数据有效
     67             .cap_vsync(RGB_VSYNC),        //输出场同步
     68             .cap_clk(CLOCK),            //本地逻辑时钟
     69             .img_en(),                
     70             .cmd_rdy(),                    //命令行准备好,代表可以读取
     71             .cmd_rdat(),                //命令行数据输出
     72             .cmd_rdreq()                //命令行读取请求
     73         );
     74     
     75     defparam video_cap_inst.DW_DVD         = dvd_dw;
     76     defparam video_cap_inst.DW_LOCAL     = local_dw;
     77     defparam video_cap_inst.DW_CMD         = cmd_dw;
     78     defparam video_cap_inst.DVD_CHN     = dvd_chn;
     79     defparam video_cap_inst.TRIG_VALUE  = trig_value;
     80     defparam video_cap_inst.IW             = iw;
     81     defparam video_cap_inst.IH             = ih;
     82     
     83     RGB2YCrCb RGB2YCrCb_Inst(
     84             .RESET(RSTn),                //异步复位信号
     85             
     86             .RGB_CLK(CLOCK),            //输入像素时钟
     87             .RGB_VSYNC(RGB_VSYNC),        //输入场同步信号
     88             .RGB_DVALID(RGB_DVALID),    //输入数据有信号
     89             .RGB_DAT(RGB_DAT),            //输入RGB通道像素流,24位
     90             
     91             .YCbCr_CLK(GRAY_CLK),        //输出像素时钟
     92             .YCbCr_VSYNC(GRAY_VSYNC),    //输出场同步信号
     93             .YCbCr_DVALID(GRAY_DVALID),    //输出数据有效信号
     94             .Y_DAT(Y_DAT),                //输出Y分量
     95             .Cb_DAT(Cb_DAT),            //输出Cb分量
     96             .Cr_DAT(Cr_DAT)                //输出Cr分量
     97         );    
     98 
     99     defparam RGB2YCrCb_Inst.RGB_DW = local_dw;
    100     defparam RGB2YCrCb_Inst.YCbCr_DW = dvd_dw;
    101 
    102 
    103     hist_equalized hist_equalized_inst(
    104         .rst_n(RSTn),
    105         .clk(GRAY_CLK),
    106         .din_valid(GRAY_DVALID),        //输入数据有效
    107         .din(Y_DAT),                    //输入数据
    108         .dout(HISTEQUAL_DAT),            //输出数据
    109         .vsync(GRAY_VSYNC),                //输入场同步
    110         .dout_valid(HISTEQUAL_VALID),    //输出有效
    111         .vsync_out(HISTEQUAL_VSYNC)        //输出场同步
    112     );
    113     
    114     defparam hist_equalized_inst.DW = dvd_dw;
    115     defparam hist_equalized_inst.IH = ih;
    116     defparam hist_equalized_inst.IW = iw;
    117     defparam hist_equalized_inst.TW = tw;
    118     
    119 endmodule

      (4)用于Modelsim仿真的testbench文件;

      1 `timescale 1ps/1ps
      2 
      3 module histequalized_tb;
      4 
      5 
      6     /*image para*/
      7     parameter iw             = 640;        //image width
      8     parameter ih            = 512;        //image height
      9     parameter trig_value    = 400;     //250
     10 
     11     /*video parameter*/
     12     parameter h_total        = 2000;
     13     parameter v_total        = 600;
     14     parameter sync_b        = 5;
     15     parameter sync_e        = 55;
     16     parameter vld_b            = 65;
     17 
     18     parameter clk_freq         = 72;
     19 
     20     /*data width*/
     21     parameter dvd_dw     = 8;    //image source data width
     22     parameter dvd_chn    = 3;    //channel of the dvd data: when 3 it's rgb or 4:4:YCbCr
     23     parameter local_dw    = dvd_dw * dvd_chn;    //local algorithem process data width
     24     parameter cmd_dw    = dvd_dw * dvd_chn;    //local algorithem process data width
     25 
     26 
     27     /*test module enable*/
     28     parameter hist_equalized_en    = 1;
     29 
     30     /*signal group*/
     31     reg pixel_clk = 1'b0;
     32     reg reset_l;
     33     reg [3:0] src_sel;
     34 
     35 
     36     /*input dv group*/
     37     wire dv_clk;
     38     wire dvsyn;
     39     wire dhsyn;
     40     wire [dvd_dw-1:0] dvd;
     41     
     42     /*dvd source data generated for simulation*/
     43     image_src image_src_inst//#(iw*dvd_chn, ih+1, dvd_dw, h_total, v_total, sync_b, sync_e, vld_b)
     44     (
     45         .clk(pixel_clk),
     46         .reset_l(reset_l),
     47         .src_sel(src_sel),
     48         .test_data(dvd),
     49         .test_dvalid(dhsyn),
     50         .test_vsync(dvsyn),
     51         .clk_out(dv_clk)
     52     );
     53         
     54     defparam image_src_inst.iw = iw*dvd_chn;
     55     defparam image_src_inst.ih = ih + 1;
     56     defparam image_src_inst.dw = dvd_dw;
     57     defparam image_src_inst.h_total = h_total;
     58     defparam image_src_inst.v_total = v_total;
     59     defparam image_src_inst.sync_b = sync_b;
     60     defparam image_src_inst.sync_e = sync_e;
     61     defparam image_src_inst.vld_b = vld_b;
     62     
     63     /*local clk: also clk of all local modules*/
     64     reg cap_clk = 1'b0;
     65         
     66     /*hist equalized operation module*/
     67     generate
     68         if(hist_equalized_en != 0)begin : equalized_operation
     69             wire equalized_dvalid;
     70             wire [dvd_dw-1:0] equalized_data;
     71             wire equalized_vsync;
     72             
     73             wire equalized_dvalid_in;
     74             wire [dvd_dw-1:0] equalized_data_in;
     75             wire equalized_vsync_in;
     76         
     77             integer fp_equalized,cnt_equalized = 0;
     78         
     79             /*video capture: capture image src and transfer it into local timing*/
     80             hist_equal hist_equal_inst(
     81                 .RSTn(reset_l),                        //全局复位
     82                 .CLOCK(cap_clk),                    //系统时钟
     83                 
     84                 .IMG_CLK(pixel_clk),                //像素时钟
     85                 .IMG_DVD(equalized_data_in),        //像素值
     86                 .IMG_DVSYN(equalized_vsync_in),        //输入场信号
     87                 .IMG_DHSYN(equalized_dvalid_in),    //输入数据有效信号
     88                 .HISTEQUAL_DAT(equalized_data),        //输出直方图统计数据
     89                 .HISTEQUAL_VALID(equalized_dvalid),    //输出直方图统计有效
     90                 .HISTEQUAL_VSYNC(equalized_vsync)    //数据读出请求
     91             );
     92             
     93             assign equalized_data_in = dvd;
     94             assign equalized_dvalid_in = dhsyn;
     95             assign equalized_vsync_in = dvsyn;
     96     
     97             always@(posedge cap_clk or posedge equalized_vsync)begin
     98                 if((~(equalized_vsync)) == 1'b0)
     99                     cnt_equalized = 0;
    100                 else begin
    101                     if(equalized_dvalid == 1'b1)begin
    102                         fp_equalized = $fopen("E:/Modelsim/hist_equalized/sim/equalized.txt","r+");
    103                         $fseek(fp_equalized,cnt_equalized,0);
    104                         $fdisplay(fp_equalized,"%02X",equalized_data);
    105                         $fclose(fp_equalized);
    106                         cnt_equalized <= cnt_equalized + 4;
    107                     end
    108                 end
    109             end
    110         end
    111     endgenerate
    112     
    113     initial
    114     begin: init
    115         reset_l <= 1'b1;
    116         src_sel <= 4'b0000;
    117         #(100);            //reset the system
    118         reset_l <= 1'b0;
    119         #(100);    
    120         reset_l <= 1'b1;
    121     end
    122     
    123     //dv_clk generate
    124     always@(reset_l or pixel_clk)begin
    125         if((~(reset_l)) == 1'b1)
    126             pixel_clk <= 1'b0;
    127         else 
    128         begin
    129             if(clk_freq == 48)            //48MHz
    130                 pixel_clk <= #10417 (~(pixel_clk));
    131             
    132             else if(clk_freq == 51.84)    //51.84MHz
    133                 pixel_clk <= #9645 (~(pixel_clk));
    134             
    135             else if(clk_freq == 72)        //72MHz
    136                 pixel_clk <= #6944 (~(pixel_clk));
    137         end
    138     end
    139     
    140     //cap_clk generate: 25MHz
    141     always@(reset_l or cap_clk)begin
    142         if((~(reset_l)) == 1'b1)
    143             cap_clk <= 1'b0;
    144         else
    145             cap_clk <= #20000 (~(cap_clk));    
    146     end
    147     
    148     wire clk;
    149     assign clk = ~cap_clk;
    150     
    151 endmodule
    152     

      (5)用于Modelsim仿真的.do文件;

     1 #切换至工程目录
     2 cd E:/Modelsim/hist_equalized/sim
     3 
     4 #打开工程
     5 project open E:/Modelsim/hist_equalized/sim/hist_equalized
     6 
     7 #添加指定设计文件
     8 project addfile E:/Modelsim/hist_equalized/sim/histequalized_tb.v
     9 project addfile E:/Modelsim/hist_equalized/src/cross_clock_fifo.v
    10 project addfile E:/Modelsim/hist_equalized/src/hist_buffer.v
    11 project addfile E:/Modelsim/hist_equalized/src/hist_equal.v
    12 project addfile E:/Modelsim/hist_equalized/src/hist_equalized.v
    13 project addfile E:/Modelsim/hist_equalized/src/histogram_2d.v
    14 project addfile E:/Modelsim/hist_equalized/src/image_src.v
    15 project addfile E:/Modelsim/hist_equalized/src/line_buffer_new.v
    16 project addfile E:/Modelsim/hist_equalized/src/RGB2YCbCr.v
    17 project addfile E:/Modelsim/hist_equalized/src/video_cap.v
    18 project addfile E:/Modelsim/hist_equalized/prj/hist_buffer_cnt.v
    19 
    20 #编译工程内的所有文件
    21 project compileall
    22 
    23 #仿真work库下面的histequalized_tb实例,同时调用altera_lib库,不进行任何优化
    24 vsim -t 1ps -novopt -L altera_lib work.histequalized_tb
    25 
    26 #添加输入灰度图像信号
    27 add wave -divider GRAYImg
    28 
    29 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/clk
    30 
    31 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/vsync
    32 
    33 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/din_valid
    34 
    35 add wave -radix unsigned -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/din
    36 
    37 #输入信号缓存及处理
    38 add wave -divider DataCache
    39 
    40 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/vsync_r
    41 
    42 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/frame_cnt
    43 
    44 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/hist_valid_temp
    45 
    46 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/vsync_fall
    47 
    48 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/valid
    49 
    50 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/valid_r
    51 
    52 add wave -radix unsigned -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/din_r
    53 
    54 add wave -radix unsigned -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/hist_cnt_addr
    55 
    56 #计算归一化的值
    57 add wave -divider Calculator
    58 
    59 add wave -radix unsigned -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/mul_temp
    60 
    61 #add wave -radix unsigned -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/dout_temp_r
    62 
    63 add wave -radix unsigned -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/dout_temp
    64 
    65 #添加数据输出
    66 add wave -divider EqualDataOut
    67 
    68 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/dout_valid
    69 
    70 add wave -radix binary -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/vsync_out
    71 
    72 add wave -radix unsigned -position insertpoint sim:/histequalized_tb/equalized_operation/hist_equal_inst/hist_equalized_inst/dout
    73 
    74 #复位
    75 restart
    76 
    77 #取消警告
    78 set StdArithNoWarnings 1
    79 
    80 #开始
    81 run 35ms

    四、仿真结果

      (1)算法时序仿真:存在两个问题,第一,书中提供的代码仿真时,帧计数有问题,主要是图像数据采集模块生成的场信号有问题;因此实际是帧计数为3时,

    开始进行直方图均衡化操作。第二个问题就是计算开销latenc= 6?计算开销为6,没整明白?

      

      (2)FPGA处理结果与Matlab处理结果对比,可见算法对图像有一定增强对比度的作用,但是效果的没有Matlab直接变换的效果好;

      

    初入江湖,天下无敌;再学三年,寸步难行。
  • 相关阅读:
    windows安装psycopg2问题解决方案
    error: Setup script exited with error: Unable to find vcvarsall.bat
    python web server comparison
    重置mysql root密码
    Nginx + uWSGI + web.py 搭建示例
    《凉州曲》——吴践道
    C#代理服务器
    OpenGL 分形 Sierpinski镂垫
    Chap02_递归与分治_判断两棵二叉树是否同构
    OpenGL C#绘图环境配置
  • 原文地址:https://www.cnblogs.com/huangwei0521/p/12496362.html
Copyright © 2020-2023  润新知