直方图均衡实现"/>
FPGA图像处理之直方图均衡实现
FPGA图像处理之直方图均衡实现
1. 直方图均衡的原理
直方图均衡的基本原理,就是对在图像中像素个数多的灰度值(即对画面起主要作用的灰度值)进行拉伸,而对像素个数较少的灰度值(即对画面不起主要作用的灰度值)进行合并,从而提高对比度,使图像清晰,达到增强的目的。
为了将原图像的亮度范围进行扩展, 需要一个映射函数, 将原图像的像素值均衡映射到新直方图中, 这个映射函数有两个条件:
- 不能打乱原有的像素值大小顺序, 映射后亮、 暗的大小关系不能改变;
- 映射后必须在原有的范围内,即像素映射函数的值域应在0和255之间;
综合以上两个条件,累积分布函数是个好的选择,因为累积分布函数是单调增函数(控制大小关系),并且值域是0到1(控制越界问题),所以直方图均衡化中使用的是累积分布函数。
2. 直方图均衡的FPGA实现
根据直方图均衡的功能将其划分为两个子模块,分别为hist_stat模块和histEQ_proc模块,直方图均衡FPGA设计框架,如图1所示。hist_stat模块的功能是进行像素的灰度级数统计和灰度级数累积统计;histEQ_proc模块的功能是对原始图像进行直方图均衡。
直方图均衡的业务处理流程如下。
(1)将原始图像输入到hist_stat模块进行像素灰度级数统计和灰度级数累计统计,同时原始图像存储于外部存储器。
(2)在(1)完成后,histEQ_proc模块从外部存储器读取原始图像进行直方图均衡,输出增强后的图像。
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
beginfor(i = 0;i <= C_MEM_DEPTH;i = i+1)mem[i] = 0;
end always @(posedge clka)
beginif(wea == 1'b1)mem[addra] <= dina; elsedouta <= mem[addra];
endalways @(posedge clkb)
beginif(web == 1'b1)mem[addrb] <= dinb; elsedoutb <= mem[addrb];
endendmodule
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)
beginpixel_data <= img_gray;
endreg pixel_data_valid;always @(posedge clk or negedge rst_n)
beginif(!rst_n)pixel_data_valid <= 1'b0;elsepixel_data_valid <= img_href;
endreg img_vsync_dly;always @(posedge clk or negedge rst_n)
beginif(!rst_n)img_vsync_dly <= 1'b0;elseimg_vsync_dly <= img_vsync;
endwire pixel_data_eop;
assign pixel_data_eop = img_vsync_dly & ~img_vsync;//----------------------------------------------------------------------
// preprocess
reg [7:0] pixel_data_tmp;always @(posedge clk)
beginpixel_data_tmp <= pixel_data;
endreg pixel_data_valid_tmp1;always @(posedge clk or negedge rst_n)
beginif(!rst_n)pixel_data_valid_tmp1 <= 1'b0;elsebeginif(pixel_data_valid == 1'b1)beginif(pixel_data_valid_tmp1 == 1'b0)pixel_data_valid_tmp1 <= 1'b1;elsebeginif(pixel_data_tmp == pixel_data)pixel_data_valid_tmp1 <= 1'b0;elsepixel_data_valid_tmp1 <= 1'b1;endendelsepixel_data_valid_tmp1 <= 1'b0;end
endreg [1:0] pixel_data_cnt_tmp;always @(posedge clk or negedge rst_n)
beginif(!rst_n)pixel_data_cnt_tmp <= 2'd1;elsebeginif((pixel_data_valid == 1'b1)&&(pixel_data_valid_tmp1 == 1'b1)&&(pixel_data_tmp == pixel_data))pixel_data_cnt_tmp <= 2'd2;elsepixel_data_cnt_tmp <= 2'd1;end
endreg pixel_data_eop_tmp;always @(posedge clk or negedge rst_n)
beginif(!rst_n)pixel_data_eop_tmp <= 1'b0;elsepixel_data_eop_tmp <= pixel_data_eop;
end//----------------------------------------------------------------------
// c1
reg [7:0] pixel_data_c1;always @(posedge clk)
beginpixel_data_c1 <= pixel_data;
endreg pixel_data_eop_c1;
reg pixel_data_valid_tmp_c1;always @(posedge clk or negedge rst_n)
beginif(!rst_n)beginpixel_data_eop_c1 <= 1'b0;pixel_data_valid_tmp_c1 <= 1'b0;endelsebeginpixel_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)
beginpixel_data_c2 <= pixel_data_c1;
endreg pixel_data_eop_tmp1_c2;
reg pixel_data_eop_tmp2_c2;
reg pixel_data_eop_c2;always @(posedge clk or negedge rst_n)
beginif(!rst_n)beginpixel_data_eop_tmp1_c2 <= 1'b0;pixel_data_eop_tmp2_c2 <= 1'b0;pixel_data_eop_c2 <= 1'b0;endelsebeginpixel_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)
beginif(!rst_n)bram_rw_ctrl_flag_c3 <= 1'b0;elsebeginif(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;elsebram_rw_ctrl_flag_c3 <= bram_rw_ctrl_flag_c3;end
endreg [8:0] bram_rw_ctrl_cnt_dly;always @(posedge clk)
beginbram_rw_ctrl_cnt_dly <= bram_rw_ctrl_cnt;
endalways @(*)
beginif(bram_rw_ctrl_flag_c3 == 1'b1)bram_rw_ctrl_cnt <= bram_rw_ctrl_cnt_dly + 1'b1;elsebram_rw_ctrl_cnt <= 9'b0;
endwire [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)
beginif(!rst_n)bram_rw_ctrl_flag_c4 <= 1'b0;elsebram_rw_ctrl_flag_c4 <= bram_rw_ctrl_flag_c3;
endreg [7:0] bram_addr_c4;always @(posedge clk)
beginbram_addr_c4 <= bram_addr_c3;
end//----------------------------------------------------------------------
// c5
reg [7:0] pixel_level_c5;always @(posedge clk)
beginpixel_level_c5 <= bram_addr_c4;
endreg [19:0] pixel_level_num_c5;always @(posedge clk)
beginif(bram_rw_ctrl_flag_c4 == 1'b1)pixel_level_num_c5 <= bram_b_rdata;elsepixel_level_num_c5 <= 20'b0;
endreg pixel_level_valid_c5;always @(posedge clk or negedge rst_n)
beginif(!rst_n)pixel_level_valid_c5 <= 1'b0;elsepixel_level_valid_c5 <= bram_rw_ctrl_flag_c4;
end//----------------------------------------------------------------------
// c6
reg [7:0] pixel_level_c6;always @(posedge clk)
beginpixel_level_c6 <= pixel_level_c5;
endreg [19:0] pixel_level_acc_num_c6;always @(posedge clk)
beginif(pixel_level_valid_c5 == 1'b1)beginif(pixel_level_c5 == 8'b0)pixel_level_acc_num_c6 <= pixel_level_num_c5;elsepixel_level_acc_num_c6 <= pixel_level_acc_num_c6 + pixel_level_num_c5;endelsepixel_level_acc_num_c6 <= pixel_level_acc_num_c6;
endreg pixel_level_valid_c6;always @(posedge clk or negedge rst_n)
beginif(!rst_n)pixel_level_valid_c6 <= 1'b0;elsepixel_level_valid_c6 <= pixel_level_valid_c5;
end//----------------------------------------------------------------------
// signal output
always @(posedge clk)
beginpixel_level <= pixel_level_c6;pixel_level_acc_num <= pixel_level_acc_num_c6;
endalways @(posedge clk or negedge rst_n)
beginif(!rst_n)pixel_level_valid <= 1'b0;elsepixel_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)
beginif(!rst_n)histEQ_start_flag <= 1'b0;elsebeginif((pixel_level_valid == 1'b1)&&(pixel_level == 8'd255))histEQ_start_flag <= 1'b1;elsehistEQ_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)
beginmult_result <= bram_b_rdata * 18'd136957;
endreg [7:0] pixel_data;always @(posedge clk)
beginpixel_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)
beginif(!rst_n)beginper_img_vsync_r <= 3'b0;per_img_href_r <= 3'b0;endelsebeginper_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.
更多推荐
FPGA图像处理之直方图均衡实现
发布评论