理论我就不写了,可以参考<一种动态阈值白平衡算法实现>,公式部分写的还比较详细,我就不赘述了。只是网上我搜到的这个算法的实现只有java和c++版本的,所以就自己写了python版本的,只是对于理论上说的把图像分块计算我还没实现,可能是我理解有问题,分块后的效果很差,所以就没实现。如果有人知道的话还请给我说一声,谢谢!
顺带说一声,直接用cv2的颜色转换函数将bgr转成YCrCb空间的话,在计算白点的判别式的时候总是出现全False的情况,后来改用公式自己计算就好了,可能是cv2的函数内部做过处理,没仔细研究。还有个地方,就是在计算‘参考白色点”中最大的10%的亮度(Y分量)值’的时候,理解这句话费了好大的劲才又仔细参考了c++的代码才实现。最终效果还是很好的!
全部代码:
def auto_whiteBalance(img):
b, g, r = cv2.split(img)
Y = 0.299 * r + 0.587 * g + 0.114 * b
Cr = 0.5 * r - 0.419 * g - 0.081 * b
Cb = -0.169 * r - 0.331 * g + 0.5 * b
Mr = np.mean(Cr)
Mb = np.mean(Cb)
Dr = np.var(Cr)
Db = np.var(Cb)
temp_arry = (np.abs(Cb - (Mb + Db * np.sign(Mb))) < 1.5 * Db) & (np.abs(Cr - (1.5 * Mr + Dr * np.sign(Mr))) < 1.5 * Dr)
RL = Y * temp_arry
# 选取候选白点数的最亮10%确定为最终白点,并选择其前10%中的最小亮度值
L_list = list(np.reshape(RL, (RL.shape[0] * RL.shape[1],)).astype(np.int))
hist_list = np.zeros(256)
min_val = 0
sum = 0
for val in L_list:
hist_list[val] += 1
for l_val in range(255,0,-1):
sum += hist_list[l_val]
if sum >= len(L_list) * 0.1:
min_val = l_val
break
# 取最亮的前10%为最终的白点
white_index = RL < min_val
RL[white_index] = 0
# 计算选取为白点的每个通道的增益
b[white_index] = 0
g[white_index] = 0
r[white_index] = 0
Y_max = np.max(RL)
b_gain = Y_max / (np.sum(b) / np.sum(b>0))
g_gain = Y_max / (np.sum(g) / np.sum(g>0))
r_gain = Y_max / (np.sum(r) / np.sum(r>0))
b, g, r = cv2.split(img)
b = b * b_gain
g = g * g_gain
r = r * r_gain
# 溢出处理
b[b > 255] = 255
g[g > 255] = 255
r[r > 255] = 255
res_img = cv2.merge((b,g,r))
return res_img
img_data = cv2.imread('1.jpg')
img = auto_whiteBalance(img_data)
cv2.imwrite('1_auto.jpg', img)
效果:
2019.7.1更新,添加C#版本,python c++ java c#就都有了,一家人嘛就应该整整齐齐 哈哈
private Bitmap AutoWriteBalance(string img_file)
{
Bitmap img = new Bitmap(img_file);
int pixelsize = img.Width * img.Height;
double[,,] YCbCr = new double[img.Width,img.Height,3];
double Mr = 0, Mb = 0, Ymax = 0;
for (int i = 0; i < img.Width; i++)
{
for (int j = 0; j < img.Height; j++)
{
double[] YCbCr_value = toYCbCr(img.GetPixel(i, j));
for (int k = 0; k < 3; k++)
YCbCr[i, j, k] = YCbCr_value[k];
Mr += YCbCr[i,j,2];
Mb += YCbCr[i,j,1];
Ymax = Math.Max(Ymax, YCbCr[i,j,0]);
}
}
Mr /= pixelsize;
Mb /= pixelsize;
double Dr = 0, Db = 0;
for (int i = 0; i < YCbCr.GetLength(0); i++)
{
for (int j = 0; j < YCbCr.GetLength(1); j++)
{
Db += Math.Pow((YCbCr[i, j, 1] - Mb),2);
Dr += Math.Pow((YCbCr[i, j, 2] - Mr),2);
}
}
Dr /= pixelsize;
Db /= pixelsize;
double[,] Y = new double[img.Width,img.Height];
double[] Yhistogram = new double[256];
double Ysum = 0;
for (int i = 0; i < img.Width; i++)
{
for (int j = 0; j < img.Height; j++)
{
int value = (Math.Abs(YCbCr[i,j,1] - (Mb + Db * Math.Sign(Mb))) < 1.5 * Db) && (Math.Abs(YCbCr[i,j,2]) - (1.5 * Mr + Dr * Math.Sign(Mr))) < 1.5 * Dr ? 1 : 0;
if (value <= 0)
continue;
double y = YCbCr[i, j, 0];
Y[i,j] = y;
Yhistogram[(int)Y[i,j]]++;
Ysum++;
}
}
double Yhistogramsum = 0;
double Ymin = 0;
for (int i = Yhistogram.Count() - 1; i >= 0; i--)
{
Yhistogramsum += Yhistogram[i];
if (Yhistogramsum > 0.1 * Ysum)
{
Ymin = i;
break;
}
}
double Raver = 0, Gaver = 0, Baver = 0;
double averSum = 0;
for (int i = 0; i < img.Width; i++)
{
for (int j = 0; j < img.Height; j++)
{
if (Y[i,j] > Ymin)
{
Color color = img.GetPixel(i, j);
int r = color.R;
int g = color.G;
int b = color.B;
Raver += r;
Gaver += g;
Baver += b;
averSum++;
}
}
}
Raver /= averSum;
Gaver /= averSum;
Baver /= averSum;
double Rgain = Ymax / Raver, Ggain = Ymax / Gaver, Bgain = Ymax / Baver;
for (int i = 0; i < img.Width; i++)
{
for (int j = 0; j < img.Height; j++)
{
Color color = img.GetPixel(i, j);
int r = ensureColor((int)Math.Floor(color.R * Rgain));
int g = ensureColor((int)Math.Floor(color.G * Ggain));
int b = ensureColor((int)Math.Floor(color.B * Bgain));
img.SetPixel(i, j, Color.FromArgb(r, g, b));
}
}
return img;
}
private double[] toYCbCr(System.Drawing.Color color)
{
int r = color.R;
int g = color.G;
int b = color.B;
double Y = 0.299 * r + 0.587 * g + 0.114 * b;
double Cb = 0.5 * r - 0.419 * g - 0.081 * b;
double Cr = -0.169 * r - 0.331 * g + 0.5 * b;
return new double[] { Y, Cb, Cr };
}
private int ensureColor(double color)
{
if (color < 0)
return 0;
if (color > 255)
return 255;
return (int)color;
}