【opencv15】cv::Mat类单独访问数组元素

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qiu931110/article/details/86560406
1.利用at<>()成员函数访问数组元素

最基础的直接访问手段是通过模板成员函数at<>(),对数组元素进行访问。因为是模板函数,所以该函数可以接受各种类型和维度的参数。使用该函数访问数组元素的例子如下:

#include "opencv2/opencv.hpp"
#include <iostream>
using namespace cv;

int main()
{
	
	Mat m_1 = cv::Mat::eye(10,10,CV_32FC1);
	Mat m_2 = cv::Mat::eye(3, 3, CV_32FC3);

	int sz[] = { 3, 3, 3 };
	Mat m_3(3, sz, CV_32FC3, Scalar::all(0));

//part one
	printf(
		"Float Element (3,3) is %f\n",
		m_1.at<float>(3, 3)
		);


//part two	
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			printf(
				"Float Element (x,y)=(%d,%d) is (C0,C1,C2)=(%f,%f,%f)\n",
				i,
				j,	
				m_2.at<cv::Vec3f>(i, j)[0],
				m_2.at<cv::Vec3f>(i, j)[1],
				m_2.at<cv::Vec3f>(i, j)[2]

			);
		}
	}
	


//part three
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			for (int k = 0; k < 3; k++)
			{
				printf(
					"Float Element (x,y,z)=(%d,%d,%d) is (C0,C1,C2)=(%f)\n",
					i,
					j,
					k,
					m_3.at<cv::Vec3f>(i, j,k)[0],
					m_3.at<cv::Vec3f>(i, j,k)[1],
					m_3.at<cv::Vec3f>(i, j,k)[2]
				);
			}

		}
	}
	
	

	getchar();

	return 0;


}

代码输出结果

结果分析:上述代码有三个部分。
部分一:访问的是二维,单通道数组。

//part 1
Mat m_1 = cv::Mat::eye(10,10,CV_32FC1);
printf(
		"Float Element (3,3) is %f\n",
		m_1.at<float>(3, 3)
		);
  • Mat m_1 = cv::Mat::eye(10,10,CV_32FC1):定义了一个10乘以10的32float的一维单位数组(CV_32FC1)
  • m_1.at(3,3):模板成员函数m_1.at<>,输入模板参数float,(3,3)表示访问的是一维数组中第四行第四列的元素,如下图所示。
    二维数组

部分二:访问的是二维,三通道数组。

    //part 2
    Mat m_2 = cv::Mat::eye(3,3,CV_32FC3);
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            printf(
                "Float Element (x,y)=(%d,%d) is (Z0,Z1,Z2)=(%f,%f,%f)\n",
                i,
                j,
                m_2.at<cv::Vec3f>(i, j)[0],
                m_2.at<cv::Vec3f>(i, j)[1],
                m_2.at<cv::Vec3f>(i, j)[2]
            );
        }
    }
  • Mat m_2 = cv::Mat::eye(3,3,CV_32FC3):定义了一个3乘以3的32float的三维单位数组(CV_32FC3)
  • m_2.atcv::Vec3f(i, j)[0]:模板成员函数m_2.at<>,输入模板参数cv::Vec3f,(i,j)[0]表示访问的是三维数组中第i+1行,第j+1列,通道一的元素。
  • m_2.atcv::Vec3f(i, j)[1]:模板成员函数m_2.at<>,输入模板参数cv::Vec3f,(i,j)[0]表示访问的是三维数组中第i+1行,第j+1列,通道二的元素。
  • m_2.atcv::Vec3f(i, j)[2]:模板成员函数m_2.at<>,输入模板参数cv::Vec3f,(i,j)[0]表示访问的是三维数组中第i+1行,第j+1列,通道三的元素。
  • cv::Vec3f:对于访问多通道(本例中为3通道)数组而言,不能直接用float作为模板参数,因为正如上面三个访问元素语句所示,其实对于当前类型而言直接访问的不是单个元素,而是所有通道中的第i+1行,j+1列的元素向量(m_2.atcv::Vec3f(i, j)),空间示意图如下图所示。因此在访问多通道数组时,需要传入固定向量类型作为模板参数。
    上图eye成员函数生成的三通道矩阵示意图
    部分三:访问的是三维,三通道数组。
//part three
    int sz[] = { 3, 3, 3 };
	Mat m_3(3, sz, CV_32FC3, Scalar::all(0));
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            for (int k = 0; k < 3; k++)
            {
                printf(
                    "Float Element (x,y,z)=(%d,%d,%d) is (C0,C1,C2)=(%f)\n",
                    i,
                    j,
                    k,
                    //对应的是此种类型CV_32FC3,若类型为CV_32FC1时
                    //需要用  m_3.at<float>(i, j,k)访问。
                    m_3.at<cv::Vec3f>(i, j,k)[0],
                    m_3.at<cv::Vec3f>(i, j,k)[1],
                    m_3.at<cv::Vec3f>(i, j,k)[2]
                );
            }

        }
    }
    

