Accessing Elements of a Cv::Mat with At<Float>(I, J). Is It (X,Y) or (Row,Col)

Accessing elements of a cv::Mat with atfloat(i, j). Is it (x,y) or (row,col)?

OpenCV, like may other libraries, treat matrices (and images) in row-major order. That means every access is defined as (row, column).

Notable exceptions from this general rule are Matlab and Eigen libraries.

Accessing a matrix element in the Mat object (not the CvMat object) in OpenCV C++

On the documentation:

http://docs.opencv.org/2.4/modules/core/doc/basic_structures.html#mat

It says:

(...) if you know the matrix element
type, e.g. it is float, then you can
use at<>() method

That is, you can use:

Mat M(100, 100, CV_64F);
cout << M.at<double>(0,0);

Maybe it is easier to use the Mat_ class. It is a template wrapper for Mat.
Mat_ has the operator() overloaded in order to access the elements.

opencv multi channel element access

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;

Accessing elements of cv::Mat

char value = mask.at<char>(pt);

OpenCV Error: Assertion failed / when visit the pixel of mat

You're doing two things wrong:

  1. You image is grayscale (uchar), so you need to access it with .at<uchar>(...)
  2. You are accessing the image as (col, row), while the OpenCV (and any matrix-related) convention is (row, col). You can also use mnemonics in your iteration: r and c for row and col respectively.

    for(int r=0; r<dst.rows; r++) {
    for( c=0; c<dst.cols; c++){
    dstSum += dst.at<uchar>(r, c);
    }
    }

You can also improve your code a little:

  1. You can use Mat1b and access it like at(i,j)

    Mat1b dst = imread(...);
    for(int r=0; r<dst.rows; r++) {
    for( c=0; c<dst.cols; c++){
    dstSum += dst(r, c);
    }
    }
  2. The new OpenCV name is IMREAD_GRAYSCALE:

    Mat1b dst = imread("path_to_image", IMREAD_GRAYSCALE);
  3. You can use cv::sum to sum up all the pixels. The resulting improved code will be:

    Mat1b dst = imread("path_to_image", IMREAD_GRAYSCALE);
    int dstSum = sum(dst)[0]; // take only first channel

opencv Mat memory access error

Um... Never mind. It turned out that the exception was real and not a problem with this code. I am using sample code from Point Grey. It converts from a Point Grey ImagePtr to a Mat using a Mat constructor that just copies the pointer to the data. The ImagePtr went out of scope, so the data was invalid when I attempted to use it. Mat.CopyTo() solved the problem. I highly recommend reading this if you need a fast way to solve a similar problem.

Convert Mat to Array/Vector in OpenCV

If the memory of the Mat mat is continuous (all its data is continuous), you can directly get its data to a 1D array:

std::vector<uchar> array(mat.rows*mat.cols*mat.channels());
if (mat.isContinuous())
array = mat.data;

Otherwise, you have to get its data row by row, e.g. to a 2D array:

uchar **array = new uchar*[mat.rows];
for (int i=0; i<mat.rows; ++i)
array[i] = new uchar[mat.cols*mat.channels()];

for (int i=0; i<mat.rows; ++i)
array[i] = mat.ptr<uchar>(i);

UPDATE: It will be easier if you're using std::vector, where you can do like this:

std::vector<uchar> array;
if (mat.isContinuous()) {
// array.assign(mat.datastart, mat.dataend); // <- has problems for sub-matrix like mat = big_mat.row(i)
array.assign(mat.data, mat.data + mat.total()*mat.channels());
} else {
for (int i = 0; i < mat.rows; ++i) {
array.insert(array.end(), mat.ptr<uchar>(i), mat.ptr<uchar>(i)+mat.cols*mat.channels());
}
}

p.s.: For cv::Mats of other types, like CV_32F, you should do like this:

std::vector<float> array;
if (mat.isContinuous()) {
// array.assign((float*)mat.datastart, (float*)mat.dataend); // <- has problems for sub-matrix like mat = big_mat.row(i)
array.assign((float*)mat.data, (float*)mat.data + mat.total()*mat.channels());
} else {
for (int i = 0; i < mat.rows; ++i) {
array.insert(array.end(), mat.ptr<float>(i), mat.ptr<float>(i)+mat.cols*mat.channels());
}
}

UPDATE2: For OpenCV Mat data continuity, it can be summarized as follows:

  • Matrices created by imread(), clone(), or a constructor will always be continuous.
  • The only time a matrix will not be continuous is when it borrows data (except the data borrowed is continuous in the big matrix, e.g. 1. single row; 2. multiple rows with full original width) from an existing matrix (i.e. created out of an ROI of a big mat).

Please check out this code snippet for demonstration.



Related Topics



Leave a reply



Submit