赞
踩
首先再次列出直方图均衡化的公式:
上式中,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所示。
由测试结果可见,这两幅过曝光和曝光不足的图像,经过均衡化 之后的效果图差别不大,对比度都非常高,设计代码达到了预期的均 衡目的。 对于直方图的规定操作,也可以采用类似直方图均衡的运算方 法,有兴趣的读者可以自己尝试去设计相关逻辑。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。