OpenCV多通道元素访问

57

我正在尝试学习如何使用OpenCV的新C++接口。

如何访问多通道矩阵的元素?例如:

Mat myMat(size(3, 3), CV_32FC2);

for (int i = 0; i < 3; ++i)
{
    for (int j = 0; j < 3; ++j)
    {
        //myMat_at_(i,j) = (i,j);
    }
}

如何最简便地实现?类似旧接口中的cvSet2D。
哪种方法最有效?就像在旧接口中使用直接指针一样。

5个回答

65
typedef struct elem_ {
        float f1;
        float f2;
} elem;
elem data[9] = { 0.0f };
CvMat mat = cvMat(3, 3, CV_32FC2, data );

float f1 = CV_MAT_ELEM(mat, elem, row, col).f1;
float f2 = CV_MAT_ELEM(mat, elem, row, col).f2;

CV_MAT_ELEM(mat, elem, row, col).f1 = 1212.0f;
CV_MAT_ELEM(mat, elem, row, col).f2 = 326.0f;

更新:适用于OpenCV2.0

1. 选择一种类型来代表元素

Mat(或CvMat)有三个维度:行、列和通道。
我们可以通过指定行和列来访问矩阵中的一个元素(或像素)。

CV_32FC2 表示该元素是具有两个通道的32位浮点值。
因此,上面代码中的 elemCV_32FC2 的一种可接受的表示方法。

您可以使用其他您喜欢的表示方法。例如:

typedef struct elem_ { float val[2];    } elem;
typedef struct elem_ { float x;float y; } elem;

OpenCV2.0新增了一些用于表示矩阵中元素的新类型,例如:

template<typename _Tp, int cn> class CV_EXPORTS Vec // cxcore.hpp (208)

因此我们可以使用Vec<float,2>来表示CV_32FC2,或者使用:

typedef Vec<float, 2> Vec2f; // cxcore.hpp (254)

查看源代码可以获取更多能够表示您的元素的类型。
这里我们使用Vec2f

2. 访问元素

在Mat类中访问元素最简单有效的方法是使用Mat::at函数。
它有4种重载:

template<typename _Tp> _Tp& at(int y, int x);                // cxcore.hpp (868)
template<typename _Tp> const _Tp& at(int y, int x) const;    // cxcore.hpp (870)
template<typename _Tp> _Tp& at(Point pt);                    // cxcore.hpp (869)
template<typename _Tp> const _Tp& at(Point pt) const;        // cxcore.hpp (871)
// defineded in cxmat.hpp (454-468)

// we can access the element like this :
Mat m( Size(3,3) , CV_32FC2 );
Vec2f& elem = m.at<Vec2f>( row , col ); // or m.at<Vec2f>( Point(col,row) );
elem[0] = 1212.0f;
elem[1] = 326.0f;
float c1 = m.at<Vec2f>( row , col )[0]; // or m.at<Vec2f>( Point(col,row) );
float c2 = m.at<Vec2f>( row , col )[1];
m.at<Vec2f>( row, col )[0] = 1986.0f;
m.at<Vec2f>( row, col )[1] = 326.0f;

3. 与旧界面进行交互

Mat提供了2个转换函数:

// converts header to CvMat; no data is copied     // cxcore.hpp (829)
operator CvMat() const;                            // defined in cxmat.hpp
// converts header to IplImage; no data is copied
operator IplImage() const;

// we can interact a Mat object with old interface :
Mat new_matrix( ... );
CvMat old_matrix = new_matrix;  // be careful about its lifetime
CV_MAT_ELEM(old_mat, elem, row, col).f1 = 1212.0f;

谢谢,但不是我想要的。您正在使用CvMat - 旧版(版本1.1)c API。 我想要一个类似的方法,用于新的(2.0a)c++ API,使用Mat类。 是否有像CV_MAT_ELEM这样的Mat类? - Yair
对不起,我忘了OpenCV2.0已经发布。我下载了2.0源代码,并找到了一些实现你目标的方法。请看更新后的代码。 - OwnWaterloo
有没有更好的方法来访问CvMat,而不使用CV_MAT_ELEM,例如CV_MAT_ELEM(old_mat,elem,row,col)。f1??由于f1或f2是固定的,我们无法通过矩阵进行编程迭代,即i,j索引。 - TSL_

22

你必须使用Vec3b而不是Vec3i,Vic:

for (int i=0; i<image.rows; i++)
{
    for (int j=0; j<image.cols; j++)
    {
        if (someArray[i][j] == 0)
        {
            image.at<Vec3b>(i,j)[0] = 0;
            image.at<Vec3b>(i,j)[1] = 0;
            image.at<Vec3b>(i,j)[2] = 0;
        }
    }
}

8
您可以直接访问底层数据数组:
Mat myMat(size(3, 3), CV_32FC2);

myMat.ptr<float>(y)[2*x]; // first channel
myMat.ptr<float>(y)[2*x+1]; // second channel

3
值得一提的是,y代表图像的行数,x代表图像的列数。由于在OpenCV中图像以行优先顺序表示为高度×宽度×通道数的矩阵,因此您需要将x乘以2,因为您有2个通道。 - Temak

3

这取决于您使用的Mat数据类型,如果它是数值型的,例如CV_32FC1,您可以使用以下代码:

myMat.at<float>(i, j)

如果它是uchar类型,那么您可以使用以下方式访问元素:
(symb.at<Vec3b>(i, j)).val[k]

k代表通道,对于灰度图像来说是0,对于彩色图像来说是3。


0

使用c++ api访问多通道数组的最佳方法是使用ptr方法创建指向特定行的指针。

例如:

type elem = matrix.ptr<type>(i)[N~c~*j+c]

其中

  • type:数据类型(float、int、char等)
  • i:您感兴趣的行
  • Nc:通道数
  • j:您感兴趣的列
  • c:您感兴趣的列(0-3)

有关其他c->c++转换的信息,请查看此链接:Source


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接