【camera】【ISP】AWB-自动白平衡

文章转载自:ISP——AWB(Auto White Balance)


AWB-Auto White Blance白平衡

1. 色温

色温,简单理解不同温度下,绝对黑体表现出来的颜色不同。

色温是表示光线中包含颜色成分的一个计量单位,从理论上讲,黑体温度指绝对黑体从绝对零度(-273°c)开始加温后所呈现的颜色。黑体受热后,逐渐由黑变红,转黄,发白,最后发出蓝色光。

当加热到一定温度,黑体发出的光所含的光谱成分,称为这一温度下的色温,计量单位为"K"开尔文。

色温在生活中的表现,如早晨和傍晚的天空一般是橙黄色的,中午的天空一般是蓝色的。

2. 颜色恒常性

照度发生变化后,人眼对物体表面颜色的知觉趋于稳定的心理倾向。

3. 白平衡原理

传感器不具有人眼的不同光照色温下的色彩稳定性,白平衡就是将人眼看到的白色物体进行色彩还原,使其在照片上也呈现白色。

4. 校正方法

YCbCr颜色空间

RGB颜色空间与YCbCr颜色空间的转换关系如下:
[ Y U V ] = [ 0.30 0.59 0.11 − 0.15 − 0.29 0.44 0.51 − 0.52 − 0.095 ] [ R G B ] \begin{bmatrix} Y\\ U\\ V \\ \end{bmatrix}=\begin{bmatrix} 0.30 & 0.59 &0.11 \\ -0.15 & -0.29 &0.44 \\ 0.51& -0.52 & -0.095 \end{bmatrix}\begin{bmatrix} R\\ G\\ B \end{bmatrix} YUV = 0.300.150.510.590.290.520.110.440.095 RGB

校正方式一:灰度世界法 Gray World Assumption

任一幅图像,当它有足够的色彩变化,则它的RGB分量的均值会趋于相等。

校正的时候可以按照如下计算:
r g a i n = g m e a n r m e a n b g a i n = g m e a n b m e a n r_{gain} = \frac{g_{mean}}{r_{mean}} \\ b_{gain} = \frac {g_{mean}}{b_{mean}} rgain=rmeangmeanbgain=bmeangmean
得到r_gain,b_gain后,对于某一点像素p的值为(R,G,B),校正后的值为 ( R ∗ r g a i n , G , B ∗ b g a i n ) (R*r_{gain},G,B*b_{gain}) (Rrgain,G,Bbgain),类似做了一个归一化的处理。

bool gray_world(const char *filename, const char *filename_dst)
{
	// 灰度世界法
	// r_mean/g_mean/b_mean
	// r_gain = g_mean/r_mean;
	// b_gain = g_mean/b_mean
	// r' = r*r_gain
	// b' = b*b_gain

	Mat img = imread(filename);
	if (img.empty())
	{
		printf("load image fail!\n");
		return false;
	}

	// 获取每个通道的均值
	
	cv::Scalar s = cv::mean(img);
	double b_mean = s[0];
	double g_mean = s[1];
	double r_mean = s[2];

	printf("gray_world:mean_r = %f, mean_g=%f, mean_b=%f\n", r_mean, g_mean, b_mean);

	double r_gain = g_mean / r_mean;
	double b_gain = g_mean / b_mean;

	// correction (r*r_gain,g,b*b_gain)
	Mat dst = cv::Mat::zeros(img.rows, img.cols, CV_8UC3);
	unsigned char tmp = 0;
	for (int row = 0; row < img.rows; row++)
	{
		Vec3b *psrc = img.ptr<Vec3b>(row);
		Vec3b *pdst = dst.ptr<Vec3b>(row);
		for (int col = 0; col < img.cols; col++)
		{
			tmp = (unsigned char)(psrc[col][0] * b_gain);
			pdst[col][0] = (tmp > 255 ? 255 : tmp);

			pdst[col][1] = psrc[col][1];

			tmp = (unsigned char)(psrc[col][2] * r_gain);
			pdst[col][2] = (tmp > 255 ? 255 : tmp);

		}
	}

	cv::imshow("src", img);
	cv::imshow("dst", dst);
	cv::imwrite(filename_dst, dst);
	cv::waitKey(0);

	return true;
}

