zoukankan      html  css  js  c++  java
  • 基于Modelsim的直方图线性拉伸

    一、前言

      本篇主要针对牟新刚编著《基于FPGA的数字图像处理原理及应用》一书中第六章第5.3小节FPGA直方图线性拉伸的内容进行modelsim

    层面的仿真,做必要的总结及解读。  2020-03-18 13:35:14

    二、FPGA直方图线性拉伸原理

      2.1 直方图线性拉伸基本原理

      在视频处理中,为了能够实时调节图像的对比度,通常需要对直方图进行拉伸处理。直方图拉伸是指将图像灰度直方图较窄的灰度区间

    向两端拉伸,增强整幅图像像素的灰度级对比度,达到增强图像的效果。

      线性拉伸也即灰度拉伸,属于线性点运算的一种。它扩展图像的直方图,使其充满整个灰度级范围内。

      设f(x,y)为输入图像,它的最小灰度级A和最大灰度级B的定义如下:

              A = min[f(x,y)], B = max[f(x,y)];

      将A和B分别映射到0和255,则最终得到输出图像g(x,y)为

              g(x,y) = 255*[f(x,y) - A]/(B-A)。

      基本意思就是将整个图像的灰度等级拉伸到0到255的像素等级区间,使得图像对比度进一步提高。

      2.2 直方图线性拉伸的FPGA实现

      计算直方图线性拉伸处理后的像素值的步骤如下:

     (1)确定高低阈值Thr_Min和Thr_Max。

     (2)计算系数A和B。

     (3)计算当前像素与A的差值。

     (4)计算255与(B-A)的商。

     (5)计算第(3)步与第(4)步的乘积。

      其具体实现电路如下图所示;

      

      其中,Thr_Min和Thr_Max为给定的两个截断区间,用来定义首尾被截断的直方图统计数目。截断区间由自己定义,根据前一帧的累加和统计

    结果,可以计算出A和B两个系数。

    三、代码实现

      代码参照书中代码稍做调整,包括直方图累加和统计文件histogram_2d.v、直方图线性拉伸文件hist_linear_transform.v、顶层文件及用于测试

    仿真的文件。

     (1)histogram_2d.v,累加和统计文件,视频流的第一帧用于累加和统计,后续前一阵的累加和统计结果用作后一帧图像的线性拉伸处理。代码

    中新增例化了DPRAM用于存储图像的直方图统计累加和。提供读写端口,用于累加和的读取。

      映射区间主要由输入阈值和直方图累加和计算出来,因此映射区间的计算同样可以放在直方图统计阶段进行。

      同时还需要注意,和直方图均衡一样,我们不考虑帧缓存的问题,也就是当前的查找结果为上一帧的结果。

      查找步骤如下:

     (1)当前图像到来之前,加载默认映射值。

     (2)根据上一帧的查找结果输出映射值。

     (3)对本帧进行直方图统计。

       (4)统计过程中根据定义查找区间映射值。

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

      (2)hist_linear_transform.v,直方图线性拉伸文件。该部分代码唯一要注意的时,除法IP核的例化,计算时钟延迟为15个时钟,与书中所提19个时钟有偏差,事实上两个16位的数据除法

    最大结果输出延迟应该只有16个,不应该有19个。

      1 `timescale 1ps/1ps 
      2 
      3 //===================================================================================================//
      4 //FileName: hist_linear_transform.v
      5 //Date: 2020-03-16
      6 //===================================================================================================//
      7 
      8 module hist_linear_transform(
      9     rst_n,
     10     clk,
     11     din_valid,            //输入有效
     12     din,                //输入数据流
     13     dout,                //输出数据
     14     vsync,                //输入场同步
     15     dout_valid,            //输出有效
     16     vsync_out,            //输出场同步
     17     lowCnt,                //输入低阈值
     18     highCnt                //输入高阈值
     19 );
     20 
     21     parameter DW = 8;
     22     parameter IH = 512;
     23     parameter IW = 640;
     24     parameter TW = 32;
     25     parameter DW_DIVIDE = 16;    //除法器位宽
     26     localparam TOTAL_CNT = IW * IH;
     27     localparam HALF_WIDTH = (TW >> 1);
     28     
     29     localparam divide_latency = 15;     //除法器计算延迟
     30     localparam latency = divide_latency + 2;
     31     
     32     //port declared
     33     input rst_n;
     34     input clk;
     35     
     36     input din_valid;
     37     input [DW-1:0] din;
     38     output [DW-1:0] dout;
     39     input vsync;
     40     output vsync_out;
     41     output dout_valid;
     42     input [TW-1:0] lowCnt;
     43     input [TW-1:0] highCnt;
     44     
     45     //variable declared
     46     //索引值计算值,也就是公式中的A和B
     47     wire [DW-1:0] lowIndex;
     48     wire [DW-1:0] highIndex;
     49     
     50     //首先例化一个直方图统计模块,计算两个截断索引值
     51     histogram_2d histogram_2d_inst(
     52         .rst_n(rst_n),
     53         .clk(clk),
     54         .din_valid(din_valid),
     55         .din(din),
     56         .dout(),
     57         .vsync(vsync),
     58         .dout_valid(),
     59         .int_flag(),
     60         .lowCnt(lowCnt),
     61         .highCnt(highCnt),
     62         .rdyOutput(1'b1),
     63         .hist_cnt_addr(),
     64         .hist_cnt_out(),
     65         .lowIndex(lowIndex),    //索引值输出A
     66         .highIndex(highIndex)    //索引值输出B
     67     );
     68     
     69     defparam histogram_2d_inst.DW = DW;
     70     defparam histogram_2d_inst.IH = IH;
     71     defparam histogram_2d_inst.IW = IW;
     72     
     73     //由于至少需要等待一帧输出数据,需对输入图像帧进行计数
     74     wire vsync_fall;
     75     wire valid;
     76     reg [1:0] frame_cnt;
     77     reg hist_valid_temp;
     78     reg vsync_r;
     79     
     80     always@(posedge clk or negedge rst_n)begin
     81         if((~(rst_n)) == 1'b1)begin
     82             vsync_r <= #1 1'b0;
     83             hist_valid_temp <= 1'b0;
     84             frame_cnt <= 2'b00;
     85         end
     86         else begin
     87             vsync_r <= #1 vsync;
     88             
     89             if(vsync_fall)
     90                 frame_cnt <= frame_cnt + 2'b01;
     91             else
     92                 frame_cnt <= frame_cnt;
     93                 
     94             if(frame_cnt >= 2'b10)
     95                 hist_valid_temp <= 1'b1;
     96         end
     97     end
     98     
     99     assign vsync_fall = (vsync & ~vsync_r);
    100     
    101     //全局有效信号
    102     assign valid = hist_valid_temp & din_valid;
    103     
    104     reg [latency:0] vlaid_r;
    105     //缓存有效信号,以等待除法运算
    106     always@(posedge clk or negedge rst_n)begin
    107         if((~(rst_n)) == 1'b1)
    108         begin
    109             vlaid_r[latency:0] <= {latency+1{1'b0}};
    110         end
    111         else 
    112         begin
    113             vlaid_r <= #1 {vlaid_r[latency-1:0],valid};
    114         end
    115     end
    116 
    117 /*    
    118     reg [DW-1:0] din_r;
    119     always@(posedge clk or negedge rst_n)begin
    120         if((~(rst_n)) == 1'b1)
    121         begin
    122             din_r <= {DW{1'b0}};
    123         end
    124         else 
    125         begin
    126             din_r <= #1 din;
    127         end
    128     end
    129 */    
    130     //1.首先计算(B-A)
    131     reg [DW-1:0] diff;
    132     reg [DW-1:0] diff_r;    //将计算结果缓存一拍
    133     
    134     always@(posedge clk or negedge rst_n)begin
    135         if((~(rst_n)) == 1'b1)
    136         begin
    137             diff     <= {DW{1'b0}};
    138             diff_r     <= {DW{1'b0}};
    139         end
    140         else 
    141         begin
    142             diff_r <= #1 diff;
    143             if(valid == 1'b1)
    144                 if(highIndex > lowIndex)
    145                     diff <= #1 highIndex - lowIndex;    //(B-A)输出
    146                 else
    147                     diff <= {DW{1'b1}};                    //指示异常
    148             else
    149                 diff <= {DW{1'b0}};
    150         end
    151     end
    152     
    153     //2.接着计算(f(x,y)-A)
    154     reg [DW-1:0] diff_l;
    155     always@(posedge clk or negedge rst_n)begin
    156         if((~(rst_n)) == 1'b1)
    157         begin
    158             diff_l <= {DW{1'b0}};
    159         end
    160         else
    161         begin
    162             if(valid == 1'b1)
    163             begin
    164                 if(din <= lowIndex)        //当f(x,y) <= A时置零
    165                     diff_l <= {DW{1'b0}};
    166                 else
    167                     diff_l <= #1 din - lowIndex;    //这里也包含了当f(x,y)>=B的情况,我们将在除法之后进行处理
    168             end
    169         end
    170     end
    171     
    172     //3.下一个时钟计算(f(x,y)-A)*255
    173     reg [2*DW-1:0] square;
    174     always@(posedge clk or negedge rst_n)begin
    175         if((~(rst_n)) == 1'b1)
    176         begin
    177             square <= {2*DW{1'b0}};
    178         end
    179         else
    180         begin
    181             if(vlaid_r[0] == 1'b1)
    182             begin
    183                 square <= #1 diff_l * {DW{1'b1}};        //直接相乘
    184             end
    185         end    
    186     end
    187     
    188     //4.计算商
    189     wire divide_en;
    190     wire [DW_DIVIDE-1:0] quotient;    //
    191     wire [DW_DIVIDE-1:0] nc_rem;    //余数
    192     wire [DW_DIVIDE-1:0] denom;        //分子
    193     wire [DW_DIVIDE-1:0] numer;        //分母
    194     
    195     assign denom = {diff_r == {DW{1'b0}}} ? {DW_DIVIDE{1'b1}} : diff_r; //防止分母出现0
    196     
    197     assign numer = (vlaid_r[0] == 1'b1) ? square : {DW_DIVIDE{1'b0}};
    198     
    199     //调用除法器IP核
    200     slope_cal cal_slope(
    201             .clken(1'b1),
    202             .clock(clk),            
    203             .denom(denom),            //分母
    204             .numer(numer),            //分子
    205             .quotient(quotient),    //
    206             .remain(nc_rem)            //余数总为正
    207         );
    208         
    209     wire [DW-1:0] quotient_temp;
    210     wire [DW-1:0] quotient_temp1;
    211     
    212     //除法结果进行四舍五入处理
    213     assign quotient_temp1 = (nc_rem >= diff[DW-1:1]) ? (quotient+1) : quotient;
    214     
    215     //所得结果大于255? 说明输入像素大于B,直接置255
    216     assign quotient_temp = (quotient_temp1 >= {DW{1'b1}}) ? ({DW{1'b1}}) : (quotient_temp1[DW-1:0]);
    217     
    218     reg [DW-1:0] dout_temp;
    219     
    220     always@(posedge clk or negedge rst_n)begin
    221         if((~(rst_n)) == 1'b1)
    222         begin
    223             dout_temp <= {DW{1'b0}};
    224         end    
    225         else
    226         begin
    227             if(vlaid_r[latency-1])
    228                 dout_temp <= #1 quotient_temp;    //输出结果
    229         end    
    230     end
    231     
    232     assign dout = dout_temp;
    233     assign dout_valid = vlaid_r[latency];
    234     assign vsync_out = vsync;
    235     
    236 endmodule

      (3)顶层代码设计,hist_transform.v文件。包括视频流捕获、灰度图像输出及直方图线性拉伸模块。

      1 `timescale 1ps/1ps
      2 
      3 //=========================================================================================//
      4 //FileName: hist_transform.v TOP FILE
      5 //Date: 2020-03-16
      6 //=========================================================================================//
      7 
      8 module hist_transform(
      9     RSTn,                //全局复位
     10     CLOCK,                //系统时钟
     11     
     12     IMG_CLK,            //像素时钟
     13     IMG_DVD,            //像素值
     14     IMG_DVSYN,            //输入场信号
     15     IMG_DHSYN,            //输入数据有效信号
     16     HISTTRANS_DAT,        //输出直方图线性拉伸数据
     17     HISTTRANS_VALID,    //输出直方图线性拉伸数据有效信号
     18     HISTTRANS_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] HISTTRANS_DAT;
     43     output HISTTRANS_VALID;
     44     output HISTTRANS_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_linear_transform hist_linear_transform_Inst(
    104         .rst_n(RSTn),
    105         .clk(GRAY_CLK),
    106         .din_valid(GRAY_DVALID),        //输入数据有效
    107         .din(Y_DAT),                    //输入数据
    108         .dout(HISTTRANS_DAT),            //输出数据
    109         .vsync(GRAY_VSYNC),                //输入场同步
    110         .dout_valid(HISTTRANS_VALID),    //输出有效
    111         .vsync_out(HISTTRANS_VSYNC),    //输出场同步
    112         .lowCnt(32'd5000),                //输入低阈值
    113         .highCnt(32'd5000)                //输入高阈值
    114     );
    115     
    116     defparam hist_linear_transform_Inst.DW = dvd_dw;
    117     defparam hist_linear_transform_Inst.IH = ih;
    118     defparam hist_linear_transform_Inst.IW = iw;
    119     defparam hist_linear_transform_Inst.TW = tw;
    120     
    121 endmodule

      (4)用于Modelsim仿真的Testbench文件,同样参照书中代码。

      1 `timescale 1ps/1ps
      2 
      3 module hist_transform_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    = 0;
     29     parameter display_transform_en = 1;
     30     
     31 
     32     /*signal group*/
     33     reg pixel_clk = 1'b0;
     34     reg reset_l;
     35     reg [3:0] src_sel;
     36 
     37 
     38     /*input dv group*/
     39     wire dv_clk;
     40     wire dvsyn;
     41     wire dhsyn;
     42     wire [dvd_dw-1:0] dvd;
     43     
     44     /*dvd source data generated for simulation*/
     45     image_src image_src_inst//#(iw*dvd_chn, ih+1, dvd_dw, h_total, v_total, sync_b, sync_e, vld_b)
     46     (
     47         .clk(pixel_clk),
     48         .reset_l(reset_l),
     49         .src_sel(src_sel),
     50         .test_data(dvd),
     51         .test_dvalid(dhsyn),
     52         .test_vsync(dvsyn),
     53         .clk_out(dv_clk)
     54     );
     55         
     56     defparam image_src_inst.iw = iw*dvd_chn;
     57     defparam image_src_inst.ih = ih + 1;
     58     defparam image_src_inst.dw = dvd_dw;
     59     defparam image_src_inst.h_total = h_total;
     60     defparam image_src_inst.v_total = v_total;
     61     defparam image_src_inst.sync_b = sync_b;
     62     defparam image_src_inst.sync_e = sync_e;
     63     defparam image_src_inst.vld_b = vld_b;
     64     
     65     /*local clk: also clk of all local modules*/
     66     reg cap_clk = 1'b0;
     67         
     68     /*hist equalized operation module*/
     69     generate
     70         if(hist_equalized_en != 0)begin : equalized_operation
     71             wire equalized_dvalid;
     72             wire [dvd_dw-1:0] equalized_data;
     73             wire equalized_vsync;
     74             
     75             wire equalized_dvalid_in;
     76             wire [dvd_dw-1:0] equalized_data_in;
     77             wire equalized_vsync_in;
     78         
     79             integer fp_equalized,cnt_equalized = 0;
     80         
     81             /*video capture: capture image src and transfer it into local timing*/
     82             hist_equal hist_equal_inst(
     83                 .RSTn(reset_l),                        //全局复位
     84                 .CLOCK(cap_clk),                    //系统时钟
     85                 
     86                 .IMG_CLK(pixel_clk),                //像素时钟
     87                 .IMG_DVD(equalized_data_in),        //像素值
     88                 .IMG_DVSYN(equalized_vsync_in),        //输入场信号
     89                 .IMG_DHSYN(equalized_dvalid_in),    //输入数据有效信号
     90                 .HISTEQUAL_DAT(equalized_data),        //输出直方图统计数据
     91                 .HISTEQUAL_VALID(equalized_dvalid),    //输出直方图统计有效
     92                 .HISTEQUAL_VSYNC(equalized_vsync)    //数据读出请求
     93             );
     94             
     95             assign equalized_data_in = dvd;
     96             assign equalized_dvalid_in = dhsyn;
     97             assign equalized_vsync_in = dvsyn;
     98     
     99             always@(posedge cap_clk or posedge equalized_vsync)begin
    100                 if((~(equalized_vsync)) == 1'b0)
    101                     cnt_equalized = 0;
    102                 else begin
    103                     if(equalized_dvalid == 1'b1)begin
    104                         fp_equalized = $fopen("E:/Modelsim/hist_equalized/sim/equalized.txt","r+");
    105                         $fseek(fp_equalized,cnt_equalized,0);
    106                         $fdisplay(fp_equalized,"%02X",equalized_data);
    107                         $fclose(fp_equalized);
    108                         cnt_equalized <= cnt_equalized + 4;
    109                     end
    110                 end
    111             end
    112         end
    113     endgenerate
    114     
    115     /*hist linear transform module*/
    116     generate
    117         if(display_transform_en != 0) begin: display_transform_operation
    118             wire dis_trans_dvalid;
    119             wire [dvd_dw-1:0] dis_trans_data;
    120             wire dis_trans_vsync;
    121             wire dis_trans_dvalid_in;
    122             wire [dvd_dw-1:0] dis_trans_data_in;
    123             wire dis_trans_vsync_in;
    124             
    125             integer fp_dis_trans,cnt_dis_trans = 0;
    126             
    127             hist_transform hist_transform_inst(
    128                 .RSTn(reset_l),                                //全局复位
    129                 .CLOCK(cap_clk),                            //系统时钟
    130     
    131                 .IMG_CLK(pixel_clk),                        //像素时钟
    132                 .IMG_DVD(dis_trans_data_in),                //像素值
    133                 .IMG_DVSYN(dis_trans_vsync_in),                //输入场信号
    134                 .IMG_DHSYN(dis_trans_dvalid_in),            //输入数据有效信号
    135                 .HISTTRANS_DAT(dis_trans_data),                //输出直方图线性拉伸数据
    136                 .HISTTRANS_VALID(dis_trans_dvalid),            //输出直方图线性拉伸数据有效信号
    137                 .HISTTRANS_VSYNC(dis_trans_vsync)            //输出直方图线性拉伸场有效信号
    138             );
    139             
    140             assign dis_trans_data_in = dvd;
    141             assign dis_trans_dvalid_in = dhsyn;
    142             assign dis_trans_vsync_in = dvsyn;
    143             
    144             always@(posedge cap_clk or posedge dis_trans_vsync)begin
    145                 if((~(dis_trans_vsync)) == 1'b0)
    146                     cnt_dis_trans = 0;
    147                 else 
    148                 begin
    149                     if(dis_trans_dvalid == 1'b1)
    150                     begin
    151                         fp_dis_trans = $fopen("E:/Modelsim/hist_transform/sim/dis_trans.txt","r+");
    152                         $fseek(fp_dis_trans,cnt_dis_trans,0);
    153                         $fdisplay(fp_dis_trans,"%02x",dis_trans_data);
    154                         $fclose(fp_dis_trans);
    155                         cnt_dis_trans <= cnt_dis_trans + 4;    
    156                     end
    157                 end
    158             end    
    159         end
    160     endgenerate
    161     
    162     initial
    163     begin: init
    164         reset_l <= 1'b1;
    165         src_sel <= 4'b0000;
    166         #(100);            //reset the system
    167         reset_l <= 1'b0;
    168         #(100);    
    169         reset_l <= 1'b1;
    170     end
    171     
    172     //dv_clk generate
    173     always@(reset_l or pixel_clk)begin
    174         if((~(reset_l)) == 1'b1)
    175             pixel_clk <= 1'b0;
    176         else 
    177         begin
    178             if(clk_freq == 48)            //48MHz
    179                 pixel_clk <= #10417 (~(pixel_clk));
    180             
    181             else if(clk_freq == 51.84)    //51.84MHz
    182                 pixel_clk <= #9645 (~(pixel_clk));
    183             
    184             else if(clk_freq == 72)        //72MHz
    185                 pixel_clk <= #6944 (~(pixel_clk));
    186         end
    187     end
    188     
    189     //cap_clk generate: 25MHz
    190     always@(reset_l or cap_clk)begin
    191         if((~(reset_l)) == 1'b1)
    192             cap_clk <= 1'b0;
    193         else
    194             cap_clk <= #20000 (~(cap_clk));    
    195     end
    196     
    197 endmodule
    198     

     四、实验结果

      (1)仿真过程中,设置Thr_Min和Thr_Max的值均为100,这两个值定义了首尾被截断的直方图统计数目(其实就是累加和小于100或者大于IH*IW-100的部分被剔除掉)。

       lowIndex的计算过程时序图;从图中可以看出当clr_addr=10的时候,累加和为105大于100,因此lowIndex(即A)=9。

      highIndex的计算过程时序图;当clr_addr为223时,累加和为327600大于(327680-100),因此highIndex(即B) = 222。

     (3)线性拉伸计算过程的仿真时序,从时序中可以看出经过两个时钟后得到了f(x,y)-A的值,同时除法器消耗了15个时钟,最后在数据有效后的第17个时钟输出了正确的计算结果。其中以第一个像素值

    为例,255*(137-9)/(222-9) = 153,与实际仿真结果相符合。

     (4)图像仿真结果,如下图所示,为图像直方图拉伸前后的统计直方图对比。从图中可以看出经过算法处理后直方图由原来的[9 222]的区间映射到了[0 255]的区间。由于小于100和大于327580的部分被

    置成了0和255,所以拉伸后的图像中有许多黑/白点。另外由于100的阈值可能不是很适合Lena这副图像的处理,所以对比度上虽然有所增强,但是效果并不明显。个人尝试调整过更改阈值,增大后对比度

    确实有明显的增强,但随之而来也会引入大量的黑/白点噪声,因此这个值的选择是一个需要权衡的问题。

     

      

      

    初入江湖,天下无敌;再学三年,寸步难行。
  • 相关阅读:
    javascript 中的nextSibling和previousSibling使用注意事项
    inline-block(行内区块元素)的详解和应用
    createElement()结合appendChild()的实例
    JavaScript 的setAttribute兼容性解决
    css 雪碧图 及jquery定位代码
    jquery图片轮播代码
    jquery 的attr()方法解析
    用jquery修改默认的单选框radio或者复选框checkbox选择框样式
    html form <label>标签基础语法结构与使用案例教程(转载)
    CoreText
  • 原文地址:https://www.cnblogs.com/huangwei0521/p/12519981.html
Copyright © 2011-2022 走看看