基于FPGA的数字图像处理-直方图操作【3.3】

6.5.2 FPGA直方图均衡化

首先再次列出直方图均衡化的公式:


上式中,H(i)为第i级灰度的像素个数,A0 为图像的面积,也即像素总数。因此,计算均衡后的图像步骤如下:
(1)首先计算出当前图像的直方图H(i)。

        在实际应用中,处理帧缓存是费时费力费资源的一件事情,在许 多情况下,图像的变换比较慢,在这种情况下的一个近似是当建立当 前帧的直方图统计结果时使用从前一帧得到的映射。结果是直方图均 衡化可能不是非常准确,但是消耗的资源和处理的延时都有显著地减 少,如图6-27所示。

        图6-27 近似的直方图均衡化 第一步的直方图计算我们已经在前面介绍过了,接下来详细介绍 后面的几个计算步骤。 1.直方图累计和 根据数学定义,直方图累加和的定义是小于指定像素的所有像素 的统计值之和。因此,这个阶段放在直方图统计值读出阶段来进行累 加是再合适不过了。 在直方图统计代码里面添加如下代码:

reg [TW-1:0]hist_cnt; //直方图统计累加和寄存器
always @(posedge clk or negedge rst_n)
if ((~(rst_n)) == 1'b1)
hist_cnt <= {TW{1'b0}}; //复位清零
else
if (vsync_r == 1'b0 & vsync == 1'b1) //新的一帧到来时清
零
hist_cnt <= {TW{1'b0}};
else
if (out_pixel[0]== 1'b1) //每个像素读出时刻
hist_cnt <= hist_cnt + q_b; //将结果累加

        仅仅得到累加和是不够的,因为需要对累加和进行随机地址访 问,而不是顺序读出。这个时候就需要将累加和缓存。同样地,采用 一个双口RAM对累加和结果进行缓存,同时完成时序对齐。 需要在之前的直方图统计模块加入两个接口,一个是统计和的地 址输入,负责选通对累计和进行寻址,一个是数据输出,如下所示:

module histogram_2d(
rst_n,clk,
din_valid,
din,
dout,
vsync,
dout_valid,
rdyOutput,
'ifdef Equalize
hist_cnt_addr,
hist_cnt_out,
'endif
int_flag
);
'ifdef Equalize
input [DW-1:0]hist_cnt_addr;
output reg [TW-1:0]hist_cnt_out;
'endif
代码如下:
reg [DW:0]out_pixel_r;
reg [DW-1:0]out_pixel_r2;
wire [TW-1:0]hist_cnt_temp;
always @(posedge clk or negedge rst_n)
if ((~(rst_n)) == 1'b1)
begin
out_pixel_r <= {DW+1{1'b0}};
out_pixel_r2 <= {DW{1'b0}};
hist_cnt_out <= {TW{1'b0}};end
else
begin
out_pixel_r <= #1 out_pixel;
out_pixel_r2 <= #1 out_pixel_r[DW:1];
hist_cnt_out <= #1 hist_cnt_temp; //将数据打一拍后输出
end
hist_buffer hist_cnt_buf(
.address_a(out_pixel_r2), //写入地址,直方图当前地址
.address_b(hist_cnt_addr), //读出地址
.clock(clk), //同步时钟
.data_a(hist_cnt), //写入数据
.data_b(),
.wren_a(dout_valid), //写入时刻:直方图数据有效
.wren_b(1'b0),
.q_a(),
.q_b(hist_cnt_temp) //输出数据
);
defparam hist_cnt_buf.AW = DW;
defparam hist_cnt_buf.DW = TW;

累加和的计算情况如图6-28所示。

可见,累加和很好地完成了累加工作。不妨输入前几个累积和地址对累加和进行读出,如图6-29所示。

        由此可见,数据输出正确。由于我们将输出打了一个节拍输出, 因 此 输 出 会 延 迟 一 个 时 钟 。 注 意 , 到 此 时 由 于 已 经 统 计 完 毕 , hist_cnt刚好为图像的像素总数327680,即640×512。 2.归一化计算 将第3步和第4步放在一起进行计算,由于这个过程也是把像素归 一化到0~255的过程,因此我们也将此成为归一化计算。 归一化计算的步骤是先乘以灰度最大值,然后再除以像素总数: 图像宽度×图像高度。 通常情况下不会直接调用乘法器和除法器对上式进行计算,这样 做是不合适的。对于固定的位宽和图像分辨率来说,这个计算过程是 可定量的,也就是这个归一化系数是已知的。而往往在应用过程中, 大部分场合有效的数据位宽和分辨率的组合也不会太多。 本 书 以 图 像 位 宽 为 8 位 , 图 像 分 辨 率 为 640×512 和 分 辨 率 为 512×512位例来进行说明。首先我们设定这个归一化系数为N。 对于第一种情况,有

其他的分辨率及位宽也可以通过类似的方法进行转换。接下来我们将会详细给出其Verilog实例代码设计。

3.Verilog代码设计
模块定义如下:
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;
//由于至少需要等到第一帧输出完毕之后才能完成第一帧数据的
直方图统计信息,
//因此有必要先对图像帧进行计数
always @(posedge clk or negedge rst_n)
if (((~(rst_n))) == 1'b1)
begin
vsync_r <= #1 1'b0;
hist_valid_temp <= 1'b0;
frame_cnt <= 2'b00;
end
elsebegin
vsync_r <= #1 vsync;
if(vsync_fall)
frame_cnt <= frame_cnt + 2'b01;//每帧结束时帧
计数加1
else
frame_cnt <= frame_cnt;
if(frame_cnt >= 2'b10) //第二行开始输入均衡操作才开始有
效
hist_valid_temp <= 1'b1;
end
//场同步下降沿信号
assign vsync_fall = (vsync & ~vsync_r);
//全局有效信号
assign valid = hist_valid_temp & din_valid;
//缓存全局有效信号,完成时序对齐
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
din_r <= {DW{1'b0}};
end
else
begin
din_r <= #1 din;
end
//查询当前像素的直方图统计累加和
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;
//对于分辨率为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,
mul_temp[0] <= #1 {{TWDW{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
//对于分辨率为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
4.仿真结果
Testbench设计如下:
/*hist equalized operation module*/
generate
if(hist_equalized_en != 0)begin :equalized_operation
wire equalized_dvalid;
wire [local_dw - 1:0]equalized_data;
wire equalized_vsync;
wire equalized_dvalid_in;
wire [local_dw - 1:0]equalized_data_in;
wire equalized_vsync_in;
integer fp_equalized,cnt_equalized=0;
hist_equalized #(8,ih,iw,hist_dw)
equalized_ins(
.rst_n (reset_l),
.clk (cap_clk),
.din (equalized_data_in[7:0]),
.din_valid (equalized_dvalid_in),
.dout_valid (equalized_dvalid),
.vsync(equalized_vsync_in),
.vsync_out(equalized_vsync),
.dout (equalized_data[7:0])
);
assign equalized_data_in = cap_data;
assign equalized_vsync_in = cap_vsync;assign equalized_dvalid_in = cap_dvalid;
always @(posedge cap_clk or posedge equalized_vsync )
if (((~(equalized_vsync))) == 1'b0)
cnt_equalized=0;
else
begin
if (equalized_dvalid == 1'b1)
begin
fp_equalized =
$fopen("txt_out/equalized.txt","r+");
$fseek(fp_equalized,cnt_equalized,0);
$fdisplay(fp_equalized,"%04x\n",equalized_data[7:0]
);
$fclose(fp_equalized);
cnt_equalized<=cnt_equalized+6;
end
end
end
endgenerate

通过两张测试图来验证逻辑功能:一幅较暗的图像和一幅较亮的图像,处理结果如图6-30和图6-31所示。

        由测试结果可见,这两幅过曝光和曝光不足的图像,经过均衡化 之后的效果图差别不大,对比度都非常高,设计代码达到了预期的均 衡目的。 对于直方图的规定操作,也可以采用类似直方图均衡的运算方 法,有兴趣的读者可以自己尝试去设计相关逻辑。

  • 7
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BinaryStarXin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值