在 C++ 中修改 cv::Mat 数据时出现分段错误

Posted

技术标签:

【中文标题】在 C++ 中修改 cv::Mat 数据时出现分段错误【英文标题】:Segmentation faults when modifying cv::Mat data in c++ 【发布时间】:2021-08-06 16:24:01 【问题描述】:

如果这个问题太宽泛,请告诉我,但我正在尝试学习一些 c++,所以我认为尝试重新创建一些 opencv 函数是个好主意。

我仍在使用 opencv 的 API 抓取帧或读取图像,但是我想将 cv::Mat 输入到我的自定义函数中,在那里我修改其数据并将其返回以显示它。 (例如一个模糊图像的函数,我将原始 Mat 传递给一个填充函数,然后将其输出到一个 fn,该函数将填充的图像与模糊内核进行卷积,并将 Mat 返回给 cv 进行显示)

我有点困惑最好(或正确)的方法是什么。 OpenCV 函数使用函数参数作为返回矩阵(cv_foo(cv::Mat src_frame, cv::Mat dst_frame)),但我并不完全清楚它是如何工作的,所以我尝试了一种更熟悉的方法,比如

cv::Mat my_foo(cv::Mat src_frame) 
     // do processing on src_frame data
     return dst_frame;

从 src_frame 访问数据的位置我使用 uchar* framePtr = frame.data; 并创建我遵循 this 建议的 dst_frame

cv::Mat dst_frame = cv::Mat(n_rows, n_cols, CV_8UC3);
memcpy(dst_frame.data, &new_data_array, sizeof(new_data_array));

然而,我遇到了各种难以调试的分段错误,因为它们似乎几乎是随机发生的(这可能是由于我使用 frame.data 或类似的东西处理内存管理的方式吗?)。

所以回到我最初的问题,以最一致的方式访问、修改和传递来自 cv::Mat 的数据的最佳方式是什么?

我认为对我来说最直观的感觉(来自 numpy)是从原始 Mat 中提取数据数组,在我的整个处理过程中使用它,然后在显示之前将其重新打包到 Mat 中,这也允许我可以将任何自定义数组输入到处理中,而不必将其变成 Mat,但我不确定如何最好地做到这一点(或者它是否是正确的方法)。

谢谢!

编辑: 我将尝试突出显示代码中的主要错误。

我尝试复制的功能之一是从 bgr 到灰度的转换,我的代码如下所示

cv::Mat bgr_to_greyscale(cv::Mat& frame)

    int n_rows = frame.rows;
    int n_cols = frame.cols;
    uchar* framePtr = frame.data;
    int channels = frame.channels();

    uchar grey_array[n_rows*n_cols];

    for(int i=0; i<n_rows; i++)
        for(int j=0; j<n_cols; j++)
            uchar pixel_b = framePtr[i*n_cols*channels + j*channels];
            uchar pixel_g = framePtr[i*n_cols*channels + j*channels + 1];
            uchar pixel_r = framePtr[i*n_cols*channels + j*channels + 2];

            uchar pixel_grey = 0.299*pixel_r + 0.587*pixel_g + 0.144*pixel_b;

            grey_array[i*n_cols + j] = pixel_grey;

        
    

    cv::Mat dst_frame = cv::Mat(n_rows, n_cols, CV_8UC1, &grey_array);

    return dst_frame;

但是,当我在示例图像上显示此函数的结果时,我得到this 结果:图像的底部看起来像随机噪声,我该如何解决这个问题?我的代码到底出了什么问题?

谢谢!

【问题讨论】:

“这可能是因为我使用 frame.data 处理内存管理的方式吗?”。我们无从得知。分段错误是一个安全指标,表明您的代码中至少有一个错误,涉及访问不属于您的程序的内存。调试器会提供线索。 使用 cv::Mat dst = src.clone();这将为像素数据在堆上分配 ne 内存。当您返回 cv::Mat 时,返回值将链接到堆上的同一内存。当没有 cv::Mat 链接到该内存时,堆上的内存将被释放。 【参考方案1】:

这个问题过于宽泛,无法详细回答,但通常cv::Mat 是图像数据的包装器,类似于std::vector&lt;int&gt; 是动态分配的int 值数组的包装器或std::string 是动态分配的字符数组的包装器,但有一个例外:cv::Mat 不会在分配或使用复制构造函数时执行图像数据的深层复制。

std::vector<int> b =  1, 2, 3, 4;
std::vector<int> a = b;
// a now contains a copy of b and a[0] = 42 will not effect b.

cv::Mat b = cv::imread( ... );
cv::Mat a = b;
// a and b now wrap the same data.

但话虽如此,您不应该使用memcpy 等。人。复制cv::Mat ...您可以使用clonecopyTo 进行复制。 From the cv documentation:

Mat F = A.clone();
Mat G;
A.copyTo(G);

【讨论】:

以上是关于在 C++ 中修改 cv::Mat 数据时出现分段错误的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中使用向量时出现分段错误

在 C++ 中操作向量时出现分段错误

在 C++ 中使用向量时出现分段错误?

运行 C++ 代码时出现分段错误

在 C++ 索引程序中使用向量时出现分段错误 11

添加向量时出现分段错误。 (C++)