从零开始学OpenCV(三)——图像像素的遍历

3 篇文章 3 订阅

前言

提示:总结了遍历图像像素的三种方法,并对其优劣进行了比较。



一、图像像素的遍历

提示:以下遍历图像像素示例,均实现以下主功能。

/********************************************************
Copyright (c)  2021, by Oneself
Funtion: 实现将一幅图像中每个像素的灰度值减半处理
*********************************************************/
#include <iostream>
using namespace std;

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;

int main(int argc, char** argv)
{
	cv::Mat image = imread("tower.jpg", -1);
	imshow("input", image);   //显示处理之前的原图像

	//遍历图像
	.....
	
	imshow("output", image);   //显示处理之后的图像   
	waitKey(0);
}

1. at方法遍历

//------------------------------【函数定义】------------------------------
template<typename _Tp >
_Tp& cv::Mat::at	(	
int 	row,    //图像行数
int 	col     //图像列数
)	
//遍历示例
for (int j = 0; j < image.rows; j++)
	{
		for (int i = 0; i < image.cols; i++)             //注意这里的内循环,图像列数不需要 * image.channels()
		{
			//注意使用at方法的时候需要判断源图像的通道类型
			if (image.type() == CV_8UC1)
			{
				image.at<uchar>(j, i) = image.at<uchar>(j, i) / 2;
			}
			else if(image.type() == CV_8UC3)
			{
				//若统一处理
				//image.at<Vec3b>(j, i) = image.at<Vec3b>(j, i) / 2;    //图像的通道数在这里的图像类型<Vec3b> 体现了

				//若需要对三个通道分别取值
				image.at<Vec3b>(j, i)[0] = image.at<Vec3b>(j, i)[0] / 2;
				image.at<Vec3b>(j, i)[1] = image.at<Vec3b>(j, i)[1] / 3;
				image.at<Vec3b>(j, i)[2] = image.at<Vec3b>(j, i)[2] / 4;
			}
		}
	}

Tips
(1)在调用at方法时,必须指定图像元素的类型;
(2)因为cv::Mat 可以接受任何类型的元素,你需要指定返回值的预期类型,故at方法被实现成为了一个模板方法。

2. 指针遍历

//------------------------------【函数定义】------------------------------
template<typename _Tp >
using cv::Ptr = typedef std::shared_ptr<_Tp>
//遍历示例
for (int j = 0; j < image.rows; j++)
{
	uchar* data = image.ptr<uchar>(j);   //获取第j行的地址
	for (int i = 0; i < image.cols * image.channels(); i++)
	{
		data[i] = data[i] / 2;
	}
}

Tips】:ptr方法是一个模板方法,返回第j行的地址。

3. 迭代器遍历

//创建图像迭代器有两种方式

//方式一
cv::Mat_<cv::Vec3b>::Iterator it;

//方式二
cv::MatIterator_<cv::Vec3b> it;
//遍历示例
cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();
cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();

//也可以使用以下创建迭代器
//cv::MatIterator_<cv::Vec3b> it = image.begin<cv::Vec3b>();
//cv::MatIterator_<cv::Vec3b> itend = image.end<cv::Vec3b>();

while (it != itend)
{
	*it = *it / 2;
	it++;
}

Tips】:创建迭代器有两种方法:cv::Mat_<cv::Vec3b>::iterator 或 cv::MatIterator_<cv::Vec3b>;

二、遍历效果对比

(一)、需要对程序的处理耗时进行计数

//计时从某段图像处理过程从开始到结束的耗时时间
double t = getTickCount();
.....//(处理过程)
double timeConsume = (getTickCount() - t) /getTickFrequency();  //timeConsume即为这段处理过程所耗时的总时间(单位:秒)

Tips
(1)cv::getTickCount() 函数会返回从最近一次计算机开机到当前的时钟周期数;
(2)cv::getTickFrequency() 函数返回计算机每秒的周期数。

(二)、耗时对比
对于一张 499 x 335 的照片,分别遍历其所有的像素,将像素值减半处理,耗时统计结果如下:

遍历方法耗时时间
at方法遍历0.231494
指针遍历0.0241424
迭代器遍历0.262805

结论
(1)指针遍历图像像素最高效;
(2)at方法和迭代器方法遍历图像的速度差不多;
(3)at方法适用在需要随机访问像素的时候,绝不要在扫描图像时使用;
(4)对于一张连续图像来说,生成一个连续的一维循环,会比对行和列运行常规的二重循环,运行速度可以平均提高10%。


总结

以上就是今天要讲的全部内容。

参考资料

  1. OpenCV计算机视觉编程攻略(第3版);
  2. opencv官方文档https://docs.opencv.org/4.5.3/
  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值