FPGA图像处理之直方图均衡实现

FPGA图像处理之直方图均衡实现

1. 直方图均衡的原理

直方图均衡的基本原理,就是对在图像中像素个数多的灰度值(即对画面起主要作用的灰度值)进行拉伸,而对像素个数较少的灰度值(即对画面不起主要作用的灰度值)进行合并,从而提高对比度,使图像清晰,达到增强的目的。
未处理的图像
处理后的图像
为了将原图像的亮度范围进行扩展, 需要一个映射函数, 将原图像的像素值均衡映射到新直方图中, 这个映射函数有两个条件:

  1. 不能打乱原有的像素值大小顺序, 映射后亮、 暗的大小关系不能改变;
  2. 映射后必须在原有的范围内,即像素映射函数的值域应在0和255之间;

综合以上两个条件,累积分布函数是个好的选择,因为累积分布函数是单调增函数(控制大小关系),并且值域是0到1(控制越界问题),所以直方图均衡化中使用的是累积分布函数。

2. 直方图均衡的FPGA实现

根据直方图均衡的功能将其划分为两个子模块,分别为hist_stat模块和histEQ_proc模块,直方图均衡FPGA设计框架,如图1所示。hist_stat模块的功能是进行像素的灰度级数统计和灰度级数累积统计;histEQ_proc模块的功能是对原始图像进行直方图均衡。
直方图均衡的业务处理流程如下。
(1)将原始图像输入到hist_stat模块进行像素灰度级数统计和灰度级数累计统计,同时原始图像存储于外部存储器。
(2)在(1)完成后,histEQ_proc模块从外部存储器读取原始图像进行直方图均衡,输出增强后的图像。
图1

3. FPGA 代码实现

1.外部存储器模块

