`

【OpenCV】访问Mat图像中每个像素的值

 
阅读更多

今天百度搜资料还搜到了自己的。。。《访问图像中每个像素的值 》,这是之前写的了,用的也是2.0的风格IplImage*格式,不太适用后来Mat的格式,特此重写一篇。

以下例子源自《The OpenCV Tutorials --Release 2.4.2》2.2 How to scan images, lookup tables and time measurement with OpenCV

 

图像容器Mat

还是先看Mat的存储形式。Mat和Matlab里的数组格式有点像,但一般是二维向量,如果是灰度图,一般存放<uchar> 类型;如果是RGB彩色图,存放<Vec3b> 类型。
单通道灰度图数据存放格式:

多通道的图像中,每列并列存放通道数量的子列,如RGB三通道彩色图:

注意通道的顺序反转了:BGR。通常情况内存足够大的话图像的每一行是连续存放的,也就是在内存上图像的所有数据存放成一行,这中情况在访问时可以提供很大方便。可以用isContinuous() 函数来判断图像数组是否为连续的。

访问图像中的像素

 

高效的方法:C操作符[ ]

最快的是直接用C风格的内存访问操作符[]来访问:
Mat& ScanImageAndReduceC(Mat& I, const uchar* const table)
{
	// accept only char type matrices
	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;
	uchar* p;
	for( i = 0; i < nRows; ++i)
	{
		p = I.ptr<uchar>(i);
		for ( j = 0; j < nCols; ++j)
		{
			p[j] = table[p[j]];
		}
	}
	return I;
}
注意:书中这段代码是有问题的 ,前面写成了
int nRows = I.rows * channels;
int nCols = I.cols;
一般情况 isContinous为true,运行不会出错,但你可以注释掉那个if,会有访问越界的问题。
这种访问形式就是在每行定义一个指针,然后在内存上直接连续访问。如果整个数组在内存上都是连续存放的,那么只需要定义一个指针就可以访问所有的数据!如单通道的灰度图访问方式如下:
uchar* p = I.data;
for( unsigned int i =0; i < ncol*nrows; ++i)
	*p++ = table[*p];

安全的方法:迭代器iterator

相比用指针直接访问可能出现越界问题,迭代器绝对是非常安全的方法:
Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table)
{
	// accept only char type matrices
	CV_Assert(I.depth() != sizeof(uchar));
	const int channels = I.channels();
	switch(channels)
	{
	case 1:
		{
			MatIterator_<uchar> it, end;
			for( it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it)
				*it = table[*it];
			break;
		}
	case 3:
		{
			MatIterator_<Vec3b> 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]];
			}
		}
	}
	return I;
}
这里我们只定义了一个迭代器,用了一个for循环,这是因为在OpenCV里迭代器会访问每一列然后自动跳到下一行,不用管在内存上是否isContinous。另外要注意的是在三通道图像中我们定义的是 <Vec3b>格式的迭代器,如果定义成uchar,则只能访问到B即蓝色通道的值。
这种方式虽然安全,但是挺慢的,一会儿就知道了。

更慢的方法:动态地址计算

这种方法在需要连续扫描所有点的应用时并不推荐,因为它更实用与随机访问。这种方法最基本的用途是访问任意的某一行某一列:
Mat& ScanImageAndReduceRandomAccess(Mat& I, const uchar* const table)
{
	// accept only char type matrices
	CV_Assert(I.depth() != sizeof(uchar));
	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;
		}
	}
	return I;
}
因为这种方法是为随机访问设计的,所以真的是奇慢无比。。。

减小颜色空间 color space reduction

现在来介绍下上述函数对每个元素的操作,也就是用table更改像素值。这里其实是做了个减小颜色空间的操作,这在一些识别之类的应用中会大大降低运算复杂度。类如uchar类型的三通道图像,每个通道取值可以是0~255,于是就有 256*256个不同的值。我们可以通过定义:
0~9 范围的像素值为 0
10~19 范围的像素值 为 10
20~29 范围的像素值为 20
。。。。。。
着这样的操作将颜色取值降低为 26*26*26 种情况。这个操作可以用一个简单的公式:

来实现,因为C++中int类型除法操作会自动截余。 类如 Iold=14; Inew=(Iold/10)*10=(14/10)*10=1*10=10;
在处理图像像素时,每个像素需要进行一遍上述计算也需要一定的时间花销。但我们注意到其实只有 0~255 种像素,即只有256种情况。进一步可以把256种计算好的结果提前存在表中 table 中,这样每种情况不需计算直接从 table 中取结果即可。
int divideWith=10; 
uchar table[256];
for (int i = 0; i < 256; ++i)
	table[i] = divideWith* (i/divideWith);
于是table[i]存放的是值为i的像素减小颜色空间的结果,这样也就可以理解上述方法中的操作:
p[j] = table[p[j]];

LUT : Look up table

OpenCV 很聪明的有个 LUT 函数就是针对这种 Look up talbe 的操作:
Mat lookUpTable(1, 256, CV_8U);
uchar* p = lookUpTable.data;
for( int i = 0; i < 256; ++i)
	p[i] = table[i];
for (int i = 0; i < times; ++i)
	LUT(I, lookUpTable, J);

算法计时

为了验证几种方法的效率,可以用一个简单的计时和输出:
double t;
t = (double)getTickCount();
t = 1000*((double)getTickCount() - t)/getTickFrequency();
t /= times;

实验结果


原图:


降低颜色空间结果:


算法时间:


更清楚的时间对比表:


转载请注明出处:http://blog.csdn.net/xiaowei_cqu/article/details/7771760
实验代码下载:http://download.csdn.net/detail/xiaowei_cqu/4443761



分享到:
评论

相关推荐

    【OpenCV】访问Mat图像中每个像素的值.docx

    本文将深入讲解如何访问`Mat`图像中每个像素的值,并提供两种不同的方法:使用C风格的数组访问和迭代器。 首先,了解`Mat`的存储方式至关重要。`Mat`通常以二维数组的形式存储图像数据,对于灰度图,每个像素值存储...

    访问Mat图像中每个像素的值

    当你需要访问图像中的像素值时,有多种方法可以实现这一目标。以下将详细介绍三种主要的方法,以帮助你更有效地操作和理解图像数据。 1. **直接索引法** 在OpenCV中,图像通常被表示为`cv::Mat`对象,它是一个二维...

    OpenCvSharp实现Mat对象简单的像素操作

    1: 输出一个Mat对象的像素 自定义一个Mat 对象,然后输出像素值(像素值基本都在 0 – 255 之间 ,图像为三通道) ... * 小图像的每一个像素值我们都是知道的,可以输出查看,验证算法正确性,然后在ran大图 *

    OPENCV_Mat类存取方法

    * v.depth:深度,即每一个像素的位数(bits),在OpenCV的Mat.depth()中得到的是一个0–6的数字,分别代表不同的位数。 * vi.step:是一个数组,定义了矩阵的布局,具体见下面图片分析,另外注意step[k](step / ...

    opencv访问图像像素的几种方法

    对于彩色图像,你需要根据通道数进行适当偏移,例如BGR图像的每个像素需要3个字节。 3. **迭代器法** OpenCV也支持使用迭代器遍历图像像素。迭代器法特别适合在循环中批量处理像素,它提供了类似STL容器的迭代...

    Opencv访问图像像素源码 (C++)

    这里,`Vec3b`表示每个像素由三个8位的通道组成(BGR顺序),分别代表蓝色、绿色和红色。 3. **修改图像像素** 同样,我们可以通过索引来修改像素值: ```cpp pixelValue[0] = 255; // 设置蓝色通道为最大值 ...

    基于opencv二值取反

    对于每个像素,如果值为0,则变为1;如果值为1,则变为0。因此,它能轻松实现二值图像的反色。例如: ```cpp cv::Mat original, inverted; // 假设original是你的二值图像 cv::bitwise_not(original, inverted);...

    OpenCV中Mat类的数学运算和区域选取

    - **幂函数与指数函数**:`pow()`函数可以用于计算`Mat`对象中每个元素的幂次,而`exp()`函数则可以计算元素的指数,这些在处理图像的亮度、对比度或者进行非线性变换时非常实用。 2. **区域选取**: - **ROI...

    opencv对像素的访问

    2. **遍历像素**:直接使用`at`函数访问并处理每个像素。 3. **处理像素**:同样地,调整像素值完成量化处理。 #### 性能对比 关于这三种访问方式的性能差异,一般而言,**动态地址访问**由于其更直接的内存访问...

    opencv 读取图像像素值到数组

    例如,对于BGR图像,每个像素由蓝、绿、红三个通道组成,可以通过增加指针偏移量来访问不同通道的值: ```cpp for (int j = 0; j ; ++j) { uchar* row_ptr = img.ptr(j); for (int i = 0; i * img.channels(); ++i...

    将图像的像素值更改为一圈8个像素点的平均值.rar

    灰度图像是一种单通道图像,每个像素只包含一个亮度值,没有颜色信息。接着,我们需要遍历图像中的每一个像素点,进行以下操作: 1. 计算当前像素点周围一圈8个像素点(上下左右及对角线邻居)的平均值。这可以通过...

    OpenCV获取与设置像素点的值

    每个像素在每个通道上的值称为像素深度,例如 `CV_8UC3` 表示一个 8 位无符号整型的 RGB 图像。 3. **Step**:步长是指每行像素占用的字节数,包括填充字节。这有助于理解像素之间的内存布局。 4. **Data 和 ...

    opencv中对图像像素的访问

    //开始处理每个像素 (*it)[0] = (*it)[0] / div*div + div / 2; (*it)[1] = (*it)[1] / div*div + div / 2; (*it)[2] = (*it)[2] / div*div + div / 2; ++it; } } //动态地址计算像素 void atColorReduce(Mat&...

    Opencv3.0之【21】用指针访问像素.rar

    3. **计算指向第一像素的指针**:`Mat`对象的`data`成员是一个指向首像素的指针,但为了访问特定位置的像素,我们需要知道每个像素的大小。对于BGR图像,每个像素由3个字节(红、绿、蓝)组成,可以通过`elemSize()`...

    opencv中获取图片的某一像素值

    在OpenCV中,可以通过多种方式获取图像中的像素值。最直接的方法是通过图像的指针访问像素数据。这里将详细介绍一种常见的方法——使用`IplImage`结构体来获取和修改像素值。 #### `IplImage`结构体简介 `IplImage...

    Qt+OpenCV2.4.4实现拉普拉斯算子图像锐化(Mat结构实现)

    图像锐化可以通过将原图像与拉普拉斯核做卷积来实现,即计算每个像素点的新值为其原值加上周围像素值与拉普拉斯核的乘积之和。 在OpenCV中,我们可以直接使用`Laplacian()`函数来实现这个操作,该函数的签名如下: ...

    OpenCV中MatOpenCV中对Mat里面depth,dims,channels,step,data,elemSize和数据

    例如,对于`CV_8UC3`类型的图像,每个像素由3个8位无符号整数组成,因此`elemSize`为3。 **elemSize1**: `elemSize1`是每个数据类型的大小,不考虑通道数。对于`CV_8UC3`,`elemSize1`也是3,因为每个基本数据类型...

    OpenCV对不同图像深度的处理(转)

    - **IPL_DEPTH_8U**: 8位无符号整数,是图像处理中最常见的深度,每个像素值范围从0到255。 - **IPL_DEPTH_8S**: 8位有符号整数,每个像素值范围从-128到127。 - **IPL_DEPTH_16U**: 16位无符号整数,每个像素值范围...

    基于OpenCV实现图像缩放功能

    值得注意的是,虽然OpenCV的`resize`函数提供了便捷的接口,但如果我们想要实现自定义的双线性插值算法,可以手动遍历每个像素并计算新位置的像素值。这样的实现可能需要更多的代码和计算资源,但对于理解双线性插值...

    opencv中的图像线性发大

    在OpenCV库中,图像处理是一项基础且重要的任务。"图像线性发大"是其中的一个功能,它涉及到了图像的尺寸变换。本篇将详细阐述如何使用OpenCV中的线性插值方法来实现图像的放大,并探讨相关知识点。 首先,我们需要...

Global site tag (gtag.js) - Google Analytics