33

OpenCV (キャニー エッジ検出)のエッジ検出モジュールからエッジ マップを抽出しました。私がやりたいことは、エッジ マップの穴を埋めることです。

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

Q1: 'cvFloodFill()' を適用できるように、すべてのシードを見つける方法は?
Q2: 'imfill()' に相当するものを実装する方法は?

OpenCVの初心者であり、ヒントをいただければ幸いです。

4

9 に答える 9

35

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

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)

スクリーンショット1 スクリーンショット2

于 2009-11-11T18:02:16.837 に答える
12

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

以下に簡単な例を示します。

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

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);

結果

于 2010-07-30T18:21:30.427 に答える
8

私は適切な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;
}

この場合:元のバイナリ イメージ

結果:最終的なバイナリ イメージ

トリックは、cvDrawContours 関数のパラメーター設定にあります。

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

詳細については、openCV のドキュメントを参照してください。

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

于 2013-04-15T12:54:04.013 に答える
7

matlab の imfill('holes') に相当する単純な関数を作成しました。多くのケースでテストしていませんが、これまでのところ機能しています。エッジ画像で使用していますが、しきい値操作など、あらゆる種類のバイナリ画像を受け入れます。

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

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;
}

これが結果の例です

ここに画像の説明を入力

于 2014-03-05T16:34:20.113 に答える
4

これが迅速で汚いアプローチです:

  1. 入力画像に対してcannyを実行して、新しいバイナリ画像の端が1になり、それ以外の場合は0になるようにします。
  2. エッジ画像の側面に沿って最初の0を見つけ、エッジ画像をマスクとして使用して、空白の画像のそのポイントで1で塗りつぶしを開始します。(ここでは、不運にならず、この最初の塗りつぶしを画面の半分にある図形の内側にシードしないことを望んでいます)
  3. この新しい塗りつぶされた画像は「背景」です。ここで1が付いているピクセルは背景であり、0が付いているピクセルは前景です。
  4. 画像をループして、前景のピクセルを見つけます。見つけたものに塗りつぶしをシードします。
  5. または、この新しい塗りつぶされた画像に、手順1のCanny画像を追加すれば、完了です。
于 2010-01-26T18:41:30.427 に答える
3

アムロの答えの付録です。

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;
    }
}
于 2012-10-04T14:43:38.920 に答える
2

最近、私はこの問題の解決策も見つけています。ここでは、次のように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;
} 

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

于 2014-04-30T09:54:23.353 に答える
2

Cannyied 画像に対して ContourFinding を試しましたか?

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

于 2009-11-22T16:07:52.943 に答える
0

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

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

于 2012-10-04T09:17:14.897 に答える