module bram_ture_dual_port
#(
    parameter C_ADDR_WIDTH  = 8,
    parameter C_DATA_WIDTH  = 8
)(
    input  wire                     clka    ,
    input  wire                     wea     ,
    input  wire [C_ADDR_WIDTH-1:0]  addra   ,
    input  wire [C_DATA_WIDTH-1:0]  dina    ,
    output reg  [C_DATA_WIDTH-1:0]  douta   ,
    input  wire                     clkb    ,
    input  wire                     web     ,
    input  wire [C_ADDR_WIDTH-1:0]  addrb   ,
    input  wire [C_DATA_WIDTH-1:0]  dinb    ,
    output reg  [C_DATA_WIDTH-1:0]  doutb   
);
//----------------------------------------------------------------------
localparam C_MEM_DEPTH = {C_ADDR_WIDTH{1'b1}};

reg     [C_DATA_WIDTH-1:0]      mem [C_MEM_DEPTH:0];
integer                         i;

initial
begin
    for(i = 0;i <= C_MEM_DEPTH;i = i+1)
        mem[i] = 0;
end 

always @(posedge clka)
begin
    if(wea == 1'b1)
        mem[addra] <= dina; 
    else
        douta <= mem[addra];
end

always @(posedge clkb)
begin
    if(web == 1'b1)
        mem[addrb] <= dinb; 
    else
        doutb <= mem[addrb];
end

endmodule

2.hist_stat模块

module hist_stat
(
    input  wire                 clk                 ,
    input  wire                 rst_n               ,
    
    input  wire                 img_vsync           ,
    input  wire                 img_href            ,
    input  wire     [ 7:0]      img_gray            ,
    
    output reg      [ 7:0]      pixel_level         ,
    output reg      [19:0]      pixel_level_acc_num ,
    output reg                  pixel_level_valid   
);
//----------------------------------------------------------------------
//  bram signals define
wire                            bram_a_wenb;
wire            [ 7:0]          bram_a_addr;
wire            [19:0]          bram_a_rdata;
wire                            bram_b_wenb;
wire            [ 7:0]          bram_b_addr;
wire            [19:0]          bram_b_wdata;
wire            [19:0]          bram_b_rdata;

//----------------------------------------------------------------------
//  preprocess
reg             [7:0]           pixel_data;

always @(posedge clk)
begin
    pixel_data <= img_gray;
end

reg                             pixel_data_valid;

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        pixel_data_valid <= 1'b0;
    else
        pixel_data_valid <= img_href;
end

reg                             img_vsync_dly;

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        img_vsync_dly <= 1'b0;
    else
        img_vsync_dly <= img_vsync;
end

wire                            pixel_data_eop;
assign pixel_data_eop = img_vsync_dly & ~img_vsync;

//----------------------------------------------------------------------
//  preprocess
reg             [7:0]           pixel_data_tmp;

always @(posedge clk)
begin
    pixel_data_tmp <= pixel_data;
end

reg                             pixel_data_valid_tmp1;

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        pixel_data_valid_tmp1 <= 1'b0;
    else
    begin
        if(pixel_data_valid == 1'b1)
        begin
            if(pixel_data_valid_tmp1 == 1'b0)
                pixel_data_valid_tmp1 <= 1'b1;
            else
            begin
                if(pixel_data_tmp == pixel_data)
                    pixel_data_valid_tmp1 <= 1'b0;
                else
                    pixel_data_valid_tmp1 <= 1'b1;
            end
        end
        else
            pixel_data_valid_tmp1 <= 1'b0;
    end
end

reg             [1:0]           pixel_data_cnt_tmp;

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        pixel_data_cnt_tmp <= 2'd1;
    else
    begin
        if((pixel_data_valid == 1'b1)&&(pixel_data_valid_tmp1 == 1'b1)&&(pixel_data_tmp == pixel_data))
            pixel_data_cnt_tmp <= 2'd2;
        else
            pixel_data_cnt_tmp <= 2'd1;
    end
end

reg                             pixel_data_eop_tmp;

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        pixel_data_eop_tmp <= 1'b0;
    else
        pixel_data_eop_tmp <= pixel_data_eop;
end

//----------------------------------------------------------------------
//  c1
reg             [7:0]           pixel_data_c1;

always @(posedge clk)
begin
    pixel_data_c1 <= pixel_data;
end

reg                             pixel_data_eop_c1;
reg                             pixel_data_valid_tmp_c1;

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
    begin
        pixel_data_eop_c1       <= 1'b0;
        pixel_data_valid_tmp_c1 <= 1'b0;
    end
    else
    begin
        pixel_data_eop_c1       <= pixel_data_eop_tmp;
        pixel_data_valid_tmp_c1 <= pixel_data_valid_tmp1;
    end
end

//----------------------------------------------------------------------
//  c2 : delay 3 clock
reg             [7:0]           pixel_data_c2;

always @(posedge clk)
begin
    pixel_data_c2 <= pixel_data_c1;
end

reg                             pixel_data_eop_tmp1_c2;
reg                             pixel_data_eop_tmp2_c2;
reg                             pixel_data_eop_c2;

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
    begin
        pixel_data_eop_tmp1_c2 <= 1'b0;
        pixel_data_eop_tmp2_c2 <= 1'b0;
        pixel_data_eop_c2      <= 1'b0;
    end
    else
    begin
        pixel_data_eop_tmp1_c2 <= pixel_data_eop_c1;
        pixel_data_eop_tmp2_c2 <= pixel_data_eop_tmp1_c2;
        pixel_data_eop_c2      <= pixel_data_eop_tmp2_c2;
    end
end

//----------------------------------------------------------------------
//  c3
reg                             bram_rw_ctrl_flag_c3;
reg             [8:0]           bram_rw_ctrl_cnt;

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        bram_rw_ctrl_flag_c3 <= 1'b0;
    else
    begin
        if(pixel_data_eop_c2 == 1'b1)
            bram_rw_ctrl_flag_c3 <= 1'b1;
        else if(bram_rw_ctrl_cnt == 9'h100)
            bram_rw_ctrl_flag_c3 <= 1'b0;
        else
            bram_rw_ctrl_flag_c3 <= bram_rw_ctrl_flag_c3;
    end
end

reg             [8:0]           bram_rw_ctrl_cnt_dly;

always @(posedge clk)
begin
    bram_rw_ctrl_cnt_dly <= bram_rw_ctrl_cnt;
end

always @(*)
begin
    if(bram_rw_ctrl_flag_c3 == 1'b1)
        bram_rw_ctrl_cnt <= bram_rw_ctrl_cnt_dly + 1'b1;
    else
        bram_rw_ctrl_cnt <= 9'b0;
end

wire            [7:0]           bram_addr_c3;
assign bram_addr_c3 = bram_rw_ctrl_cnt - 1'b1;

//----------------------------------------------------------------------
//  c4
reg                             bram_rw_ctrl_flag_c4;

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        bram_rw_ctrl_flag_c4 <= 1'b0;
    else
        bram_rw_ctrl_flag_c4 <= bram_rw_ctrl_flag_c3;
end

reg             [7:0]           bram_addr_c4;

always @(posedge clk)
begin
    bram_addr_c4 <= bram_addr_c3;
end

//----------------------------------------------------------------------
//  c5
reg             [7:0]           pixel_level_c5;

always @(posedge clk)
begin
    pixel_level_c5 <= bram_addr_c4;
end

reg             [19:0]          pixel_level_num_c5;

always @(posedge clk)
begin
    if(bram_rw_ctrl_flag_c4 == 1'b1)
        pixel_level_num_c5 <= bram_b_rdata;
    else
        pixel_level_num_c5 <= 20'b0;
end

reg                             pixel_level_valid_c5;

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        pixel_level_valid_c5 <= 1'b0;
    else
        pixel_level_valid_c5 <= bram_rw_ctrl_flag_c4;
end

//----------------------------------------------------------------------
//  c6
reg             [7:0]           pixel_level_c6;

always @(posedge clk)
begin
    pixel_level_c6 <= pixel_level_c5;
end

reg             [19:0]          pixel_level_acc_num_c6;

always @(posedge clk)
begin
    if(pixel_level_valid_c5 == 1'b1)
    begin
        if(pixel_level_c5 == 8'b0)
            pixel_level_acc_num_c6 <= pixel_level_num_c5;
        else
            pixel_level_acc_num_c6 <= pixel_level_acc_num_c6 + pixel_level_num_c5;
    end
    else
        pixel_level_acc_num_c6 <= pixel_level_acc_num_c6;
end

reg                             pixel_level_valid_c6;

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        pixel_level_valid_c6 <= 1'b0;
    else
        pixel_level_valid_c6 <= pixel_level_valid_c5;
end

//----------------------------------------------------------------------
//  signal output
always @(posedge clk)
begin
    pixel_level         <= pixel_level_c6;
    pixel_level_acc_num <= pixel_level_acc_num_c6;
end

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        pixel_level_valid <= 1'b0;
    else
        pixel_level_valid <= pixel_level_valid_c6;
end

//----------------------------------------------------------------------
//  bram_a20d256_b20d256 module initialization
assign bram_a_wenb  = bram_rw_ctrl_flag_c4;
assign bram_a_addr  = (bram_rw_ctrl_flag_c4 == 1'b1) ? bram_addr_c4 : pixel_data_tmp;
assign bram_b_wenb  = pixel_data_valid_tmp_c1;
assign bram_b_addr  = (bram_rw_ctrl_flag_c3 == 1'b1) ? bram_addr_c3 : pixel_data_c2;
assign bram_b_wdata = bram_a_rdata + pixel_data_cnt_tmp;

bram_ture_dual_port
#(
    .C_ADDR_WIDTH   (8 ),
    .C_DATA_WIDTH   (20)
)
u_bram_ture_dual_port
(
    .clka   (clk            ),
    .wea    (bram_a_wenb    ),
    .addra  (bram_a_addr    ),
    .dina   (20'b0          ),
    .douta  (bram_a_rdata   ),
    .clkb   (clk            ),
    .web    (bram_b_wenb    ),
    .addrb  (bram_b_addr    ),
    .dinb   (bram_b_wdata   ),
    .doutb  (bram_b_rdata   )
);

endmodule

3.histEQ_proc模块

module histEQ_proc
(
    input  wire                 clk                 ,
    input  wire                 rst_n               ,
    
    input  wire     [ 7:0]      pixel_level         ,
    input  wire     [19:0]      pixel_level_acc_num ,
    input  wire                 pixel_level_valid   ,
    output reg                  histEQ_start_flag   ,
    
    input  wire                 per_img_vsync       ,
    input  wire                 per_img_href        ,
    input  wire     [ 7:0]      per_img_gray        ,
    
    output wire                 post_img_vsync      ,
    output wire                 post_img_href       ,
    output wire     [ 7:0]      post_img_gray       
);
//----------------------------------------------------------------------
//  delay 1 clock
wire                            bram_a_wenb;
wire            [ 7:0]          bram_a_addr;
wire            [19:0]          bram_a_wdata;
wire            [ 7:0]          bram_b_addr;
wire            [19:0]          bram_b_rdata;

assign bram_a_wenb  = pixel_level_valid;
assign bram_a_addr  = pixel_level;
assign bram_a_wdata = pixel_level_acc_num;
assign bram_b_addr  = per_img_gray;

bram_ture_dual_port
#(
    .C_ADDR_WIDTH   (8 ),
    .C_DATA_WIDTH   (20)
)
u_bram_ture_dual_port
(
    .clka   (clk            ),
    .wea    (bram_a_wenb    ),
    .addra  (bram_a_addr    ),
    .dina   (bram_a_wdata   ),
    .douta  (               ),
    .clkb   (clk            ),
    .web    (1'b0           ),
    .addrb  (bram_b_addr    ),
    .dinb   (20'b0          ),
    .doutb  (bram_b_rdata   )
);

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
        histEQ_start_flag <= 1'b0;
    else
    begin
        if((pixel_level_valid == 1'b1)&&(pixel_level == 8'd255))
            histEQ_start_flag <= 1'b1;
        else
            histEQ_start_flag <= 1'b0;
    end
end

//----------------------------------------------------------------------
//  uint8(CumPixel/980) = round(CumPixel * 136957/2^27) , CumPixel <= 500*500
reg             [34:0]          mult_result;

always @(posedge clk)
begin
    mult_result <= bram_b_rdata * 18'd136957;
end

reg             [7:0]           pixel_data;

always @(posedge clk)
begin
    pixel_data <= mult_result[34:27] + mult_result[26];
end

//----------------------------------------------------------------------
//  lag 3 clocks signal sync
reg             [2:0]           per_img_vsync_r;
reg             [2:0]           per_img_href_r;

always @(posedge clk or negedge rst_n)
begin
    if(!rst_n)
    begin
        per_img_vsync_r <= 3'b0;
        per_img_href_r  <= 3'b0;
    end
    else
    begin
        per_img_vsync_r <= {per_img_vsync_r[1:0],per_img_vsync};
        per_img_href_r  <= {per_img_href_r[1:0],per_img_href};
    end
end

//----------------------------------------------------------------------
assign post_img_vsync = per_img_vsync_r[2];
assign post_img_href  = per_img_href_r[2];
assign post_img_gray  = pixel_data;

endmodule

4.参考资料

1.基于MATLAB与FPGA的图像处理教程(韩彬)
2.https://blog.csdn.net/weixin_40163266/article/details/113802909

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值