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

6.5.3 FPGA直方图线性拉伸

本节将介绍如何利用FPGA实现6.4节的直方图线性拉伸。拉伸处理的运算公式如下:

计算直方图线性拉伸处理后的像素值的步骤如下:
(1)确定高低阈值Thr_Min和Thr_Max。
(2)计算系数A和B。
(3)计算当前像素与A的差值。
(4)计算255与(B-A)的商。
(5)计算第(3)步与第(4)步的乘积
计算步骤如图6-32所示

        其中,Thr_Min和Thr_Max为给定的两个截断区间,用来定义首尾 被截断的直方图统计数目。我们这里给定两个值均为100 FPGA映射的 重点在于公式中的映射区间A和B的计算。 1.映射区间计算 由计算公式可知,映射区间主要是由输入阈值和直方图累加和计 算出来的,因此,映射区间的计算同样可以放在直方图统计阶段进 行。 我们需对直方图统计增加四个接口、两个阈值输入和两个映射区 间输出,如下所示:

module histogram_2d(
rst_n,
clk,
din_valid,
din,
dout,
vsync,
dout_valid,
rdyOutput,
'ifdef Equalize
hist_cnt_addr,hist_cnt_out,
'endif
'ifdef LinearTransfer
lowCnt, //低阈值输入
highCnt, //高阈值输入
lowIndex, //映射区间左侧输出
highIndex, //映射区间右侧输出
'endif
int_flag
);
'ifdef LinearTransfer
input [TW-1:0]lowCnt;
input [TW-1:0]highCnt;
output reg[DW-1:0]lowIndex;
output reg[DW-1:0]highIndex;
'endif

        映射区间的查找也十分简单,但是也要考虑到直方图分布的极端情况,例如,输入图像全黑或全白,就可能造成查找失败。因此,需设置默认输出和查找标志。当没有查找到映射值时,加载默认值输出。
        同时还需注意,和直方图均衡一样,我们不考虑帧缓存的问题,也就是当前的查找结果为上一帧的结果。
查找步骤如下:
(1)当前图像到来之前,加载默认映射值。
(2)根据上一帧的查找结果输出映射值。
(3)对本帧进行直方图统计。
(4)统计过程中根据定义查找区间映射值。
查找电路的实例代码如下:

