2017-12-18 259 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