本文介绍了3A算法中的自动白平衡算法,参考诸多大神的文章

        图像信号处理算法(Image Signal Process,ISP),对图像传感器输出的原始图像进行处理,得到较好的场景还原效果。

其中,3A算法包括:AWB(自动白平衡)、AE(自动曝光)、AF(自动对焦)。这里先分析自动白平衡算法。

        人眼在观察物体的时候,可以根据不同光源的性质调整被观察到的物体颜色。而相机在不同色温的光源下拍摄到的图像会产生偏色。相机的自动白平衡就是通过改变RGB感光电路信号的放大比例,让受环境光影响的图像颜色保持和物体真实的颜色一致。

        1、白平衡的本质是使白色物体在任何光源下都显示白色。

        2、一般的算法通过调节白平衡增益, 使拍摄画面的颜色接近物体真实的颜色, 增益调节的根据是环境光源的色温。

        3、AWB算法的步骤:估计环境光色温,计算增益并调节。

        4、估计环境光色温算法有:灰度世界算法、完美反射算法、动态阈值算法。

        5、增益调节最简单的方式, 通过求取图像的平均颜色分量对应的增益,然后, 对整副图的RGB分量进行调整 。

       (1)灰度世界算法基本原理:对于一幅图像,当有足够的色彩变化时,可认为它的RGB分量均值倾向于相等R平均=G平均=B平均,图像呈现为灰色,公式为:

                                                                     Android相机应用关闭白平衡_计算机视觉

                                                                    Android相机应用关闭白平衡_算法_02                                                                   G'=G*(K/G平均)

Android相机应用关闭白平衡_机器学习_03

        其中,R平均、G平均、B平均是白平衡之前三个分量各自的平均值,R、G、B是白平衡之前像素点的值,R’、 G’、 B’是白平衡后所得像素点的值。

        该算法的优点是简单快捷,能应用于一般场景的处理,但是当图片颜色比较单一或者单一色块的面积较大时,灰度世界法不成立,处理结果会出现偏差。

        (a)Matlab代码:

%% 灰度世界法

clear all

close all

clc

% 输入图像(存在颜色偏差的原始图像)

imageInput=imread('Test.jpg');

% 分离各个通道

imageR=imageInput(:,:,1);    

imageG=imageInput(:,:,2);   

imageB=imageInput(:,:,3);

% 求RGB分量的均值

R=mean(mean(imageR));       

G=mean(mean(imageG));       

B=mean(mean(imageB));

% 计算各分量的增益

RGB=(R+G+B)/3;  

kR=RGB/R;   

kG=RGB/G;               

kB=RGB/B;

% 计算各通道变换后的灰度值

imageR=imageR*kR;            

imageG=imageG*kG;           

imageB=imageB*kB;

% 合并成三通道图像

% imageOutput(:,:,1)=imageR;   

% imageOutput(:,:,2)=imageG;  

% imageOutput(:,:,3)=imageB;

imageOutput=cat(3,imageR,imageG,imageB);

% 输出图像(自动白平衡校正后-灰度世界法)

% figure;imshow(imageInput); title('原始图像');  

% figure;imshow(imageOutput);title('白平衡后图像'); 

figure;imshow([imageInput imageOutput]);

        (b)opencv代码:

// 自动白平衡算法(灰度世界法)

#include <opencv2\highgui\highgui.hpp>

#include <imgproc\imgproc.hpp>

using namespace cv;

using namespace std;

int main()

{

     Mat imageInput = imread("Test.jpg");

     if (!imageInput.empty())

     {

          vector<Mat> imageRGB;

          // 分离各个通道

          split(imageSource, imageRGB);


          // 求RGB分量的均值(opencv中排列顺序是B,G,R)

          double R, G, B;

          B = mean(imageRGB[0])[0];

          G = mean(imageRGB[1])[0];

          R = mean(imageRGB[2])[0];


          // 计算各分量的增益

          double kR, kG, kB;

          kR = (R + G + B) / (3 * R);

          kG = (R + G + B) / (3 * G);

          kB = (R + G + B) / (3 * B);


          // 计算各通道变换后的灰度值

          imageRGB[0] = imageRGB[0] * kB;

          imageRGB[1] = imageRGB[1] * kG;

          imageRGB[2] = imageRGB[2] * kR;


          // 三通道图像合成

          merge(imageRGB, imageSource);

          imshow("原始图像", imageSource);

          imshow("白平衡调整后", imageSource);

          waitKey();

     }
     return 0;
}

        (c)halcon代码:

