opencv2对查找表的运用

本文深入探讨了OpenCV中查找表(LUT)的作用及其在图像处理中的应用,通过两个具体例子详细解释了如何创建和使用LUT来实现图像像素值的反转与移除像素值较小的像素点。文章最后提供了完整代码示例,帮助读者更好地理解和实践LUT在实际项目中的应用。

在opencv中查找表的作用就是将相应的灰度值通过一个写好的查找表进行一个灰度值的替换,比如说,原图像的一个像素值为0,现在定义一个查找表,该查找表的作用是将每个灰度值变为1,那么运用LUT对应关系后,原图像中的0像素值被修改为1

在《opencv2计算机视觉编程手册》一书中在这一节的扩展阅读中加了一个直方图的图像,对于初学者来说,这个直方图可以先不管,作者引入直方图的意思只是想更直观的显示修改后的结果,初学者如果对直方图过于纠结将导致误解,只要记住LUT的作用就是通过查找表更改像素值即可

下面用书上的两个例子来说明一下:

第一个:建立一个将原图像像素为i的像素值替换为255-i

首先建立一个查找表:

int dim = 256;
 Mat lut(1, &dim, CV_8U);
 for (int i = 0; i < 256; i++)
 {
  lut.at<uchar>(i) = 255 - i;
 }

然后就是建立一个对应关系,由于建立的是一个8位的查找表,所以需要LUT输入的是一个8位的图像,这样才能建立对应关系,所以我在下面的函数中先将一个多通道的图像分解到BGR的三个单通道的图像上

Mat LUTResult(const Mat &InImage, const Mat &LutImage, int channelsnum)
{
 Mat OutImage;

 vector<Mat>channelsvector;
 if (InImage.channels() == 3)
 {
  split(InImage, channelsvector);

  if (channelsnum == 0)
  {
   cvtColor(InImage, OutImage, CV_BGR2GRAY);
  }

  if (channelsnum == 1)
  {
   OutImage = channelsvector[0];
  }

  if (channelsnum == 2)
  {
   OutImage = channelsvector[1];
  }

  if (channelsnum == 3)
  {
   OutImage = channelsvector[2];
  }
 }
 else
  OutImage = InImage;

 LUT(OutImage, LutImage, OutImage);

 return OutImage;
}

完整的代码如下:

Mat SearchMat_opposite()
{
	//这个查找表的功能是反转像素值
	/*创建的是一个8位的查找表
	查找表的作用就是直接对图像的相应的灰度值进行替换,所以对于一个灰度级的图像来说,只有256项*/
	int dim = 256;
	Mat lut(1, &dim, CV_8U);
	for (int i = 0; i < 256; i++)
	{
		lut.at<uchar>(i) = 255 - i;
	}

	return lut;
}

Mat LUTResult(const Mat &InImage, const Mat &LutImage, int channelsnum)
{
	Mat OutImage;

	vector<Mat>channelsvector;
	if (InImage.channels() == 3)
	{
		split(InImage, channelsvector);

		if (channelsnum == 0)
		{
			cvtColor(InImage, OutImage, CV_BGR2GRAY);
		}

		if (channelsnum == 1)
		{
			OutImage = channelsvector[0];
		}

		if (channelsnum == 2)
		{
			OutImage = channelsvector[1];
		}

		if (channelsnum == 3)
		{
			OutImage = channelsvector[2];
		}
	}
	else
		OutImage = InImage;

	LUT(OutImage, LutImage, OutImage);

	return OutImage;
}

int main()
{
	Mat img;
	img = imread("F:\\group.jpg");
	namedWindow("img");
	imshow("img", img);

	Mat lut = SearchMat_opposite();
	Mat Mat_B = LUTResult(img, lut, 1);
	namedWindow("Mat_B");
	imshow("Mat_B", Mat_B);

	waitKey(0);

	return 0;
}

运行结果如下:


第二个例子,这个查找表的功能是移除对应的像素值个数小的像素点

首先是创建一个查找表

//先获取直方图的数据
	Mat hist = ComterGrayHistogram(InImage, false, false, Scalar(0, 0, 0), 0);

	int min = 0;
	int max = 256 - 1;

	for (; min < 256; min++)          //找到左端
	{
		if (hist.at<float>(min)>minValue)
			break;
	}

	for (; max >= 0; max--)     //找到右端
	{
		if (hist.at<float>(max) > minValue)
			break;
	}

	//创建一个8位的查找表
	int dim(256);
	Mat lookup(1, &dim, CV_8U);
	//将每个像素值替换为新的值
	for (int i = 0; i < 256; i++)         //填充查找表
	{
		if (i < min)
			lookup.at<uchar>(i) = 0;
		else if (i > max)
			lookup.at<uchar>(i) = 255;
		else
			lookup.at<uchar>(i) = static_cast<uchar>(255.0*(i - min) / (max - min));
	}

由于这个查找表先要计算出直方图,所以把上一节中的类加上,使用的LUT函数和上面的例子相同,我写的是通用版的

完整代码如下:

#include<opencv2/highgui/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/imgproc/types_c.h>
#include<opencv2/core/core.hpp>
#include<iostream>
#include<vector>

using namespace cv;
using namespace std;

