我无法将cv :: Mat保存到1D数组中

时间:2017-12-18 13:59:00

标签: c++ opencv segmentation-fault

我想将cv :: Mat数据保存为线性数组,但我不知道为什么会有错误。图像颜色为灰度(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);

我对此很困惑。感谢任何提前提出建议的人!

2 个答案:

答案 0 :(得分:4)

您已创建一个值为m * n

的int对象
uchar* res = new uchar(m * n);

不是数组,应该是

uchar* res = new uchar[m * n];

答案 1 :(得分:0)

除了rafix07指出的明显不正确的分配外,您的代码中存在多个问题。一个问题是你是否真的需要拥有uchar数组的所有权(如果没有,那么有更简单的方法可以解决这个问题),但我会假设你这样做。让我们从顶部开始。

uchar* imgToArray(cv::Mat& img, int n, int m);

  1. 返回原始指针(uchar*)。
  2. 首先,我们要返回一个数组,所以让我们明确说明。原始指针容易出现内存泄漏,C ++ 11已经存在了一段时间 - 我们可以做得更好。让它成为std::unique_ptr,它也可以正确处理数组。

    然后,我们可以使用std::make_unique<uchar[]>(size)进行分配,这样您就不太可能犯下那种错误。

    1. 返回没有大小的原始数组。
    2. 如果您想以某种方式处理返回的数据,那么没有第二个的第一个就是要求麻烦。依靠用户必须调用另一个函数来获取它,或者必须自己计算它远非理想。因此,让我们使用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);
      
      1. img视为非const引用,即使您不打算修改它。
      2. 如果您真的关心避免复制标题的开销,请使用const引用来明确您的意图。

        1. 参数int nint m是多余的。他们也没有经过验证。
        2. 除了cv::Mat之外的其他任何内容都没有理由给出与你想要变成&#34;线性数组&#34;的区域相对应的功能。 OpenCV已经提供了获得投资回报率的有效方法:cv::Mat::operator()。让用户使用它,并删除多余的参数。

          typedef std::pair<std::unique_ptr<uchar[]>, size_t> uchar_array_t;
          uchar_array_t imgToArray(cv::Mat const& image);
          
          1. 代码假定img的数据类型为CV_8UC1(即单个通道矩阵,其中每个元素都是无符号字节),但不验证确实如此。
          2. 这意味着当您使用不同类型的图像调用它时,您将无意义。这是不可取的。你可以添加一些断言来处理这个问题,但恕我直言,最好在签名中明确它。我们使用cv::Mat1b代替普通cv::Mat

            typedef std::pair<std::unique_ptr<uchar[]>, size_t> uchar_array_t;
            uchar_array_t imgToArray(cv::Mat1b const& image);
            
            1. 当代码被赋予空矩阵时,代码不会考虑该情况。
            2. 让我们为这个先决条件添加一个断言:CV_Assert(!image.empty());

              1. rafix07指出的错误分配。
              2. 这变得没有实际意义,因为我们改变了返回类型。我们可以这样重写它:

                uchar_array_t result;
                result.second = image.rows * image.cols;
                result.first = std::make_unique<uchar[]>(result.second);
                
                1. 通过&#34;手动&#34;复制数据迭代像素并调用cv::Mat::at
                2. 在这一部分中存在许多悲观情绪,以及我们在之前的观点中解决的潜在崩溃或UB。

                    当我们知道我们正在访问有效像素时,
                  • cv::Mat::at会产生不必要的开销。即使矩阵is not continuous,我们可以通过getting a pointer using cv::Mat::ptr为每一行做更好的事情,然后递增它。
                  • res[row * n + col] - 我们按顺序写入数组,为什么要重新计算位置而不是简单地递增指针?
                  • 完全执行此操作 - OpenCV允许我们使用&#34;外国数据&#34;创建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