基于FPGA的自动白平衡算法的实现(附代码)

FPGA技术江湖 2025-10-27 08:34







一、白平衡介绍


对于白平衡基本概念的详细介绍请查看文章《》,白平衡算法主要的作用是将偏暖或者偏冷的色调自动恢复到正常色调,是图像看起来更加色彩饱满正常。


基于FPGA的自动白平衡算法的实现(附代码)图1


如上图所示,左1色温偏冷,中间色温正常,右一色温偏暖。上图比较直观的展示了色温的概念。偏冷的色温会给人一种还冷的感觉,偏暖的色温会给人一种温暖的感觉。


二、白平衡算法以及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 allclear allclcI=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));    endendRaver = 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('自动白平衡图像');



效果展示:


基于FPGA的自动白平衡算法的实现(附代码)图2


三、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*600localparam 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};



//synchronizationalways @(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 endend



//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 )

);

//Kdivfunc #( .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 )

);

//RGaindivfunc #( .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 )

);

//GGaindivfunc #( .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 )

);

//BGAINdivfunc #( .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)仿真效果


基于FPGA的自动白平衡算法的实现(附代码)图3

原始图像


基于FPGA的自动白平衡算法的实现(附代码)图4

经过灰度世界法的白平衡仿真效果


2)下板验证模型以及实现效果


基于FPGA的自动白平衡算法的实现(附代码)图5


下板验证图像由笔记本电脑通过HDMI输入到FPGA芯片,经过FPGA算法处理后在通过HDMI将图像送到显示屏展示处理效果。


基于FPGA的自动白平衡算法的实现(附代码)图6

笔记本电脑显示图片


基于FPGA的自动白平衡算法的实现(附代码)图7

经过FPGA处理后的显示效果



视频效果如下:







声明:内容取材于网络,仅代表作者观点,如有内容违规问题,请联系处理。 
FPGA
more
芯片分类以及国内核心代表厂家(CPU/GPU/MCU/FPGA/TPU/NPU)
独立之后的野心:Altera要以全栈FPGA重塑竞争格局!
【直播预约】FPGA在图像与视频领域的创新应用
国际先进!中科亿海微国产嵌入式FPGA IP核及EDA系统设计技术通过科技成果评价
AI狂飙, FPGA会掉队吗? (下)
一文搞懂:CPU, MCU, MPU, SoC, DSP, ECU, GPU 和 FPGA 的核心区别
邀请报名:ADC芯片测试、FPGA机电物理仿真、Labview+大模型等|芯片测试线下技术研讨会(8月5日 苏州)
发力5G通信、汽车和工业应用,紫光同创FPGA亮相Semi-e深圳半导体展
基于Matlab与FPGA的双边滤波算法实现
报名倒计时7天:ADC芯片测试、FPGA机电物理仿真、Labview+大模型等|芯片测试线下技术研讨会(8月5日 苏州)
Copyright © 2025 成都区角科技有限公司
蜀ICP备2025143415号-1
  
川公网安备51015602001305号