数字图像处理(十一)白平衡算法
数字图像处理,彩色平衡处理,白平衡算法
·
前言
当一副彩色图像数字化后,在显示时颜色有时会看起来有些不正常。这是因为颜色通道中不同的敏感度、增光因子、偏移量等,导致数字化中的三个图像分量(R,G,B)出现不同的变换,使结果图像的三原色"不平衡",从而使景物中所有物体的颜色都偏离了其原有的真实色彩。彩色平衡处理的目的就是将有色偏的图像进行颜色校正,获得正常颜色的图像。白平衡方法使一种常见的彩色平衡处理方法。
一、白平衡算法原理
白平衡原理是,如果原始场景中的某些像素点应该是白色的(即R=G=B=255),但是由于图像存在色偏,这些点的R、G、B三个分量的值不再保持相同,通过调整这三个颜色分量的值,使之达到平衡,由此获得对整幅图像的彩色平衡映射关系,通过该映射关系对整幅图像进行处理,即可达到彩色平衡的目的。
二、算法具体步骤
实现白平衡的算法有很多,这里介绍一种基本的白平衡方法。
- 对拍摄到的有色偏的图像,按照下式计算该图像的亮度分量。 Y = 0.299 × R + 0.587 × G + 0.114 × B Y=0.299\times R+0.587\times G+0.114\times B Y=0.299×R+0.587×G+0.114×B由于存在色偏,即现实场景中白色的点,在图像中也可能不是理想状态的白色,即 Y ≠ 255 Y\neq255 Y=255。但是可以肯定的是,白色的亮度为图像中的最大亮度。所以需要求出图像中的最大亮度 Y m a x Y_{max} Ymax和平均亮度 Y ˉ \bar{Y} Yˉ。
- 考虑对环境光照的适应性,寻找图像中所有亮度 ≤ 0.95 ⋅ Y m a x \le 0.95 \cdot Y_{max} ≤0.95⋅Ymax的像素点。将这些点假设为原始场景中的白色点,即设这些点所构成的像素点集为白色点集 { f ( i , j ) ∈ Ω w h i t e } \{f(i,j)\in\Omega_{white}\} {f(i,j)∈Ωwhite}。
- 计算白色点集 Ω w h i t e \Omega_{white} Ωwhite中所有像素的R、G、B三个颜色分量的均值 R ˉ \bar{R} Rˉ、 G ˉ \bar{G} Gˉ、 B ˉ \bar{B} Bˉ。
- 按照下式计算颜色均衡调整参数: k R = Y ˉ G ˉ k_R=\frac{\bar{Y}}{\bar{G}} kR=GˉYˉ k G = Y ˉ G ˉ k_G=\frac{\bar{Y}}{\bar{G}} kG=GˉYˉ k B = Y ˉ B ˉ k_B=\frac{\bar{Y}}{\bar{B}} kB=BˉYˉ
- 对整幅图像的R、G、B三个颜色分量,进行彩色平衡调整如下: R ∗ = k R ⋅ R R^*=k_R\cdot R R∗=kR⋅R G ∗ = k G ⋅ G G^*=k_G\cdot G G∗=kG⋅G B ∗ = k B ⋅ B B^*=k_B\cdot B B∗=kB⋅B
三、C++代码
int main()
{
cv::Mat img = cv::imread("LenaRGB.bmp");
int width= img.cols;
int height = img.rows;
cv::Mat Y = cv::Mat::zeros(height, width, CV_32FC1);
cv::Mat R = cv::Mat::zeros(height, width, CV_8UC1);
cv::Mat G = cv::Mat::zeros(height, width, CV_8UC1);
cv::Mat B = cv::Mat::zeros(height, width, CV_8UC1);
for (int row = 0; row < height; row++)
{
cv::Vec3b * current_ptr = img.ptr<cv::Vec3b>(row);
for (int col = 0; col < width; col++)
{
R.at<uchar>(row, col) = (*(current_ptr + col))[2];
G.at<uchar>(row, col) = (*(current_ptr + col))[1];
B.at<uchar>(row, col) = (*(current_ptr + col))[0];
Y.at<float>(row, col) = 0.299*(*(current_ptr + col))[2]+0.587*(*(current_ptr + col))[1]+
0.144*(*(current_ptr + col))[0];
}
}
//求取Ymax
double minValue, Y_max;
cv::minMaxLoc(Y, &minValue, &Y_max);
float Y_value=0.0, R_value=0.0, G_value=0.0, B_value=0.0;
int num = 0;
for (int row = 0; row < height; row++)
{
float * current_ptr = Y.ptr<float>(row);
for (int col = 0; col < width; col++)
{
if (*(current_ptr + col) >= 0.95*Y_max)
{
num += 1;
Y_value += Y.at<float>(row, col);
R_value += R.at<uchar>(row, col);
G_value += G.at<uchar>(row, col);
B_value += B.at<uchar>(row, col);
}
}
}
Y_value = Y_value / num;
R_value = R_value / num;
G_value = G_value / num;
B_value = B_value / num;
//调整系数
float k_R = Y_value / R_value;
float k_G = Y_value / G_value;
float k_B = Y_value / B_value;
cv::Mat output_image = cv::Mat::zeros(height, width, CV_32FC3);
for (int row = 0; row < height; row++)
{
cv::Vec3b * img_ptr = img.ptr<cv::Vec3b>(row);
cv::Vec3f * output_ptr = output_image.ptr<cv::Vec3f>(row);
for (int col = 0; col < width; col++)
{
(*(output_ptr + col))[2] = k_R*(*(img_ptr + col))[2];
(*(output_ptr + col))[1] = k_G * (*(img_ptr + col))[1];
(*(output_ptr + col))[0] = k_B * (*(img_ptr + col))[0];
}
}
cv::convertScaleAbs(output_image, output_image);
cv::imshow("input_image", img);
cv::imshow("output_image", output_image);
return 0;
}
四、实验结果
参考
1.数字图像处理基础.朱虹
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
已为社区贡献2条内容
所有评论(0)