/*
Automatic white balance
直方图法,去掉两端高亮度和低亮度的像素点(1%)后,将剩余像素再归一化到原始区间[min_pre, max_pre]
t' = (t - min_cur) / (max_cur - min_cur) * (max_pre - min_pre)
若BGR三个通道分别去掉首尾1%的像素后,再分别归一化到[min_pre, max_pre],颜色会出现问题
因为三通道去掉首尾后保留的像素区间不一样
比如对两通道边界处理了丢弃了255,而对另一通道没有处理,则只剩下该通道的颜色了
故对BGR三通道所有的值一起去掉首尾1%,用同区间处理BGR三通道
*/
// 图像预处理, 确定min_pre, max_pre, min_cur, max_cur
void image_process(Mat img, float s, int histSize, vector<float>& a)
{
vector<Mat> channels;
split(img, channels);
Mat A1, A2;
vconcat(channels[0], channels[1], A1);
vconcat(A1, channels[2], A2);
Mat hist;
float range[] = { 0,256 };
const float *histRanges = { range };
float interval = 256.0f / histSize;
calcHist(&A2, 1, 0, Mat(), hist, 1, &histSize, &histRanges, true, false);
// cout << hist.type() << endl; // CV_32F
int image_size = A2.rows * A2.cols;
vector<float> Hist;
float sum = 0.0f;
for (int i = 0; i < hist.rows; ++i) // 直方图区间的累计和
{
sum += hist.at<float>(i);
Hist.push_back(sum / image_size);
}
double min_pre_value, max_pre_value;
minMaxIdx(A2, &min_pre_value, &max_pre_value);
int lmin = 0;
int lmax = histSize - 1;
while (lmin < histSize) // 去除最小的s范围内的元素
{
if (Hist[lmin] >= s)
{
break;
}
lmin += 1;
}
while (lmax > 0) // 去除最大的s范围内的元素
{
if (Hist[lmax] <= (1 - s))
{
break;
}
lmax -= 1;
}
float min_cur_value, max_cur_value;
min_cur_value = lmin * interval;
max_cur_value = lmax * interval;
a = { min_cur_value, max_cur_value, (float)min_pre_value, (float)max_pre_value };
}
// 按公式t' = (t - min_cur) / (max_cur - min_cur) * (max_pre - min_pre)修改像素值
void awb(Mat& src, Mat& dst, vector<float>& a)
{
dst = src.clone();
for (int i = 0; i < dst.rows; ++i)
{
for (int j = 0; j < dst.cols; ++j)
{
for (int k = 0; k < 3; k++)
{
if (dst.at<Vec3b>(i, j)[k] >= (int)round(a[0]) && dst.at<Vec3b>(i, j)[k] <= (int)round(a[1]))
{
dst.at<Vec3b>(i, j)[k] = (int)round((dst.at<Vec3b>(i, j)[k] - a[0]) * (a[3] - a[2]) / (a[1] - a[0]) + a[2]);
}
else if (dst.at<Vec3b>(i, j)[k] < (int)round(a[0]))
{
dst.at<Vec3b>(i, j)[k] = (int)round(a[2]);
}
else
{
dst.at<Vec3b>(i, j)[k] = (int)round(a[3]);
}
}
}
}
}
int main()
{
Mat srcImg = imread("pic.jpg");
namedWindow("srcImg", CV_WINDOW_NORMAL);
imshow("srcImg", srcImg);
vector<float> a;
Mat awbImg;
image_process(srcImg, 0.01f, 2000, a);
awb(srcImg, awbImg, a);
namedWindow("awbImg", CV_WINDOW_NORMAL);
imshow("awbImg", awbImg);
waitKey(0);
return 0;
}
实验结果: