原理
圖像白化(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.