机器视觉学习笔记(3)——常见的像素访问方式

机器视觉学习笔记(3)——常见的像素访问方式

标签: 机器视觉


图像是由一个个像素组成的,各种算法和处理都是基于像素实现的,所以访问像素的操作是最常见的操作方式。常见的像素访问方式有Mat::at访问,指针访问和迭代器访问。

为了详细的说明各个方式访问像素的方法,本文将使用不同的访问像素的方法实现Color Reduce(颜色缩减)的功能。Color Reduce可以将图像的颜色数降低,比如可以将64阶的灰度图降为8阶的灰度图,主要通过整数的除法实现,因为整数的除法可以去尾。比如某像素点的值为M,降阶之后的值就为M/8*8,原先0~63连续值就变为{0, 8, 16, 24, 32, 40, 48, 56}的离散值。本文将处理3通道的彩色图,颜色缩减的倍数为32。

原图:

处理后:

OpenCV2中存储图像的变量类型是Mat (强烈建议放弃OpenCV1中的数据结构和函数),访问像素其实就是访问Mat的元素。

1.Mat::at访问

  • Mat::at方法官方说明如下:

Returns a reference to the specified array element.
(返回指定元素的引用)

  • 该方法有各种参数的重载,使用该方法实现的Color Reduce功能代码如下:
void color_reduce1(Mat &image)
{
	for (int i=0; i<image.rows; i++) 
		for (int j=0; j<image.cols; j++)
		{
			if (image.channels() == 1)//如果是单通道图
				image.at<uchar>(i, j) = image.at<uchar>(i, j) /32 * 32;
			else if (image.channels() == 3)//如果是三通道图
			{
				image.at<Vec3b>(i, j)[0] = image.at<Vec3b>(i, j)[0] / 32 * 32;
				image.at<Vec3b>(i, j)[1] = image.at<Vec3b>(i, j)[1] / 32 * 32;
				image.at<Vec3b>(i, j)[2] = image.at<Vec3b>(i, j)[2] / 32 * 32;
			}
		}
}

2.指针访问

  • Mat::ptr方法官方说明如下:

Returns a pointer to the specified matrix row.
(返回指定行的指针)

  • 该方法可以返回尖括号指定元素类型的行指针,该指针指向该行第一个元素
void color_reduce2(Mat &image)
{
	int rows = image.rows;//行数
	int rowsCount = image.cols * image.channels();//每行的元素个数
	for (int  i=0; i<rows; i++)
	{
		uchar* data = image.ptr<uchar>(i);//每行首元素地址
		for (int j=0; j<rowsCount; j++)
			data[j] = data[j] / 32 * 32;			
	}
}
  • 如果是3通道图像,元素按照BRG通道的顺序排列,注意是BGRBGR…,而不是BB…GG…RR…
  • 还可以通过Mat::data成员变量来访问像素,该成员是一个uchar指针类型,指向像素的首元素,指向i行j列元素的地址为uchar* data = image.data+i*image.step+j*image.elemSize()

3.迭代器访问

void color_reduce3(Mat &image)
{
	Mat_<Vec3b>::iterator iter = image.begin<Vec3b>();
	for ( ; iter != image.end<Vec3b>(); iter++)
	{
		(*iter)[0] = (*iter)[0] / 32 * 32;//[]的优先级高,所以要先解引用
		(*iter)[1] = (*iter)[1] / 32 * 32;
		(*iter)[2] = (*iter)[2] / 32 * 32;
	}
}
  • 指向const元素的迭代器声明语句为Mat_<Vec3b>::const_iterator iter;
  • 如果使用Mat_定义图片,则begin()和end()就不需要写元素类型了,因为定义时写过了
Mat_<Vec3b> image;
Mat_<Vec3b>::iterator beg = image.begin();
Mat_<Vec3b>::iterator end = image.end();

4.三种访问像素的方法遍历的效率比较

既然访问像素的方法有多种,自然而然就想比较一下。OpenCV提供了测量代码运行时间的函数:

double getTickFrequency()//返回每秒钟的时钟数,即时钟频率
int64 getTickCount()//返回当前时钟数

代码如下:

int main()  
{  
	Mat image;
	image = imread("img.jpg");
	double time;
	time = (double)getTickCount();

	for (int i=0; i<10; i++)//多次运行以减小误差
		color_reduce1(image);//更换测试的函数即可

	time = (double)getTickCount() - time;
	time /= getTickFrequency();
	cout<<time/10<<endl;//除以10输出
}

各方法运行时间如下表所示(单位:秒):

测试方法Mat::at访问指针访问迭代器访问
Debug0.3161530.0030270.414347
Release0.001589680.000709580.00610587
  • 绝对运行时间是没有意义的,有意义的是相对运行时间
  • 最快的是指针访问像素的方法,Debug模式下,Mat::at访问运行时间是其104倍,迭代器访问运行时间是其137倍;Release模式下,Mat::at访问运行时间是其2.2倍,迭代器访问运行时间是其8.6

5.总结

  • 以上介绍的访问像素的方法是最基础的访问方法,其它方法大多是在这三种方法基础上衍生出来的
  • Mat::at访问和迭代器访问速度慢,但是安全,稳定性高;指针访问最快,但很危险,操作错误的话甚至会引起程序的崩溃
  • 减小程序运行时间的方法有很多,比如嵌套循环中,尽量内循环次数多,外循环次数少;比如可以检测图片是否连续,再采取相应的处理方式;比如采用位运算
  • 笔者的建议是:如果确切的知道待访问像素的位置,使用Mat::at方法;需要遍历一整行,一整列或者整个图像的时候,使用指针访问,如果怕自己出错,那就使用迭代器访问
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值