web-dev-qa-db-ja.com

交差する外接長方形を組み合わせる効率的な方法

OpenCVを使用して次の画像を簡略化しようとしています。

enter image description here

ここにあるのはたくさんの赤い形です。それらのいくつかは完全に他を含んでいます。それらのいくつかは隣人と交差します。私の目標は、交差する2つの図形をユニオンのポリゴンのバウンディングボックスで置き換えることにより、すべてのintersecting図形を統合することです。 (交差する形状がなくなるまで繰り返す)。

交差するということは、触れることも意味します。これが100%明確になることを願っています:

enter image description here

標準の形態演算を使用してこれを効率的に実行しようとしています。明らかにそれはO(N^2)で単純に行うことができますが、それは遅すぎるでしょう。一部の形状は1pxしか離れておらず、交差していない場合はそれらを結合したくないため、膨張は役に立ちません。

19
Assaf Lavie

あなたが望むものを達成するために、私たちは findContours を使用します。ここでの重要なポイントは、modeCV_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);

結果は以下のように表示され、originalprocessedが並べて表示されます。

originalprocessed

長方形が絶対に必要な場合は、 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が最もよく知られているアルゴリズムを実装すると信頼しています。

9
brunocodutra

UPDATE:質問を先ほど誤解しました。完全に他の中にある長方形は削除したくありません。 交差する長方形のみを置き換えます。したがって、最初のケースでは何もする必要はありません。

新しいAPI(2.4.9)は&と|をサポートしています演算子。

opencv doc から:

  • rect = rect1&rect2(長方形の交差)
  • rect = rect1 | rect2(rect2とrect3を含む最小領域の長方形)

等価比較(==)もサポートします

  • rect == rect1

そのため、タスクを実行するのは非常に簡単です。長方形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メソッドを使用できます。

14
asif