opencv学习(四)之像素遍历三种方式
在上一篇文章中介绍了图像颜色空间缩减、查找表等内容。在对图像像素进行遍历时共有三种方法:
(1). C操作符[] (指针方式访问)
(2). 迭代器iterator
(3). 动态地址计算
这三种像素遍历方式在速度上有所不同,上一篇文章介绍过用C操作符[]是最快的访问方式。下面会通过对同一幅图像进行处理来直观的比较三种访问方式的速度差异。首先介绍一下opencv中提供的计时函数
1.计时函数
opencv中提供两个简便的计时函数getTickCount()和getTickFrequency(),这两个函数配合使用即可计算出程序耗时,这两个函数有点类似于C++中的clock()函数和CLK_TCK(C\C++中计时、延时函数)
getTickCount():返回CPU自某时间开始的时钟周期
getTickFrequency():获取CPU时钟频率
其用法如下所示:
double start,stop,duration;
start = static_cast<double>(getTickCount()); //记录当前时刻CPU时钟周期
/*
...
代码片段
...
*/
stop = static_cast<double>(getTickCount()); //记录程序运行结束CPU时钟周期
duration = ((double)(stop - start))/getTickFrequency(); //计算时间,以秒为单位//上述代码也可精简
double timeConsume,start;
start = static_cast<double>(getTickCount()); //记录当前时刻CPU时钟周期
/*
...
代码片段
...
*/
timeConsume = ((double)getTickCount() - start) / getTickFrequency();
2.常用像素存储结构
在介绍像素遍历之前需要先对opencv中像素的存储方式有一定的了解,不然直接看代码的适合可能不知所云,比较费劲。
有一些opencv中常见的数据结构使用”<<”操作符进行输出如:
#include <iostream>
#include <opencv2/core.hpp>using namespace std;
using namespace cv;int main()
{//2D PointPoint2f P(5, 1);cout << endl << "Point (2D) = " << P << endl << endl;//3D PointPoint3f P3f(2, 5, 7);cout << "Point (3D) = " << P3f << endl << endl;//std::vector via cv::Matvector<float> v;v.push_back((float)CV_PI); //将pi的值以float类型存入v,push_back是C++中容器的一种操作方式v.push_back(2);v.push_back(3.01f);cout << "vector of floats via Mat = " << endl << Mat(v) << endl << endl;//std::vector of pointsvector<Point2f> vPoints(20);for(size_t i = 0; i < vPoints.size(); ++i)vPoints[i] = Point2f((float)(i * 5), (float)(i % 7));cout << "vector of 2D points = " << endl << vPoints << endl << endl;return 0;
}
opencv中有模板类Vec,可以表示一个向量。opencv中使用Vec类预定义了一些小向量,可以用于矩阵元素的表达。
typedef Vec<uchar, 2> Vec2b;
typedef Vec<uchar, 3> Vec3b;
typedef Vec<uchar, 4> Vec4b;typedef Vec<short, 2> Vec2s;
typedef Vec<short, 3> Vec3s;
typedef Vec<short, 4> Vec4s;typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
typedef Vec<int, 4> Vec4i;typedef Vec<float, 2> Vec2f;
typedef Vec<float, 3> Vec3f;
typedef Vec<float, 4> Vec4f;
typedef Vec<float, 6> Vec6f;typedef Vec<double, 2> Vec2d;
typedef Vec<double, 3> Vec3d;
typedef Vec<double, 4> Vec4d;
typedef Vec<double, 6> Vec6d;
例如8U类型的RGB彩色图像也可以使用Vec3b,3通道float类型的矩阵可以使用Vec3f.对于Vec对象可以使用[]符号如数组一样对其元素进行读写操作。如:
Vec3b color; //用color变量描述一种RGB颜色
color[0] = 255; //B分量
color[1] = 0; //G分量
color[2] = 0; //R分量
3.像素值的读写
在对图像进行处理时,需要读取某个像素值或者设置某个像素值;在更多的时候需要对图像中所有像素进行遍历。在前面介绍用三种方法可以对图像像素进行遍历。下面逐一介绍。
3.1 at()函数
可以用函数at()来实现对矩阵中某个像素值进行读取或进行赋值操作,但是前提要知道该像素行列位置。其用法如下:
uchar value = img.at<uchar>(i,j); //读出第i行第j列像素值
img.at<uchar>(i,j) = 128; //将第i行第j列像素值设置为128
如果要对图像进行遍历,可以参照下面的代码:
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>using namespace std;
using namespace cv;int main()
{double timeConsume = static_cast<double>(getTickCount());Mat grayImage(400, 600, CV_8UC1); //创建一个大小为600x800的单通道灰度图Mat colorImage(400, 600, CV_8UC3); //创建一个大小为600x800的三通道彩色图//遍历所有像素并设置像素值for(int i = 0; i < grayImage.rows; ++i) //遍历行for(int j = 0; j < grayImage.cols; ++j) //遍历列grayImage.at<uchar>(i, j) = (i + j) % 255;//遍历所有像素并设置像素值for(int i = 0; i < colorImage.rows; ++i) //遍历行for(int j = 0; j < colorImage.cols; ++j) //遍历列{Vec3b pixel; //定义三通道像素值变量pixel[0] = i % 255; //Bluepixel[1] = j % 255; //Greenpixel[2] = 0; //RedcolorImage.at<Vec3b>(i, j) = pixel;}//显示简历图像的结果namedWindow("grayImage", WINDOW_AUTOSIZE);imshow("grayImage", grayImage);namedWindow("colorImage", WINDOW_AUTOSIZE);imshow("colorImage", colorImage);timeConsume = ((double)getTickCount() - timeConsume) / getTickFrequency();cout << "程序耗时: " << timeConsume << endl;waitKey(0);return 0;
}
运行结果如下:
程序中带有计时函数计算程序耗时,程序耗时根据电脑配置不同有所差异。
如果需要对图像像素进行遍历,不推荐使用at()函数,因为使用这个函数其效率不高,但是其可读性较好。
3.2 使用迭代器iterator
相信学过C++的同学一定对STL库有印象,其中就包含iterator的使用。迭代器可以很方便的遍历所有元素。Mat类支持迭代器的方式对矩阵元素进行遍历。由于使用迭代器就不需要再使用行列数进行操作。示例如下:
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>using namespace std;
using namespace cv;int main()
{double timeConsume = static_cast<double>(getTickCount());Mat grayImage(400, 600, CV_8UC1); //创建一个大小为600x800的单通道灰度图Mat colorImage(400, 600, CV_8UC3); //创建一个大小为600x800的三通道彩色图//遍历所有像素并设置像素值MatIterator_<uchar> grayit, grayend;for(grayit = grayImage.begin<uchar>(), grayend = grayImage.end<uchar>(); grayit != grayend; ++grayit)*grayit = rand() % 255;//遍历所有像素并设置像素值MatIterator_<Vec3b> colorit, colorend;for(colorit = colorImage.begin<Vec3b>(), colorend = colorImage.end<Vec3b>(); colorit != colorend; ++colorit){(*colorit)[0] = rand() % 255; //Blue(*colorit)[1] = rand() % 255; //Green(*colorit)[2] = rand() % 255; //Red}//显示简历图像的结果namedWindow("grayImage", WINDOW_AUTOSIZE);imshow("grayImage", grayImage);namedWindow("colorImage", WINDOW_AUTOSIZE);imshow("colorImage", colorImage);timeConsume = ((double)getTickCount() - timeConsume) / getTickFrequency();cout << "程序耗时: " << timeConsume << endl;waitKey(0);return 0;
}
运行结果如下:
使用迭代器进行像素遍历被认为是一种更安全的方式。使用迭代器只需要求出矩阵的开头和矩阵末尾,接下来使用for循环进行迭代,直到”it == end”.
代码中”colorit = colorImage.begin(), colorend = colorImage.end();”是求出矩阵起始位置和结束位置。
3.3 使用指针进行访问
其实在前面已经提到用指针对数据进行访问,虽然使用指针速度最快,但是指针操作不进行类型以及越界检查,所有在程序写好编译没问题,但是运行就有可能出错。而使用at()和iterator()进行遍历时,虽然速度不如指针,但是相对两件比较容易实现,可读性也比较好。示例代码如下:
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>using namespace std;
using namespace cv;int main()
{double timeConsume = static_cast<double>(getTickCount());Mat grayImage(400, 600, CV_8UC1); //创建一个大小为600x800的单通道灰度图Mat colorImage(400, 600, CV_8UC3); //创建一个大小为600x800的三通道彩色图//遍历所有像素并设置像素值for(int i = 0; i < grayImage.rows; ++i){uchar* p = grayImage.ptr<uchar>(i); //获取第i行第一个像素的指针for(int j = 0; j < grayImage.cols; ++j)p[j] = (i + j) % 255; //对每个i行的所有像素进行赋值操作}//遍历所有像素并设置像素值for(int i = 0; i < colorImage.rows; ++i){Vec3b* p = colorImage.ptr<Vec3b>(i);for(int j = 0; j < colorImage.cols; ++j){p[j][0] = i % 255; //Bluep[j][1] = j % 255; //Greep[j][2] = 0; //Red}}//显示简历图像的结果namedWindow("grayImage", WINDOW_AUTOSIZE);imshow("grayImage", grayImage);namedWindow("colorImage", WINDOW_AUTOSIZE);imshow("colorImage", colorImage);timeConsume = ((double)getTickCount() - timeConsume) / getTickFrequency();cout << "程序耗时: " << timeConsume << endl;waitKey(0);return 0;
}
运行结果如下:
下一篇对三种像素遍历方式进行一个综合比较!!!
opencv学习(四)之像素遍历三种方式相关推荐
- 【opencv学习笔记】第五篇:访问图像中像素的三种方式、ROI区域图像叠加和图像混合
1. 访问图像中像素的三种方式 任何图像处理算法,都是从操作每个像素开始的.在OpenCV中,提供了三种访问每个像素的方法. 方法1:指针访问:C操作符[] 方法2:迭代器iterator 方法3:动 ...
- OpenCV优化:图像的遍历4种方式
小知识:反色 反色原理很简单,在一个rgb色彩空间中,可将任何一种颜色看成笛卡尔坐标中的一个点,对于任意点,反色就是计算以(128, 128,128)为中心时该点的对称点,比如rgb(100, 150 ...
- pyecharts学习(part1)--绘制图表的三种方式
学习笔记,仅供参考,有错必究 文章目录 pyecharts学习 绘制图表的三种方式 绘制的图表生成HTML jupyter notebook 内嵌展示 pyecharts生成图片 pyecharts学 ...
- 【Spring MVC学习】WebApplicationContext初始化的三种方式
ApplicationContext是Spring的核心,Context我们通常解释为上下文环境,我想用"容器"来表述它更容易理解一些,ApplicationContext则是&q ...
- Spring学习总结3——配置datasource三种方式
为什么80%的码农都做不了架构师?>>> jdbc.properties文件信息 ##Oracle 11g jdbc.driverClassName=oracle.jdbc.O ...
- 【CSDN软件工程师能力认证学习精选】vue.js 三种方式安装(vue-cli)
CSDN软件工程师能力认证(以下简称C系列认证)是由中国软件开发者网CSDN制定并推出的一个能力认证标准.C系列认证历经近一年的实际线下调研.考察.迭代.测试,并梳理出软件工程师开发过程中所需的各项技 ...
- Java学习笔记——显示当前日期的三种方式
一.Date类:这是一种过时的表达方式 import java.util.Date; Date date = new Date();System.out.println((1900+date.getY ...
- java如何显示当天世界_Java学习笔记——显示当前日期的三种方式
一.Date类:这是一种过时的表达方式 import java.util.Date;Date date = new Date(); System.out.println((1900+date.getY ...
- 二叉树前序遍历三种方式(c++ 实现)
一.递归 递归很简单,只要在调用子节点前对当前节点进行操作即可 struct TreeNode {int val;TreeNode *left;TreeNode *right;TreeNode() : ...
最新文章
- [shell]简单的shell提示和参数脚本
- 现代中产男人必备的8种气质[zt] 来自9G群里发的,据说是BF推荐给9G的
- VS2010强大的一塌糊涂
- MySQL多表查询核心优化
- java网络文章博客抓取系统_java 后端博客系统文章系统——No3
- 联想微型计算机报价,联想电脑一体机报价
- 【数位DP】CF 54C,509C,431D,628D,855E,1245F,95D
- android: a system image must be selected to continmue
- Ember.js 1.0 RC6 发布,JavaScript 框架
- OpenSSH学习笔记(安装配置openssh-4.6p1)[zz]
- [2010-8-22]
- html5 的 webScoket 和 C# 建立Socket连接
- 《软件测试价值提升之路》学习之—测试工程师能力模型
- java验证码问题(不区分大小写)升级版,输入不正确则一直输入
- simulink仿真之阶梯步长
- 5.24 通过高级筛选功能将筛选结果放置在其它位置 [原创Excel教程]
- unity显示no camera rendering
- 【ACCV2022】论文阅读笔记Lightweight Alpha Matting Network Using Distillation-Based Channel Pruning
- Java获取图片RGB值
- jsoup的简单实用兼谈一个简单的汇率查询(原创)