OpenCV应用查找表修改图像

212 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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 变为 2551 变为 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 方法, 此外,在实践中,不仅可以忽略值为 0bin,还可以忽略计数小于给定值的 bin (此处定义为 minValue)。 调用方法如下:

cv::Mat streteched = h.stretch(image,0.01f);

得到的拉伸图像如下:

拉伸后图像