* 自动白平衡-灰度世界法

read_image (Image, 'Test.jpg')

* 分离各个通道

decompose3 (Image, ImageR, ImageG, ImageB)

* 求RGB分量的均值

intensity (ImageR, ImageR, Mean_R, Deviation_R)

intensity (ImageG, ImageG, Mean_G, Deviation_G)

intensity (ImageB, ImageB, Mean_B, Deviation_B)

* 计算各分量的增益

K:=(Mean_R+Mean_G+Mean_B)/3

Kr:=K/Mean_R

Kg:=K/Mean_G

Kb:=K/Mean_B

* 计算各通道变换后的灰度值

scale_image (ImageR, ImageScaled_R, Kr, 0)

scale_image (ImageG, ImageScaled_G, Kg, 0)

scale_image (ImageB, ImageScaled_B, Kb, 0)

* 合并成三通道图像

compose3 (ImageScaled_R, ImageScaled_G, ImageScaled_B, MultiChannelImage)

        处理效果:

                                          

Android相机应用关闭白平衡_灰度_04

 

Android相机应用关闭白平衡_机器学习_05

原理:假设图像中最亮的点就是白点,并以此白点为参考对图像进行自动白平衡,最亮点定义为R+G+B的最大值。

算法步骤:计算每个像素点的R+G+B之和并保存;按照值的大小计算出其前10%或其他比例的白色参考点阈值T;遍历图像计算其中R+G+B值大于T的所有点的R、G、B分量的累积和的平均值;将每个像素量化到[0,255]。

完美反射算法优点是比灰度世界算法稍好,但是依赖比例值的选取,并且对亮度最亮区域不是白色的图像效果不佳。             (a)matlab代码:

%% 完美反射法

clear all

close all

clc

% 输入图像(存在颜色偏差的原始图像)

I=im2double(imread('Test.jpg'));

% 分离各个通道

R=I(:,:,1);     G=I(:,:,2);    B=I(:,:,3);

% 计算每个RGB灰度值之和

sumRGB=R+G+B;

% 将RGB值的大小进行排序

