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

啥是直方图均衡化

  直方图均衡化,通过一种灰度映射使输入图像经过转换后,在每一灰度级上都有相近似的输出图像,输出的灰度值是均匀的。经过这样处理的图像具有较高的对比对和较大的动态范围。
  对于离散的灰度级,直方图均衡化有如下关系:
在这里插入图片描述
  其中Db是转换过后的像素的灰度值,Dmax是输入图像经过直方图统计后得到的最大的灰度值,A0是一幅图像面积,也就是所有的像素值H(i)是灰度级的各级的统计结果。Da是当前像素的灰度级。
  直方图均衡化就的作用就是,对于当前的输入像素,需要首先求出小于当前像素灰度值的所有像素统计值的和,然后与该图像中的最大灰度值进行相乘,最后除以图像的面积。

基于FPGA的直方图均衡化

  基于FPGA也是能够完成图像的直方图的处理的。具体的计算步骤如下:

  1. 首先需要统计出该图像的直方图分布H(i)
  2. 其次需要计算出各个灰度级下的直方图累加和也即
    在这里插入图片描述
  3. 找到图像的灰度最大值Dmax,然后将Dmax与对应灰度级的累加和相乘
  4. 除以总的像素面积,完成图像直方图的转换

  在本人进行算法验证的时候,并没有对输入图像进行帧缓存,正常的操作应该是,将本帧的直方图统计完成后,在将缓存好的图像从内存中取出,并进行直方图均衡化。在进行算法验证时,未进行帧缓存,相当于将上一帧图像数据的统计结果作为下一帧图像的变换因子。这在变化速率不太快的情况下是可以采取的一种方式。

具体实现过程

1. 求累加和

  在上一篇博客中介绍了图像直方图的统计方法,并且最终得到的输出的直方图统计值和有效信号。因此上游模块输出直方图统计信息时,可以进行计算直方图对应灰度级下的累加和,并且确定灰度的最大值。
  采用一个BRAM来存储一幅图像的灰度累加和。于此同时确定当前图形中的灰度最大值。
FPGA图像处理基础----直方图均衡化_第1张图片

2. 进行乘法计算

  在上一步求得灰度累加和之后,当前帧图像像素已经来临,因此可以将该像素对应的灰度级和最大灰度值相乘。由于FPGA并不擅长乘法运算,尤其不擅长除法运算。因此在进行乘除法时,会调用内部的专用的DSP资源,因此可以调用Xilinx的乘法器或者除法器IP来进行乘除法的运算。
在进行乘法运算时,需要根据仿真的结果来确定乘法的Latency。

3. 进行除法计算

  在上一步的乘法计算完成后,可以进行除法的运算,在除法IP的配置时,可以设置除法的运算的潜伏期。本人所设置的Latency为7。

代码设计

