OpenCV之查找表与直方图拉伸

时间:2022-02-07 23:35:51
  1. 本文代码使用OpenCV版本:2.4.13
  2. 本文代码在Win10+Visual Studio 2013 Update 3下测试通过

上一篇博客《OpenCV之直方图拉伸》讲述了直方图拉伸的原理及实现,其中得到了如下公式:

g=0,(ggrayMin)255(grayMaxgrayMin),255,if g<grayMinif grayMinggrayMaxif g>grayMax

其中 g代表灰度, grayMingrayMax代表某一较小的灰度和某一较大的灰度,它们的取值范围都是 [0, 255]。对于每一个像素,上一篇博客都根据此公式计算其新的灰度值。但是我们不难发现,这个公式实际上是一个多对一的映射函数,它将一个灰度值固定映射为一个新的值。所以,我们实际上无须对每个像素都进行一次计算,只要预先计算出每个灰度所映射的新值,保存在一张表中,然后对每个像素进行查表即可。

推广来说,灰度图像的某些变换,实际上就相当于一个多对一或者一对一的映射函数,它将灰度值从[0, 255]映射到其他区间。此时我们就可以预先计算出此映射表,然后通过查表进行逐像素的变换,这个映射表就称为查找表(Look Up Table)。

举例来说,灰度图像到黑白图像的变换,就是一个多对一的映射函数,其将[0, 127]映射到0,将[128, 255]映射到1。还有,如果我们将灰度值g映射到255-g,那么这是一个一对一的映射函数,其实现的效果是图像反色。这些变换都可以通过查找表来实现。

LUT

OpenCV实现了查找表功能,接口如下所示:

void LUT(InputArray src, InputArray lut, OutputArray dst, int interpolation=0);

其参数的涵义如下:

  • src,输入图像,其元素必须是8位的。
  • lut,输入的查找表,其必须含有256个元素。如果输入图像是多通道的,此查找表可以只有一个通道,这个通道应用于输入图像的所有通道;也可以有和输入图像通道数相同的通道,此时通道之间分别对应。
  • dst,变换后的输出图像,其大小与通道数与输入图像相同;深度与lut相同。注意,这就说明lut的深度并不一定是CV_8UCV_8S,其映射的范围不局限于[0, 255]

其映射过程如下所示:

dst(I)lut(src(I)+d)

其中,

d={0128if src has depth CV_8Uif src has depth CV_8S

用LUT实现直方图拉伸

下面我们使用LUT来实现直方图拉伸,以提高上一个博客中代码的效率:

// 直方图拉伸
// grayImage - 要拉伸的单通道灰度图像
// hist - grayImage的直方图
// minValue - 忽略像数个数小于此值的灰度级
void histStretch(cv::Mat& grayImage, const cv::Mat& hist, int minValue)
{
    CV_Assert(!grayImage.empty() && grayImage.channels() == 1 && grayImage.depth() == CV_8U);
    CV_Assert(!hist.empty() && hist.rows == 256 && hist.cols == 1 && hist.depth() == CV_32F);
    CV_Assert(minValue >= 0);

    // 求左边界
    uchar grayMin = 0;
    for (int i = 0; i < hist.rows; ++i)
    {
        if (hist.at<float>(i, 0) > minValue)
        {
            grayMin = static_cast<uchar>(i);
            break;
        }
    }

    // 求右边界
    uchar grayMax = 0;
    for (int i = hist.rows - 1; i >= 0; --i)
    {
        if (hist.at<float>(i, 0) > minValue)
        {
            grayMax = static_cast<uchar>(i);
            break;
        }
    }

    if (grayMin >= grayMax)
    {
        return;
    }

    // 创建查找表
    cv::Mat lut(1, 256, CV_8UC1);
    for (int g = 0; g < 256; ++g)
    {
        if (g < static_cast<int>(grayMin))
        {
            lut.at<uchar>(0, g) = 0;
        }
        else if (g > static_cast<int>(grayMax))
        {
            lut.at<uchar>(0, g) = 255;
        }
        else
        {
            lut.at<uchar>(0, g) = static_cast<uchar>((g - grayMin) * 255.0 / (grayMax - grayMin));
        }
    }

    // 应用查找表
    cv::LUT(grayImage, lut, grayImage);
}

参考链接

  1. 《OpenCV 2 视觉编程手册》4.3节

(转载请保留作者信息)