我已经创建了一个函数来过滤掉我在std :: vector中不喜欢的元素,在本例中是一个opencv轮廓的向量。下面的代码看起来会起作用,但它没有,我怀疑这是因为每当我擦除索引时索引都会改变,但是我继续下一个i值迭代。
void FilterContours( std::vector<std::vector<cv::Point>> contours )
{
for ( int i = 0; i < contours.size(); i++ ) {
//Remove contours smaller than 5 from vector - example
if ( contours[i].size() < 5 ) {
contours.erase(contours.begin() + i);
continue;
}
//Other filtering...
}
return;
}
所以问题是,这是否按预期工作(我认为不是这样),如果没有,我该如何使其按预期工作?我应该在擦除后添加i - = 1以保持正确的索引位置吗?
答案 0 :(得分:3)
使用擦除删除习语:
contours.erase(
std::remove_if(contours.begin(), contours.end(), [](const std::vector<cv::Point>& v){
return v.size() < 5;
}),
contours.end()
);
答案 1 :(得分:2)
每次erase()
来自容器的元素,其size()
递减,其余元素的索引也会递减。但是你无条件地递增你的循环计数器,所以每当你擦除一个元素时,你跳过跟随它的下一个元素!
此外,您传递的是vector
按值,因此您正在使用vector
的副本进行操作,并且调用者不会看到任何更改原vector
。
正确的方法是:
仅在未删除元素时才在循环体内增加索引变量。在擦除元素时保持变量原样:
void FilterContours( std::vector<std::vector<cv::Point>> &contours )
{
int i = 0;
while ( i < contours.size() ) {
if ( contours[i].size() < 5 ) {
contours.erase(contours.begin() + i);
continue;
}
//Other filtering...
++i;
}
}
使用迭代器而不是索引:
void FilterContours( std::vector<std::vector<cv::Point>> &contours )
{
auto it = contours.begin();
while ( it != contours.end() ) {
if ( it->size() < 5 ) {
it = contours.erase(it);
continue;
}
//Other filtering...
++it;
}
}
使用erase-remove成语:
void FilterContours( std::vector<std::vector<cv::Point>> &contours )
{
contours.erase(
std:::remove_if(
contours.begin(),
contours.end(),
[](const std::vector<cv::Point> &v)
{
if (v.size() < 5) return true;
//Other filtering...
return false;
}
),
contours.end()
);
}
答案 2 :(得分:2)
一般情况下,当您迭代删除时,最好迭代向后:
for ( int i = contours.size()-1; i >=0; --i)
这会起作用,但会导致代码变慢,因为每次删除时,删除后面的元素都会被复制/移回。因此,使用标准算法库提供的专用习语更好,更快,更具可读性,这些习惯用法通常都是非常优化的。在这种情况下,您拥有erase/remove_if
组合:
contours.erase(std::remove_if(contours.begin(), contours.end(), [](const auto& elem) { return elem.size() < 5; }), contours.end() );
这里的一大优势是std::remove_if()
以比直觉循环更聪明的方式起作用:它首先&#34;标记&#34;要删除的元素,然后将剩余的元素压缩在一起。这个过程是O(N),而(直观)循环是O(N ^ 2),对于大向量来说是一个巨大的差异。
p.s。:FilterContours
函数的签名,用于通过引用获取向量:
void FilterContours( std::vector<std::vector<cv::Point>>& contours ) // <-- by reference
答案 3 :(得分:1)
您的FilterContours
应该参考,否则不会对来电者产生任何影响。
void FilterContours(std::vector<std::vector<cv::Point>>& contours)
{
for (auto it = contours.begin(); it != contours.end(); )
{
if (it->size() < 5)
it = contours.erase(it);
else
++it;
}
}
编辑: 如果您想按相反的顺序执行此操作:
void FilterContours_reverse(std::vector<std::vector<cv::Point>>& contours)
{
for (auto it = contours.rbegin(); it != contours.rend(); )
{
if (it->size() < 5)
contours.erase(std::next(it++).base());
else
++it;
}
}