reg [DW-1:0]lowIndex_tmp;
reg [DW-1:0]highIndex_tmp;
reg [DW-1:0]highIndex_tmp2;
reg bFindMax;
reg bFindMin;
always @(posedge clk or negedge rst_n)
if ((~(rst_n)) == 1'b1)
begin
lowIndex_tmp <= {DW{1'b0}};
highIndex_tmp <= {DW{1'b1}};
bFindMin <= 1'b0;
bFindMax <= 1'b0;
highIndex_tmp2 <= {DW{1'b0}};
end
else
begin
if (vsync_r == 1'b0 & vsync == 1'b1)
begin
lowIndex_tmp <= {DW{1'b0}};
highIndex_tmp <= {DW{1'b1}};
highIndex_tmp2 <= {DW{1'b0}};
lowIndex <= lowIndex_tmp;
if (bFindMax == 1'b1)
highIndex <= highIndex_tmp;
else
highIndex <= highIndex_tmp2;bFindMin <= 1'b0;
bFindMax <= 1'b0;
end
else
begin
if (out_pixel[0]== 1'b1)
begin
if ((~(q_b == {HALF_WIDTH{1'b0}})))
highIndex_tmp2 <= clr_addr - 4'h1;
if ((hist_cnt >= lowCnt) & bFindMin ==
1'b0)
begin
lowIndex_tmp <= clr_addr - 4'h1;
bFindMin <= 1'b1;
end
if(hist_cnt>=(TOTAL_CNThighCnt)&bFindMax==1'b0)
begin
highIndex_tmp <= clr_addr - 4'h1;
bFindMax <= 1'b1;
end
end
end
end
2.Verilog代码实例
//模块声明
module hist_linear_transform(rst_n,
clk,
din_valid, //输入有效
din, //输入数据流
dout, //输出数据
vsync, //输入场同步
dout_valid, //输出有效
vsync_out, //输出场同步
lowCnt, //输入低阈值
highCnt //输入高阈值
);
parameter DW = 8;
parameter IH = 512;
parameter IW = 640;
parameter TW = 32;
parameter DW_DIVIDE = 16; //除法器位宽
localparam TOTAL_CNT = IW * IH;
localparam HALF_WIDTH = (TW>>1);
localparam divide_latency = 19; //除法器计算延迟
localparam latency = divide_latency+2;
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;
input [TW-1:0]lowCnt;
input [TW-1:0]highCnt;
//索引值计算值,也就是公式中的A和B
wire[DW-1:0]lowIndex;
wire[DW-1:0]highIndex;
//首先例化一个直方图统计模块,计算两个截断索引值
histogram_2d hist(
.rst_n(rst_n),
.clk(clk),
.din_valid(din_valid),
.din(din),
.dout(),
.vsync(vsync),
.dout_valid(),
.int_flag(),
.lowCnt(lowCnt),
.highCnt(highCnt),
.rdyOutput(1'b1),
.lowIndex(lowIndex), //索引值输出A
.highIndex(highIndex) //索引值输出B
);
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
else
begin
vsync_r <= #1 vsync;
if(vsync_fall)
frame_cnt <= frame_cnt + 2'b01;
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
//首先计算 (B-A)
reg [DW-1:0]diff; reg [DW-1:0]diff_r;//将计算结果缓存一
拍
always @(posedge clk or negedge rst_n)
if (((~(rst_n))) == 1'b1)
begin
diff <= {DW{1'b0}};
diff_r <= {DW{1'b0}};
endelse
begin
diff_r <= #1 diff;
if(valid == 1'b1)
if(highIndex > lowIndex)
diff <= #1 highIndex - lowIndex; //(B-A)
输出
else
diff <= {DW{1'b1}}; //指示异常
else
diff <= {DW{1'b0}};
end
//接着计算(f(x,y)-A)
reg [DW-1:0]diff_1;
always @(posedge clk or negedge rst_n)
if (((~(rst_n))) == 1'b1)
begin
diff_1 <= {DW{1'b0}};
end
else
begin
if(valid == 1'b1)
begin
if(din <= lowIndex) //当f(x,y)<=A时置零
diff_1 <= {DW{1'b0}};
elsediff_1 <= #1 din - lowIndex;//这里也包含了
当f(x,y)>=B的情况 我们将在除法之后进行处理
end
end
//下一时钟计算(f(x,y)-A)*255
reg [2*DW-1:0]square;
always @(posedge clk or negedge rst_n)
if (((~(rst_n))) == 1'b1)
begin
square <= {2*DW{1'b0}};
end
else
begin
if(valid_r[0]== 1'b1)
begin
square <= #1 diff_1*{DW{1'b1}}; //直接相乘
end
end
//计算商
wire divide_en;
wire [DW_DIVIDE-1:0]quotient; //商
wire [DW_DIVIDE-1:0]nc_rem; //余数
wire [DW_DIVIDE-1:0]denom; //分子
wire [DW_DIVIDE-1:0]numer; //分母
assign denom = (diff_r =={DW{1'b0}})?{DW_DIVIDE{1'b1}}:
diff_r;//防止分母出现0assign numer = (valid_r[0]==1'b1)?square:
{DW_DIVIDE{1'b0}};
//调用除法器IP核
slope_cal#(DW_DIVIDE,divide_latency)
cal_slope(
.clken(1'b1),
.clock(clk),
.denom(denom), //分母
.numer(numer), //分子
.quotient(quotient), //商
.remain(nc_rem) //余数总为正
);
wire [DW-1:0]quotient_temp;
wire [DW-1:0]quotient_temp1;
//除法结果进行四舍五入处理
assign quotient_temp1=(nc_rem>=diff[DW-1:1])?
(quotient+1):(quotient);
//所得结果大于255?说明输入像素大于B,直接置255
assign quotient_temp = (quotient_temp1 >={DW{1'b1}})?
({DW{1'b1}}):
( quotient_temp1 [DW-1:0]);
reg [DW-1:0]dout_temp;
always @(posedge clk or negedge rst_n)
begin
if (((~(rst_n))) == 1'b1)
begin
dout_temp <= {DW{1'b0}};end
else
begin
if(valid_r[latency-1])
dout_temp <= #1 quotient_temp;//输出结果
end
end
assign dout = dout_temp;
assign dout_valid = valid_r[latency];
assign vsync_out = vsync;
endmodule

3.仿真结果
以图6-12作为测试图,分辨率为500×500,位宽为8位,对设计代码进行仿真验证,TestBench如下所示: 


generate
if(display_transform_en != 0)begin
:display_transform_operation
wire dis_trans_dvalid;
wire [local_dw - 1:0]dis_trans_data;
wire dis_trans_vsync;
wire dis_trans_dvalid_in;
wire [local_dw - 1:0]dis_trans_data_in;
wire dis_trans_vsync_in;
integer fp_dis_trans,cnt_dis_trans=0;
wire [local_dw:0]lowIndex;
wire [local_dw:0]highIndex;
hist_linear_transform u0(.rst_n(reset_l),
.clk(cap_clk),
.din_valid(dis_trans_dvalid_in),
.din(dis_trans_data_in),
.dout(dis_trans_data),
.vsync(dis_trans_vsync_in),
.dout_valid(dis_trans_dvalid),
.vsync_out(dis_trans_vsync),
.lowCnt(32'd100),
.highCnt(32'd100)
);
defparam u0.DW = local_dw;
defparam u0.IH = ih;
defparam u0.IW = iw;
defparam u0.TW = 32;
defparam u0.DW_DIVIDE = 16;
assign dis_trans_data_in = cap_data;
assign dis_trans_vsync_in = cap_vsync;
assign dis_trans_dvalid_in = cap_dvalid;
always @(posedge cap_clk or posedge dis_trans_vsync )
if (((~(dis_trans_vsync))) == 1'b0)
cnt_dis_trans=0;
else
begin
if (dis_trans_dvalid == 1'b1)
beginfp_dis_trans =
$fopen("txt_out/dis_trans.txt","r+");
$fseek(fp_dis_trans,cnt_dis_trans,0);
$fdisplay(fp_dis_trans,"%04x\n",dis_trans_data[7:
0]);
$fclose(fp_dis_trans);
cnt_dis_trans<=cnt_dis_trans+6;
end
end
end
endgenerate

        首先,来看一下两个阶段区间的求取,如果分辨率为500×500, 那么在hist_cnt大于100和大于250000-100=249900时应该被截断,结 果如图6-33所示。

        可见,当前帧的截断区间为[91,135],这也说明输入图像的直方图分布主要分布在这个区间。我们的目的就是把这个区间拉伸到整个直方图分布范围[0,255]。图6-34是测试结果。

测试结果也验证了设计逻辑的正确性。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

BinaryStarXin

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

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

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

打赏作者

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

抵扣说明:

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

余额充值