问题非常基本。 (我很困惑为什么搜索没找到任何东西)
我有一个矩形的“图片”,用于存储它的像素颜色线 在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;
}
甚至没有编译.....
Addit:澄清: 我知道如何“手工”做到这一点。这不是问题。但我会喜欢一些c ++ stl iterator魔法,它会做同样的事情,但速度更快......更多c ++时尚。
另外:我为算法提供了pictureDataVector,图片的宽度和高度以及一个矩形,表示我想从图片中复制出来的区域。 返回值应该是一个带有矩形内容的新向量。
将其视为打开您最喜欢的图像编辑器,并从中复制矩形区域。 图片存储为像素颜色的长1D数组(矢量)。
答案 0 :(得分:3)
for (int r = startRow; r < endRow; r++)
for (int c = startCol; c < endCol; c++)
rect[r-startRow][c-startCol] = source[r*rowWidth+c];
答案 1 :(得分: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在循环中的增益之间的竞争。
但是,如果您的其他模板代码通常用于处理迭代器,而不是将vector作为特定容器,则可能会更灵活。 copyRectFromBiggerRect可以使用数组,双端队列甚至列表作为输入,就像向量一样容易,虽然它对于非随机访问的迭代器来说当前不是最佳的,因为它当前在每个复制的行中前进两次。
对于使其更像其他C ++代码的其他方法,请考虑对多维数组使用boost :: multi_array(在这种情况下,实现将与此完全不同),并避免返回诸如vector之类的集合(首先,如果你没有得到返回值优化,那么它可能是低效的,其次是对分配的资源的控制权保留在最高级别。)
答案 2 :(得分:2)
优秀的C ++代码必须首先易于阅读和理解(就像任何代码一样),面向对象(就像面向对象语言中的任何代码),然后应该使用语言工具来简化实现。
我不担心使用STL算法使它看起来更像C ++ - 是的,以面向对象的方式开始简化可用性(接口)会好得多。不要在外部使用普通矢量来表示图像。提供抽象级别:创建一个表示图像的类,并提供您需要的功能。这将通过封装细节从常规使用中提高可用性(2D区域对象可以知道其尺寸,用户不需要将它们作为参数传递)。这将使代码更强健壮,因为用户可以减少错误。
即使您使用STL容器,也要先考虑可读性。如果在常规for循环方面实现起来更简单,并且使用STL算法更难阅读,请忘记它们:使代码简单易维护。
这应该是你的重点:制作更好,更简单,更易读的代码。使用语言功能来改进代码,而不是使用代码来练习或展示语言中的功能。如果您需要在两个月后维护该代码,它将获得回报。
注意:使用更多STL不会使您的代码在C ++中更具惯用性,我相信这是其中一种情况。滥用STL可能会使代码变得更糟。
答案 3 :(得分: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获取灵感。