白平衡之灰度世界算法

一、算法背景

  白平衡是图像处理比较常见的一个概念,在采集图像的过程中,相机的感光元件或者镜头会对原始色彩造成影响,而白平衡技术通常可以用来校正这种光线和镜头对颜色影响。所以现在先记录一个白平衡算法,叫做灰度世界算法,这个算法很多博客都有介绍,而且实际上就几个公式,所以我也是简单记录重要的公式和实现代码而已。

二、算法原理

  灰度世界算法(Gray World)假设认为,一幅彩色图像中,RGB三个通道的颜色平均值是趋于同一个灰度值K的,所以如果当前的通道的均值与K值存在差异,就需要对该通道的像素值进行矫正,矫正的结果则是与K有关。K值的取法有两种,一种是取最大像素值的一般,即8位图中的127或者128;第二种是以三通道的均值来作为K值。

  灰度世界白平衡算法流程如下:

  (1)、计算RGB三个通道各自的均值,得到RavgGavgBavg

  (2)、取RGB均值的均值得到K值:K = ( Ravg + Gavg + Bavg ) / 3 ;

  (3)、计算各通道相对于K值的增益:

      KR = K / Ravg

      KG = K / Gavg

      KB = K / Bavg

  (4)、根据增益逐个调整RGB三通道的像素值:

      Rnew = R * KR

      Gnew = G * KG

      Bnew = B * KB

  (5)、调整输出,由于上面的计算可能存在溢出,因此需要对结果进行调整,调整有两种做法:

      a)、超出部分取255;

      b)、计算RnewGnewBnew的最大值,然后映射回0到255。

三、实现代码

cv::Mat srcDouble;
src.convertTo(srcDouble, CV_64FC3);
cv::Scalar meanScale = cv::mean(src);

double K = (meanScale[0] + meanScale[1] + meanScale[2]) / 3;
double Kb = K / (meanScale[0]);
double Kg = K / (meanScale[1]);
double Kr = K / (meanScale[2]);

cv::Mat src_new = cv::Mat::zeros(srcDouble.size(), srcDouble.type());
for (int i = 0; i < srcDouble.rows; i++)
{
    const double *ptrSrc = srcDouble.ptr<double>(i);
    double *ptrSrc_new = src_new.ptr<double>(i);
    for (int j = 0; j < srcDouble.cols; j++)
    {
		*(ptrSrc_new + 3 * j) = *(ptrSrc + 3 * j) * Kb;
		*(ptrSrc_new + 3 * j + 1) = *(ptrSrc + 3 * j + 1) * Kg;
		*(ptrSrc_new + 3 * j + 2) = *(ptrSrc + 3 * j + 2) * Kr;
    }
}

double maxPix = -4096;
double minPix = 4096;
for (int i = 0; i < src_new.rows; i++)
{
	double *ptrSrc_new = src_new.ptr<double>(i);
	for (int j = 0; j < src_new.cols; j++)
	{
        maxPix = maxPix > *(ptrSrc_new + j * 3) ? maxPix : *(ptrSrc_new + j * 3);
        maxPix = maxPix > *(ptrSrc_new + j * 3 + 1) ? maxPix : *(ptrSrc_new + j * 3 + 1);
        maxPix = maxPix > *(ptrSrc_new + j * 3 + 2) ? maxPix : *(ptrSrc_new + j * 3 + 2);

        minPix = minPix < *(ptrSrc_new + j * 3) ? minPix : *(ptrSrc_new + j * 3);
        minPix = minPix < *(ptrSrc_new + j * 3 + 1) ? minPix : *(ptrSrc_new + j * 3 + 1);
        minPix = minPix < *(ptrSrc_new + j * 3 + 2) ? minPix : *(ptrSrc_new + j * 3 + 2);
	}
}

cv::Mat dst = cv::Mat::zeros(src.size(), src.type());
for (int i = 0; i < dst.rows; i++)
{
	double *ptrSrc_new = src_new.ptr<double>(i);
	uchar *ptrDst = dst.ptr<uchar>(i);
	for (int j = 0; j < dst.cols; j++)
	{
        *(ptrDst + j * 3) = (uchar)((*(ptrSrc_new + j * 3) / maxPix) * 255);
        *(ptrDst + j * 3 + 1) = (uchar)((*(ptrSrc_new + j * 3 + 1) / maxPix) * 255);
        *(ptrDst + j * 3 + 2) = (uchar)((*(ptrSrc_new + j * 3 + 2) / maxPix) * 255);
    }
}

cv::Mat dst1 = cv::Mat::zeros(src.size(), src.type());
for (int i = 0; i < dst.rows; i++)
{
    double *ptrSrc_new = src_new.ptr<double>(i);
    uchar *ptrDst1 = dst1.ptr<uchar>(i);
    for (int j = 0; j < dst.cols; j++)
    {
        *(ptrDst1 + j * 3) = (uchar)(*(ptrSrc_new + j * 3)>255?255: *(ptrSrc_new + j * 3));
        *(ptrDst1 + j * 3 + 1) = (uchar)(*(ptrSrc_new + j * 3 + 1)>255?255: *(ptrSrc_new + j * 3+1));
        *(ptrDst1 + j * 3 + 2) = (uchar)(*(ptrSrc_new + j * 3 + 2)>255?255: *(ptrSrc_new + j * 3+2));
    }
}
cv::imwrite("dst.jpg", dst);
cv::imwrite("dst1.jpg", dst1);
return dst;

四、算法结果

  从上到下是原图、255截断、255归一化的结果,255截断的效果会比较好一点,不过整体来说,这个算法本来就比较简单,所以也只是对于一些图像而言效果还是挺好的。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

元丹丘,爱神仙,朝饮颍川之清流,
暮还嵩岑之紫烟,三十六峰长周旋。
长周旋,蹑星虹,身骑飞龙耳生风,
横河跨海与天通,我知尔游心无穷。
– 唐代 李白 《元丹丘歌》

  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值