如何優雅地從 C++ 向量中提取二維矩形區域

itchaindev發表於2022-04-07

問題

這個問題很基本。(我很困惑為什麼搜尋沒有找到任何東西)

我有一個矩形“圖片”,它在 std::vector 中逐行儲存它的畫素顏色

我想從那張圖片中複製一個矩形區域。

我將如何在 C++ 中優雅地編寫程式碼?

我的第一次嘗試:

template <class  T>    std::vector<T> copyRectFromVector(const std::vector<T>& vec, std::size_t startx,  std::size_t starty, std::size_t endx, std::size_t endy, std::size_t fieldWidth, std::size_t fieldHeight)
    {     using namespace std;    vector<T> ret((endx-startx)*(endy-starty)+10);  // 10: chickenfactor
    // checks if the given parameters make sense:
    if (vec.size() < fieldWidth*endy)
    {
        cerr << "Error: CopyRectFromVector: vector to small to contain rectangular region!" << std::endl;        return ret;
    }
    // do the copying line by line:
    vector<T>::const_iterator vecIt = vec.begin();
    vector<T>::forward_iterator retIt = ret.end();
    vecIt += startx + (starty*fieldWidth);
     for(int i=starty; i < endy; ++i)
    {
            std::copy(vecIt, vecIt + endx - startx, retIt);
        }
        return ret;}

 

甚至不編譯…..

補充:澄清:我知道如何“手工”做到這一點。這不是問題。但我會喜歡一些 c++ stl 迭代器魔法,它做同樣的事情,但速度更快,而且……更 c++ 時尚。

補充:我給演算法圖片資料向量、圖片的寬度和高度以及一個矩形,表示我想從圖片中複製出來的區域。返回值應該是包含矩形內容的新向量。

把它想象成開啟你最喜歡的影像編輯器,然後從中複製一個矩形區域。圖片儲存為畫素顏色的長一維陣列(向量)。

解決方案

好的 C++ 程式碼首先必須易於閱讀和理解(就像任何程式碼一樣)、物件導向(就像面嚮物件語言中的任何程式碼),然後應該使用語言工具來簡化實現。

我不會擔心使用 STL 演算法讓它看起來更像 C++,最好以物件導向的方式開始簡化可用性(介面)。不要在外部使用普通向量來表示您的影像。提供一定程度的 抽象:建立一個代表影像的類並在其中提供您需要的功能。 這將通過封裝來自常規使用的細節來提高可用性(2D 區域物件可以知道它的尺寸,使用者不需要將它們作為引數傳遞)。這將使程式碼更加 健壯,因為使用者可以減少錯誤。

即使您使用 STL 容器,也請始終首先考慮 可讀性。如果按照常規 for 迴圈實現更簡單,並且使用 STL 演算法更難閱讀,請忘記它們:讓您的程式碼簡單且可維護。

這應該是你的重點:製作更好、更簡單、更易讀的程式碼。使用語言特性來改進你的程式碼,而不是你的程式碼來鍛鍊或炫耀語言的特性。如果您需要在兩個月後維護該程式碼,它將獲得回報。

注意:使用更多的 STL 不會使您的程式碼在 C++ 中更加慣用,我相信這是其中一種情況。濫用 STL 會使程式碼實際上變得更糟。

您的問題要求使用 C++ 方法來複制某個容器中的矩形元素欄位。你有一個相當接近的例子,並且會在答案中得到更多。不過,讓我們概括一下:

您需要一個迭代器,它可以在一定範圍的元素上遍歷矩形範圍的元素。那麼,如何編寫一種介面卡,它位於任何容器上並提供這個特殊的迭代器。

將在這裡使用程式碼進行廣泛的筆觸:

vector<pixels> my_picture;point selTopLeft(10,10), selBotRight(40, 50);int picWidth(640), picHeight(480);rectangular_selection<vector<pixels> > selection1(my_picture.begin(),
  my_picture.end(), picWidth, picHeight, selTopLeft, selBotRight);// Now you can use stl algorithms on your rectangular rangevector<pixels> rect_copy = std::copy(selection1.begin(), selection1.end());// or maybe you don't want to copy, you want // to modify the selection in placestd::for_each (selection1.begin(), selection1.end(), invert_color);

我確信這是完全可行的,但我不喜歡即興編碼 stl 樣式的模板內容。如果我有時間並且您有興趣,我可能會在稍後重新編輯草稿,因為這是一個有趣的概念。

基本上是相同的想法,除了它可以編譯並且更迭代:

#include <vector>#include <algorithm>#include <iostream>#include <iterator>template <typename I, typename O> 
void copyRectFromBiggerRect(
    I input,
    O output,
    std::size_t startx,  
    std::size_t cols, 
    std::size_t starty, 
    std::size_t rows, 
    std::size_t stride
) {
    std::advance(input, starty*stride + startx);    while(rows--) {
        std::copy(input, input+cols, output);
        std::advance(input, stride);
    }}template<typename T>
std::vector<T> copyRectFromVector (    const std::vector<T> &vec, 
    std::size_t startx,  
    std::size_t starty, 
    std::size_t endx, 
    std::size_t endy, 
    std::size_t stride
) {    // parameter-checking omitted: you could also check endx > startx etc.
    const std::size_t cols = endx - startx;    const std::size_t rows = endy - starty;
    std::vector<T> ret;
    ret.reserve(rows*cols);
    std::back_insert_iterator<std::vector<T> > output(ret);
    typename std::vector<T>::const_iterator input = vec.begin();    copyRectFromBiggerRect(input,output,startx,cols,starty,rows,stride);    return ret;}int main() {    std::vector<int> v(20);    for (int i = 0; i < 20; ++i) v[i] = i;
    std::vector<int> v2 = copyRectFromVector(v, 0, 0, 1, 2, 4);
    std::copy(v2.begin(), v2.end(), std::ostream_iterator<int>(std::cout, "\n"));}

我不希望這比按索引複製的兩個迴圈更快。甚至可能更慢,儘管它基本上是 vector::push_back 的開銷和 std::copy 在迴圈中的增益之間的競爭。

但是,如果您的其他模板程式碼通常設計為與迭代器一起使用,而不是向量作為特定容器,它可能會更靈活。copyRectFromBiggerRect 可以像使用向量一樣輕鬆地使用陣列、雙端佇列甚至列表作為輸入,儘管它目前對於非隨機訪問的迭代器並不是最優的,因為它當前在每個複製的行中前進兩次。

對於使它更像其他 C++ 程式碼的其他方法,考慮多維陣列的 boost::multi_array (在這種情況下,實現將與此完全不同),並避免返回集合,例如按值向量(首先它可以如果您沒有獲得返回值優化,則效率低下,其次,將對分配哪些資源的控制留在可能的最高階別)。

原文連線: 如何優雅地從 C++ 向量中提取二維矩形區域


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70016198/viewspace-2886077/,如需轉載,請註明出處,否則將追究法律責任。

相關文章