FPGA图像处理-直方图均衡化

直方图统计原理

百度百科中关于直方图均衡化的描述:
图像处理领域中利用图像直方图对对比度进行调整的方法。 对比度是画面黑与白的比值,也就是从黑到白的渐变层次。比值越大,从黑到白的渐变层次就越多,从而色彩表现越丰富。对比度对视觉效果的影响非常关键,一般来说对比度越大,图像越清晰醒目,色彩也越鲜明艳丽;而对比度小,则会让整个画面都灰蒙蒙的。
直方图均衡化分为真均衡化和伪均衡化,由于FPGA不方便实现真均衡化,所以采用伪均衡化,即前一帧的图像进行统计、帧间隙进行累计和与归一化、当前帧做归一化后的映射输出。不过仿真的话,前一帧和当前帧是同一张图片,就是真均衡化。
下图是咸鱼fpga博客中直方图均衡化的波形图:

FPGA图像处理-直方图均衡化
本人按照上图的思路实现,没有ram2的清零,因为ram2写入会覆盖旧数据。
直方图均衡化步骤:
  1. 第一帧统计直方图存入ram1
  2. 帧间隙读出ram1中数据进行计算,将计算结果存入ram2,同时对ram1进行清零
  3. 第二帧根据映射表进行输出

; 直方图统计

为了建立直方图,在FPGA中可以用256个计数器对每个灰度进行计数,不过这样做代码代码量太大,使用的资源也很多,不太现实。
像素是一个个来的,因此任何时钟周期都只有一个计数器在增加,意味着累加器可以在存储器中实现,首先需要读取相关存储单元,然后加一再写回,这需要用到双端口ram,一个读端口一个写端口。不过需要注意,因为读出数据需要一拍,图中灰度 I需要打一拍再送入写入端口的地址端。

FPGA图像处理-直方图均衡化
这种做法会有误差,因为ram在读写冲突时读出的是旧数据,所以当连续相同像素到来时,会出现统计丢失,不过对结果影响很小,视觉上难以辨别。本人水平有限,无法解决这个问题。

直方图均衡化

直方图均衡化公式:

FPGA图像处理-直方图均衡化
H(i)为第 i 级灰度的像素个数,A0为图像的面积(即分辨率),Dmax为灰度最大值,即255。
帧间隙时,设计一个计数器,从0计数到255,将ram1中的数据读出来,同时对ram1进行清零。读出的数据会通过流水线计算,得出直方图均衡化后的灰度级映射,再写入ram2。第一级进行累加,第二级乘以255,第三级除以分辨率。
第二帧只需读出ram2中的数据进行映射输出即可得到直方图均衡化后的图像。

; verilog代码

