OpenCVを使用して次の画像を簡略化しようとしています。
ここにあるのはたくさんの赤い形です。それらのいくつかは完全に他を含んでいます。それらのいくつかは隣人と交差します。私の目標は、交差する2つの図形をユニオンのポリゴンのバウンディングボックスで置き換えることにより、すべてのintersecting図形を統合することです。 (交差する形状がなくなるまで繰り返す)。
交差するということは、触れることも意味します。これが100%明確になることを願っています:
標準の形態演算を使用してこれを効率的に実行しようとしています。明らかにそれはO(N^2)
で単純に行うことができますが、それは遅すぎるでしょう。一部の形状は1pxしか離れておらず、交差していない場合はそれらを結合したくないため、膨張は役に立ちません。
あなたが望むものを達成するために、私たちは findContours
を使用します。ここでの重要なポイントは、mode
がCV_RETR_TREE
に設定されている場合の動作を理解することです。この場合、hierarchy
は、すべての偶数の深度レベルに外部輪郭が含まれ、奇数の深度レベルに内部輪郭が含まれるように構築されます。ここで必要なのは、深さレベルに関連する等高線を印刷する階層ツリーをトラバースすることです。
まず、original
という画像の輪郭を見つけます
typedef std::vector<std::vector<cv::Point> > Contours;
typedef std::vector<cv::Vec4i> Hierarchy;
Contours contours;
Hierarchy hierarchy;
cv::findContours(original, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_NONE);
processed
という画像に外部輪郭を印刷するには、再帰関数が必要です。
void printExternalContours(cv::Mat img, Contours const& contours, Hierarchy const& hierarchy, int const idx)
{
//for every contour of the same hierarchy level
for(int i = idx; i >= 0; i = hierarchy[i][0])
{
//print it
cv::drawContours(img, contours, i, cv::Scalar(255));
//for every of its internal contours
for(int j = hierarchy[i][2]; j >= 0; j = hierarchy[j][0])
{
//recursively print the external contours of its children
printExternalContours(img, contours, hierarchy, hierarchy[j][2]);
}
}
}
printExternalContours(processed, contours, hierarchy, 0);
結果は以下のように表示され、original
とprocessed
が並べて表示されます。
長方形が絶対に必要な場合は、 boundingRect
を使用して、一連の点(この場合は1つの輪郭)を指定して最小の囲み長方形を取得し、 rectangle
図面用。つまり、代用
cv::drawContours(img, contours, i, cv::Scalar(255));
沿って
cv::rectangle(img, cv::boundingRect(contours[i]), cv::Scalar(255));
findContours
は単一の8ビット画像を想定しているため、元の画像から灰色の画像を作成し、しきい値を設定して完全な黒の背景を取得するか、ケースで赤いチャネルを使用するだけで十分です。背景が完全に黒であることを確認してください。
findContours
の複雑性については、O(N ^ 2)より優れていることを証明できず、その上で入力を見つけたこともありません簡単なグーグル検索の後、OpenCVが最もよく知られているアルゴリズムを実装すると信頼しています。
UPDATE:質問を先ほど誤解しました。完全に他の中にある長方形は削除したくありません。 交差する長方形のみを置き換えます。したがって、最初のケースでは何もする必要はありません。
新しいAPI(2.4.9)は&と|をサポートしています演算子。
opencv doc から:
等価比較(==)もサポートします
そのため、タスクを実行するのは非常に簡単です。長方形rect1とrect2のすべてのペアについて、
if((rect1 & rect2) == rect1) ... // rect1 is completely inside rect2; do nothing.
else if((rect1 & rect2).area() > 0) // they intersect; merge them.
newrect = rect1 | rect2;
... // remove rect1 and rect2 from list and insert newrect.
UPDATE 2:(Javaでの翻訳用)
Javaはごくわずかです。Java APIを使用したこともありません。ここでは疑似コード(簡単に翻訳できると思います)を提供しています)
ために &
演算子。2つの長方形の交差を見つけるメソッドが必要です。
Method: Intersect (Rect A, Rect B)
left = max(A.x, B.x)
top = max(A.y, B.y)
right = min(A.x + A.width, B.x + B.width)
bottom = min(A.y + A.height, B.y + B.height)
if(left <= right && top <= bottom) return Rect(left, top, right - left, bottom - top)
else return Rect()
ために |
演算子、同様のメソッドが必要です
Method: Merge (Rect A, Rect B)
left = min(A.x, B.x)
top = min(A.y, B.y)
right = max(A.x + A.width, B.x + B.width)
bottom = max(A.y + A.height, B.y + B.height)
return Rect(left, top, right - left, bottom - top)
ために ==
演算子。オーバーロードされたequals
メソッドを使用できます。