FPGA直方圖均衡化 Label: Research

使用FPGA對圖像直方圖做出均衡化,公式如下:

$$D _ { B } = f ( D _ { A } ) = \frac { D _ { m a x } } { A _ { 0 } } \sum _ { i = 0 } ^ { D _ { A } } H ( i )$$

上式中,H(i)爲第i級灰度的像素個數,A爲圖像的面積,也即像素總數。因此,
計算均衡後的圖像步驟如下:
(1)首先計算出當前圖像的直方圖H(i)
(2)其次計算像素直方圖累計和
(3)將上式乘以灰度值的最大值
(4)將上式除以圖像像素總數

後兩步運算統稱爲歸一化運算。


要把上述算法映射到FPGA中,第一步需要做的工作是什麼呢?自然是幀緩存。
這是由於直方圖統計需要至少一幀的數據才能完成。第一幀完成後,根據上述步驟計算出來的累加和∑,H()中的D,已經是一頓之前的像素值了。要得到這個值進行累加和查找,就必須要至少緩存一幀圖像。

加速操作-近似直方圖均衡化
在實際應用中,處理幀緩存是費時費力費資源的一件事情,在許多情況下,圖像的變換比較慢,在這種情況下的一個近似是當建立當前幀的直方圖統計結果時使用從前一幀得到的映射。結果是直方圖均衡化可能不是非常準確,但是消耗的資源和處理的延時都有顯著地減少。

 

/***********************************************************
********* Company: WHUT
********* Engineer:        ZhengXiaoliang
********* Design Name:      
********* Module Name:     hist_equalized.v
********* Project Name:    Image Process 
********* Description:     histogram equalized operation
********* Dependencies:    
***********************************************************/

// Engineer: radiumlrb
// Date: 2023.10.03
// Version: 1.0



`timescale 1ns/1ns

module hist_equalized(
	rst_n, 
	clk, 
	din_valid, 
	din, 
	dout, 
	vsync, 
	dout_valid, 
	vsync_out
);

parameter  DW = 8;   
parameter  IH = 512;
parameter  IW = 640;
parameter  TW = 32;

localparam TOTAL_CNT = IW * IH;
localparam HALF_WIDTH = (TW>>1);

localparam latency = 6;

input  rst_n;
input  clk;
input  din_valid;
input  [DW-1:0]din;
output [DW-1:0]dout;
input  vsync;
output vsync_out;
output dout_valid;

reg [DW-1:0]hist_cnt_addr;
wire [TW-1:0]hist_cnt_out;

histogram_2d hist(
	.rst_n(rst_n), 
	.clk(clk), 
	.din_valid(din_valid), 
	.din(din), 
	.vsync(vsync), 
	.hist_cnt_addr(hist_cnt_addr),
	.hist_cnt_out(hist_cnt_out)
);

defparam hist.DW = DW;
defparam hist.IH = IH;
defparam hist.IW = IW;

wire vsync_fall;
wire valid;
reg  [1:0]frame_cnt;
reg  hist_valid_temp;
reg  vsync_r;

reg [latency:0]valid_r;

always @(posedge clk or negedge rst_n)
if (((~(rst_n))) == 1'b1) begin
	valid_r[latency:0] <= {latency+1{1'b0}};
end
else begin
	valid_r <= #1 {valid_r[latency-1:0],valid};
end

reg [DW-1:0]din_r;


always @(posedge clk or negedge rst_n)
if (((~(rst_n))) == 1'b1) begin
	hist_cnt_addr <= {DW{1'b0}};
end
else begin
	if(valid_r[0])
		hist_cnt_addr <= #1 din_r;
end

reg [2*TW-1:0]mul_temp[0:2];
reg [DW-1:0]dout_temp;

// for 512*512
generate
if((IW ==512) & (IH ==512) )begin :IW_512

	always @(posedge clk or negedge rst_n)
	if (((~(rst_n))) == 1'b1) begin
		mul_temp[0] <= {2*TW{1'b0}};
	end
	else begin
		if(valid_r[1])
			//hist_cnt_out*255,DW must be 8 
			mul_temp[0] <= #1 {{TW-DW{1'b0}},hist_cnt_out[TW-1:0],{DW{1'b0}}} - {{TW{1'b0}},hist_cnt_out};
		if(valid_r[1])
			//hist_cnt_out/(512*512) IW = IH = 512 
			mul_temp[1] <= #1 {{18{1'b0}},mul_temp[0][2*TW-1:18]};
		if(valid_r[2])
			dout_temp <= #1 mul_temp[1][DW-1:0];
	end
end 
endgenerate

// for 640*512
generate
if(IW ==640 & IH ==512 ) begin :IW_640
	wire [2*TW-1:0]dout_tmp ;
	assign dout_tmp = {{16{1'b0}},mul_temp[2][2*TW-1:16]};
	always @(posedge clk or negedge rst_n)
	if (((~(rst_n))) == 1'b1) begin
		mul_temp[0] <= {2*TW{1'b0}};
	end
	else begin
		if(valid_r[1])
			//hist_cnt_out*51,DW must be 8 
			//hist_cnt_out*32 + hist_cnt_out*16
			mul_temp[0] <= #1 {{TW-5{1'b0}},hist_cnt_out[TW-1:0],{5{1'b0}}} + {{TW-4{1'b0}},hist_cnt_out[TW-1:0],{4{1'b0}}};
			//hist_cnt_out*2 + hist_cnt_out*1
			mul_temp[1] <= #1 {{TW{1'b0}},hist_cnt_out[TW-1:0]} + {{TW-1{1'b0}},hist_cnt_out[TW-1:0],{1{1'b0}}};
		if(valid_r[1])
			//hist_cnt_out/(64*2*512)  
			mul_temp[2] <= #1 mul_temp[0] + mul_temp[1];
			//
		if(valid_r[2])
			dout_temp <= #1 dout_tmp[DW-1:0];
	end
end 
endgenerate 


assign dout = dout_temp;
assign dout_valid  = valid_r[latency];
assign vsync_out  = vsync;

endmodule

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章