该部分通过直接访问的手段,让我们能够进一步感受到,多维度和多通道的区别。下图之前也有用到过,多维度和多通道的概念切记不要混淆。

  • m_3.at(i, j,k):上述注释中也有提到,当数据类型为单通道的时候,需要用float访问,多通道的时候需要用固定向量类的对应格式的别名来访问(此例中是三通道浮点数所以用Vec3f)。

注意多维度和多通道的区别

2.利用ptr<>()成员函数访问数组元素

要访问二维数组,还可以提取指向数组特定行的C样式指针。 这是通过cv :: Mat的ptr <>()模板成员函数完成的(回想一下,数组中的数据是连续的,因此以这种方式访问特定列是没有意义的; 我们很快就会看到正确的方法。)
与at>()一样,ptr <>()是一个使用类型名称实例化的模板函数。 它需要一个整数参数来指示您希望获得指针的行。 该函数返回一个指向构造数组的基本类型的指针(即,如果数组类型是CV_32FC3,则返回值的类型为float *)。因此,给定float类型的三通道矩阵mtx,构造mtx.ptr (3)将返回指向mtx第3行中第一个元素的第一个(浮点)通道的指针。这通常是 访问数组元素的最快方法,因为一旦有了指针,就可以找到数据。

因此有两种方法可以在矩阵mtx中获得指向数据的指针。 一种是上述的使用ptr <>()成员函数。 另一种是直接使用成员指针数据,并使用成员数组step []来计算地址。后一种选择类似于C接口中常用的选项,但在at<>(),ptr <>()和迭代器访问数组的过程中,通常不再优先 。 话虽如此,直接地址计算可能仍然是最有效的,特别是当您处理大于两个维度的数组时。

关于C风格的指针访问,最终要的一点是, 如果要访问数组中的所有内容,您可能希望能够一次迭代一行; 这是因为行不一定会在数组中连续打包(packed?)。但是成员函数isContinuous()会告诉你行是否在数组中连续打包(packed)。如果判断完了之后,当前数组中行是连续的,那么你只需要找到第一行中第一个元素的指针,那么你就可以访问真个数组,就好像这个多维数组是个巨大的一维数组一样。

3.利用迭代器访问数组元素

顺序访问的另一种形式是使用cv :: Mat中内置的迭代器机制。这种机制和STL标准库中的提供的机制非常的类似。基本思想是OpenCV提供了迭代器模板。cv :: Mat成员函数begin()和end()返回此类型的对象。这种迭代方法很方便,因为迭代器足够智能,可以自动处理连续打包和非连续打包,以及处理数组中的任意数量的维度。

必须声明每个迭代器,并在声明阶段将其指定为构造数组对象的类型。以下是迭代器的一个简单示例,用于计算三通道元素的三维数组中的“最长”元素。

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

int main()
{

	//int sz[] = { 3, 3, 3 };
	//Mat m_3(3, sz, CV_32FC3, Scalar::all(0));


	int sz[3] = { 4, 4, 4 };
	cv::Mat m(3, sz, CV_32FC3); // A three-dimensional array of size 4-by-4-by-4
	cv::randu(m, -1.0f, 1.0f); // fill with random numbers from -1.0 to 1.0
	Mat_<Vec3f>::iterator it = m.begin<Vec3f>();
	Mat_<Vec3f>::iterator itend = m.end<Vec3f>();

	while (it != itend) {
		(*it)[0] = 255;
		(*it)[1] = 255;
		(*it)[2] = 255;
		it++;
	}

	getchar();
	return 0;
}

  • cv::randu(m, -1.0f, 1.0f):随机生成-1到1的值,并填满多维多通道数组。
  • Mat_::iterator it = m.begin():利用begin成员函数,返回Vec3f类型的指针(迭代器),指向数组的头部。
  • Mat_::iterator itend = m.end():利用end成员函数,返回Vec3f类型的指针(迭代器),指向数组的尾部。
  • (*it)[0] = 255;
    (*it)[1] = 255;
    (*it)[2] = 255:对每一个位置的三个通道作一个相同的操作。

在对整个数组执行操作时,通常会使用基于迭代器的访问,或者在多个数组之间执行一些基于元素级的操作(Elementwise)。比如将两个数组相加或将数组从RGB颜色空间转换为HSV颜色空间这样的案例中,对于每个像素位置而言,进行的都是相同精确操作,正如上述代码所示,此时使用迭代器会比较方便。

猜你喜欢

转载自blog.csdn.net/qiu931110/article/details/86560406
今日推荐