2017-12-18 265 views
2

我想将cv :: Mat数据保存到一个线性数组中,但我不知道为什么会有错误。图像颜色是灰度(CV_8UC1)。下面是代码:我无法将cv :: Mat保存到一维数组中

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

我很困惑这个问题。感谢任何提前给出建议的人!

+0

'n'和'm'在这里与'img'的实际大小有什么关系?是否允许提取锚定在左上角的较小投资回报率? (尽管为什么你不会仅仅通过适当的投资回报率,利用现有的OpenCV功能)。然而,如果你想保留它们,增加一些验证可能是一个好主意。 –

+0

你打算如何处理原始数组?你是否需要将其所有权转让给另一段无法修改的代码? |我看到的进一步问题是假设输入Mat是'CV_8UC1',但从未实际验证它。 –

+0

我想分配一个大小为n * m的数组,并将cv :: Mat图像保存到数组中。我应该写成'uchar * res = new uchar [m * n];',那个错误简单而愚蠢。万分感谢! – sunjy

回答

4

您已经创建了一个int对象与价值m * n

uchar* res = new uchar(m * n); 

不阵列,应该是

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

非常感谢。 – sunjy

0

有你的代码的多个问题,较明显不正确分配等通过rafix07指出。一个问题是,你是否真的需要拥有uchar(如果没有,那么有更简单的方法去做这件事),但我会假设你这样做。我们从顶部开始。

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

  1. 返回原始指针(uchar*)。

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

然后我们可以用std::make_unique<uchar[]>(size)进行分配,在这种情况下,您很可能会犯这种错误。

  1. 返回没有大小的原始数组。

如果您想要以某种方式处理返回的数据,第一个没有第二个就是要求麻烦。依靠用户不得不调用另一个函数来获得它,或者不得不自己计算它是远非理想的。因此,让我们使用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引用来明确您的意图。

    1. 参数int nint 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());

  • 不正确的分配,如通过rafix07指出。
  • 由于我们改变了返回类型,这变得毫无意义。我们可以把它改写为这样:

    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] - 我们按顺序写入数组,为什么重新计算位置而不是简单地增加一个指针?
    • 完成此操作 - OpenCV允许我们使用“外部数据”创建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

    +0

    这对我很有帮助。事实上,我是C++ 11和OpenCV的新手,对于它的一些功能我不太了解。你的评论对我很有帮助。非常感谢你。 – sunjy