FPGA实现直方图均衡(一)

直方图均衡的原理笔者就不写了,主要记录如何用verilog写出来。
首先需要实现直方图统计,就是统计一幅图中各灰度级的像素数量。
那么这里参考《基于FPGA的数字图像处理原理及应用》这本书,也推荐大家看一看这本书,讲解了许多图像处理在FPGA中实现的方法。
首先,很显然,需要用一个ram来缓存统计数据。
那么统计的流程主要就是计数,输出数据和清零。清零很简单,只需要在
输出数据的下一个时钟对上一次输出的数据进行清零就可以了,比较简单,直接给时序图:

FPGA实现直方图均衡(一)

如果按照一般思路,每次将当前像素点的灰度级作为地址,然后每次读出上一次记录的数量再写入,比较耗时,所以实现的方法是,对输入数据进行一个缓存,并且缓存的数据作为读地址。然后比较当前输入数据与缓存是否相同,相同则计数器加一,不同则将计数器与读出的数据相加作为写入数据。下图是时序图:

FPGA实现直方图均衡(一)
de代表数据有效,de1和de2是de的一级缓存和二级缓存;
data_in是当前数据,也就是像素,范围是0到255;
rd_addr和wr_addr是读地址和写地址;
cnt_en是计数使能,高有效;
wr_en是写使能,高有效;
cnt是计数器,默认值是1;
rd_data是读数据,rd_data1是rd_data1的一级缓存;
wr_data是写数据,它的值是cnt与rd_data1的和;
这里要说明的是笔者使用的ram ip核是读数据无缓存的,也就是给ram读地址后,这个周期就得到数据。
那么根据上面的时序图就可以写了。
下面是全部代码
module he_top(
    input clk,
    input reset_n,
    input [7:0]data_in,
    input vs,
    input hs,
    input de,
    output [19:0]he_calculate,
    output o_he_de,
    output [7:0]o_he_addr

   );
   localparam IH='d768;
   reg  de_r;
   reg  de_r2;
   reg  hs_r;
   reg  vs_r;
   reg  [9:0]CNT;
   wire     CNT_en;
   reg  [7:0]rd_addr;
   reg  [7:0]wr_addr;
   wire     wr_en;

   wire [19:0]rd_data;
   wire [19:0]wr_data;
   reg  [19:0]rd_data_r;

   reg  [9:0] row_cnt;
   wire       row_cnt_en;
   reg  [1:0] hs_reg;

   reg  [7:0]po_cnt;
   reg  calculate_end;
   reg  [1:0]calculate_end_r;

   always@(posedge clk or negedge reset_n)begin
        if(!reset_n)begin
            de_r0;
            de_r20;
        end
        else begin
            de_rde;
            de_r2de_r;
        end
    end

    always@(posedge clk or negedge reset_n)begin
        if(!reset_n)
            hs_r0;
        else
            hs_rhs;
    end

   always@(posedge clk or negedge reset_n)begin
        if(!reset_n)
            vs_r0;
        else
            vs_rvs;
    end

   always@(posedge clk or negedge reset_n)begin
        if(!reset_n)
            rd_addr0;
        else if(de==1'b1)
            rd_addrdata_in;
        else if(calculate_end==1'b1)
            rd_addrpo_cnt;
    end

    always@(posedge clk or negedge reset_n)begin
        if(!reset_n)
            wr_addr0;
        else
            wr_addrrd_addr;
    end

    assign CNT_en=(rd_addr==wr_addr)?de_r2:0;
    always@(posedge clk or negedge reset_n)begin
        if(!reset_n)
            CNT1'b1;
        else if(CNT_en==1'b1)
            CNTCNT+1'b1;
        else if(CNT_en==0)
            CNT1'b1;
        else
            CNTCNT;
    end

    always@(posedge clk or negedge reset_n)begin
        if(!reset_n)
            rd_data_r0;
        else
            rd_data_rrd_data;
    end

    assign wr_en=(rd_addr==wr_addr)?0:de_r2;
    assign wr_data=calculate_end_r[1]?0:(CNT+rd_data_r);

    always@(posedge clk or negedge reset_n)begin
        if(!reset_n)begin
            hs_reg[0]0;
            hs_reg[1]0;
        end
        else begin
            hs_reg[0]hs;
            hs_reg[1]hs_reg[0];
        end
    end

    assign  row_cnt_en=((!de_r)&&(de_r2))?1'b1:0;

    always@(posedge clk or negedge reset_n)begin
        if(!reset_n)
            row_cnt0;
        else if((row_cnt_en==1'b1)&&(row_cntIH-1))
            row_cntrow_cnt+1'b1;
        else if(row_cnt==IH)
            row_cnt0;
    end

    always@(posedge clk or negedge reset_n)begin
        if(!reset_n)
            calculate_end0;
        else if(row_cnt==IH)
            calculate_end1'b1;
        else if(po_cnt==8'd255)
            calculate_end0;
        else
            calculate_endcalculate_end;
    end

    always@(posedge clk or negedge reset_n)begin
        if(!reset_n)
            calculate_end_r0;
        else
            calculate_end_r{calculate_end_r[0],calculate_end};
    end

    always@(posedge clk or negedge reset_n)begin
        if(!reset_n)
            po_cnt0;
        else if(calculate_end==1'b1)
            po_cntpo_cnt+1'b1;
    end

    assign he_calculate=calculate_end_r[0]?rd_data:0;
    wire he_wr_en;
    assign  he_wr_en=(wr_en)||(row_cnt_en)||(calculate_end_r[1]);

    assign o_he_de=calculate_end_r[0];
    assign o_he_addr=calculate_end_r[0]?rd_addr:0;

    he_cal he_cal_t1 (
  .wr_data(wr_data),
  .wr_addr(wr_addr),
  .rd_addr(rd_addr),
  .wr_clk(clk),
  .rd_clk(clk),
  .wr_en(he_wr_en),
  .rst(!reset_n),
  .rd_data(rd_data)
);

endmodule

截取一部分仿真图:

FPGA实现直方图均衡(一)
后续有空再加注释。

Original: https://blog.csdn.net/qq_41527741/article/details/121567657
Author: 满城風絮
Title: FPGA实现直方图均衡(一)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/645975/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球