运行 使用 cv::Mat::at 分配 cv::Mat 元素值时出错

Run time error assigning cv::Mat element value using cv::Mat::at

在下面的代码中,我想在循环中为 Mat 变量的元素赋值。我收到以下 运行 时间错误。

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

调试时的这张图;

这是我得到的 运行 时间错误:

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

感谢您的回答。

我假设您打算生成类似于 numpy.meshgrid, and Matlab meshgrid 的输出。

您的代码中有几个错误:

  1. cv::Mat 初始化为 CV_16U 类型(即 16 位无符号值),但是当您使用 at 访问元素时,您使用 int (这是32 位签名)。 您应该将其更改为 at<unsigned short>(或将 cv::Mat 的类型更改为 32 位有符号 - CV_32S)。
  2. 您用错误的大小初始化了 cv::Matxmat 的大小为 (sy, sx),但 ymat 的大小为(sy, sy).
  3. 您用来访问 mat 元素的索引(行、列)不正确。为了更容易正确使用,我将尺寸的名称更改为 rowscols、 和 iRowiCol.
  4. 的循环索引
  5. 矩阵中的值应来自 xy 向量(而非索引)中的值。

请参阅下面的更新代码(以及后面有关更改的注释):

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

输出:

1       2
1       2
1       2

10      10
11      11
12      12

一些注意事项:

  1. 我可能误解了您想在 cv::Mat 中设置的值。但至少现在您的代码不会崩溃。您可以根据需要更改分配的值。
  2. 使用at 一个接一个地访问cv::Mat 元素是非常低效的,因为at 包含对每次访问的一些验证。 使用 cv::Mat 方法 ptr 的效率要高得多,它会为您提供指向一行数据的指针。然后你可以使用这个指针更有效地遍历行 - 见上文
  3. 在任何方法中,逐行(而不是逐列)遍历 cv::Mat 效率更高。这会导致您访问连续内存,并减少高速缓存未命中的次数。
  4. 在您的实际代码中,最好将计算与 I/O 分开。因此,如果您的 meshgrid 函数只创建矩阵会更好。如果需要,请打印出来。
  5. 无需将 cv::Mat 初始化为 1,因为之后我们会立即设置所有元素的值。
  6. 在我的代码中,xy 通过常量引用传递给函数。它更有效(避免复制)并且还强制编译器验证向量未被修改。
  7. 最好避免 using namespace std - 请参阅此处 Why is "using namespace std;" considered bad practice?。 出于类似的原因,我也建议避免 using namespace cv