原理
图像白化(whitening)可用于对过度曝光或低曝光的图片进行处理,处理的方式就是改变图像的平均像素值为 0 ,改变图像的方差为单位方差 1。我们需要先计算原图像的均值和方差,然后对原图像的每个像素值做变换。假设图像 P 有 I 行 J 列,每个像素的值为 pij, 均值和方差的计算公式如下。
变换后新图像的每个像素值 xij 为
OpenCV 实现
用 OpenCV 的内置函数计算均值和方差,然后对遍历每个像素值并对每个像素做变换。这里需要注意的是变换后的像素值肯定是有一部分会是负值(小于均值的那部分),我们需要把变换后的像素值重新映射到 [0, 255] 的范围内。因为 OpenCV 中的 normalize 函数无法实现这种任意范围内的映射,我们需要自己去实现这类映射。我们需要找出变换后图像中的最小 min 和最大像素值 max, 假设需要映射的范围为 [a, b]。 该映射可用函数(b-a)*(xij-min)/(max-min) 实现。关键部分实现代码如下所示:
1 void whitening() { 2 Mat image = imread("test.jpg",IMREAD_GRAYSCALE); 3 4 double mean, stddev; 5 Mat temp_m, temp_sd; 6 meanStdDev(image, temp_m, temp_sd); 7 mean = temp_m.at<double>(0, 0)/255.0; 8 stddev = temp_sd.at<double>(0, 0)/255.0; 9 Mat temp_image( image.rows, image.cols, CV_64F); 10 for (int i = 0; i < image.rows; i++) 11 for (int j = 0; j < image.cols; j++) { 12 double pixelVal = image.at<uchar>(i, j)/255.0; 13 double temp = (pixelVal - mean) / stddev; 14 temp_image.at<double>(i, j) = temp; 15 } 16 17 double max, min; 18 minMaxLoc(temp_image, &min, &max); 19 for (int i = 0; i < image.rows; i++) 20 for (int j = 0; j < image.cols; j++) { 21 double pixelVal = temp_image.at<double>(i, j); 22 image.at<uchar>(i, j) = (uchar)round(255.0 * (pixelVal - min) / (max - min)); 23 } 24 25 imshow("New Image", image); 26 waitKey(0); 27 }
结果
如下图所示,可以看到对左边过度曝光的图片经过白化处理后图片的曝光程度减弱了。再看图像直方图,白化变换似乎是对原来的直方图做了一个横向的拉伸,使得像素值的分布更加的均匀,而不是集中在一个有限的(高曝光的)范围内。
参考
[1] Computer vision: models, learning and inference, Simon J. D. Prince.