昊虹AI笔记网

 找回密码
 立即注册
搜索
查看: 877|回复: 0
收起左侧

OpenCV-C++对图像像素的四种遍历操作

[复制链接]

238

主题

241

帖子

931

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
931
昊虹君 发表于 2022-11-7 12:01 | 显示全部楼层 |阅读模式
OpenCV-C++对图像像素的四种遍历操作

在OpenCV-C++环境下,图像的存储容器是Mat对象,所以对图像像素的遍历,就是对Mat对象每一个数据元素的遍历。

关于Mat对象的详细介绍,可以参见博文 https://www.hhai.cc/thread-70-1-1.html

本文提供四种方式实现对OpenCV的Mat类矩阵元素的遍历。

我们通过遍历矩阵的元素,实现图像的反色处理,实际上就是用最大灰度值255减去元素值。

四种方法中用到的图像的下载链接如下:
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++中对图像像素值访问和遍历最常用的方法。

示例代码如下:
[C++] 纯文本查看 复制代码
//出处:昊虹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 inverseColor1(cv::Mat srcImage)
{
        cv::Mat tempImage = srcImage.clone();
        int row = tempImage.rows;
        int col = tempImage.cols;
        // 对各个像素点遍历进行取反操作
        for (int i = 0; i < row; i++)
        {
                for (int j = 0; j < col; j++)
                {
                        // 分别对各个通道进行反色处理
                        tempImage.at<cv::Vec3b>(i, j)[0] = 255 - tempImage.at<cv::Vec3b>(i, j)[0];
                        tempImage.at<cv::Vec3b>(i, j)[1] = 255 - tempImage.at<cv::Vec3b>(i, j)[1];
                        tempImage.at<cv::Vec3b>(i, j)[2] = 255 - tempImage.at<cv::Vec3b>(i, j)[2];
                }
        }
        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 = inverseColor1(srcImage);

        cv::imshow("dstImage", dstImage);

        cv::waitKey(0);
        return 0;
}

方法二:利用行指针进行遍历
代码如下:
[C++] 纯文本查看 复制代码
//出处:昊虹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()判断数据是否是连续存储的,满足此条件,才可使用该方法。
示例代码如下:
[C++] 纯文本查看 复制代码
//出处:昊虹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的迭代器使用很简单,大家看了大家的代码就知道怎么用了,这里就不多叙述了。
示例代码如下:
[C++] 纯文本查看 复制代码
//出处:昊虹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法),用时最多的是方法四(迭代器法)。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|昊虹AI笔记网 ( 蜀ICP备2024076726 )

GMT+8, 2024-5-19 12:37 , Processed in 0.021726 second(s), 21 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表