Home > Back-end >  Run time error assigning cv::Mat element value using cv::Mat::at
Run time error assigning cv::Mat element value using cv::Mat::at

Time:05-13

In the following code I would like to assign a values to elements of a Mat variable in a loop. I get the runtime error below.

pair<Mat, Mat> meshgrid(vector<int> x, vector<int> y) {

    int sx = (int)x.size();
    int sy = (int)y.size();
    
    Mat xmat = Mat::ones(sy, sx, CV_16U);
    Mat ymat = Mat::ones(sy, sy, CV_16U);

    for (int i = 0; i < sx; i  ) {
        for (int j = 0; j < sy; j  ) {
            xmat.at<int>(i, j) = j;  // <------- here is place of error.
            cout << j << "\t";
        }
        cout << endl;
    }

    for (int i = 0; i < sx; i  ) {
        for (int j = 0; j < sy; j  ) {
            ymat.at<int>(i, j) = i;  // <------- here is place of error.
            cout << j << "\t";
        }
        cout << endl;
    }

    return make_pair(xmat, ymat);
}

This picture when debuging;

enter image description here

This is the run time error I get:

OpenCV(...) Error: Assertion failed
(((((sizeof(size_t)<<28)|0x8442211) >> ((traits::Depth<_Tp>::value) &
((1 << 3) - 1))*4) & 15) == elemSize1()) in cv::Mat::at, file
...\include\opencv2\core\mat.inl.hpp, line 1108

Thank you for your answers.

CodePudding user response:

I assume you meant to generate output similar to numpy.meshgrid, and Matlab meshgrid.

There are several errors in your code:

  1. The cv::Mat is initialized with type CV_16U (i.e. 16 bit unsigned value), but when you access the elements with at you use int (which is 32bit signed). You should change it to at<unsigned short> (or change the type of the cv::Mat to 32 bit signed - CV_32S).
  2. You initialized the cv::Mat with wrong sizes: xmat has size of (sy, sx), but ymat has size of (sy, sy).
  3. The indices (row, col) you used to access the mat elements were incorrect. To make it easier to use correctly, I changed the names of the dimentions to rows, cols, and the loop indices to iRow, iCol.
  4. The values in the matrices should come from the values in x and y vectors (not the indices).

See updated code below (and the notes following it regarding the changes):

#include <opencv2/core/core.hpp>
#include <vector>
#include <iostream>

std::pair<cv::Mat, cv::Mat> meshgrid(std::vector<unsigned short> const & x, std::vector<unsigned short> const & y)
{
    int cols = static_cast<int>(x.size());
    int rows = static_cast<int>(y.size());
    cv::Mat xmat(rows, cols, CV_16U);
    cv::Mat ymat(rows, cols, CV_16U);

    for (int iRow = 0; iRow < rows;   iRow) {
        auto * pRowData = xmat.ptr<unsigned short>(iRow);
        for (int iCol = 0; iCol < cols;   iCol) {
            pRowData[iCol] = x[iCol];
            std::cout << pRowData[iCol] << "\t";
        }
        std::cout << std::endl;
    }

    std::cout << std::endl;

    for (int iRow = 0; iRow < rows;   iRow) {
        auto * pRowData = ymat.ptr<unsigned short>(iRow);
        for (int iCol = 0; iCol < cols;   iCol) {
            pRowData[iCol] = y[iRow];
            std::cout << pRowData[iCol] << "\t";
        }
        std::cout << std::endl;
    }

    return std::make_pair(std::move(xmat), std::move(ymat));
}

int main()
{
    std::vector<unsigned short> xxx{ 1,2 };
    std::vector<unsigned short> yyy{ 10,11,12 };
    auto p = meshgrid(xxx, yyy);
    return 0;
}

Output:

1       2
1       2
1       2

10      10
11      11
12      12

Some notes:

  1. I might have misunderstood which values you wanted to set in the cv::Mat's. But at least now you have code that does not crash. You can change the assigned values as you wish.
  2. Using at to access cv::Mat elements one after the other is very inefficient, because at contains some validations for every access. It's a lot more efficient to use the cv::Mat method ptr, that gives you a pointer to the data of a row. Then you can use this pointer to traverse the row more efficiently - see above
  3. In any method, it is more efficient to traverse a cv::Mat one row after another (and not column by column). This causes you to access continous memory, and decrease the number of cache misses.
  4. In your real code, it's better to separate calculations from I/O. Therefore it's better if your meshgrid function will only create the matrices. Print them outside if you need.
  5. No need to initialize the cv::Mats to ones, because immediatly afterwards we set the values for all elements.
  6. In my code x and y are passed to the function by const refernce. It is more efficient (avoid copy) and also forces the compiler to verify the vectors are not modified.
  7. Better to avoid using namespace std - see here Why is "using namespace std;" considered bad practice?. From similar reasons I recomend to avoid using namespace cv as well.
  • Related