如何优雅地从 C++ 向量中提取二维矩形区域

问题

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

我有一个矩形“图片”,它在 std::vector 中逐行存储它的像素颜色

我想从那张图片中复制一个矩形区域。

我将如何在 C++ 中优雅地编写代码?

我的第一次尝试:

<span style="color:#003b61"><span style="background-color:#ffffff"><code class="language-cpp"> 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;
}</code></span></span>

 

甚至不编译…..

补充:澄清:我知道如何“手工”做到这一点。这不是问题。但我会喜欢一些 c++ stl 迭代器魔法,它做同样的事情,但速度更快,而且……更 c++ 时尚。

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

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

解决方案

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

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

即使您使用 STL 容器,也请始终首先考虑可读性。如果按照常规 for 循环实现更简单,并且使用 STL 算法更难阅读,请忘记它们:让您的代码简单且可维护。

这应该是你的重点:制作更好、更简单、更易读的代码。使用语言特性来改进你的代码,而不是你的代码来锻炼或炫耀语言的特性。如果您需要在两个月后维护该代码,它将获得回报。

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

您的问题要求使用 C++ 方法来复制某个容器中的矩形元素字段。你有一个相当接近的例子,并且会在答案中得到更多。不过,让我们概括一下:

您需要一个迭代器,它可以在一定范围的元素上遍历矩形范围的元素。那么,如何编写一种适配器,它位于任何容器上并提供这个特殊的迭代器。

将在这里使用代码进行广泛的笔触:

<span style="color:#333333"><span style="background-color:#ffffff"><code class="language-cpp">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);
</code></span></span>

我确信这是完全可行的,但我不喜欢即兴编码 stl 样式的模板内容。如果我有时间并且您有兴趣,我可能会在稍后重新编辑草稿,因为这是一个有趣的概念。

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

<span style="color:#333333"><span style="background-color:#ffffff"><code class="language-cpp">#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"));
}
</code></span></span>

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

但是,如果您的其他模板代码通常设计为与迭代器一起使用,而不是矢量作为特定容器,它可能会更灵活。copyRectFromBiggerRect 可以像使用向量一样轻松地使用数组、双端队列甚至列表作为输入,尽管它目前对于非随机访问的迭代器并不是最优的,因为它当前在每个复制的行中前进两次。

对于使它更像其他 C++ 代码的其他方法,考虑多维数组的 boost::multi_array (在这种情况下,实现将与此完全不同),并避免返回集合,例如按值向量(首先它可以如果您没有获得返回值优化,则效率低下,其次,将对分配哪些资源的控制留在可能的最高级别)。

原文连接:如何优雅地从 C++ 向量中提取二维矩形区域

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值