class HistgramShow
{
private:
	Mat ShowHisImage;
	//通道数
	int channels[1];	
	//项的数量
	int histSize[1];	
	//像素值的范围
	const float *ranges[1];
	//像素的最小及最大值,为了给hranges一个变量的分配的空间
	float hranges[2];

public:
	HistgramShow()
	{
		//计算0号通道
		channels[0] = 0;
		histSize[0] = 256;
		ranges[0] = hranges;
		//存放最小范围
		hranges[0] = 0;
		//存放最大范围
		hranges[1] = 255;
	}

	//这个函数仅仅是封装了一下显示函数
	void show(String name, Mat &img);
	MatND ComterGrayHistogram(const Mat &InImage, bool isdisplay, bool isshow, const Scalar &color, int channelsnum);

	//这个函数用来得到私有里面的ShowHisImage
	Mat getShowHisImage()
	{
		return ShowHisImage;
	}
};

void HistgramShow::show(String name, Mat &img)
{
	namedWindow(name);
	imshow(name, img);
}

MatND HistgramShow::ComterGrayHistogram(const Mat &InImage, bool isdisplay, bool isshow, const Scalar &color, int channelsnum)
{
	Mat GrayImage;
	vector<Mat>channelsvector;
	if (InImage.channels() == 3)
	{
		split(InImage, channelsvector);

		if (channelsnum == 0)
		{
			GrayImage = channelsvector[0];
		}

		if (channelsnum == 1)
		{
			GrayImage = channelsvector[1];
		}

		if (channelsnum == 2)
		{
			GrayImage = channelsvector[2];
		}
	}
	else
	{
		GrayImage = InImage;
	}

	MatND hist;
	calcHist(&GrayImage, 1, channels, Mat(), hist, 1, histSize, ranges);

	if (isdisplay == 1)
	{
		for (int i = 0; i < 256; i++)
		{
			cout << "Value" << i << "=" << hist.at<float>(i) << endl;
		}
	}

	/*开始直观的显示出直方图,如果将isshow置零,将不显示*/
	if (isshow == 1)
	{
		double minVal;
		double maxVal;

		//找到数组的最大值和最小值,目的是需要用最大和最小值将得到的对应像素的像素点个数归一化,这里的归一化是都归在0~255之间
		//这个处理仅仅是为了显示起来方便,如果不规定一个统一的最大值,那么显示的尺寸就要就会随着最大值在不断的变换
		minMaxLoc(hist, &minVal, &maxVal, 0, 0);

		//定义显示直方图的Mat型,包括显示窗口的长度和宽度
		//这里就把最大值限制在histSize[0]上了
		Mat showhist(histSize[0], histSize[0], CV_8UC3, Scalar(255, 255, 255));

		//将最高点设置为横坐标总长的90%,最大值就不会超过256*0.9,这样在一个正好是256*256的图像中就不会有值刚好在边界上,增强了可视化
		int hpt = static_cast<int>(0.9*histSize[0]);

		for (int x = 0; x < histSize[0]; x++)       //绘制直方图
		{
			//得到每个像素点对应的像素的个数
			float binVal = hist.at<float>(x);
			int intensity = static_cast<int>(binVal*hpt / maxVal);

			//由于图像的y轴是竖直向下的,所以连线的时候起始坐标是(x, histSize[0]),而不是(0, 0))
			line(showhist, Point(x, histSize[0]), Point(x, histSize[0] - intensity), color);
		}

		ShowHisImage = showhist;
	}

	return hist;
}

HistgramShow P;

Mat SearchMat_contrast(const Mat &InImage, int minValue)
{
	//先获取直方图的数据
	Mat hist = P.ComterGrayHistogram(InImage, false, false, Scalar(0, 0, 0), 0);

	int min = 0;
	int max = 256 - 1;

	for (; min < 256; min++)          //找到左端
	{
		if (hist.at<float>(min)>minValue)
			break;
	}

	for (; max >= 0; max--)     //找到右端
	{
		if (hist.at<float>(max) > minValue)
			break;
	}

	//创建一个8位的查找表
	int dim(256);
	Mat lookup(1, &dim, CV_8U);
	//将每个像素值替换为新的值
	for (int i = 0; i < 256; i++)         //填充查找表
	{
		if (i < min)
			lookup.at<uchar>(i) = 0;
		else if (i > max)
			lookup.at<uchar>(i) = 255;
		else
			lookup.at<uchar>(i) = static_cast<uchar>(255.0*(i - min) / (max - min));
	}

	return lookup;
}

Mat LUTResult(const Mat &InImage, const Mat &LutImage, int channelsnum)
{
	Mat OutImage;

	vector<Mat>channelsvector;
	if (InImage.channels() == 3)
	{
		split(InImage, channelsvector);

		if (channelsnum == 0)
		{
			cvtColor(InImage, OutImage, CV_BGR2GRAY);
		}

		if (channelsnum == 1)
		{
			OutImage = channelsvector[0];
		}

		if (channelsnum == 2)
		{
			OutImage = channelsvector[1];
		}

		if (channelsnum == 3)
		{
			OutImage = channelsvector[2];
		}
	}
	else
		OutImage = InImage;

	LUT(OutImage, LutImage, OutImage);

	return OutImage;
}

int main()
{
	Mat img;
	img = imread("F:\\group.jpg");
	namedWindow("img");
	imshow("img", img);

	Mat lut = SearchMat_contrast(img, 100);
	Mat Mat_B = LUTResult(img, lut, 1);
	namedWindow("Mat_B");
	imshow("Mat_B", Mat_B);

	waitKey(0);

	return 0;
}


运行结果:





评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值