代码之家  ›  专栏  ›  技术社区  ›  AndreasT

如何从C++矢量中提取二维矩形区域

  •  1
  • AndreasT  · 技术社区  · 15 年前

    这个问题很基本。 (我很困惑为什么搜索没有找到任何东西)

    我有一个矩形的“图片”,它存储了像素颜色线 在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++时尚。

    另外:我给了算法picturedatavector,图片的宽度和高度,以及一个表示要从图片中复制的区域的矩形。 返回值应该是一个包含矩形内容的新向量。

    把它想象成打开你最喜欢的图像编辑器,从中复制出一个矩形区域。 图片存储为像素颜色的长一维数组(矢量)。

    4 回复  |  直到 15 年前
        1
  •  2
  •   Community CDub    7 年前

    您的问题要求在某些容器中复制矩形元素的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 range
    vector<pixels> rect_copy = std::copy(selection1.begin(), selection1.end());
    // or maybe you don't want to copy, you want 
    // to modify the selection in place
    std::for_each (selection1.begin(), selection1.end(), invert_color);
    

    我敢肯定这是完全可行的,但我不舒服的编码STL风格的模板材料的袖口。如果我有时间,你感兴趣,我可以稍后重新编辑一个草稿,因为这是一个有趣的概念。

    看到这个 SO question's answer 为了灵感。

        2
  •  3
  •   Jeff Kotula    15 年前
    for (int r = startRow; r < endRow; r++)
        for (int c = startCol; c < endCol; c++)
            rect[r-startRow][c-startCol] = source[r*rowWidth+c];
    
        3
  •  3
  •   Steve Jessop    15 年前

    基本上是相同的想法,除了它编译并且更具迭代性:

    #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"));
    }
    

    我不希望这比按索引复制两个循环更快。可能更慢,甚至更慢,尽管它基本上是向量::push_back的开销和std::copy通过循环获得的收益之间的竞争。

    但是,如果您的其他模板代码设计为一般使用迭代器,而不是作为特定容器的向量,那么它可能更灵活。copyrectFromBiggerRect可以像向量一样轻松地使用数组、deque甚至列表作为输入,尽管目前它对于非随机访问的迭代器不是最佳选择,因为它当前在每个复制行中前进两次。

    对于其他类似于C++代码的其他方法,请考虑多维数组的Booo::Mulk数组(在这种情况下,实现将完全不同于此),并且避免返回诸如向量值的集合(首先,如果没有得到返回值优化,则它可能是低效的,第二个是这样)控制分配的资源尽可能保持在最高级别)。

        4
  •  2
  •   David Rodríguez - dribeas    15 年前

    良好的C++代码必须首先易于阅读和理解(就像任何代码),面向对象(类似于面向对象语言中的任何代码),然后应该使用语言设施来简化实现。

    我不担心使用STL算法使它看起来更像C++-ISH,开始以面向对象的方式简化可用性(接口)会更好。不要在外部使用纯向量来表示图像。提供的级别 抽象化 :创建一个表示图像的类,并在其中提供所需的功能。这将提高可用性 包封 常规使用的详细信息(二维区域对象可以知道其尺寸,用户不需要将其作为参数传递)。这将使代码更加 稳健的 因为用户可以减少错误。

    即使使用STL容器,也要考虑 可读性 第一。如果按照常规for循环实现更简单,并且使用STL算法阅读起来更困难,那么请忘记它们:使代码简单且可维护。

    这应该是您的重点:制作更好、更简单、更可读的代码。使用语言特性来改进代码,而不是使用代码来练习或展示语言中的特性。如果您需要在两个月后维护该代码,它将得到回报。

    注意:使用更多的STL不会使你的代码在C++中更习惯,我相信这是其中的一个例子。滥用STL会使代码变得更糟。