私はOpenCVを使用していて、かなり重複している長方形をグループ化したいです。これにgroupRectangles
を使用してみましたが、これはグループしきい値引数を取ります。しきい値が0の場合、グループ化はまったく行われず、しきい値が1の場合は、少なくとも2つの長方形の結果である長方形のみが返されます。たとえば、下の画像の左側にある長方形を考えると、右側に2つの長方形ができあがります。
最終的には3つの長方形になりたいです。上の画像の右側にある2と、画像の右上にある他の長方形と重ならない長方形。これを達成するための最良の方法は何ですか?
私が最終的に行った解決策は、groupRectangles
を呼び出す前にすべての最初の長方形を複製することでした。そうすれば、すべての入力長方形が少なくとも1つの他の長方形とグループ化されることが保証され、出力に表示されます。
int size = rects.size();
for( int i = 0; i < size; i++ )
{
rects.Push_back(Rect(rects[i]));
}
groupRectangles(rects, 1, 0.2);
Opencv-3.3.0ソースコードの groupRectangles() をチェックアウトすることによって:
if( groupThreshold <= 0 || rectList.empty() )
{
// ......
return;
}
groupThreshold
が0以下に設定されている場合、関数はグループ化を行わずに戻るだけであることがわかりました。
一方、次の code は、groupThreshold
を超える類似性を持たないすべての長方形を削除しました。
// filter out rectangles which don't have enough similar rectangles
if( n1 <= groupThreshold )
continue;
これが、groupThreshold = 1の場合、少なくとも2つのオーバーラップがある長方形のみが出力に含まれる理由を説明しています。
考えられる解決策の1つは、上記のソースコードを変更して(n1 <= groupThreshold
をn1 < groupThreshold
に置き換える)、OpenCVを再コンパイルすることです。
パーティーに少し遅れましたが、「複製」ソリューションは私にとって適切に機能しませんでした。また、マージされた長方形がオーバーラップし、マージする必要があるという別の問題もありました。
だから私はやり過ぎの解決策を思いついた(C++ 14コンパイラが必要かもしれない)。使用例は次のとおりです。
std::vector<cv::Rect> rectangles, test1, test2, test3;
rectangles.Push_back(cv::Rect(cv::Point(5, 5), cv::Point(15, 15)));
rectangles.Push_back(cv::Rect(cv::Point(14, 14), cv::Point(26, 26)));
rectangles.Push_back(cv::Rect(cv::Point(24, 24), cv::Point(36, 36)));
rectangles.Push_back(cv::Rect(cv::Point(37, 20), cv::Point(40, 40)));
rectangles.Push_back(cv::Rect(cv::Point(20, 37), cv::Point(40, 40)));
test1 = rectangles;
test2 = rectangles;
test3 = rectangles;
//Output format: {Rect(x, y, width, height), ...}
//Merge once
mergeRectangles(test1);
//Output rectangles: test1 = {Rect(5, 5, 31, 31), Rect(20, 20, 20, 20)}
//Merge until there are no rectangles to merge
mergeRectangles(test2, true);
//Output rectangles: test2 = {Rect(5, 5, 35, 35)}
//Override default merge (intersection) function to merge all rectangles
mergeRectangles(test3, false, [](const cv::Rect& r1, const cv::Rect& r2) {
return true;
});
//Output rectangles: test3 = {Rect(5, 5, 35, 35)}
関数:
void mergeRectangles(std::vector<cv::Rect>& rectangles, bool recursiveMerge = false, std::function<bool(const cv::Rect& r1, const cv::Rect& r2)> mergeFn = nullptr) {
static auto defaultFn = [](const cv::Rect& r1, const cv::Rect& r2) {
return (r1.x < (r2.x + r2.width) && (r1.x + r1.width) > r2.x && r1.y < (r2.y + r2.height) && (r1.y + r1.height) > r2.y);
};
static auto innerMerger = [](std::vector<cv::Rect>& rectangles, std::function<bool(const cv::Rect& r1, const cv::Rect& r2)>& mergeFn) {
std::vector<std::vector<std::vector<cv::Rect>::const_iterator>> groups;
std::vector<cv::Rect> mergedRectangles;
bool merged = false;
static auto findIterator = [&](std::vector<cv::Rect>::const_iterator& iteratorToFind) {
for (auto groupIterator = groups.begin(); groupIterator != groups.end(); ++groupIterator) {
auto foundIterator = std::find(groupIterator->begin(), groupIterator->end(), iteratorToFind);
if (foundIterator != groupIterator->end()) {
return groupIterator;
}
}
return groups.end();
};
for (auto rect1_iterator = rectangles.begin(); rect1_iterator != rectangles.end(); ++rect1_iterator) {
auto groupIterator = findIterator(rect1_iterator);
if (groupIterator == groups.end()) {
groups.Push_back({rect1_iterator});
groupIterator = groups.end() - 1;
}
for (auto rect2_iterator = rect1_iterator + 1; rect2_iterator != rectangles.end(); ++rect2_iterator) {
if (mergeFn(*rect1_iterator, *rect2_iterator)) {
groupIterator->Push_back(rect2_iterator);
merged = true;
}
}
}
for (auto groupIterator = groups.begin(); groupIterator != groups.end(); ++groupIterator) {
auto groupElement = groupIterator->begin();
int x1 = (*groupElement)->x;
int x2 = (*groupElement)->x + (*groupElement)->width;
int y1 = (*groupElement)->y;
int y2 = (*groupElement)->y + (*groupElement)->height;
while (++groupElement != groupIterator->end()) {
if (x1 > (*groupElement)->x)
x1 = (*groupElement)->x;
if (x2 < (*groupElement)->x + (*groupElement)->width)
x2 = (*groupElement)->x + (*groupElement)->width;
if (y1 >(*groupElement)->y)
y1 = (*groupElement)->y;
if (y2 < (*groupElement)->y + (*groupElement)->height)
y2 = (*groupElement)->y + (*groupElement)->height;
}
mergedRectangles.Push_back(cv::Rect(cv::Point(x1, y1), cv::Point(x2, y2)));
}
rectangles = mergedRectangles;
return merged;
};
if (!mergeFn)
mergeFn = defaultFn;
while (innerMerger(rectangles, mergeFn) && recursiveMerge);
}