校正方式二:完美反射法 Perfect Reflector Assumption

完美反射法基于假设:一幅图像中最亮的像素相当于物体有光泽或者是镜面上的点,它传达了很多关于场景照明条件的信息

如果景物中有纯白的部分,那么就可以直接从这些像素中提取出光源信息,因为镜面或有光泽的平面本身不吸收光线,所以其反射的颜色即为光源的真实颜色,这是因为镜面或有光泽的平面的反射比函数在很长的一段波长范围内是保持不变的。完美反射法利用这种特性来对图像进行调整。

算法执行时,检测图像中亮度最高的像素并将它作为参考白点,基于这种思想的方法被称为完美反射法,也称镜面法。

  1. 首先找到每个通道的最大值

{ R m a x = m a x ( R i j ) ( i = 1   N , j = 1   M ) G m a x = m a x ( G i j ) ( i = 1   N , j = 1   M ) B m a x = m a x ( B i j ) ( i = 1   N , j = 1   M ) \left\{\begin{matrix} R_{max} = max(R_{ij}) (i=1~N,j=1~M)\\ G_{max} = max(G_{ij}) (i=1~N,j=1~M)\\ B_{max} = max(B_{ij}) (i=1~N,j=1~M) \end{matrix}\right. Rmax=max(Rij)(i=1 N,j=1 M)Gmax=max(Gij)(i=1 N,j=1 M)Bmax=max(Bij)(i=1 N,j=1 M)

  1. 然后计算增益

{ G a i n R m a x = m a x ( R m a x , G m a x , B m a x ) / R m a x G a i n G m a x = m a x ( R m a x , G m a x , B m a x ) / G m a x G a i n B m a x = m a x ( R m a x , G m a x , B m a x ) / B m a x \left\{\begin{matrix} Gain_{R_{max}} = max(R_{max},G_{max},B_{max})/R_{max} \\ Gain_{G_{max}} = max(R_{max},G_{max},B_{max})/G_{max} \\ Gain_{B_{max}} = max(R_{max},G_{max},B_{max})/B_{max} \end{matrix}\right. GainRmax=max(Rmax,Gmax,Bmax)/RmaxGainGmax=max(Rmax,Gmax,Bmax)/GmaxGainBmax=max(Rmax,Gmax,Bmax)/Bmax

  1. 校正

R m a x ′ = { R ∗ G a i n R m a x , R ∗ G a i n R m a x ≤ 255 255 , R ∗ G a i n R m a x > 255 G m a x ′ = { G ∗ G a i n G m a x , G ∗ G a i n G m a x ≤ 255 255 , G ∗ G a i n G m a x > 255 B m a x ′ = { B ∗ G a i n B m a x , B ∗ G a i n B m a x ≤ 255 255 , B ∗ G a i n B m a x > 255 R'_{max} = \left\{\begin{matrix} R*Gain_{R_{max}} & ,R*Gain_{R_{max}}\leq 255 \\ 255 & ,R*Gain_{R_{max}}>255 \end{matrix}\right. \\ G'_{max} = \left\{\begin{matrix} G*Gain_{G_{max}} & ,G*Gain_{G_{max}}\leq 255 \\ 255 & ,G*Gain_{G_{max}}>255 \end{matrix}\right. \\ B'_{max} = \left\{\begin{matrix} B*Gain_{B_{max}} & ,B*Gain_{B_{max}}\leq 255 \\ 255 & ,B*Gain_{B_{max}}>255 \end{matrix}\right. Rmax={RGainRmax255,RGainRmax255,RGainRmax>255Gmax={GGainGmax255,GGainGmax255,GGainGmax>255Bmax={BGainBmax255,BGainBmax255,BGainBmax>255

bool perfect_reflector(const char *filename, const char *filename_dst)
{
	// 完美反射法

	Mat img = imread(filename);
	if (img.empty())
	{
		printf("load image fail!\n");
		return false;
	}

	// 找到每个通道的最大值
	// 拆分通道
	vector<cv::Mat> channelsimg;
	cv::split(img, channelsimg);

	double r_max, g_max, b_max;
	cv::minMaxIdx(channelsimg[0], NULL, &b_max, NULL, NULL);
	cv::minMaxIdx(channelsimg[1], NULL, &g_max, NULL, NULL);
	cv::minMaxIdx(channelsimg[2], NULL, &r_max, NULL, NULL);


	printf("prefect_reflector:max_r = %f, max_g=%f, max_b=%f\n", r_max, g_max, b_max);

	// 计算增益
	double max = cv::max(r_max, cv::max(b_max, g_max));
	double r_gain = max / r_max;
	double g_gain = max / g_max;
	double b_gain = max / b_max;

	// 校正
	Mat dst = cv::Mat::zeros(img.rows, img.cols, CV_8UC3);
	unsigned char tmp = 0;
	for (int row = 0; row < img.rows; row++)
	{
		Vec3b *psrc = img.ptr<Vec3b>(row);
		Vec3b *pdst = dst.ptr<Vec3b>(row);
		for (int col = 0; col < img.cols; col++)
		{
			tmp = (unsigned char)(psrc[col][0] * b_gain);
			pdst[col][0] = (tmp > 255 ? 255 : tmp);

			tmp = (unsigned char)(psrc[col][1] * g_gain);
			pdst[col][1] = (tmp > 255 ? 255 : tmp);


			tmp = (unsigned char)(psrc[col][2] * r_gain);
			pdst[col][2] = (tmp > 255 ? 255 : tmp);

		}
	}

	cv::imshow("src", img);
	cv::imshow("dst", dst);
	cv::imwrite(filename_dst, dst);
	cv::waitKey(0);
	return true;
}

校正方式三:QCGP - 灰度世界和完美反射结合

基于灰度世界和完美反射的假说,又有以灰度世界和完美反射正交的方式结合的方式
{ u ∗ r m e a n 2 + v ∗ r m e a n = k m e a n u ∗ r m a x 2 + v ∗ r m a x = k m a x \left\{\begin{matrix} u*r_{mean}^{2} + v*r_{mean} = k_{mean} \\ u*r_{max}^{2} + v*r_{max} = k_{max} \end{matrix}\right. {urmean2+vrmean=kmeanurmax2+vrmax=kmax
其中, k m e a n = ( r m e a n + g m e a n + b m e a n ) / 3 k_{mean} = (r_{mean} + g_{mean} + b_{mean})/3 kmean=(rmean+gmean+bmean)/3 k m a x = ( r m a x + g m a x + b m a x ) / 3 k_{max} = (r_{max} + g_{max} + b_{max})/3 kmax=(rmax+gmax+bmax)/3,计算得到u,v。

校正公式: r n e w = u ∗ r o r g 2 + v ∗ r o r g r_{new} = u*r_{org}^2 + v*r_{org} rnew=urorg2+vrorg

其它通道类似。

void get_u_v(double max, double k_max, double mean, double k_mean,double &u,double &v)
{
	u = v = 0;
	double ret[2] = { 0, 0 };
	// a*x+by=c                       d*x+ey=f
	// max^2 * u + max * v = kmax     mean^2*u + mean*v = kmean
	u = (k_max / max - k_mean / mean) / (max - mean);
	v = k_max / max - max*u;
	
}

bool QCGP(const char *filename, const char *filename_dst)
{
	// 完美反射法

	Mat img = imread(filename);
	if (img.empty())
	{
		printf("load image fail!\n");
		return false;
	}

	// 找到每个通道的均值
	cv::Scalar s = cv::mean(img);
	double b_mean = s[0];
	double g_mean = s[1];
	double r_mean = s[2];
	double k_mean = (r_mean + b_mean + g_mean) / 3;
	// 找到每个通道的最大值
	// 拆分通道
	vector<cv::Mat> channelsimg;
	cv::split(img, channelsimg);

	double r_max, g_max, b_max;
	cv::minMaxIdx(channelsimg[0], NULL, &b_max, NULL, NULL);
	cv::minMaxIdx(channelsimg[1], NULL, &g_max, NULL, NULL);
	cv::minMaxIdx(channelsimg[2], NULL, &r_max, NULL, NULL);
	double k_max = (r_max + b_max + g_max) / 3;

	int size = channelsimg.size();
	
	double dmax[] = { b_max, g_max, r_max };
	double dmean[] = { b_mean, g_mean, r_mean };
	double u[3], v[3];
	for (int i = 0; i < 3; i++)
	{
		get_u_v(dmax[i], k_max, dmean[i], k_mean,u[i],v[i]);
	}

	// 校正
	Mat dst = cv::Mat::zeros(img.rows, img.cols, CV_8UC3);
	short tmp = 0;
	for (int row = 0; row < img.rows; row++)
	{
		Vec3b *psrc = img.ptr<Vec3b>(row);
		Vec3b *pdst = dst.ptr<Vec3b>(row);
		for (int col = 0; col < img.cols; col++)
		{
			
			tmp = psrc[col][0];
			tmp = tmp*tmp*u[0] + tmp*v[0];
			tmp = (tmp < 0 ? 0 : tmp);
			pdst[col][0] = (tmp > 255 ? 255 : tmp);

			tmp = psrc[col][1];
			tmp = tmp*tmp*u[1] + tmp*v[1];
			tmp = (tmp < 0 ? 0 : tmp);
			pdst[col][1] = (tmp > 255 ? 255 : tmp);


			tmp = psrc[col][2];
			tmp = tmp*tmp*u[2] + tmp*v[2];
			tmp = (tmp < 0 ? 0 : tmp);
			pdst[col][2] = (tmp > 255 ? 255 : tmp);

		}
	}

	cv::imshow("src", img);
	cv::imshow("dst", dst);
	cv::imwrite(filename_dst, dst);
	cv::waitKey(0);
	return true;
}

上面三种方法校正后的图像效果:

如下原始图像使用完美反射法校正后的效果比较好;

如下图片使用QCGP方法校正后效果较好:

校正方式四:基于模糊逻辑的算法

【camera】【ISP】AWB-自动白平衡_第1张图片

将图像分成8个块,计算每个块的权重(这个权重和亮度、色度相关)。得到权重后计算整个图像的加权均值,让这个加权均值往白点上靠,通过调整增益的方式调试,调整完增益后,每个块的均值又会发生变化,重新计算每个块的权重,再通过权重计算出整个图像的均值。

如果加权均值和白点的差距在一个设定的范围内,则认为完成白平衡,否则继续调整增益重复上述步骤进行校正。

校正方式五:基于白点的算法

  1. 将RGB颜色空间转换为YUV空间;

[ Y U V ] = [ 0.30 0.59 0.11 − 0.15 − 0.29 0.44 0.51 − 0.52 − 0.095 ] [ R G B ] \begin{bmatrix} Y\\ U\\ V \\ \end{bmatrix}=\begin{bmatrix} 0.30 & 0.59 &0.11 \\ -0.15 & -0.29 &0.44 \\ 0.51& -0.52 & -0.095 \end{bmatrix}\begin{bmatrix} R\\ G\\ B \end{bmatrix} YUV = 0.300.150.510.590.290.520.110.440.095 RGB

  1. 判断白点区域,如果是白点记录下来参与计算,不是白点直接舍弃;判断是否为白点:

{ Y < χ − α < U < α − β < V < β Y − ∣ U ∣ − ∣ V ∣ > ϕ , ϕ = 180 \left\{\begin{matrix} Y<\chi \\ -\alpha \phi, \phi=180 Y<χα<U<αβ<V<βYUV>ϕ,ϕ=180

  1. 找到白点集合后,对白点集合运用灰度世界算法或者其它算法计算gain值进而校正;

校正方式六:基于色温的方法

  1. 在不同色温的环境下拍摄灰卡可以得到色温-RG曲线与BG-RG曲线。获取一张图像后,如果知道拍摄的色温,根据色温-RG曲线就可以获取到RG,然后将这个值带入到BG-RG曲线就可以得到BG;

  2. 获取色温的方法:一种方式是通过加一个色温传感器获取环境色温,另一种方式是通过计算求出T。

    1. 定义T_min = 2000K,T_max=15000K;
    2. 判断T_max-T_min是否大于10,如果小于或等于10,直接返回T,T可取min或者max或者(min+max)/2,如果大于10,T取(min+max)/2;
    3. 根据公式,通过T计算出R’G’B’;

    x D = { 0.27475 e 9 T 3 − 0.98598 e 6 T 2 + 1.17444 e 3 T + 0.145986 2000 ≤ T ≤ 4000 − 4.6070 e 9 T 3 − 2.9678 e 6 T 2 + 0.09911 e 3 T + 0.244063 4000 < T ≤ 7000 − 2.0064 e 9 T 3 − 1.9018 e 6 T 2 + 0.24748 e 3 T + 0.23704 7000 < T ≤ 15000 y D = − 3 ∗ x D 2 + 2.87 ∗ x D − 0.275 X = x D y D Y = 1 Z = 1 − x D − y D y D [ R G B ] = [ 3.24071 − 0.969258 0.0556352 − 1.53726 1.87599 − 0.203996 − 0.498571 0.0415557 1.05707 ] [ X Y Z ] xD=\left\{\begin{matrix} \frac{0.27475e^{9}}{T_{3}}- \frac{0.98598e^{6}}{T_{2}} + \frac{1.17444e^{3}}{T} + 0.145986 & 2000\leq T\leq 4000\\ \frac{-4.6070e^{9}}{T_{3}}- \frac{2.9678e^{6}}{T_{2}} + \frac{0.09911e^{3}}{T} + 0.244063 & 4000 < T\leq 7000\\ \frac{-2.0064e^{9}}{T_{3}}- \frac{1.9018e^{6}}{T_{2}} + \frac{0.24748e^{3}}{T} + 0.23704 & 7000 < T\leq 15000 \end{matrix}\right. \\ \\ yD = -3 \ast xD^{2} + 2.87 \ast xD-0.275 \\ \\ X=\frac{xD}{yD} \\ \\ Y=1 \\ \\ Z=\frac{1-xD-yD}{yD} \\ \\ \begin{bmatrix} R\\ G\\ B \end{bmatrix} = \begin{bmatrix} 3.24071 & -0.969258 & 0.0556352 \\ -1.53726 & 1.87599 & -0.203996\\ -0.498571 & 0.0415557 & 1.05707 \end{bmatrix}\begin{bmatrix} X\\ Y\\ Z \end{bmatrix} xD= T30.27475e9T20.98598e6+T1.17444e3+0.145986T34.6070e9T22.9678e6+T0.09911e3+0.244063T32.0064e9T21.9018e6+T0.24748e3+0.237042000T40004000<T70007000<T15000yD=3xD2+2.87xD0.275X=yDxDY=1Z=yD1xDyD RGB = 3.240711.537260.4985710.9692581.875990.04155570.05563520.2039961.05707 XYZ

    1. 计算原始图像的各通道均值RGB,如果B’/R’>B/R,那么T_max = T,否则T_min = T,重复上述步骤可迭代求出色温T;

    校正方式七:基于边缘的方法

    1. 求出图像的边缘,在边缘的左右两侧各提取两个点作为参考点;
    2. 得到参考后,使用灰度世界或者其它算法求出gain值;

    基于边缘的方法可以减少大色块的干扰,可以避免大色块分量太大造成白平衡异常的问题。

    校正方式八:多方法融合

    来自:https://gitee.com/wtzhu13/ISPAlgorithmStudy

    如图是一款ISP主控的白平衡tuning的图片。每个蓝色的框就代表一种色温,比如9代表2000K,8代表2500K,这个是通过实验和经验值确定的。图中绿色的点就是通过白点算法筛选出来的白点候选点。然后调试的时候就是在不同色温下拍摄灰卡,然后挪动蓝色选框,使其包围绿点,然后右上角就是估计色温,调试的时候就是使得这个估计色温和真是色温不要相差太多。然后通过标定若干组色温之后就确定了该方案的一个色温曲线。后续再pipeline中通过白点检测算法筛选出白点,然后根据白点的分布,可以找到大多数白点分布的色温,那么该色温就是当前的色温,然后通过色温再按照前面提到的算法就可以计算出一个gain值,然后再和灰度世界算法进行一个blending就可以得到最终ISP中使用的gain值。

【camera】【ISP】AWB-自动白平衡_第2张图片

你可能感兴趣的:(camera,ISP,Camera)