直方图均衡化实验,主要包括三部分,直方图统计、计算累加直方图、均衡化。
1、直方图统计
直方图统计就是记录每个灰度值在图片中出现的像素次数,灰度图片有0-255个灰度级,如果我们定义256个寄存器来存储数据比较麻烦,所以借用RAM来实现直方图统计。当输入一个像素点的灰度值,将其作为地址,在RAM中读出该地址对应的数据,进行加1操作,然后再写会该地址。
我配置了一个双端口RAM u1来进行直方图统计,当输入图片数据流有效时,读地址为输入图片数据流的灰度值,将读出的数据加一后再写回,从a端口进行读数据,从b端口进行写 数据,为了防止读写数据产生冲突,我将写数据的地址延迟了一个时钟周期;两张图片输入之间,场同步信号会有一段时间保持在低电平,这段时间没有输入图片像素数据,在这段时间将RAM中的数据读出,并将RAM清零。地址从0遍历到255,每读出一个地址的数据后,相应将该地址清零,这个过程和读出数据加一再写回的过程类似。
2、计算累加直方图
将上一步读出的RAM数据进行累加,存放在另一个RAM u2中,u2中存放累加直方图的信息,这个过程也是在场同步信号会有一段时间保持在低电平时期完成。地址从0遍历到255,将累加后的数据sum写入RAM,地址遍历完累加直方图计算完成。
3、均衡化
在第二张图片输入时,根据像素点灰度值对应的地址,在u2中读出累计直方图数据,通过图中的公式,计算出均衡化后的灰度值,最后将计算后的数据输出。
4、代码
module histogram_equ(
input clk,
input rst_n,
input per_frame_vsync,
input per_frame_href,
input per_frame_clken,
input [7:0] per_img_Y,
output post_frame_vsync,
output post_frame_href,
output post_frame_clken,
output [7:0] post_img_Y
);
localparam IMG_TOTAL_PIXEL=307200; //640*480
localparam IMG_MAX_GRAY=256; //0-255
//reg web;
wire [18:0] cnt,cntplus; //同一灰度级像素个数计数器
wire [18:0] ram_his_rd_data; //从累计直方图中读出的数据
reg vsync_delay;
wire vsync_negedge; //场同步信号下降沿
wire vsync_posedge; //场同步信号上升沿
reg web2; //双端口RAM u2的写使能信号
reg tag; //标志位
reg [7:0] dizhi;
reg [7:0] dizhi1,dizhi2; //双端口RAM u2的写数据地址
wire [7:0] addra; //双端口RAM u1的a端口地址
wire [18:0] dout; //双端口RAM u1的输出数据
wire [18:0] din; //双端口RAM u1的输入数据
reg [7:0] addrb; //双端口RAM u1的b端口地址
wire web; //双端口RAM的写使能
reg [18:0] sum; //为了计算累加直方图,用于存放直方图数据的和
wire [18:0] ram_accu_rddata; //从累加直方图中读出的数据
//统计像素个数时的写使能信号
//always @(posedge clk)
//begin
// web<=per_frame_clken;
// //addrb<=per_img_Y;
//end
assign cntplus=cnt+1;
//将场同步信号延时一拍
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
vsync_delay<=1'b0;
else
vsync_delay<=per_frame_vsync;
end
assign vsync_negedge=vsync_delay&(!per_frame_vsync);
assign vsync_posedge=(!vsync_delay)&per_frame_vsync;
always @(posedge clk)
begin
dizhi1<=dizhi;
dizhi2<=dizhi1;
end
//在一帧图像输入完成后需要对RAM进行数据读出和数据清0,此时的读写数据地址为变量dizhi
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
dizhi<=8'd0;
else
begin
if(tag==0)
begin
if(dizhi==255)
dizhi<=dizhi;
else
dizhi<=dizhi+1;
end
end
end
//在对累计直方图进行写操作时,只有在标志位tag=0时才进行,并且只遍历一次0-255地址
always @(posedge clk)
begin
if(tag==0)
begin
if(dizhi2==255)
web2<=0;
else
web2<=1;
end
end
//当场有效信号为上升沿时,标志位置1,下降沿时,标志位置0。tag=1代表正在输入图像数据流,tag=0代表两帧图像输入数据流之间的间隙
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
tag<=1;
else
begin
if(vsync_posedge)
tag<=1;
if(vsync_negedge)
tag<=0;
end
end
always @(posedge clk)
begin
addrb<=addra;
end
assign addra=tag?per_img_Y:dizhi; //当输入图像数据流时,地址为每个像素点图像灰度值对应的地址,当处于两帧图像输入数据流之间的间隙时,地址为0-255遍历过程的地址
assign cnt=tag?dout:cnt; //当输入图像数据流时,cnt与u1 的输出数据相连,否则保持
assign ram_his_rd_data=web2?dout:ram_his_rd_data; //当web2有效时,从累计直方图中读出的数据与双端口RAM u2的输出相连,否则保持
assign din=tag?cntplus:(19'd0); //当tag=1时,输入为+1后的数据,当tag=0时,需要对RAN进行清0操作,所以输入为0
assign web=tag?per_frame_clken:1; //tag=1时,使能信号为帧时钟使能信号,tag=0时,要对RAM进行清零操作,所以使能信号一直为1
//计算累加直方图要输入的数据
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
sum<=19'd0;
end
else
begin
if(web2==1)
begin
sum<=sum+ram_his_rd_data;
end
end
end
//双端口RAM u1,用于存放直方图的数据,当输入图片数据流有效时,将每个像素点灰度值对应的地址存放的数据读出,再加1,然后再写回去
//在帧与帧之间的间隙,从地址0-255,将累计直方图数据读出,再将RAM清零
ram_ip u1(
.clka(clk),
.wea(1'b0),
.addra(addra), // input wire [7 : 0] addra
.dina(0), // input wire [18 : 0] dina
.douta(dout), // output wire [18 : 0] douta
.clkb(clk), // input wire clkb
.web(web), // input wire [0 : 0] web
.addrb(addrb), // input wire [7 : 0] addrb
.dinb(din), // input wire [18 : 0] dinb
.doutb() // output wire [18 : 0] doutb
);
//双端口RAM u2,用于存放累加直方图数据,将累加直方图的数据从a端口写入,b端口读出
ram_ip u2(
.clka(clk),
.wea(web2),
.addra(dizhi2), // input wire [7 : 0] addra
.dina(sum), // input wire [18 : 0] dina
.douta(), // output wire [18 : 0] douta
.clkb(per_frame_clken), // input wire clkb
.web(1'b0), // input wire [0 : 0] web
.addrb(per_img_Y), // input wire [7 : 0] addrb
.dinb(), // input wire [18 : 0] dinb
.doutb(ram_accu_rddata) // output wire [18 : 0] doutb
);
/直方图均衡化
reg [26:0] data_mult; //乘法运算结果
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
data_mult<=27'd0;
else
data_mult<=ram_accu_rddata*(IMG_MAX_GRAY-1);
end
reg [7:0] data_div; //除法运算结果
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
data_div<=8'd0;
else
data_div<=data_mult/IMG_TOTAL_PIXEL;
end
//------------------------------------------
//lag 3 clocks signal sync
reg [2:0] per_frame_vsync_r;
reg [2:0] per_frame_href_r;
reg [2:0] per_frame_clken_r;
always@(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
per_frame_vsync_r <= 0;
per_frame_href_r <= 0;
per_frame_clken_r <= 0;
end
else
begin
per_frame_vsync_r <= {per_frame_vsync_r[1:0], per_frame_vsync};
per_frame_href_r <= {per_frame_href_r[1:0], per_frame_href};
per_frame_clken_r <= {per_frame_clken_r[1:0], per_frame_clken};
end
end
assign post_frame_vsync = per_frame_vsync_r[2];
assign post_frame_href = per_frame_href_r[2];
assign post_frame_clken = per_frame_clken_r[2];
assign post_img_Y = post_frame_href?data_div:8'd0;
endmodule
5、结果图
6、参考资料