四种方式实现对OpenCV的MAT类矩阵元素的遍历

Posted 昊虹算法

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了四种方式实现对OpenCV的MAT类矩阵元素的遍历相关的知识,希望对你有一定的参考价值。

图像的每个通道都可看作一个二维矩阵,矩阵中的元素,是我们进行运算时的基本单位,所以遍历矩阵每个元素的操作是我们经常要用到的。

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

以下四个代码通过对矩阵元素的遍历实现图像的反色操作。

四个代码中用到的图像的下载链接如下:

https://pan.baidu.com/s/1JEy2tiuwCKDi4n9TfuVyTA 提取码:201q 

方法一:下标遍历法,格式为M.at<typename>(i,j)

image.at<cv::Vec3b>(i,j):取出灰度图像image中第i行第j列点的像素值;
image.at<Vec3b>(i,j)[k]:取出彩色图像image第k通道中第i行第j列点的像素值。
Vec3b表示数据类型为:uchar类型、长度为3的向量,其定义如下:

typedef Vec< uchar, 3 > 	cv::Vec3b
//opencv版本:OpenCV3.0
//VS版本:VS2013
//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理需求也可联系博主
//图像处理技术交流QQ群 271891601
 
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>    
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>
 
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;

方法二:利用行指针遍历

//opencv版本:OpenCV3.0
//VS版本:VS2013
//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理需求也可联系博主
//图像处理技术交流QQ群 271891601
 
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>    
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>

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行数据的指针
要使用这种方法有一个前提:那就是图像每一行的数据在内存里是连续存储的,并且每个像素的三个通道数据按顺序存储。
如果不仅每一行的数据在内存里是连续存储的,行与行间也是连续的,那么就可以用下面的第三种方法。

方法三:利用函数isContinuous()先判断矩阵数据是否连续存储

//opencv版本:OpenCV3.0
//VS版本:VS2013
//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理需求也可联系博主
//图像处理技术交流QQ群 271891601
 
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>    
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>

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;

如果矩阵满足isContinuous的条件,即不仅每一行的数据在内存里是连续存储的,行与行间也是连续的,我们就可以把图像完全展开,看成是一行。这种方法的运行效率要比方法二高。

方法四:使用OpenCV的迭代器来遍历

OpenCV的迭代器使用很简单,大家看了大家的代码就知道怎么用了,这里就不多叙述了。

//opencv版本:OpenCV3.0
//VS版本:VS2013
//博主微信/QQ 2487872782
//有问题可以联系博主交流
//有图像处理需求也可联系博主
//图像处理技术交流QQ群 271891601
 
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>    
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/highgui/highgui_c.h>

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://blog.csdn.net/wenhao_ir/article/details/51546399 中提到的程序运行时间测量方法,可以测试出运行时间最短的是方法三(isContinuous判别法),其次依次是方法二(行指针法)、方法一(at法)。用时最多的是方法四(迭代器法)。

以上是关于四种方式实现对OpenCV的MAT类矩阵元素的遍历的主要内容,如果未能解决你的问题,请参考以下文章

opencv怎么给mat赋值

02OpenCV数据载入显示与保存

OpenCV中的MAT类矩阵的各种基本运算及示例代码(加减乘点乘点除乘方累加转置等)

OPENCV学习笔记2-8_Mat_类

OpenCV_Mat类对象常用属性值的获取方法

如何使用 opencv 库将 3D 矩阵的元素归零?