57

I'm trying to learn how to use OpenCV's new C++ interface.

How do I access elements of a multi channel matrix? For example:

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

What is the easiest way to do this? Something like cvSet2D of the old interface.
What is the most efficient way? Similar to using direct pointers in the old interface.

Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
Yair
  • 1,325
  • 2
  • 13
  • 18

5 Answers5

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;

Update : for OpenCV2.0

1. choose one type to represent the element

Mat (or CvMat) has 3 dimensions: row, col, channel.
We can access one element (or pixel) in the matrix by specifying the row and col.

CV_32FC2 means the element is 32bit floating point value with 2 channels.
So elem in above code is one acceptable representation of CV_32FC2.

You can use other representations you like. For example :

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

OpenCV2.0 adds some new types to represent the element in the matrix,like :

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

So we can use Vec<float,2> to represent CV_32FC2, or use :

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

See the source code to get more type that can represent your element.
Here we use Vec2f

2. access the element

The easiest and efficiant way to access the element in the Mat class is Mat::at.
It has 4 overloads :

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. interact with old interface

Mat provides 2 conversion functions:

// 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;
Steve
  • 1,448
  • 11
  • 18
OwnWaterloo
  • 1,963
  • 14
  • 11
  • Thanks, but not quite what I was asking for. You are using CvMat - the old (version 1.1) c API. I want a similar method for the new (2.0a) c++ API using the Mat class. Is there something like CV_MAT_ELEM for the Mat class? – Yair Dec 03 '09 at 08:34
  • I'm sorry.I forget that OpenCV2.0 has been released. I downloaded the 2.0 source and found some ways to approach to your goal.See the updated code. – OwnWaterloo Dec 03 '09 at 16:43
  • is there a better way to access CvMat wihtout using CV_MAT_ELEM like CV_MAT_ELEM(old_mat, elem, row, col).f1 ?? with f1 or f2 fixed, we cannot iterate programmatically through matrix i.e. i,j index – TSL_ Aug 29 '12 at 06:21
22

Vic you must use Vec3b instead of Vec3i:

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;
        }
    }
}
Ivan
  • 221
  • 2
  • 2
8

You can access the underlying data array directly:

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

myMat.ptr<float>(y)[2*x]; // first channel
myMat.ptr<float>(y)[2*x+1]; // second channel
crenate
  • 3,370
  • 2
  • 20
  • 13
  • 3
    It worths saying that y is image rows and x is image columns. You need to multiply x by 2, because you have 2 chanels and in OpenCV image is represented as HEIGHT x WIDTH x CHANNELS matrix in a row-major order. – Temak Dec 09 '15 at 12:17
3

it depends on the datatype of the Mat you are using, if it is numeric like CV_32FC1 you can use:

myMat.at<float>(i, j)

if it is a uchar type then you can access an element using

(symb.at<Vec3b>(i, j)).val[k]

where k is the channel, thats 0 for grayscale images and 3 for colored

Sami Dalati
  • 151
  • 1
  • 3
0

The best way to access multi channel array with the c++ api is by creating a pointer to a specific row using the ptr method.

For example;

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

where

  • type: the datatype(float, int, char ect..)
  • i: row you're interested in
  • Nc: the number of channels
  • j: the column you're interested in
  • c: the column you're interested in(0-3)

For information on other c->c++ conversion check out this link: Source

aheigins
  • 2,574
  • 3
  • 19
  • 26