转载自:https://blog.csdn.net/qq_38906523/article/details/79853984
我们从逐像素处理来开始讲诉图像预处理。我们将代表原始图像的二维矩阵标记为,其中的代表第行和第列的像素值,代表该像素点的灰度强度。逐像素处理返回的就是和尺寸大小一样包含的二维矩阵。
Whitening(白化处理)
一幅图像最终成像会受环境照明强度、物体反射、拍摄相机等多因素的影响。为了能获得图像中包含的那些不受外界影响的恒定信息,我们需要对图像进行白化处理。(图像白化(whitening)可用于对过度曝光或低曝光的图片进行处理,处理的方式就是改变图像的平均像素值为 0 ,改变图像的方差为单位方差 1。)一般为了去除这些因素的影响,我们将它的像素值转化成零均值和单位方差。所以我们首先计算原始灰度图像的像素平均值和方差值。
接下来我们将使用和来对原始灰度图像的每个像素值进行转化:
对于彩色图像,我们需要在三个通道分别计算和,然后根据公式(3)分别进行像素转化。
当然这种简单的转换可能会妨碍之后的图像处理,因为这些去除的信息有可能就是我们接下来需要的。
下面我们通过一段代码来展示一下白化处理的效果。
OpenCV 实现
用 OpenCV 的内置函数计算均值和方差,然后对遍历每个像素值并对每个像素做变换。这里需要注意的是变换后的像素值肯定是有一部分会是负值(小于均值的那部分),我们需要把变换后的像素值重新映射到 [0, 255] 的范围内。因为 OpenCV 中的 normalize 函数无法实现这种任意范围内的映射,我们需要自己去实现这类映射。我们需要找出变换后图像中的最小 min 和最大像素值 max, 假设需要映射的范围为 [a, b]。 该映射可用函数(b-a)*(xij-min)/(max-min) 实现。关键部分实现代码如下所示:
void whitening() {
Mat image = imread("test.jpg",IMREAD_GRAYSCALE);
double mean, stddev;
Mat temp_m, temp_sd;
meanStdDev(image, temp_m, temp_sd);
mean = temp_m.at<double>(0, 0)/255.0;
stddev = temp_sd.at<double>(0, 0)/255.0;
Mat temp_image( image.rows, image.cols, CV_64F);
for (int i = 0; i < image.rows; i++)
for (int j = 0; j < image.cols; j++) {
double pixelVal = image.at<uchar>(i, j)/255.0;
double temp = (pixelVal - mean) / stddev;
temp_image.at<double>(i, j) = temp;
}
double max, min;
minMaxLoc(temp_image, &min, &max);
for (int i = 0; i < image.rows; i++)
for (int j = 0; j < image.cols; j++) {
double pixelVal = temp_image.at<double>(i, j);
image.at<uchar>(i, j) = (uchar)round(255.0 * (pixelVal - min) / (max - min));
}
imshow("New Image", image);
waitKey(0);
}
结果
如下图所示,可以看到对左边过度曝光的图片经过白化处理后图片的曝光程度减弱了。再看图像直方图,白化变换似乎是对原来的直方图做了一个横向的拉伸,使得像素值的分布更加的均匀,而不是集中在一个有限的(高曝光的)范围内。