基于FPGA自动白平衡算法的实现
1 白平衡介绍
对于白平衡基本概念的详细介绍请查看文章《白平衡初探》,白平衡算法主要的作用是将偏暖或者偏冷的色调自动恢复到正常色调,是图像看起来更加色彩饱满正常。
如上图所示,左1色温偏冷,中间色温正常,右一色温偏暖。上图比较直观的展示了色温的概念。偏冷的色温会给人一种还冷的感觉,偏暖的色温会给人一种温暖的感觉。
2 白平衡算法以及matlab实现
1)原始的灰度世界算法
灰度世界算法(Gray World)是以灰度世界假设为基础的,该假设认为对于一幅有着大量色彩变化的图像, R、 G、 B 三个分量的平均值趋于同一个灰度K。一般有两种方法来确定该灰度。
(1)直接给定为固定值, 取其各通道最大值的一半,即取为127或128;
(2)令 K = (Raver+Gaver+Baver)/3,其中Raver,Gaver,Baver分别表示红、 绿、 蓝三个通道的平均值。
算法的第二步是分别计算各通道的增益:
Kr=K/Raver;
Kg=K/Gaver;
Kb=K/Baver;
算法第三步为根据Von Kries 对角模型,对于图像中的每个像素R、G、B,计算其结果值:
Rnew = R * Kr;
Gnew = G * Kg;
Bnew = B * Kb;
对于上式,计算中可能会存在溢出(>255,不会出现小于0的)现象,处理方式有两种。
a、 直接将像素设置为255,这可能会造成图像整体偏白。
b、 计算所有Rnew、Gnew、Bnew的最大值,然后利用该最大值将将计算后数据重新线性映射到[0,255]内。实践证明这种方式将会使图像整体偏暗,建议采用第一种方案。
2)完美反射算法
原理:完美全反射理论perfect Reflector假设图像上最亮点就是白点,并以此白点为参考对图像进行自动白平衡,最亮点定义为R+G+B的最大值,具体编码步骤如下:
(1)计算每个像素的R\G\B之和,并保存到一临时内存块中。
(2)按R+G+B值的大小计算出其前10%或其他Ratio的白色参考点的的阈值T。
(3)遍历图像中的每个点,计算其中R+G+B值大于T的所有点的R\G\B分量的累积和的平均值。
(4)对每个点将像素量化到[0,255]之间。
3)动态阈值法
(1). 把图像w*h从RGB空间转换到YCrCb空间。
(2). 选择参考白色点:
a. 把图像分成宽高比为4:3个块(块数可选)。
b. 对每个块,分别计算Cr,Cb的平均值Mr,Mb。
c. 对每个块,根据Mr,Mb,用下面公式分别计算Cr,Cb的方差Dr,Db。
d. 判定每个块的近白区域(near-white region)。
判别表达式为:
设一个“参考白色点”的亮度矩阵RL,大小为w*h。
若符合判别式,则作为“参考白色点”,并把该点(i,j)的亮度(Y分量)值赋给RL(i,j);
若不符合,则该点的RL(i,j)值为0。
(3). 选取参考“参考白色点”中最大的10%的亮度(Y分量)值,并选取其中的最小值Lu_min.
(4). 调整RL,若RL(i,j)<Lu_min, RL(i,j)=0; 否则,RL(i,j)=1;
(5). 分别把R,G,B与RL相乘,得到R2,G2,B2。 分别计算R2,G2,B2的平均值,Rav,Gav,Bav;
(6). 得到调整增益:
Ymax=double(max(max(Y)))/5;
Rgain=Ymax/Rav;
Ggain=Ymax/Gav;
Bgain=Ymax/Bav;
(7). 调整原图像:
Ro= R*Rgain;
Go= G*Ggain;
Bo= B*Bgain;
FPGA的实现采用灰度世界算法。
Matlab代码:
close all
clear all
clc
I=imread('test1.jpg');
[H,W,L]=size(I);%得到图像长宽高
Rsum = 0;
Gsum = 0;
Bsum = 0;
Rsum = double(Rsum);
Gsum = double(Gsum);
Bsum = double(Bsum);
for i = 1 : H
for j = 1 :W
Rsum = Rsum + double(I(i,j,1));
Gsum = Gsum + double(I(i,j,2));
Bsum = Bsum + double(I(i,j,3));
end
end
Raver = Rsum / (H*W);
Gaver = Gsum / (H*W);
Baver = Bsum / (H*W);
%K=128;%第一种K取值方法
K = (Raver+Gaver+Baver)/3;%第二种方法
Rgain = K / Raver;
Ggain = K / Gaver;
Bgain = K / Baver;
Iwb(:,:,1) = I(:,:,1) * Rgain;
Iwb(:,:,2) = I(:,:,2) * Ggain;
Iwb(:,:,3) = I(:,:,3) * Bgain;
imwrite(Iwb,'Result1.jpg');
figure(1),
subplot(121),imshow(I),title('原始图像');
subplot(122),imshow(Iwb),title('自动白平衡图像');
效果展示:
3 FPGA实现自动白平衡灰度世界法
灰度世界法Verilog代码:
/**********************************
copyright@FPGA OPEN SOURCE STUDIO
微信公众号:FPGA开源工作室
Algorithm:Auto White balance
Gray World
800X600X255=480000
***********************************/
`define XLEN 32
`define N(n) [(n)-1:0]
module White_balance#(
parameter DW = 24
)(
input pixelclk,
input reset_n,
input [DW-1:0] din, //rgb in
input i_hsync,
input i_vsync,
input i_de,
output [DW-1:0] dout, //gray out
output o_hsync,
output o_vsync,
output o_de
);
localparam STAGE_LIST = 32'h0101_0101;//32'b00000000_00000000_00000000_00000000;
localparam divisor = 32'h0007_5300;//800*600
localparam divisorK = 32'h0000_0003;//800*600
wire [7:0] R,G,B;
reg [27:0] sumr,sumg,sumb;
reg [27:0] sumr_r,sumg_r,sumb_r;
wire [9:0] Kave;
wire [7:0] K;
wire Rack;
wire `N(`XLEN) Rquo,Rrem;
wire Gack;
wire `N(`XLEN) Gquo,Grem;
wire Back;
wire `N(`XLEN) Bquo,Brem;
wire Kack;
wire `N(`XLEN) Kquo,Krem;
wire GRack;
wire `N(`XLEN) GRquo,GRrem;
wire GGack;
wire `N(`XLEN) GGquo,GGrem;
wire GBack;
wire `N(`XLEN) GBquo,GBrem;
reg [39:0] R_r,G_r,B_r;
wire [7:0] RO,GO,BO;
reg hsync_r1;
reg vsync_r1;
reg de_r1;
reg i_vsync_r;
reg vsync_neg_r;
wire vsync_neg=(i_vsync_r&(~i_vsync));
assign Kave=(Rquo+Gquo+Bquo);
assign K=Kquo[7:0];
assign R=din[23:16];
assign G=din[15:8];
assign B=din[7:0];
assign o_hsync = hsync_r1;
assign o_vsync = vsync_r1;
assign o_de = de_r1;
assign RO=(R_r[18:10]>255)?255:R_r[17:10];
assign GO=(G_r[18:10]>255)?255:G_r[17:10];
assign BO=(B_r[18:10]>255)?255:B_r[17:10];
assign dout={RO,GO,BO};
//synchronization
always @(posedge pixelclk) begin
hsync_r1 <= i_hsync;
vsync_r1 <= i_vsync;
de_r1 <= i_de;
end
always @(posedge pixelclk or negedge reset_n)begin
if(!reset_n)begin
sumr<=28'd0;< span="">
sumg<=28'd0;< span="">
sumb<=28'd0;< span="">
sumr_r<=28'd0;< span="">
sumg_r<=28'd0;< span="">
sumb_r<=28'd0;< span="">
i_vsync_r<=1'b0;< span="">
vsync_neg_r<=1'b0;< span="">
R_r<=40'b0;< span="">
G_r<=40'b0;< span="">
B_r<=40'b0;< span="">
end
else begin
i_vsync_r<=i_vsync;< span="">
vsync_neg_r<=vsync_neg;< span="">
R_r<=r*grquo;< span="">
G_r<=g*ggquo;< span="">
B_r<=b*gbquo;< span="">
if(vsync_neg==1'b1) begin
sumr_r<=sumr;< span="">
sumg_r<=sumg;< span="">
sumb_r<=sumb;< span="">
end
if(i_vsync==1'b1) begin
if(i_de==1'b1) begin
sumr<=sumr+r;< span="">
sumg<=sumg+g;< span="">
sumb<=sumb+b;< span="">
end
else begin
sumr<=sumr;< span="">
sumg<=sumg;< span="">
sumb<=sumb;< span="">
end
end
else begin
sumr<=28'd0;< span="">
sumg<=28'd0;< span="">
sumb<=28'd0;< span="">
end
end
end
//Rave
divfunc
#(
.XLEN ( `XLEN ),
.STAGE_LIST ( STAGE_LIST )
) i_divR (
.clk ( pixelclk ),
.rst ( ~reset_n ),
.a ( {4'b0,sumr_r} ),
.b ( divisor ),
.vld ( vsync_neg_r ),
.quo ( Rquo ),
.rem ( Rrem ),
.ack ( Rack )
);
//Gave
divfunc
#(
.XLEN ( `XLEN ),
.STAGE_LIST ( STAGE_LIST )
) i_divG (
.clk ( pixelclk ),
.rst ( ~reset_n ),
.a ( {4'b0,sumg_r} ),
.b ( divisor ),
.vld ( vsync_neg_r ),
.quo ( Gquo ),
.rem ( Grem ),
.ack ( Gack )
);
//Bave
divfunc
#(
.XLEN ( `XLEN ),
.STAGE_LIST ( STAGE_LIST )
) i_divB (
.clk ( pixelclk ),
.rst ( ~reset_n ),
.a ( {4'b0,sumb_r} ),
.b ( divisor ),
.vld ( vsync_neg_r ),
.quo ( Bquo ),
.rem ( Brem ),
.ack ( Back )
);
//K
divfunc
#(
.XLEN ( `XLEN ),
.STAGE_LIST ( STAGE_LIST )
) i_divK (
.clk ( pixelclk ),
.rst ( ~reset_n ),
.a ( {22'b0,Kave} ),
.b ( divisorK ),
.vld ( Back ),
.quo ( Kquo ),
.rem ( Krem ),
.ack ( Kack )
);
//RGain
divfunc
#(
.XLEN ( `XLEN ),
.STAGE_LIST ( STAGE_LIST )
) i_divGR (
.clk ( pixelclk ),
.rst ( ~reset_n ),
.a ( {14'b0,K,10'b0} ),
.b ( Rquo ),
.vld ( Kack ),
.quo ( GRquo ),
.rem ( GRrem ),
.ack ( GRack )
);
//GGain
divfunc
#(
.XLEN ( `XLEN ),
.STAGE_LIST ( STAGE_LIST )
) i_divGG (
.clk ( pixelclk ),
.rst ( ~reset_n ),
.a ( {14'b0,K,10'b0} ),
.b ( Gquo ),
.vld ( Kack ),
.quo ( GGquo ),
.rem ( GGrem ),
.ack ( GGack )
);
//BGAIN
divfunc
#(
.XLEN ( `XLEN ),
.STAGE_LIST ( STAGE_LIST )
) i_divGB (
.clk ( pixelclk ),
.rst ( ~reset_n ),
.a ( {14'b0,K,10'b0} ),
.b ( Bquo ),
.vld ( Kack ),
.quo ( GBquo ),
.rem ( GBrem ),
.ack ( GBack )
);
endmodule
1)仿真效果
原始图像
经过灰度世界法的白平衡仿真效果
2)下板验证模型以及实现效果
下板验证图像由笔记本电脑通过HDMI输入到FPGA芯片,经过FPGA算法处理后在通过HDMI将图像送到显示屏展示处理效果。
笔记本电脑显示图片
经过FPGA处理后的显示效果
视频效果