2009-04-23 81 views
1

这个问题非常基本。 (我很不解,为什么搜索没有找到任何东西)Howto从C++向量中优雅地提取2D矩形区域

我有一个长方形的“图片报”存储它的像素颜色线 在一个std ::行向量

后,我想复制一个矩形区域出于该图片。

我将如何在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; 
} 

甚至不编译.....

ADDIT:澄清: 我知道这个 “手工” 做。这不是一个问题。但我会喜欢一些C++ stl迭代器的魔法,但它的功能相同,但速度更快,而且...更多C++时尚。

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

认为它是打开你最喜欢的图像编辑器,并复制出一个矩形区域。 图片存储为像素颜色的一维长阵列(矢量)。

+0

尼尔你是一个生命的救星:) 写评论,这样我可以+1你的:) – AndreasT 2009-04-23 18:49:00

+0

澄清:尼尔刚救我的(糟糕)的示例代码:) 仍不能确定我做错了什么?但到底是什么... – AndreasT 2009-04-23 18:56:14

+2

你试图使用HTML - 不这样做。相反,用鼠标选择所有代码并键入Ctrl-K。 – 2009-04-23 19:01:08

回答

2

你的问题要求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的灵感。

3
for (int r = startRow; r < endRow; r++) 
    for (int c = startCol; c < endCol; c++) 
     rect[r-startRow][c-startCol] = source[r*rowWidth+c]; 
2

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

我不担心使用STL算法使它看起来更多C++ - ish,以面向对象的方式开始简化可用性(接口)会好得多。不要在外部使用普通矢量来表示图像。提供抽象级别:创建一个代表图像并提供您需要的功能的类。这可以提高可用性封装从常规使用的细节(2D区域对象可以知道它的尺寸,用户不需要将它们作为参数传递)。这将使代码更加稳健,因为用户可以减少错误。

即使您使用STL容器,也应首先考虑可读性。如果按照常规for循环实现起来更简单,并且使用STL算法读取会更困难,请忘记它们:使代码简单易维护。

这应该是你的焦点:做出更好,更简单,更易读的代码。使用语言功能来改善您的代码,而不是您的代码来锻炼或炫耀语言中的功能。如果您需要在两个月后继续维护该代码,它将得到回报。

注意:使用更多的STL不会让你的代码在C++中变得更加惯用,我相信这是其中的一种情况。滥用STL会使代码变得更糟。

3

基本上相同的想法,但它编译,更有点iteratory:

#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可以使用一个数组,一个deque甚至一个列表作为输入,就像向量一样容易,尽管它对于不是随机访问的迭代器来说目前并不是最优的,因为它当前通过每个复制的行两次。

为了使这更像其他C++代码的其它方式,可以考虑提高::多维阵列(在这种情况下,实施将来自该无论如何完全不同的),的multi_array和避免返回集合,如由值向量(首先,如果你没有得到返回值优化,效率可能会很低,其次是控制分配哪些资源的可能性最高)。