您的位置:首页 > 运维架构

opencv中访问图像像素方式

2015-03-20 22:37 169 查看
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;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: