web-dev-qa-db-ja.com

OpenCVの穴を埋める

OpenCV (canny Edge detection)のEdge detectionモジュールから抽出されたEdgeマップがあります。私がしたいのは、エッジマップの穴を埋めることです。

私はC++およびOpenCVライブラリを使用しています。 OpenCVには cvFloodFill() 関数があり、穴をシードで塗りつぶします(フラッディングを開始する場所の1つを使用)。ただし、シードを知らずに内部の穴をすべて埋めようとしています(MATLABの imfill() と同様)

Q1:「cvFloodFill()」を適用できるように、すべてのシードを見つける方法は?
Q2:「imfill()」と同等の実装方法

OpenCVの初心者、および任意のヒントをいただければ幸いです。

33
Lily

MATLABの imfill のドキュメントによると:

BW2 = imfill(BW,'holes');

バイナリ画像の穴を埋めますBW。穴は、画像のエッジから背景を塗りつぶしても到達できない背景ピクセルのセットです。

したがって、「穴」ピクセルを取得するには、画像の左隅のピクセルをシードとして cvFloodFill を呼び出します。前の手順で取得した画像を補完することにより、穴を取得します。

MATLABの例:

BW = im2bw( imread('coins.png') );
subplot(121), imshow(BW)

% used here as if it was cvFloodFill
holes = imfill(BW, [1 1]);    % [1 1] is the starting location point

BW(~holes) = 1;               % fill holes
subplot(122), imshow(BW)

screenshot1screenshot2

34
Amro

cvDrawContours関数には、描画した輪郭を塗りつぶすオプションがあります。

CvDrawContours(IplImage、contours、color、color、-1、CV_FILLED、8);の短い例です。

ここにドキュメントがあります

http://opencv.willowgarage.com/documentation/drawing_functions.html?highlight=cvdrawcontours#cvDrawContours

ずっと前にこれを投稿したと思いますが、誰かの助けになることを願っています。

