OpenCV-白平衡(完美反射算法)

简介: OpenCV-白平衡(完美反射算法)

实现原理

      白平衡的意义在于,对在特定光源下拍摄时出现的偏色现象,通过加强对应的补色来进行补偿,使白色物体能还原为白色。


      完美反射算法是白平衡各种算法中较常见的一种,比灰度世界算法更优。它假设图像世界中最亮的白点是一个镜面,能完美反射光照;基于白点,将三通道的数值进行适当地调整,以达到白平衡效果;除此之外,还需要统计最亮的一定区间的三通道均值,该均值与该通道最大值的差距决定了该通道调整的力度。


      通俗的讲,若图像中绿色分量最大值是255,但是绿色最亮的前百分之10个点的平均值只有80,说明原图的绿色分量整体较低,需要对其加强;若最大值只有100,那么加强的系数就较低,白平衡的效果就不达预期。这就是完美反射算法比较依赖图像中存在白点的原因,白点的三通道灰度值接近【255,255,255】。最下方将用实际图像作进一步说明,以帮助读者理解。


      完美反射算法的实现流程如下:  


      1.计算图像RGB三通道各自的灰度最大值Rmax、Gmax、Bmax。


      2.利用三通道数值和,确定图像最亮区间的下限T。


      3.计算图像三通道数值和大于T的点的三通道均值Rm、Gm、Bm。


      4.计算三通道的补偿系数,即单通道最大值除以单通道亮区平均值。

功能函数代码

// 白平衡-完美反射
cv::Mat WhiteBalcane_PRA(cv::Mat src)
{
  cv::Mat result = src.clone();
  if (src.channels() != 3)
  {
    cout << "The number of image channels is not 3." << endl;
    return result;
  }
  // 通道分离
  vector<cv::Mat> Channel;
  cv::split(src, Channel);
  // 定义参数
  int row = src.rows;
  int col = src.cols;
  int RGBSum[766] = { 0 };
  uchar maxR, maxG, maxB;
  // 计算单通道最大值
  for (int i = 0; i < row; ++i)
  {
    uchar *b = Channel[0].ptr<uchar>(i);
    uchar *g = Channel[1].ptr<uchar>(i);
    uchar *r = Channel[2].ptr<uchar>(i);
    for (int j = 0; j < col; ++j)
    {
      int sum = b[j] + g[j] + r[j];
      RGBSum[sum]++;
      maxB = max(maxB, b[j]);
      maxG = max(maxG, g[j]);
      maxR = max(maxR, r[j]);
    }
  }
  // 计算最亮区间下限T
  int T = 0;
  int num = 0;
  int K = static_cast<int>(row * col * 0.1);
  for (int i = 765; i >= 0; --i)
  {
    num += RGBSum[i];
    if (num > K)
    {
      T = i;
      break;
    }
  }
  // 计算单通道亮区平均值
  double Bm = 0.0, Gm = 0.0, Rm = 0.0;
  int count = 0;
  for (int i = 0; i < row; ++i)
  {
    uchar *b = Channel[0].ptr<uchar>(i);
    uchar *g = Channel[1].ptr<uchar>(i);
    uchar *r = Channel[2].ptr<uchar>(i);
    for (int j = 0; j < col; ++j)
    {
      int sum = b[j] + g[j] + r[j];
      if (sum > T)
      {
        Bm += b[j];
        Gm += g[j];
        Rm += r[j];
        count++;
      }
    }
  }
  Bm /= count;
  Gm /= count;
  Rm /= count;
  // 通道调整
  Channel[0] *= maxB / Bm;
  Channel[1] *= maxG / Gm;
  Channel[2] *= maxR / Rm;
  // 合并通道
  cv::merge(Channel, result);
  return result;
}

C++测试代码

