AWB白平衡算法

本文介绍了3A算法中的自动白平衡算法,参考诸多大神的文章,由于整理时间较晚,未完全记得参考的原文链接,如有版权权限问题,望联系进行更正,谢谢!

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

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

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

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

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

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

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

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

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

其中,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 
 
#include 
 
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)

处理效果:
AWB白平衡算法_第2张图片
2)完美反射算法基本原理:假设图像中最亮的点就是白点,并以此白点为参考对图像进行自动白平衡,最亮点定义为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 
 
#include 
 
#include 
 
#include 
 
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;
 
}

处理效果:
AWB白平衡算法_第3张图片
(3)基于动态阈值算法:

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

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

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

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

Y=0.257R+0.504G+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为每块区域的像素个数:
在这里插入图片描述

b.计算每个区域的Cr,Cb分量的绝对偏差的均值Dr,Db:
在这里插入图片描述

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

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

e.选择候补白点,若某像素满足一下条件:
AWB白平衡算法_第4张图片

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

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

其中,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]);

处理效果:
AWB白平衡算法_第6张图片
三种方法结果对比:
原图:
AWB白平衡算法_第7张图片
1:AWB白平衡算法_第8张图片

2:
AWB白平衡算法_第9张图片

3:

AWB白平衡算法_第10张图片

你可能感兴趣的:(fpga开发)