Opencv3基础学习

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

2018-12-24  本文已影响5人  yuanCruise
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)
        );

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

    //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]
            );
        }
    }
//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]
                );
            }

        }
    }
    

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

注意多维度和多通道的区别
2.利用ptr<>()成员函数访问数组元素

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

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

给自己打个小广告

本人211硕士毕业生,目前从事深度学习,机器学习计算机视觉算法行业,目前正在将我的各类学习笔记发布在我的公众号中,希望感兴趣一起学习的同学们可以关注下~~~
本人微信公众号:yuanCruise


上一篇下一篇

猜你喜欢

热点阅读