module histgram_equ(
    input           clk,
    input           rst_n,

    input           pre_vsync,
    input           pre_href,
    input           pre_clken,
    input   [7:0]   pre_img_Y,

    output  reg     post_vsync,
    output  reg     post_href,
    output  reg     post_clken,
    output  [7:0]   post_img_Y
);

    wire    [7:0]   rd_addr_bus;

    wire    [31:0]  rd_data;

    wire            wren_bus;

    wire    [7:0]   wr_addr_bus;

    wire    [31:0]  wr_data_bus;

    reg [7:0]       pre_img_Y_r;

    reg             pre_clken_r;

    reg             pre_vsync_r;

    reg [7:0]       rd_addr_cnt;

    reg             rd_addr_cnt_en;

    reg [3:0]       cnt_en_lag4;

    reg [7:0]       rd_addr_cnt_r1;
    reg [7:0]       rd_addr_cnt_r2;
    reg [7:0]       rd_addr_cnt_r3;
    reg [7:0]       rd_addr_cnt_r4;

    reg [31:0]      sum;

    reg [31:0]      sum_x_255;

    reg [7:0]      sum_x_255_div_307200;

    assign rd_addr_bus = pre_vsync ? pre_img_Y     : rd_addr_cnt;
    assign wren_bus    = pre_vsync ? pre_clken_r   : rd_addr_cnt_en;
    assign wr_addr_bus = pre_vsync ? pre_img_Y_r   : rd_addr_cnt;
    assign wr_data_bus = pre_vsync ? (rd_data + 1) : 8'd0;

    always @(posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            pre_img_Y_r  0;
            pre_clken_r  1'b0;
        end else begin
            pre_img_Y_r  pre_img_Y;
            pre_clken_r  pre_clken;
        end
    end

    ram_32x256 inst_ram_32x256(
        .clock     (clk),
        .data      (wr_data_bus),
        .rdaddress (rd_addr_bus),
        .wraddress (wr_addr_bus),
        .wren      (wren_bus),
        .q         (rd_data)
    );

    always @(posedge clk, negedge rst_n) begin
        if(!rst_n)
            pre_vsync_r  0;
        else
            pre_vsync_r  pre_vsync;
    end

    always @(posedge clk, negedge rst_n) begin
        if(!rst_n)
            rd_addr_cnt_en  1'b0;
        else if(~pre_vsync & pre_vsync_r)
            rd_addr_cnt_en  1'b1;
        else if(rd_addr_cnt == 8'd255)
            rd_addr_cnt_en  1'b0;
    end

    always @(posedge clk, negedge rst_n) begin
        if(!rst_n)
            rd_addr_cnt  0;
        else if(rd_addr_cnt_en)
            rd_addr_cnt  rd_addr_cnt + 8'd1;
    end

    always @(posedge clk, negedge rst_n) begin
        if(!rst_n)
            sum  0;
        else if(cnt_en_lag4[0])
            sum  sum + rd_data;
        else
            sum  0;
    end

    always @(posedge clk, negedge rst_n) begin
        if(!rst_n)
            sum_x_255  0;
        else
            sum_x_255  (sum << 8) - sum;
    end

    always @(posedge clk, negedge rst_n) begin
        if(!rst_n)
            sum_x_255_div_307200  0;
        else
            sum_x_255_div_307200  sum_x_255 / 307200;
    end

    always @(posedge clk, negedge rst_n) begin
        if(!rst_n)
            cnt_en_lag4  0;
        else
            cnt_en_lag4  {cnt_en_lag4[2:0], rd_addr_cnt_en};
    end

    always @(posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            rd_addr_cnt_r1  0;
            rd_addr_cnt_r2  0;
            rd_addr_cnt_r3  0;
            rd_addr_cnt_r4  0;
        end else begin
            rd_addr_cnt_r1  rd_addr_cnt;
            rd_addr_cnt_r2  rd_addr_cnt_r1;
            rd_addr_cnt_r3  rd_addr_cnt_r2;
            rd_addr_cnt_r4  rd_addr_cnt_r3;
        end
    end

    ram_8x256 inst_ram_8x256(
        .clock     (clk),
        .data      (sum_x_255_div_307200),
        .rdaddress (pre_img_Y),
        .wraddress (rd_addr_cnt_r4),
        .wren      (cnt_en_lag4[3]),
        .q         (post_img_Y)
    );

    always @(posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            post_vsync  1'b0;
            post_href   1'b0;
            post_clken  1'b0;
        end else begin
            post_vsync  pre_vsync;
            post_href   pre_href;
            post_clken  pre_clken;
        end
    end
endmodule

结果

输入图像:

FPGA图像处理-直方图均衡化
处理后图像:
FPGA图像处理-直方图均衡化
可以看出对比度明显提升,下图是它们的直方图:
FPGA图像处理-直方图均衡化
可以看出直方图分布更加均匀,但灰度接近255的部分却没有像素了,就是因为上面所说的ram读写冲突导致的统计丢失,同matlab直方图均衡化相比,视觉上会稍微暗一点点。

Original: https://blog.csdn.net/lzl1342848782/article/details/126121649
Author: 学习就van事了
Title: FPGA图像处理-直方图均衡化

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

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

(0)

大家都在看

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