これはソースコードです(C#):

        Image<Gray, byte> image = new Image<Gray, byte>(@"D:\final.bmp");
        CvInvoke.cvShowImage("image 1", image);

        var contours = image.FindContours();
        while (contours != null)
        {
            CvInvoke.cvDrawContours(image, contours, new Gray(255).MCvScalar, new Gray (255).MCvScalar, 0, -1, Emgu.CV.CvEnum.LINE_TYPE.CV_AA, new DPoint(0, 0));
            contours = contours.HNext;
        }
        CvInvoke.cvShowImage("image 2", image);

Result

12
NotNamedDwayne

適切なimfill関数(Matlabの関数として)を見つけるためにインターネットを探していましたが、OpenCVを使用してC++で作業しています。いくつかの調査の後、私は最終的に解決策を思いつきました:

IplImage* imfill(IplImage* src)
{
    CvScalar white = CV_RGB( 255, 255, 255 );

    IplImage* dst = cvCreateImage( cvGetSize(src), 8, 3);
    CvMemStorage* storage = cvCreateMemStorage(0);
    CvSeq* contour = 0;

    cvFindContours(src, storage, &contour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
    cvZero( dst );

    for( ; contour != 0; contour = contour->h_next )
    {
        cvDrawContours( dst, contour, white, white, 0, CV_FILLED);
    }

    IplImage* bin_imgFilled = cvCreateImage(cvGetSize(src), 8, 1);
    cvInRangeS(dst, white, white, bin_imgFilled);

    return bin_imgFilled;
}

このため: 元のバイナリイメージ

結果は次のとおりです。 最終バイナリイメージ

その秘Drawは、cvDrawContours関数のパラメーター設定にあります。cvDrawContours(dst、contour、white、white、0、CV_FILLED);

  • dst =宛先イメージ
  • contour =最初の輪郭へのポインタ
  • 白=輪郭の塗りつぶしに使用される色
  • 0 =描かれた輪郭の最大レベル。 0の場合、輪郭のみが描画されます
  • CV_FILLED =輪郭が描かれる線の太さ。負の場合(たとえば、= CV_FILLED)、輪郭の内部が描画されます。

詳細はopenCVのドキュメントをご覧ください。

おそらくバイナリイメージとして「dst」を直接取得する方法がありますが、バイナリ値でcvDrawContours関数を使用する方法を見つけることができませんでした。

7
Jeremy.S

Matlabのimfill( 'holes')と同等の簡単な関数を作成しました。私は多くの場合それをテストしていませんが、今のところうまくいきました。 Edgeイメージで使用していますが、しきい値処理など、あらゆる種類のバイナリイメージを受け入れます。

穴は、背景が塗りつぶされたときに「到達」できないピクセルのセットにすぎないため、

void fillEdgeImage(cv::Mat edgesIn, cv::Mat& filledEdgesOut) const
{
    cv::Mat edgesNeg = edgesIn.clone();

    cv::floodFill(edgesNeg, cv::Point(0,0), CV_RGB(255,255,255));
    bitwise_not(edgesNeg, edgesNeg);
    filledEdgesOut = (edgesNeg | edgesIn);

    return;
}

結果の例を次に示します

enter image description here

6
Pedro Batista

迅速で汚いアプローチを次に示します。

  1. 入力画像にキャニーを実行して、新しいバイナリ画像の端に1があり、それ以外の場合は0になるようにします
  2. Edge画像の側面に沿って最初の0を見つけ、Edge画像をマスクとして使用して、空白画像のそのポイントで1で塗りつぶしを開始します。 (ここでは、不幸にならず、この最初の塗りつぶしを画面の半分のシェイプの内側にシードすることを望んでいます)
  3. この新しい塗りつぶし画像は「背景」です。ここで1のピクセルは背景であり、0のピクセルは前景です。
  4. 画像をループして、前景ピクセルを見つけます。 Seedあなたが見つけたものにあふれます。
  5. または、この新しい塗りつぶし画像とステップ1のキャニー画像を使用して、完了です。
4
rcv

Amroの答え。 の付録

void cvFillHoles(cv::Mat &input)
{
    //assume input is uint8 B & W (0 or 1)
    //this function imitates imfill(image,'hole')
    cv::Mat holes=input.clone();
    cv::floodFill(holes,cv::Point2i(0,0),cv::Scalar(1));
    for(int i=0;i<input.rows*input.cols;i++)
    {
        if(holes.data[i]==0)
            input.data[i]=1;
    }
}
3
Tae-Sung Shin

最近、この問題の解決策も見つけています。ここで Amro のアイデアを次のように実装しました。

#include <iostream>
using namespace std;
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>
using namespace cv;

int main()
{
    IplImage *im = cvLoadImage("coin.png",CV_LOAD_IMAGE_ANYDEPTH);
    IplImage *hole = cvCreateImage(cvSize(im->width,im->height),8,1);
    cvShowImage("Original",im);

    cvCopyImage(im,hole);
    cvFloodFill(hole,cvPoint(0,0),cvScalar(255));
    cvShowImage("Hole",hole);
    cvSaveImage("hole.png",hole);

    cvNot(hole,hole);
    cvAdd(im,hole,im);
    cvShowImage("FillHole",im);
    cvSaveImage("fillHole.png",im);

    cvWaitKey(0);
    system("pause");
    return 0;
} 

これが役立つことを願っています。

2
Bill Xia

Cannyied ImageでContourFindingを試しましたか?

cvFindContoursは、外側の郡が内側の輪郭(「穴」)の親となる一種のツリーを作成します。 contours.pyサンプルを参照してください。輪郭から種を抽出できます

2
dnul

エッジからポイントがある場合は、fillConvexPoly()またはfillPoly()(ポリゴンが凸でない場合)を使用できます。

エッジからポイントを取得する1つの方法は、findContours()->近似PolyDP()を実行することです。

0
Rui Marques