OpenCV学习之路(七) 遍历图像中的像素

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dashujua/article/details/82283935

官方文档

若矩阵元素存储的是单通道像素,使用 C 或 C++ 无符号字符类型(uchar),那么像素可有 256 个不同的取值(0 ~255)。若图像采用三通道存储,颜色可有一千六百多万中取值(0 ~ 2^{24}-1),如此之多的颜色可能会对我们的算法性能造成影响。我们可以使用其中具有代表性的一部分来达到与原来近似的效果,即颜色空间缩减,做法是:将颜色值 0~9 映射为 0,10~19映射为10,等等。这样做可将颜色取值降低到 26 * 26 * 26 种。

由此,在处理较大的图像时,我们可以预先计算出所有可能的像素取值,将其存入一个 table 中,之后可以使用查表法来进行赋值(随机存取)。

//查找表设置
int divideWith = 10;
uchar table[256];
for(int i = 0; i < 256; ++i)
    table[i] = divideWith * (i / divideWith);


//遍历图像对每个像素赋新值
p[j] = table[p[j]];

1. LUT 函数:Look up table 操作。OpenCV官方提供的函数,原型为:

void LUT(InputArray src, InputArray lut, OutputArray dst);

(1)第一个参数,InputArray 类型的 src,输入图像。

(2)第二个参数,InputArray 类型的 lut,查找表。

(3)第三个参数,OutputArray 类型的 dst,输出图像。

使用方法如下:

//创建一个 Mat 类型的查找表
Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.ptr();
//uchar* p = lookUpTable.data;
for( int i = 0; i < 256; ++i)
    p[i] = table[i];

//调用 LUT函数, times 遍历次数
for(int i = 0; i < tiems; i++)
    LUT(I, lookUpTable, J);

2.自定义遍历像素的方法。

方法一     指针访问:C 操作符 [ ]

方法二     迭代器 iterator

方法三     动态地址计算

示例代码如下:

#include<opencv2/opencv.hpp>
#include<iostream>

using namespace cv;
using namespace std;

Mat& scanImageAndReduceC(Mat srcImg, const uchar* table);
Mat& scanImageAndReduceIterator(Mat srcImg, const uchar* table);
Mat& scanImageAndReduceRandomAccess(Mat srcImg, const uchar* table);

int main()
{
	Mat srcImage, dstImage;
	srcImage = imread("cat.jpg");
	imshow("原图", srcImage);

	int divideWith = 32;
	uchar table[256];
	for (int i = 0; i < 256; i++)
		table[i] = divideWith * (i / divideWith);

	const int times = 100;

	//1. 使用方法一遍历 100 次,取平均遍历时间
	double t = (double)getTickCount();
	for (int i = 0; i < times; i++)
	{
		Mat clone_src = srcImage.clone();
		dstImage = scanImageAndReduceC(clone_src, table);
	}
	t = 1000 * ((double)getTickCount() - t) / getTickFrequency();
	t /= times;
	cout << "Time of reducing with the C operator [] (averaged for "
		<< times << " runs): " << t << " milliseconds." << endl;

	//2. 使用方法二遍历 100 次,取平均遍历时间
	t = (double)getTickCount();
	for (int i = 0; i < times; i++)
	{
		Mat clone_src = srcImage.clone();
		dstImage = scanImageAndReduceIterator(clone_src, table);
	}
	t = 1000 * ((double)getTickCount() - t) / getTickFrequency();
	t /= times;
	cout << "Time of reducing with the Iterator (averaged for"
		<< times << "runs): " << t << " milliseconds." << endl;

	//3. 使用方法三遍历 100 次,取平均遍历时间
	t = (double)getTickCount();
	for (int i = 0; i < times; i++)
	{
		Mat clone_src = srcImage.clone();
		dstImage = scanImageAndReduceRandomAccess(clone_src, table);
	}
	t = 1000 * ((double)getTickCount() - t) / getTickFrequency();
	t /= times;
	cout << "Time of reducing with the on-the-fly address generation - at function (averaged for "
		<< times << " runs): " << t << " milliseconds." << endl;

	//4. 使用官方 LUT 函数遍历100次,取平均时间
	t = (double)getTickCount();
	Mat lookUpTable(1, 256, CV_8U);
	uchar* p = lookUpTable.ptr();
	for (int i = 0; i < 256; i++)
		p[i] = table[i];
	for (int i = 0; i < times; i++)
		LUT(srcImage, lookUpTable, dstImage);
	t = 1000 * ((double)getTickCount() - t) / getTickFrequency();
	t /= times;
	cout << "Time of reducing with the LUT function (averaged for "
		<< times << " runs): " << t << " milliseconds." << endl;
	imshow("LUT 处理图像", dstImage);

	waitKey(0);
	return 0;

}

//C操作符[ ] 遍历
Mat& scanImageAndReduceC(Mat srcImg, const uchar* table)
{
	//只对 uchar 类型的图像做处理
	CV_Assert(srcImg.depth() == CV_8U);

	int channels = srcImg.channels();
	int rows = srcImg.rows; //通道数 * 每个通道包含的列数
	int cols = channels * srcImg.cols;

	//判断是否是连续存储
	if (srcImg.isContinuous())
	{
		cols *= rows;
		rows = 1;
	}

	uchar* p;
	for(int i = 0; i < rows; i++)
	{ 
		p = srcImg.ptr<uchar>(i);
		for (int j = 0; j < cols; j++)
		{
			p[j] = table[p[j]];
		}
	}

	return srcImg;
}
Mat& scanImageAndReduceIterator(Mat srcImg, const uchar* table)
{
	CV_Assert(srcImg.depth() == CV_8U);

	int channels = srcImg.channels();

	switch (channels)
	{
	case 1:
	{
		MatIterator_<uchar> it, end;
		for (it = srcImg.begin<uchar>(), end = srcImg.end<uchar>(); it != end; it++)
		{
			*it = table[*it];
		}
		break;
	}
	case 3:
	{
		MatIterator_<Vec3b> it, end;
		for (it = srcImg.begin<Vec3b>(), end = srcImg.end<Vec3b>(); it != end; it++)
		{
			(*it)[0] = table[(*it)[0]];
			(*it)[1] = table[(*it)[1]];
			(*it)[2] = table[(*it)[2]];
		}
		break;
	}
	default:
		break;
	}

	return srcImg;
}
Mat& scanImageAndReduceRandomAccess(Mat srcImg, const uchar* table)
{
	CV_Assert(srcImg.depth() == CV_8U);

	int channels = srcImg.channels();
	int rows = srcImg.rows;
	int cols = srcImg.cols;

	switch (channels)
	{
	case 1:
	{
		for(int i = 0; i < rows; i++)
			for (int j = 0; j < cols; j++)
			{
				srcImg.at<uchar>(i, j) = table[srcImg.at<uchar>(i, j)];
			}
		break;
	}
	case 3:
	{
		Mat_<Vec3b> temp = srcImg;
		for(int i = 0; i < rows; i++)
			for (int j = 0; j < cols; j++)
			{
				temp(i, j)[0] = table[temp(i, j)[0]];
				temp(i, j)[1] = table[temp(i, j)[1]];
				temp(i, j)[2] = table[temp(i, j)[2]];
			}
		srcImg = temp;
		break;
	}
	default:
		break;
	}

	return srcImg;
}

执行结果如下图:

 

 

猜你喜欢

转载自blog.csdn.net/dashujua/article/details/82283935