sumsort=sort(sumRGB(:)');

count=round(size(sumsort,2)*0.9);

T=sumsort(count);

index=sumRGB>T;

KR=max(R(:))/mean(R(index));

KG=max(G(:))/mean(G(index));

KB=max(B(:))/mean(B(index));

R1=R*KR;G1=G*KG;B1=B*KB;

out=cat(3,R1,G1,B1);

figure;imshow([I out]);

(b)opencv代码:

// 自动白平衡(完美反射算法)

#include <opencv2\highgui\highgui.hpp>

#include <imgproc\imgproc.hpp>

#include <iostream>

#include <time.h>

using namespace cv;

using namespace std;

Mat PerfectReflectionAlgorithm(Mat src) {

     int row = src.rows;

     int col = src.cols;

     Mat dst(row, col, CV_8UC3);

     int HistRGB[767] = { 0 };

     int MaxVal = 0;

     for (int i = 0; i < row; i++) {

          for (int j = 0; j < col; j++) {

                MaxVal = max(MaxVal, (int)src.at<Vec3b>(i, j)[0]);

                MaxVal = max(MaxVal, (int)src.at<Vec3b>(i, j)[1]);

                MaxVal = max(MaxVal, (int)src.at<Vec3b>(i, j)[2]);

                int sum = src.at<Vec3b>(i, j)[0] + src.at<Vec3b>(i, j)[1] + src.at<Vec3b>(i, j)[2];

                HistRGB[sum]++;

          }

     }

     int Threshold = 0;

     int sum = 0;

     for (int i = 766; i >= 0; i--) {

          sum += HistRGB[i];

          if (sum > row * col * 0.1) {

                Threshold = i;

                break;

          }

     }

     int AvgB = 0;

     int AvgG = 0;

     int AvgR = 0;

     int cnt = 0;

     for (int i = 0; i < row; i++) {

          for (int j = 0; j < col; j++) {

                int sumP = src.at<Vec3b>(i, j)[0] + src.at<Vec3b>(i, j)[1] + src.at<Vec3b>(i, j)[2];

                if (sumP > Threshold) {

                      AvgB += src.at<Vec3b>(i, j)[0];

                      AvgG += src.at<Vec3b>(i, j)[1];

                      AvgR += src.at<Vec3b>(i, j)[2];

                      cnt++;

                }

          }

     }

     AvgB /= cnt;

     AvgG /= cnt;

     AvgR /= cnt;

     for (int i = 0; i < row; i++) {

          for (int j = 0; j < col; j++) {

                int Blue = src.at<Vec3b>(i, j)[0] * MaxVal / AvgB;

                int Green = src.at<Vec3b>(i, j)[1] * MaxVal / AvgG;

                int Red = src.at<Vec3b>(i, j)[2] * MaxVal / AvgR;

                if (Red > 255) {

                      Red = 255;

                }

                else if (Red < 0) {

                      Red = 0;

                }

                if (Green > 255) {

                      Green = 255;

                }

                else if (Green < 0) {

                      Green = 0;

                }

                if (Blue > 255) {

                      Blue = 255;

                }

                else if (Blue < 0) {

                      Blue = 0;

                }

                dst.at<Vec3b>(i, j)[0] = Blue;

                dst.at<Vec3b>(i, j)[1] = Green;

                dst.at<Vec3b>(i, j)[2] = Red;

          }

     }

     return dst;

}



int main() {

     Mat src = imread("Test.jpg");

     Mat dst = PerfectReflectionAlgorithm(src);

     imshow("origin", src);

     imshow("result", dst);

     waitKey(0);

     return 0;

}

 

处理效果:

                                            

Android相机应用关闭白平衡_灰度_06

 

Android相机应用关闭白平衡_计算机视觉_07

        (3)基于动态阈值算法:

YUV颜色空间(亦称YCrCb)主要用于优化彩色视频信号的传输,Y表示亮度,U和V表示色度(色调和饱和度)。亮度是通过RGB输入信号来建立的,方法是将RGB信号的特定部分叠加到一起。“色度”则定义了颜色的两个方面─色调与饱和度,分别用Cr和Cb来表示。其中,Cr反映了RGB输入信号红色部分与RGB信号亮度值之间的差异。而Cb反映的是RGB输入信号蓝色部分与RGB信号亮度值之同的差异。

动态阈值算法通过将RGB变化到YCrCb颜色空间进行分析来确定白点,其选择参考白点的阈值是动态变化的。我们通过对图片的YCrCb坐标空间的分析,可以找到一个接近白色的区域,该区域是包含着参考白点的,通过设定一个阈值来规定某些点为参考白点。因此该算法是一个动态的自适应白平衡算法。

该算法共分为两步:白色点的检测,白色点的调整。具体的算法过程如下:

1.将图像从RGB空间变换到YCrCb空间

       Y=0.257*R+0.504*G+0.098*B+16/255

      Cb=−0.148∗R−0.291∗G+0.439∗B+128/255

      Cr=−0.439∗R−0.368∗G−0.071∗

2.白点检测:为了增强算法的鲁棒性,将图像分为12部分

a.计算每个区域的Cr,Cb的均值Mr,Mb,N为每块区域的像素个数:

                                  

Android相机应用关闭白平衡_计算机视觉_08

b.计算每个区域的Cr,Cb分量的绝对偏差的均值Dr,Db:

                            

Android相机应用关闭白平衡_机器学习_09

c.如果Db,Dr的值偏小,则忽略这一块,因为这表明这一块的颜色分布比较均匀,而这样的局部对于白平衡不好。

d.最后整幅图像的均值 Mb,Mr 以及方差 Db,Dr 由除去T条件c后剩下的块计算平均值得到。

e.选择候补白点,若某像素满足一下条件:

                                   

Android相机应用关闭白平衡_灰度_10

3.然后根据候补白点的像素亮度值由高到低排列,从候补白点中选取亮度值在前 10%的白点做为参考白点。白平衡的增益值就是根据选取的参考白点确定的。

为了让校正后的图像亮度跟校正前的图像亮度保持在同一水平,在增益计算时采用最大的亮度值作为参考。增益系数的计算公式公式如下:

                           

Android相机应用关闭白平衡_算法_11

其中,Ravgw ,Gavgw ,Bavgw是参考白点的RGB三通道均值,Ymax是图像中所有图像亮度的最大值。

Matlab代码:

函数:

function [output] = dynamic_awb(im)

%% 基于阈值的动态白平衡

[m,n,k] = size(im);

R = im(:,:,1);

G = im(:,:,2);

B = im(:,:,3);

Y = 0.257*R+0.504*G+0.098*B+16/255;

Cb = -0.148*R-0.291*G+0.439*B+128/255;

Cr = -0.439*R-0.368*G-0.071*B+128/255;



row = m/3; % 将image分块4*4块,则row为每分块的行数

col = n/3; %col为列数

count = 1;

Mb = 0;

Mr = 0;

Db = 0;

Dr = 0;



for i=1:row:m

    for j=1:col:n

        Ib = Cb(i:1:i+row-1,j:1:j+col-1); %每分块的Cb值

        Ir = Cr(i:1:i+row-1,j:1:j+col-1);

        Mbt = mean(mean(Ib)); % 分块的Cb均值

        Mrt = mean(mean(Ir)); % 分块的Cr均值

        Dbt = sum(sum(abs(Ib-Mbt)))/(row*col);% 分块的绝对偏差

        Drt = sum(sum(abs(Ir-Mrt)))/(row*col);

        Mb(count) = Mbt;

        Mr(count) = Mrt;

%         if Dbt>0.01 && Drt>0.01  % 判断该分块的方差是否足够大

            Db(count) = Dbt;

            Dr(count) = Drt;

            count = count+1;

%         end

    end

end



Mb = mean(Mb); %得到分块Mb的均值

Mr = mean(Mr);

Db = mean(Db);

Dr = mean(Dr);

J = zeros(m,n); % 记录候补白点的位置信息,若(i,j)位置为1,则(i,j)像素是候补白点



for i=1:1:m

    for j=1:1:n

        bv = abs(Cb(i,j)-(Mb+Db*sign(Mb)));

        rv = abs(Cr(i,j)-(1.5*Mr+Dr*sign(Mr)));

        if (bv<1.5*Db) && (rv<1.5*Dr) % 判断是否满足候补条件

            J(i,j) = 1;

        end

    end

end

candidate = reshape(Y.*J,m*n,1);

candidate = sort(candidate,'descend'); % 将候补白点的亮度值Y降序排序

kk = round(sum(sum(J))*0.1);

min_v = candidate(kk); % 得到前10%的最小值



Y1 = (Y>(ones(m,n)*min_v));  % 得到参考白点的亮度矩阵

R1 = R.*Y1; % RGB三通道的参考白点

G1 = G.*Y1;

B1 = B.*Y1;

Ravg = sum(sum(R1))/sum(sum(Y1)); % 参考白点RGB三通道的均值

Gavg = sum(sum(G1))/sum(sum(Y1));

Bavg = sum(sum(B1))/sum(sum(Y1));

Ymax = double(max(max(Y))); % 亮度最大值



Rgain = Ymax/Ravg; % 计算增益

Ggain = Ymax/Gavg;

Bgain = Ymax/Bavg;

% R = R*Rgain;

% G = G*Ggain;

% B = B*Bgain;

im(:,:,1) = im(:,:,1)*Rgain;

im(:,:,2) = im(:,:,2)*Ggain;

im(:,:,3) = im(:,:,3)*Bgain;

output = im;

end

调用:

clear all

close all

clc

% 输入图像(存在颜色偏差的原始图像)

I=im2double(imread('t.png'));

ImageOutput = dynamic_awb(I);

figure;imshow([I ImageOutput]);

 

处理效果:

                                             

Android相机应用关闭白平衡_灰度_12

    

Android相机应用关闭白平衡_灰度_13

三种方法结果对比:

                         

Android相机应用关闭白平衡_算法_14