wake-up-neo.com

Füllen Sie die Löcher in OpenCV

Ich habe eine Edge-Map aus dem Edge-Erkennungsmodul in OpenCV (Canny Edge-Erkennung) extrahiert. Ich möchte die Löcher in der Edge-Map füllen. 

Ich verwende die Bibliotheken C++ und OpenCV. In OpenCV gibt es eine cvFloodFill () - Funktion, die die Löcher mit einem Samen füllt (mit einer der Stellen, an denen die Flutung beginnen soll). Ich versuche jedoch, alle inneren Löcher zu füllen, ohne die Samen zu kennen. (Ähnlich wie imfill () in MATLAB)

Q1: wie finde ich alle Samen, damit ich 'cvFloodFill ()' anwenden kann?
Q2: Wie implementiere ich ein "imfill ()" - Äquivalent?

Neuling in OpenCV, und jeder Hinweis wird geschätzt.

32
Lily

Laut der Dokumentation von imfill in MATLAB:

BW2 = imfill(BW,'holes');

füllt Löcher im Binärbild BW. Eine Lücke ist eine Gruppe von Hintergrundpixeln, die nicht erreicht werden kann, wenn der Hintergrund vom Rand des Bildes aus ausgefüllt wird.

Um die "Löcher" -Pixel zu erhalten, rufen Sie cvFloodFill mit dem Pixel der linken Ecke des Bildes als Startwert auf. Sie erhalten die Löcher, indem Sie das im vorherigen Schritt erhaltene Bild ergänzen.

MATLAB-Beispiel:

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)

 screenshot1  screenshot2

34
Amro

die Funktion cvDrawContours verfügt über eine Option zum Füllen der von Ihnen gezeichneten Konturen.

Hier ist ein kurzes Beispiel cvDrawContours (IplImage, Konturen, Farbe, Farbe, -1, CV_FILLED, 8);

Hier ist die Dokumentation

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

Ich denke, Sie haben das vor langer Zeit gepostet, aber ich hoffe, es hilft jemandem.

Dies ist der Quellcode (in 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

Ich habe mich im Internet umgesehen, um eine richtige imfill - Funktion (wie die in Matlab) zu finden, aber in C++ mit OpenCV zu arbeiten. Nach einigen Neuerungen habe ich endlich eine Lösung gefunden: 

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

Hierfür: Original-Binärbild

Ergebnis ist: Endgültiges Binärbild

Der Trick besteht in der Parametereinstellung der Funktion cvDrawContours: CvDrawContours (dst, contour, white, white, 0, CV_FILLED);

  • dst = Zielbild
  • kontur = Zeiger auf die erste Kontur
  • weiß = Farbe, mit der die Kontur gefüllt wird
  • 0 = Maximalwert für gezeichnete Konturen. Bei 0 wird nur eine Kontur gezeichnet
  • CV_FILLED = Dicke der Linien, mit denen die Konturen gezeichnet werden. Wenn es negativ ist (z. B. = CV_FILLED), werden die Konturinnenräume gezeichnet.

Weitere Informationen finden Sie in der openCV-Dokumentation.

Es gibt wahrscheinlich eine Möglichkeit, "dst" direkt als binäres Bild abzurufen, aber ich konnte nicht finden, wie die Funktion cvDrawContours mit binären Werten verwendet wird. 

7
Jeremy.S

Ich habe eine einfache Funktion erstellt, die mit Matlabs Nachfüllung ("Löchern") vergleichbar ist. Ich habe es in vielen Fällen nicht getestet, aber es hat bisher funktioniert. Ich verwende es für Edge-Images, aber es akzeptiert jede Art von Binärbild, wie bei einer Thresholding-Operation.

Ein Loch ist nicht mehr als ein Satz von Pixeln, der bei gefülltem Hintergrund nicht "erreicht" werden kann.

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

Hier ist ein Beispielergebnis

enter image description here

6
Pedro Batista

Hier ist ein schneller und schmutziger Ansatz:

  1. Führen Sie das Eingabebild so aus, dass das neue Binärbild an den Rändern 1 und ansonsten 0 hat
  2. Suchen Sie die erste 0 auf einer Seite Ihres Edge-Images und initiieren Sie eine Flutfüllung mit 1-Werten an diesem Punkt auf einem leeren Image. Verwenden Sie dabei Ihr Edge-Image als Maske. (Wir hoffen, dass wir hier nicht unglücklich waren und diese erste Füllung auf der Innenseite einer Form aussäen, die zur Hälfte vom Bildschirm weg ist.)
  3. Dieses neue überflutete Bild ist der "Hintergrund". Jedes Pixel, das eine 1 hat, ist der Hintergrund, und jedes Pixel, das eine 0 hat, ist der Vordergrund.
  4. Durchlaufen Sie das Bild und suchen Sie nach Vordergrundpixeln. Seed eine Flut von allen, die Sie finden. 
  5. ODER dieses neue geflutete Bild mit Ihrem Canny-Bild aus Schritt 1, und schon sind Sie fertig.
4
rcv

Nur ein Anhang für Amro's answer.

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

Kürzlich habe ich auch die Lösung für dieses Problem gefunden. Hier habe ich die Idee von Amro wie folgt umgesetzt:

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

Hoffe das wird hilfreich sein.

2
Bill Xia

Haben Sie ContourFinding über das Cannyied-Bild ausprobiert? 

cvFindContours erstellt eine Art Baum, in dem die äußeren Konturen den inneren Konturen ('Löcher') übergeordnet sind. Siehe Beispiel für contours.py. Aus den Konturen konnte man Samen entnehmen

2
dnul

Wenn Sie die Punkte von den Kanten haben, können Sie fillConvexPoly () oder fillPoly () (wenn Poly nicht konvex) verwenden.

Eine Möglichkeit, die Punkte von Kanten zu erhalten, ist das Ausführen von findContours () -> approxPolyDP () .

0
Rui Marques