在 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<int>
是动态分配的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
...您可以使用clone
或copyTo
进行复制。 From the cv documentation:
Mat F = A.clone();
Mat G;
A.copyTo(G);
【讨论】:
以上是关于在 C++ 中修改 cv::Mat 数据时出现分段错误的主要内容,如果未能解决你的问题,请参考以下文章