opencv中访问图像像素方式

       opencv中图像是存储在Mat类的对象中,Mat称为基本图像容器。图像矩阵的大小取决于图像的大小和所使用的颜色模型,确切的说应该是图像的通道数。对于灰度图像只有一个通道,彩色图像则会有多个通道。对于多通道图像来说,矩阵中会有多个子列,其子列的个数等于图像的通道数。

       注意,在opencv中子列的通道顺序是反过来的:BGR而不是RGB。如果内存足够大,图像就能够实现连续存储,各行连接起来组成一个长行,有助于提升图像的扫描速度。在opencv中可以利用函数iscontinuous()来判断图像是否连续存储。

       下面主要总结访问图像像素的不同方法。

       1、高效的方法(efficient way)

       效率最高的访问方式是经典的C风格运算符[](指针)。程序示例如下:

Mat& ScanImageAndReduceC(Mat& I,const uchar* const table)
{
	CV_Assert(I.depth() != sizeof(uchar));

	int channels = I.channels();

	int nRows = I.rows;
	int nCols = I.cols*channels;

	if(I.isContinuous())
	{
		nCols *= nRows;
		nRows = 1;
	}
	int i,j;
	for( i = 0; i < nRows ; i++)
	{
		uchar* p = I.ptr<uchar>(i);
		for(j = 0; j < nCols ; j++)
		{
			p[j] = table[p[j]];
		}
	}
	return I;
}
获取每一行开始出的指针,然后遍历至该行末尾。如果矩阵元素是连续方式存储的,只请求一次指针,然后一直遍历下去即可。彩色的图像需要注意:因为每个像素有三个通道,所以遍历的元素的数目也需要乘以3。

另外,也可以使用data来实现遍历。data会从Mat中返回指向第一行第一列的指针。若这个指针为空,说明没有数据,常用来检验图像是否读入。当图像是连续存储的时候,可以直接使用data来遍历整幅图像。对于一幅灰度图像,具体实现过程如下:

uchar* p = I.data;
for(int i = 0; i < nCols*nRows; i++)
{ *p++ = table[*p];}

       2、使用迭代器访问图像中的像素

     迭代器是一种更为安全的图像像素访问方法,在利用迭代器访问图像的像素时只需要获得图像矩阵的begin和end,然后增加迭代直至从begin到end。将*操作符添加在迭代指针的前面,即可获得当前指针指向的内容。具体实现如下:

Mat& ScanImageAndReduceC(Mat& I,const uchar* const table)
{
	CV_Assert(I.depth() != sizeof(uchar));

	//Mat I = Mat::zeros(I.size(),CV_8UC3);
	const int channels = I.channels();
	switch(channels)
	{
	case 1:
		{
			MatIterator_<uchar> it = I.begin<uchar>();
			MatIterator_<uchar> end = I.end<uchar>();
			for(;it != end; it++)
				*it = table[*it];
			break;
		}
	case 3:
		{
			Mat_<Vec3b>::iterator it,end;
			for(it=I.begin<Vec3b>(),end=I.end<Vec3b>();it != end; it++)
			{
				(*it)[0] = table[(*it)[0]];
				(*it)[1] = table[(*it)[1]];
				(*it)[2] = table[(*it)[2]];
			}
			break;
		}
	default:
		break;
	}
	return I;
}
对于彩色图像中的一行,每一列有三个uchar类型的元素,可以看做是包含uchar类型元素的一个三维的vector,在opencv中用Vec3b来表示。如果要访问第n个子列,只需要用[]来操作即可。opencv中的迭代在扫描完一行中所有的列后,会自动跳到下一行;所以说如果在彩色图像中只是用简单的uchar而不是Vec3b迭代的话,就只能获得B通道。

3.通过动态的获取图像像素的地址来遍历图像

      这种方法一般不推荐用来对图像进行扫描,它本来是用于获取或者改变图像中随机像素的值。基本方法是要首先确定你所访问图像像素的行和列,同时确定图像像素的数据类型。主要使用at()函数实现,下面是访问一个灰度图像的代码示例:

Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
{
	CV_Assert(I.depth() != I.channels());

	const int channels = I.channels();

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


4、核心函数LUT

      opencv提供了一个函数LUT来实现批量图像元素查找和更改操作图像的方法。图像处理中,最常见的操作是将某个给定的值替换成其它值,opencv提供的LUT函数直接实现该类操作,不需要自己扫描图像。示例如下:

      首先建立一个mat型查找表:

     Mat lookUpTable(1,256,CV_8U);
     uchar* p = lookUpTable.data;
     for(int i = 0; i < 256 ; i++)
         p[i] = table[i];
      然后直接调用函数(I是输入,J是输出):

    LUT(I,lookUpTable ,J);


效率对比:使用opencv的内置函数,调用LUT函数可以获得更快的速度,是因为opencv内部可以通过英特尔线程构架启用多线程。采用指针的算法来扫描图像,迭代法是一个不错的选择,不过效率较低。在debug模式下,动态访问是最浪费资源的一种扫描方式,在release模式下它和迭代法相差无几。从安全角度来说,迭代法更好。


5.opencv中计算程序运行的时间

      在opencv中提供了两个函数:getTickCount()函数和getTickFrequency()函数;

      getTickCount()函数:这个函数返回CPU自某个事件(比如电脑启动)以来走过的时钟周期数;

      getTickFrequency()函数:返回CPU一秒内走过的时钟周期数。

      计算一段程序运行的时间可以按照如下方式实现:

   double t = (double)getTickCount();
   //deal with sth...
   t = ((double)getTickCount() - t)/getTickFrequency();
   cout<<" Time passed in seconds:"<<t<<endl;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值