持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第21天,点击查看活动详情
前言
图像直方图使用可用的像素强度值捕捉场景的渲染方式。通过分析图像上像素值的分布,可以修改并改进图像。在本节中,将介绍如何使用由查找表表示的简单映射函数来修改图像的像素值,查找表通常是根据直方图分布定义的。
应用查找表修改图像
查找表是一个简单的一对一(或多对一)函数,它定义了如何将像素值转换为新值。它是一个一维数组,在常规灰度图像的情况下,有 256 个条目:
1. 表中第 i 项给出对应灰度的新强度值:
newIntensity= lookup[oldIntensity];
2. OpenCV 中的 cv::LUT 函数将查找表应用于图像以生成新图像:
// 应用查找表将输入图像转换为单通道图像
static cv::Mat applyLookUp(const cv::Mat& image, const cv::Mat& lookup) {
cv::Mat result;
cv::LUT(image, lookup, result);
return result;
}
当查找表应用于图像时,它会产生一个新图像,其中像素强度值按照查找表的规则进行修改,例如,一个简单的转换如下:
// 创建图像反转表
int dim(256);
cv::Mat lut(1, &dim, CV_8U);
for (int i=0; i<256; i++) {
lut.at<uchar>(i) = 255 - i;
}
这种变换只是简单地反转像素强度,即强度 0 变为 255,1 变为 254,依此类推。在图像上应用该查找表可以得到原始图像的负片,结果如下所示:
如上图所示,与原始图像相比,图像像素强度被反转了。
拉伸直方图以提高图像对比度
可以通过定义修改原始图像直方图的查找表来提高图像的对比度。因此,可以通过拉伸直方图以生成具有较大对比度的图像。为此,我们使用百分比阈值来定义拉伸图像中黑白像素百分比,因此,我们必须找到最低 (imin) 和最高 (imax) 强度值,以便我们得到低于或高于指定百分位数所需的最小像素数。然后可以重新映射强度值,以便将 imin 值重新定位为强度 0 ,并为 imax 值重映射为 255,图像中的中间的像素强度 i 进行简单地线性重新映射:
255.0*(i-imin)/(imax-imin);
完整的图像拉伸方法如下:
// 使用具有最小值的 bin 拉伸图像
cv::Mat stretch(const cv::Mat& image, int minValue=0) {
// 计算直方图
cv::Mat hist = getHistogram(image);
// 找到直方图的左端
int imin = 0;
for (; imin<histSize[0]; imin++) {
// 忽略小于 minValue 的 bins
if (hist.at<float>(imin) > minValue) break;
}
// 找到直方图的右端
int imax = histSize[0] - 1;
for (; imax>=0; imax--) {
// 忽略小于 minValue 的 bins
if (hist.at<float>(imax) > minValue) break;
}
// 创建查找表
int dims[1] = {256};
cv::Mat lookup(1, dims, CV_8U);
for (int i=0; i<256; i++) {
if (i<imin) lookup.at<uchar>(i) = 0;
else if (i>imax) lookup.at<uchar>(i) = 255;
else lookup.at<uchar>(i) = cvRound(255.0*(i-imin)/(imax-imin));
}
// 应用查找表
cv::Mat result;
result = applyLookUp(image, lookup);
return result;
}
计算完之后调用 applyLookUp 方法, 此外,在实践中,不仅可以忽略值为 0 的 bin,还可以忽略计数小于给定值的 bin (此处定义为 minValue)。 调用方法如下:
cv::Mat streteched = h.stretch(image,0.01f);
得到的拉伸图像如下: