opencv 几种不同遍历图像像素的方法详解

简述

我们在图像处理时经常会用到遍历图像像素点的方式,同样是遍历图像像素点,共有很多中方法可以做到;在这些方法中,有相对高效的,也有低效的;不是说低效的方法就不好,不同场景使用不同方法。

方法

下面将一一介绍这些遍历图像像素点的方法:

方法一:数组遍历法1

图像Mat中每个像素点,其实就是一个值(int、float、double、uchar等类型),而Mat是一个二维数组。

1、单通道图像(CV_8UC1);backImg是单通道灰色图。

int height = backImg.rows;
int width = backImg.cols;
for (int i = 0; i < height; i++)
{
	for (int j = 0; j < width; j++)
	{
		int index = i * width + j;
		//像素值
		int data = (int)backImg.data[index];
	}
}

2、三通道图像(CV_8UC3);backImg、colorImg均为三通道彩色图,且大小相同。

int height = backImg.rows;
int width = backImg.cols;

//差值图
//三个通道中只要有一个差值大于阈值,就进行提取
Mat subImg(height, width, CV_8UC1);
subImg.setTo(0);
for (int i = 0; i < height; i++)
{
	for (int j = 0; j < width; j++)
	{
		int index = i * width + j;
		//背景
		int b1 = (int)backImg.data[3 * index + 0];
		int g1 = (int)backImg.data[3 * index + 1];
		int r1 = (int)backImg.data[3 * index + 2];
		//当前图
		int b2 = (int)colorImg.data[3 * index + 0];
		int g2 = (int)colorImg.data[3 * index + 1];
		int r2 = (int)colorImg.data[3 * index + 2];
		
		//th为阈值
		if (g1 - g2 > th || b1 - b2 > th || r1 - r2 > th)
		{
			subImg.data[index] = 255;
		}
	}
}

方法二:数组遍历法2 -- at(i,j)

Mat类提供了一个at的方法用于取得图像上的点,它是一个模板函数,可以取到任何类型的图像上的点。

void colorReduce(Mat& image)
{
     for(int i=0;i(i,j) = image.at(i,j) / 2;			
			}
			else if(image.channels() == 3)
			{
				//取出彩色图像中i行j列第k(0/1/2)通道的颜色点
				image.at(i,j)[0]=image.at(i,j)[0] / 2;
				image.at(i,j)[1]=image.at(i,j)[1] / 2;
				image.at(i,j)[2]=image.at(i,j)[2] / 2;
			}			 
		 }
	 }
}    

方法三:指针遍历法

1、这种图像遍历方法相比“数组遍历法”更高效

 void colorReduce(Mat& srcImg,Mat& dstImg)
{
	dstImg = srcImg.clone();
	dstImg.setTo(0);
	int height = srcImg.rows;
	// 将3通道转换为1通道
	int width = srcImg.cols * srcImg.channels();
	for(int k = 0;k < height; k++)
	{
		// 获取第k行的首地址
		const uchar* inData = srcImg.ptr(k);
		uchar* outData = dstImg.ptr(k);
		//处理每个像素
		for(int i = 0; i < width; i++)
		{
			outData[i] = inData[i] / 2;
		}
	}
}   

程序中将三通道的数据转换为1通道,在建立在每一行数据元素之间在内存里是连续存储的,每个像素三通道像素按顺序存储。
但是这种用法不能用在行与行之间,因为图像在opencv里的存储机制问题,行与行之间可能有空白单元。这些空白单元对图像来说是没有意思的,只是为了在某些架构上能够更有效率,比如intel MMX可以更有效的处理那种个数是4或8倍数的行。

2、但是我们可以申明一个连续的空间来存储图像,这样效率就会更高。图像可以是连续的,也可以是不连续的,Mat提供了一个检测图像是否连续的函数isContinuous(),当图像连通时,我们就可以把图像完全展开,看成是一行。

void colorReduce(Mat& srcImg,Mat& dstImg)
{
	int height = srcImg.rows;
	int width = srcImg.cols;
	dstImg = srcImg.clone();
	dstImg.setTo(0);
	//判断图像是否连续
	if(srcImg.isContinuous() && dstImg.isContinuous())
	{
		height = 1;
		width = width * srcImg.rows * srcImg.channels();
	}
	for(int i = 0; i< height; ++i)
	{
		const uchar* inData = srcImg.ptr(i);
		uchar* outData = dstImg.ptr(i);
		for(int j = 0; j< width; ++j)
		{
		   outData[j] = inData[j] / 2;
		}
	}
}

方法四:迭代器遍历法

1、迭代器Matlterator_

Matlterator_是Mat数据操作的迭代器,:begin()表示指向Mat数据的起始迭代器,:end()表示指向Mat数据的终止迭代器。迭代器方法是一种更安全的用来遍历图像的方式,首先获取到数据图像的矩阵起始,再通过递增迭代实现移动数据指针。

//三通道彩色图
void colorReduce(Mat srcImage)
{
	Mat tempImage = srcImage.clone();  
  
    // 初始化原图像迭代器  
    MatConstIterator_ srcIterStart = srcImage.begin();  
    MatConstIterator_ srcIterEnd = srcImage.end();  
  
    // 初始化输出图像迭代器  
    MatIterator_ resIterStart = tempImage.begin();  
    MatIterator_ resIterEnd = tempImage.end();  
  
    while (srcIterStart != srcIterEnd)   
    {  
        (*resIterStart)[0] = 255 - (*srcIterStart)[0];  
        (*resIterStart)[1] = 255 - (*srcIterStart)[1];  
        (*resIterStart)[2] = 255 - (*srcIterStart)[2];  
  
        srcIterStart++;  
        resIterStart++;  
    } 
}
//单通道灰色图
void colorReduce(Mat srcImage)
{
	Mat tempImage = srcImage.clone();  
  
    // 初始化原图像迭代器  
    MatConstIterator_ srcIterStart = srcImage.begin();  
    MatConstIterator_ srcIterEnd = srcImage.end();  
  
    // 初始化输出图像迭代器  
    MatIterator_ resIterStart = tempImage.begin();  
    MatIterator_ resIterEnd = tempImage.end();  
  
    while (srcIterStart != srcIterEnd)   
    {  
        *resIterStart = 255 - *srcIterStart;    
        srcIterStart++;  
        resIterStart++;  
    } 
}

2、迭代器Mat_

OpenCV定义了一个Mat的模板子类为Mat_,它重载了operator()让我们可以更方便的取图像上的点。

void colorReduce(Mat &img)
{
	uchar t;
	//单通道图像
	if(img.channels() == 1)
	{
		Mat_::iterator it = img.begin();
		Mat_::iterator itend = img.end();
		while(it != itend)
		{
			//相关操作
			*it = 0;
			it++;
		}
	}
	//三通道图像
	else if(img.channels() == 3)
	{
		Mat_::iterator it = img.begin();
		Mat_::iterator itend = img.end();
		while(it != itend)
		{
			//相关操作
			(*it)[0] = 0;
			(*it)[1] = 1;
			(*it)[2] = 2;
			it++;
		}
	}
}
//单通道图像(CV_8UC1)src是单通道灰色图
//作用:统计图像中白色像素个数
int BaseFun::whiteSums(Mat src)	
{	
	int counter = 0;
	//迭代器访问像素点
	Mat_::iterator it = src.begin();
	Mat_::iterator itend = src.end();  
	for (; it!=itend; ++it)
	{
		if((*it)>0) counter+=1;//二值化后,像素点是0或者255
	}			
	return counter;
}

方法五:Mat_数组遍历法(非迭代器法,Mat_迭代器法已在方法四中介绍)

Mat_也可以通过下标的方式来遍历图像,但是相比Mat::at()方法,无疑Mat_通过下标遍历图像是慢的,不推荐这种方式,如果要使用Mat_,最好只是使用迭代器Mat_<>::iterator。

//单通道图像
void colorReduce(Mat srcImg)
{
	int height = srcImg.rows;
	int width = srcImg.cols;
	//转化为Mat_
	Mat_ tempImg = (Mat_&)srcImg;

	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			tempImg(i,j) = tempImg(i,j) * 2;
			printf("%d ",tempImg(i,j));	
		}
		printf("\n");
	}
}
//三通道图像	
void colorReduce(Mat srcImg)
{		
	Mat_ tempImg = (Mat_&)srcImg;
	int height = srcImg.rows;
	int width = srcImg.cols;
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			printf("%d ",tempImg(i,j)[0]);	
			printf("%d ",tempImg(i,j)[1]);	
			printf("%d ",tempImg(i,j)[2]);	
		}
		printf("\n");
	}
}

 Mat_对应的是CV_8U,Mat_对应的是CV_8S,Mat_对应的是CV_32S,Mat_对应的是CV_32F,Mat_对应的是CV_64F,对应的数据深度如下:

• CV_8U - 8-bit unsigned integers ( 0..255 )

• CV_8S - 8-bit signed integers ( -128..127 )

• CV_16U - 16-bit unsigned integers ( 0..65535 )

• CV_16S - 16-bit signed integers ( -32768..32767 )

• CV_32S - 32-bit signed integers ( -2147483648..2147483647 )

• CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )

• CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )

附一张opencv官方给出的关于Mat_解释的图片:

opencv 几种不同遍历图像像素的方法详解_第1张图片

总结

目前找到了这些方式来遍历图像像素,全部亲测可用。如有朋友发现新的遍历方法,可一起交流。

本文部分内容参考博客:http://www.cnblogs.com/ronny/p/opencv_road_2.html,感谢这位博主。

你可能感兴趣的:(图像处理--Opencv,opencv遍历图像像素方法,opencv遍历Mat的方法,Mat::at()遍历图像,Mat_迭代器遍历图像)