#include <iostream>
#include <opencv.hpp>
using namespace std;
// 白平衡-完美反射
cv::Mat WhiteBalcane_PRA(cv::Mat src)
{
  cv::Mat result = src.clone();
  if (src.channels() != 3)
  {
    cout << "The number of image channels is not 3." << endl;
    return result;
  }
  // 通道分离
  vector<cv::Mat> Channel;
  cv::split(src, Channel);
  // 定义参数
  int row = src.rows;
  int col = src.cols;
  int RGBSum[766] = { 0 };
  uchar maxR, maxG, maxB;
  // 计算单通道最大值
  for (int i = 0; i < row; ++i)
  {
    uchar *b = Channel[0].ptr<uchar>(i);
    uchar *g = Channel[1].ptr<uchar>(i);
    uchar *r = Channel[2].ptr<uchar>(i);
    for (int j = 0; j < col; ++j)
    {
      int sum = b[j] + g[j] + r[j];
      RGBSum[sum]++;
      maxB = max(maxB, b[j]);
      maxG = max(maxG, g[j]);
      maxR = max(maxR, r[j]);
    }
  }
  // 计算最亮区间下限T
  int T = 0;
  int num = 0;
  int K = static_cast<int>(row * col * 0.1);
  for (int i = 765; i >= 0; --i)
  {
    num += RGBSum[i];
    if (num > K)
    {
      T = i;
      break;
    }
  }
  // 计算单通道亮区平均值
  double Bm = 0.0, Gm = 0.0, Rm = 0.0;
  int count = 0;
  for (int i = 0; i < row; ++i)
  {
    uchar *b = Channel[0].ptr<uchar>(i);
    uchar *g = Channel[1].ptr<uchar>(i);
    uchar *r = Channel[2].ptr<uchar>(i);
    for (int j = 0; j < col; ++j)
    {
      int sum = b[j] + g[j] + r[j];
      if (sum > T)
      {
        Bm += b[j];
        Gm += g[j];
        Rm += r[j];
        count++;
      }
    }
  }
  Bm /= count;
  Gm /= count;
  Rm /= count;
  // 通道调整
  Channel[0] *= maxB / Bm;
  Channel[1] *= maxG / Gm;
  Channel[2] *= maxR / Rm;
  // 合并通道
  cv::merge(Channel, result);
  return result;
}
int main()
{
  // 载入原图
  cv::Mat src = cv::imread("test21.jpg");
  // 白平衡-完美反射
  cv::Mat result = WhiteBalcane_PRA(src);
  // 显示
  cv::imshow("src", src);
  cv::imshow("result", result);
  cv::waitKey(0);
  return 0;
}

测试效果

图1 原图

图2 白平衡后图像

      如图1所示,是傍晚的一张图像,众所周知,傍晚的色温是较低的,此时采用高于傍晚色温的色温值拍照,就会得到一张暖色系的图片,偏黄;对其进行白平衡,使图片颜色回归真实的环境色温,就得到如图2的效果。


      如果你用过灰度世界算法,你会发现完美反射算法的效果更亮些;这是因为蓝通道最大值和蓝通道亮区平均值的差距较大,因而补偿的强度很强;若原图中不存在白色区,那蓝通道的补偿就会较弱,达不到较好的预期,因此该算法所处理的图像中最好有较白的点。


图3 单色原图

图4 单色图白平衡效果

       如图3所示,是一张色彩相对一致的图像,整体呈粉色系。灰度世界法将三通道数值进行平均再补偿,就会使三通道的数值趋近一致,进而呈现灰色;而完美反射算法,计算的是三通道各自数值最大值和其亮区平均值的差距,再进行补偿,因此三通道的数值都会适当加强,使光感更强,即图片更亮。


       接下来做个有趣的测试,将原本粉色的墙纸设为较纯的绿色。

图5 调色后的图像