/*============================================
#
# Author: Wcc - [email protected]
#
# QQ : 1530604142
#
# Last modified: 2020-07-08 20:02
#
# Filename: histogram_equalization.v
#
# Description: 
#
============================================*/
`timescale 1ns / 1ps
module histogram_equalization(
	input	wire 			clk				,
	input	wire 			rst				,
	input 	wire 			pi_hsync		,//输入的视频流信号
	input	wire 			pi_vsync		,
	input 	wire			pi_de 			,
	input	wire 			pi_data_vld		,
	input 	wire 	[7:0]	pi_data 		,
	
	input 	wire 			pi_histo_vld	,//输入的直方图统计信息
	input 	wire 	[31:0]	pi_histo_data	,

	output 	wire 			po_hsync		,//输出的视频流信号
	output	wire 			po_vsync		,
	output	wire			po_de			,
	output	wire 			po_data_vld		,
	output 	wire 	[7:0]	po_data 			
    );

//==========================================
//parameter define
//==========================================
parameter IMG_WIDTH 	= 	128 	;
parameter IMG_HEIGHT 	=	128 	;
localparam TOTAL_PIXEL	= IMG_WIDTH * IMG_HEIGHT;


//==========================================
//internal signal
//==========================================
reg 	[1:0]		vsync_dd	;//场同步信号延时
reg 	[7:0]		pi_data_dd	;//输入数据延时
reg 				data_vld_dd	;//输入数据有效延时
reg 	[2:0]		stream_vld_dd	;

//==========================================
//求累加和与找寻灰度最大最小值	
//==========================================
reg 	[31:0]		gray_sum 	;//灰度累加和
reg 	[7:0]		gray_max 	;//灰度值最大值
reg 	[7:0]		gray_min 	;//灰度值最小值
reg 	[7:0]		gray_idx	;//灰度值索引

//==========================================
//确定当前像素在图像中的位置
//==========================================
reg 	[12:0]		cnt_col 	;
wire				add_cnt_col ;
wire				end_cnt_col	;

reg 	[12:0]		cnt_row 	;
wire				end_cnt_row	;
wire				add_cnt_row ;

//==========================================
//ram 相关
//==========================================
reg 				wr_ram_en	;//存储灰度累加和的RAM
wire 	[31:0]		wr_ram_data ;//写入RAM的数据
reg 	[7:0]		wr_ram_addr	;//写RAM时的地址
wire	[7:0]		rd_ram_addr	;
wire	[31:0]		rd_ram_data	;

//==========================================
//multiplier
//==========================================
wire 	[39:0]		mult_value	;//乘积
wire 				mult_vld 	;//乘积有效信号

//==========================================
//divider
//==========================================
wire 			div_tvalid	;
wire [63 : 0] 	div_tdata	;

//----------------vsync_dd------------------
always @(posedge clk) begin
	if (rst==1'b1) begin
		vsync_dd <= 'd0;
	end
	else begin
		vsync_dd <= {vsync_dd[0], pi_vsync};
	end
end


//==========================================
//将累加和写入到RAM中
//==========================================

//----------------gray_sum------------------
always @(posedge clk) begin
	if (rst==1'b1) begin
		gray_sum <= 'd0;
	end
	else if (pi_histo_vld == 1'b1) begin
		gray_sum <= gray_sum + pi_histo_data;	
	end
	else begin
		gray_sum <= 'd0;
	end
end
//----------------wr_ram_data------------------
assign wr_ram_data = gray_sum;

//----------------wr_ram_en------------------
always @(posedge clk) begin
	if (rst==1'b1) begin
		wr_ram_en <= 1'b0;
	end
	else begin
		wr_ram_en <= pi_histo_vld;
	end
end

//----------------wr_ram_addr------------------
always @(posedge clk) begin
	if (rst==1'b1) begin
		wr_ram_addr <= 'd0;
	end
	else if (wr_ram_en == 1'b1) begin
		wr_ram_addr <= wr_ram_addr + 1'b1;
	end
	else begin
		wr_ram_addr <= 'd0;
	end
end

//==========================================
//找寻最大最小值
//==========================================
//----------------gray_indx------------------
always @(posedge clk) begin
	if (rst==1'b1) begin
		gray_idx <= 'd0;
	end
	else if (pi_histo_vld == 1'b1) begin
		gray_idx <= gray_idx + 1'b1;
	end
	else begin
		gray_idx <= 'd0;
	end
end

//----------------gray_max------------------
always @(posedge clk) begin
	if (rst==1'b1) begin
		gray_max <= 'd0;
		gray_min <= 'd255;
	end
	//检测到一帧图像结束
	else if (end_cnt_row == 1'b1) begin
		gray_max <= 'd0;
		gray_min <= 'd255;	
	end
	else if (pi_histo_data != 0 && pi_histo_vld == 1'b1 ) begin
		if (gray_max <= gray_idx) begin
			gray_max <= gray_idx;
		end
		if (gray_min >= gray_idx) begin
			gray_min <= gray_idx;
		end
	end
end

//----------------pi_data_dd, data_vld_dd------------------
always @(posedge clk) begin
	if (rst==1'b1) begin
		pi_data_dd <= 'd0;
		data_vld_dd <= 'd0;
	end
	else begin
		pi_data_dd <= pi_data;
		data_vld_dd <= pi_data_vld;	
	end
end
//----------------stream_vld_dd------------------
always @(posedge clk) begin
	if (rst==1'b1) begin
		stream_vld_dd <= 'd0;
	end
	else begin
		stream_vld_dd <= {stream_vld_dd[1:0], data_vld_dd};
	end
end

assign mult_vld = stream_vld_dd[2];


//----------------rd_ram_addr------------------
assign rd_ram_addr = (pi_data_vld) ? pi_data : 'd0;


sum_ram inst_sum_ram (
  	.clka(clk),    			// input wire clka
  	.wea(wr_ram_en),      	// input wire [0 : 0] wea
  	.addra(wr_ram_addr),  	// input wire [7 : 0] addra
  	.dina(wr_ram_data),    	// input wire [31 : 0] dina
  	.clkb(clk),    			// input wire clkb
  	.addrb(rd_ram_addr),  	// input wire [7 : 0] addrb
  	.doutb(rd_ram_data)  	// output wire [31 : 0] doutb
);
//==========================================
//乘法器 3个时钟周期的潜伏期
//==========================================
mul_graylevel int_multiplier (
  .CLK(clk),  		// input wire CLK
  .A(gray_max),     // input wire [7 : 0] A
  .B(rd_ram_data),  // input wire [31 : 0] B
  .P(mult_value)    // output wire [39 : 0] P
);

//==========================================
//除法器 7个时钟周期的Latency
//==========================================
// m_axis_dout_tdata[63 : 0 ]
//[63:24] 商
//[21:0] 余数
div_gray inst_divider (
  .aclk(clk),                                      	// input wire aclk
  .s_axis_divisor_tvalid(1'b1),    					// input wire s_axis_divisor_tvalid
  .s_axis_divisor_tdata(TOTAL_PIXEL[23:0]),      	// input wire [23 : 0] s_axis_divisor_tdata
  .s_axis_dividend_tvalid(mult_vld),  				// input wire s_axis_dividend_tvalid
  .s_axis_dividend_tdata(mult_value),    			// input wire [39 : 0] s_axis_dividend_tdata
  .m_axis_dout_tvalid(div_tvalid),          		// output wire m_axis_dout_tvalid
  .m_axis_dout_tdata(div_tdata)            			// output wire [63 : 0] m_axis_dout_tdata
);
assign po_data_vld = div_tvalid;	

//==========================================
//确定当前像素在图像中的位置
//==========================================

//----------------cnt_col------------------
always @(posedge clk) begin
	if (rst == 1'b1) begin
		cnt_col <= 'd0;
	end
	else if (add_cnt_col) begin
		if(end_cnt_col)
			cnt_col <= 'd0;
		else
			cnt_col <= cnt_col + 1'b1;
	end
	else begin
		cnt_col <= 'd0;
	end
end

assign add_cnt_col = div_tvalid == 1'b1;
assign end_cnt_col = add_cnt_col &&	cnt_col == IMG_WIDTH - 1;

//----------------cnt_row------------------
always @(posedge clk) begin
	if (rst == 1'b1) begin
		cnt_row <= 'd0;
	end
	else if (add_cnt_row) begin
		if(end_cnt_row)
			cnt_row <= 'd0;
		else
			cnt_row <= cnt_row + 1'b1;
	end
end

assign add_cnt_row = end_cnt_col;
assign end_cnt_row = add_cnt_row &&	cnt_row == IMG_HEIGHT - 1;



//----------------pi_hsync_dd/pi_vsync_dd,pi_de_dd------------------
reg 	[10:0]	pi_hsync_dd;
reg 	[10:0]	pi_vsync_dd;
reg 	[10:0]	pi_de_dd;
reg 	[87:0]	data_dd ;
//从数据输出到输出共有11个时钟周期的latency
always @(posedge clk) begin
	if (rst==1'b1) begin
		pi_hsync_dd <= 'd0;
		pi_vsync_dd <= 'd0;
		pi_de_dd 	<= 'd0;	
		data_dd 	<= 'd0;
	end
	else begin
		pi_hsync_dd <= {pi_hsync_dd[9:0], pi_hsync};
		pi_vsync_dd <= {pi_vsync_dd[9:0], pi_vsync};
		pi_de_dd 	<= {pi_de_dd[9:0], pi_de};	
		data_dd 	<= {data_dd[79:0], pi_data};
	end
end

assign po_hsync = pi_hsync_dd[10];
assign po_vsync = pi_vsync_dd[10];
assign po_de = pi_de_dd[10];
assign po_data = (po_data_vld) ? div_tdata[31:24] : data_dd[87:80];

endmodule

测试结果

  在片内存储中,存储有一幅灰度图像的信息,下面的两幅图像中,第一幅图像是经过直方图均衡化过后的结果,第一幅图像的对比度相较于第二幅要高。
FPGA图像处理基础----直方图均衡化_第2张图片


参考:《基于FPGA的数字图像处理》牟新刚

你可能感兴趣的:(FPGA图像处理)