- 本文代码使用OpenCV版本:2.4.13
- 本文代码在Win10+Visual Studio 2013 Update 3下测试通过
上一篇博客《OpenCV之直方图拉伸》讲述了直方图拉伸的原理及实现,其中得到了如下公式:
其中
g
代表灰度,
grayMin
和
grayMax
代表某一较小的灰度和某一较大的灰度,它们的取值范围都是
[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_8U
或CV_8S
,其映射的范围不局限于[0, 255]
。
其映射过程如下所示:
其中,
用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);
}
参考链接
- 《OpenCV 2 视觉编程手册》4.3节