图6 白平衡效果图

       如图5所示,因为图像中存在色调相冲的两个部分,完美反射算法的优势在这种场景下体现的很明显。因为绿色区域较大,灰度世界算法中单纯的计算均值再平衡,会无脑地将整图的绿色分量降低,红色分量提高,使得结果异常滑稽;而完美反射算法,因为图中有白值,三通道的最大值均在250左右,对绿色分量而言,其最亮区的均值接近于230,而红色分量和蓝色分量而言,其最亮区的均值也接近于200多,因此三通道的平衡结果就是整体提亮,而不是被无脑平均。       如果函数有什么可以改进完善的地方,非常欢迎大家指出,一同进步何乐而不为呢~

      如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!

相关文章
|
25天前
|
算法 计算机视觉 Python
圆形检测算法-基于颜色和形状(opencv)
该代码实现了一个圆检测算法,用于识别视频中的红色、白色和蓝色圆形。通过将图像从RGB转换为HSV颜色空间,并设置对应颜色的阈值范围,提取出目标颜色的区域。接着对这些区域进行轮廓提取和面积筛选,使用霍夫圆变换检测圆形,并在原图上绘制检测结果。
62 0
|
3月前
|
算法 定位技术 vr&ar
一文了解PnP算法,python opencv中的cv2.solvePnP()的使用,以及使用cv2.sovlePnP()方法标定相机和2D激光雷达
一文了解PnP算法,python opencv中的cv2.solvePnP()的使用,以及使用cv2.sovlePnP()方法标定相机和2D激光雷达
427 0
一文了解PnP算法,python opencv中的cv2.solvePnP()的使用,以及使用cv2.sovlePnP()方法标定相机和2D激光雷达
|
5月前
|
机器学习/深度学习 算法 计算机视觉
基于opencv的SVM算法的车牌识别系统设计与实现
基于opencv的SVM算法的车牌识别系统设计与实现
136 3
基于opencv的SVM算法的车牌识别系统设计与实现
|
5月前
|
移动开发 算法 计算机视觉
技术笔记:openCV特征点识别与findHomography算法过滤
技术笔记:openCV特征点识别与findHomography算法过滤
97 0
|
5月前
|
机器学习/深度学习 编译器 算法框架/工具
OpenCV算法库
numba是一个用于编译Python数组和数值计算函数的编译器,这个编译器能够大幅提高直接使用Python编写的函数的运算速度。
|
6月前
|
算法 计算机视觉
【OpenCV】- 分水岭算法
【OpenCV】- 分水岭算法
|
6月前
|
算法
白平衡相关内容,算法
色温: 讨论白平衡,就要从色温谈起,色温顾名思义就是色彩的温度,它指的是绝对黑体从绝对零度开始持续加热所呈现出来的颜色。温度升高,颜色开始从红、橙、黄、绿、蓝、靛、紫,逐渐变化。这些颜色的差异来自于不同波长光线的比例不同,色温越低,波长较长的光线比例大,红色成分就多。色温越高,波长较短的光线比例大,蓝色的成分就多。
74 0
|
6月前
|
算法 C++ 计算机视觉
Opencv(C++)学习系列---Laplacian拉普拉斯边缘检测算法
Opencv(C++)学习系列---Laplacian拉普拉斯边缘检测算法
282 0
|
15天前
|
算法 安全 数据安全/隐私保护
基于game-based算法的动态频谱访问matlab仿真
本算法展示了在认知无线电网络中,通过游戏理论优化动态频谱访问,提高频谱利用率和物理层安全性。程序运行效果包括负载因子、传输功率、信噪比对用户效用和保密率的影响分析。软件版本:Matlab 2022a。完整代码包含详细中文注释和操作视频。
|
1天前
|
算法 调度
基于遗传模拟退火混合优化算法的车间作业最优调度matlab仿真,输出甘特图
车间作业调度问题(JSSP)通过遗传算法(GA)和模拟退火算法(SA)优化多个作业在并行工作中心上的加工顺序和时间,以最小化总完成时间和机器闲置时间。MATLAB2022a版本运行测试,展示了有效性和可行性。核心程序采用作业列表表示法,结合遗传操作和模拟退火过程,提高算法性能。