基于FPGA的自动白平衡算法的实现

2021-04-22 15:11:16 浏览数 (1)

基于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)计算每个像素的RGB之和,并保存到一临时内存块中。

(2)按R G B值的大小计算出其前10%或其他Ratio的白色参考点的的阈值T。

(3)遍历图像中的每个点,计算其中R G B值大于T的所有点的RGB分量的累积和的平均值。

(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代码:

代码语言:javascript复制
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代码:

代码语言:javascript复制
/**********************************
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处理后的显示效果


视频效果

0 人点赞