OpenCV-C++对图像像素的四种遍历操作
在OpenCV-C++环境下,图像的存储容器是Mat对象,所以对图像像素的遍历,就是对Mat对象每一个数据元素的遍历。
关于Mat对象的详细介绍,可以参见博文 https://www.hhai.cc/thread-70-1-1.html
本文提供四种方式实现对OpenCV的Mat类矩阵元素的遍历。
以下四个代码通过对矩阵元素的遍历实现图像的反色操作。
四个代码中用到的图像的下载链接如下:
https://pan.baidu.com/s/1JEy2tiuwCKDi4n9TfuVyTA 提取码:201q
下面的四种方法的代码中会经常用到名字“Vec3b”,关于“Vec3b”的详细介绍,可参见下面这篇博文:
https://www.hhai.cc/thread-80-1-1.html
方法一:下标遍历法,格式为M.at<typename>(i,j)
这种方法实际上就是用Mat类的成员函数at()实现,
关于成员函数at()的详细介绍,
大家可参考博文 https://www.hhai.cc/thread-75-1-1.html (打开页面后搜索关键字“成员函数Mat:at()”)
这种方法是OpenCV-C++中对图像像素值访问和遍历最常用的方法。
这种方法的示例代码请参见下面这篇博文:
https://www.hhai.cc/thread-110-1-1.html
方法二:利用行指针遍历
//出处:昊虹AI笔记网(hhai.cc)
//用心记录计算机视觉和AI技术
//博主微信/QQ 2487872782
//QQ群 271891601
//欢迎技术交流与咨询
//OpenCV版本 OpenCV3.0
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
cv::Mat inverseColor2(cv::Mat srcImage)
{
cv::Mat tempImage = srcImage.clone();
int row = tempImage.rows;
// 由于图像是三通道图像,所以要作下面这样的处理
int nStep = tempImage.cols * tempImage.channels();
for (int i = 0; i < row; i++)
{
// 取源图像的第i行指针
const uchar* pSrcData = srcImage.ptr<uchar>(i);
// 取目标图像的第i行指针
uchar* pResultData = tempImage.ptr<uchar>(i);
for (int j = 0; j < nStep; j++)
{
pResultData[j] = cv::saturate_cast<uchar>(255 - pSrcData[j]);
}
}
return tempImage;
}
int main()
{
// 装载图像
cv::Mat srcImage = cv::imread("F:/material/images/P0028-flower-02.jpg");
cv::Mat dstImage;
if (!srcImage.data)
return -1;
cv::imshow("srcImage", srcImage);
dstImage = srcImage.clone();
dstImage = inverseColor2(srcImage);
cv::imshow("dstImage", dstImage);
cv::waitKey(0);
return 0;
}
行指针遍历法的代码补充说明:
image.ptr<uchar>(i):取出图像中第i行数据的指针。
使用这种方法有一个前提:那就是图像每一行的数据在内存里是连续存储的,并且每个像素的三个通道数据按顺序存储。
方法三:利用数据的头指针直接按序遍历
这种方法的效率很高,但是要求不仅每一行的数据在内存里是连续存储的,行与行间也是连续的。
使用这种方法应先用Mat类的成员函数isContinuous()判断数据是否是连续存储的,满足此条件,才可使用该方法。
示例代码如下:
//出处:昊虹AI笔记网(hhai.cc)
//用心记录计算机视觉和AI技术
//博主微信/QQ 2487872782
//QQ群 271891601
//欢迎技术交流与咨询
//OpenCV版本 OpenCV3.0
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
cv::Mat inverseColor3(cv::Mat srcImage)
{
int row = srcImage.rows;
int col = srcImage.cols;
cv::Mat tempImage = srcImage.clone();
// 判断是否是连续图像,即是否有像素填充
if (srcImage.isContinuous() && tempImage.isContinuous())
{
row = 1;
// 按照行展开
col = col * srcImage.rows * srcImage.channels();
}
// 遍历图像的每个像素
for (int i = 0; i < row; i++)
{
// 设定图像数据源指针及输出图像数据指针
const uchar* pSrcData = srcImage.ptr<uchar>(i);
uchar* pResultData = tempImage.ptr<uchar>(i);
for (int j = 0; j < col; j++)
{
*pResultData++ = 255 - *pSrcData++;
}
}
return tempImage;
}
int main()
{
// 装载图像
cv::Mat srcImage = cv::imread("F:/material/images/P0028-flower-02.jpg");
cv::Mat dstImage;
if (!srcImage.data)
return -1;
cv::imshow("srcImage", srcImage);
dstImage = srcImage.clone();
dstImage = inverseColor3(srcImage);
cv::imshow("dstImage", dstImage);
cv::waitKey(0);
return 0;
}
方法四:使用OpenCV的迭代器来遍历
OpenCV的迭代器使用很简单,大家看了大家的代码就知道怎么用了,这里就不多叙述了。
//出处:昊虹AI笔记网(hhai.cc)
//用心记录计算机视觉和AI技术
//博主微信/QQ 2487872782
//QQ群 271891601
//欢迎技术交流与咨询
//OpenCV版本 OpenCV3.0
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
cv::Mat inverseColor4(cv::Mat srcImage)
{
cv::Mat tempImage = srcImage.clone();
// 初始化源图像迭代器
cv::MatConstIterator_<cv::Vec3b> srcIterStart = srcImage.begin<cv::Vec3b>();
cv::MatConstIterator_<cv::Vec3b> srcIterEnd = srcImage.end<cv::Vec3b>();
// 初始化输出图像迭代器
cv::MatIterator_<cv::Vec3b> resIterStart = tempImage.begin<cv::Vec3b>();
cv::MatIterator_<cv::Vec3b> resIterEnd = tempImage.end<cv::Vec3b>();
// 遍历图像反色处理
while (srcIterStart != srcIterEnd)
{
(*resIterStart)[0] = 255 - (*srcIterStart)[0];
(*resIterStart)[1] = 255 - (*srcIterStart)[1];
(*resIterStart)[2] = 255 - (*srcIterStart)[2];
// 迭代器递增
srcIterStart++;
resIterStart++;
}
return tempImage;
}
int main()
{
// 装载图像
cv::Mat srcImage = cv::imread("F:/material/images/P0028-flower-02.jpg");
cv::Mat dstImage;
if (!srcImage.data)
return -1;
cv::imshow("srcImage", srcImage);
dstImage = srcImage.clone();
dstImage = inverseColor4(srcImage);
cv::imshow("dstImage", dstImage);
cv::waitKey(0);
return 0;
}
四种方法的运行结果都是一样的,如下:
用我在博文 https://www.hhai.cc/thread-111-1-1.html 中给出的C++代码段运行时间测量代码,可以测试出运行时间最短的是方法三(数据头指针法),其次依次是方法二(行指针法)、方法一(at法),用时最多的是方法四(迭代器法)。
相关文章
- qt实现web服务器加载vue应用进行C++和html混合编程-连载【6】-企业级系统开发实战连载系列 -技术栈(vue、element-ui、qt、c++、sqlite)
- 用C++_OpenCV生成一幅灰度值为0~255的测试图像(测试矩阵)
- Python-OpenCV难道比C++_OpenCV快?以调用Canny边缘检测函数为例
- OpenCV读取图片、判断读取是否成功、显示图片的代码(C++代码和Python代码)
- 详解OpenCV的视频背景/前景分割(背景建模/前景提取)类cv::BackgroundSubtractorKNN,并利用它实现对道路监控视频前景/背景的提取
- 图像或轮廓的Hu矩的定义、优缺点、适用范围,并利用OpenCV的函数HuMoments()和matchShapes()实现Hu矩的计算和轮廓匹配
- 详解C++标准库<sstream>中的类stringstream,并利用它实现OpenCV下的图片批量读取
- OpenCV-C++选择、提取感兴趣区域(ROI区域)【附用鼠标选取ROI区域的代码】
- OpenCV是可以把C++中的动态向量转换为MAT类型的
- 使用OpenCV的函数createTrackbar()创建窗口滑动条查找图像二值化的最优阈值的C++源码
- 实现图像旋转的MATLAB源码和OpenCV下的C/C++源码
- 利用OpenCV的函数findContours()和函数drawContours()进行轮廓的检测与绘制
- C++的Opencv动态库遇到的问题
- 遍历opencv中的mat像素的几种方法和概念
- c#调用c++(Opencv)dll的实例
- C#调用C++(opencv)中图片数据传递的问题
- opencv----(1) mat最好用,和IplImage,cvmat 比较
- OpenCV梯度运算、礼帽与黑帽
- Ubuntu16.04下python3.5和C++安装 / 卸载opencv
- Python代码库OpenCV之03读取和显示图片(含代码)
- 【图像处理】openCV光流法追踪运动物体
- 【图像处理】——opencv常用函数
- opencv bgr转rgb