我想将cv :: Mat数据保存到线性数组中,但我不知道为什么会有bug。图像颜色为灰度(CV_8UC1
)。这是代码:
uchar* imgToArray(cv::Mat& img)
{
int n = img.rows;
int m = img.cols;
uchar* res = new uchar(m * n);
for(int row = 0; row < m; row++)
{
for(int col = 0; col < n; col++)
{
res[row * n + col] = img.at<uchar>(row, col);
}
}
return res;
}
提到的调试信息,
Program received signal SIGSEGV, Segmentation fault.
0x000055555555c0e9 in imgToArray (img=..., n=512, m=512) at ../conversion.cpp:10
10 res[row * n + col] = img.at<uchar>(row, col);
我对此很困惑。感谢任何提前提出建议的人!
您已创建一个值为m * n
的int对象
uchar* res = new uchar(m * n);
不是数组,应该是
uchar* res = new uchar[m * n];
除了rafix07指出的明显不正确的分配外,您的代码中还存在多个问题。一个问题是你是否真的需要拥有uchar
数组的所有权(如果没有,那么有更简单的方法可以解决这个问题),但我会假设你这样做。让我们从顶部开始吧。
uchar* imgToArray(cv::Mat& img, int n, int m);
uchar*
)。首先,我们返回一个数组,所以让我们明确一点。原始指针容易出现内存泄漏,C ++ 11已经存在了一段时间 - 我们可以做得更好。让它成为一个std::unique_ptr
,它也可以正确处理数组。
然后我们可以用std::make_unique<uchar[]>(size)
进行分配,在那里你不太可能犯下那种错误。
没有第二个的第一个问题是,如果你想以某种方式使用返回的数据,那就麻烦了。依靠用户必须调用另一个函数来获取它,或者必须自己计算它远非理想。所以,让我们使用std::pair
将大小与智能指针一起打包。
typedef std::pair<std::unique_ptr<uchar[]>, size_t> uchar_array_t;
uchar_array_t imgToArray(cv::Mat& img, int n, int m);
img
视为非const引用,即使您不打算修改它也是如此。如果您真的关心避免复制标题的开销,请使用const引用来明确您的意图。
int n
和int m
是多余的。它们也没有经过验证。除了与您想要变成“线性阵列”的区域相对应的cv::Mat
之外,没有理由给出该函数。 OpenCV已经提供了获得投资回报率的有效方法:cv::Mat::operator()
。让用户使用它,并删除多余的参数。
typedef std::pair<std::unique_ptr<uchar[]>, size_t> uchar_array_t;
uchar_array_t imgToArray(cv::Mat const& image);
img
是数据类型CV_8UC1
(即单个通道矩阵,其中每个元素是无符号字节),但不验证确实是这种情况。这意味着当您使用不同类型的图像调用它时,您将无意义。这是不可取的。你可以添加一些断言来处理这个问题,但恕我直言,最好在签名中明确它。让我们使用cv::Mat1b
而不是简单的cv::Mat
。
typedef std::pair<std::unique_ptr<uchar[]>, size_t> uchar_array_t;
uchar_array_t imgToArray(cv::Mat1b const& image);
让我们为这个先决条件添加一个断言:CV_Assert(!image.empty());
这变得没有实际意义,因为我们更改了返回类型。我们可以这样重写它:
uchar_array_t result;
result.second = image.rows * image.cols;
result.first = std::make_unique<uchar[]>(result.second);
cv::Mat::at
来复制数据。在这一部分中存在许多悲观情绪,以及我们在之前的观点中提到的潜在崩溃或UB。
cv::Mat::at
涉及不必要的开销。即使矩阵is not continuous,我们可以通过getting a pointer using cv::Mat::ptr
为每一行做得更好,并递增它。res[row * n + col]
- 我们按顺序写入数组,那么为什么要重新计算位置而不是简单地递增指针?cv::Mat
,这意味着我们自己分配的数组。通过设计,它可以按照您的需要以线性方式将数据存储在此阵列中。它还提供了方便的method to copy data from one matrix to another。所以让我们利用这个:
cv::Mat1b temp(image.rows, image.cols, result.first.get());
image.copyTo(temp);
让我们把它们加在一起吧。
#include <opencv2/opencv.hpp>
#include <memory>
typedef std::pair<std::unique_ptr<uchar[]>, size_t> uchar_array_t;
uchar_array_t to_array(cv::Mat1b const& image)
{
CV_Assert(!image.empty());
uchar_array_t result;
result.second = image.rows * image.cols;
result.first = std::make_unique<uchar[]>(result.second);
cv::Mat1b temp(image.rows, image.cols, result.first.get());
image.copyTo(temp);
return result;
}
void observe_data(uchar const[], size_t)
{
// ...
}
void modify_data(uchar[], size_t)
{
// ...
}
void take_ownership(uchar data[], size_t)
{
// ...
delete[] data;
}
int main()
{
cv::Mat image(cv::imread("itYxy.png", cv::IMREAD_GRAYSCALE));
uchar_array_t data_1(to_array(image));
uchar_array_t data_2(to_array(image(cv::Rect(10, 10, 40, 50))));
observe_data(data_2.first.get(), data_2.second);
observe_data(data_2.first.get(), data_2.second);
observe_data(data_2.first.release(), data_2.second);
return 0;
}
您可以使用std::unique_ptr::get
来观察阵列,或使用std::unique_ptr::release
来转移所有权。避免泄漏,我们有可用的大小,因此我们可以避免访问超出分配的数组范围的数据,测试前提条件。
当然,如果你不需要拥有阵列,那么大部分都可以避免。最多是一个矩阵的clone
(如果它不连续),然后调用cv::Mat::ptr
。