Заполните отверстия в OpenCV [дубликат]

этот вопрос уже есть ответ здесь:

У меня есть карта края, извлеченная из модуля обнаружения края в OpenCV (обнаружение края Кэнни). Что я хочу сделать,так это заполнить отверстия в карте края.

Я использую C++, и OpenCV библиотеки. В OpenCV есть cvFloodFill () функция, и она заполнит отверстия с семенем (с одним из положения для того чтобы начать затоплять). Тем не менее, я пытаюсь заполнить все внутренние отверстия, не зная семян.(похожие на imfill() в MATLAB)

Вопрос 1: как найти все семена, чтобы я мог применить ' cvFloodFill ()'?
Вопрос 2: как реализовать ' imfill ()' аналог?

Новичок в OpenCV, и любой намек ценится.

9 ответов


согласно документации imfill в MATLAB:

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)

screenshot1 screenshot2


функция cvDrawContours имеет возможность заполнить контуры, которые вы нарисовали.

вот краткий пример cvDrawContours (IplImage, контуры, цвет, цвет, -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


Я искал в интернете, чтобы найти правильный imfill функция (как в Matlab), но работает на C++ с OpenCV. После некоторых рассуждений я, наконец, придумал решение:

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: cvDrawContours( DST, контур, белый, белый, 0, CV_FILLED);

  • dst = изображение назначения
  • contour = указатель на первый контур
  • белый = цвет, используемый для заполнения контура
  • 0 = максимальный уровень для нарисованных контуров. Если 0, то рисуется только контур
  • CV_FILLED = толщина линий контуры рисуются. Если он отрицательный (например, =CV_FILLED), контурные интерьеры рисуются.

дополнительная информация в openCV документация.

вероятно, есть способ получить" dst " непосредственно как двоичное изображение, но я не мог найти, как использовать функцию cvDrawContours с двоичными значениями.


Я сделал простую функцию, эквивалентную imfill matlab ('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;
}

вот пример результата

enter image description here


вот быстрый и грязный подход:

  1. выполните canny на входном изображении, чтобы новое двоичное изображение имело 1 по краям, а 0 в противном случае
  2. найдите первый 0 вдоль стороны вашего изображения края и инициируйте заливку с 1 в этой точке на пустом изображении, используя ваше изображение края в качестве маски. (Мы надеемся, что здесь нам не повезло и семян первой заливки внутри формы, которая находится от экрана)
  3. новый floodfilled изображение "фон". Любой пиксель здесь есть 1-это фон, и любой пиксель, который имеет 0-это передний.
  4. петли через изображения и изображения пикселей. Семян floodfill на любой Вы найдете.
  5. или это новый floodfilled изображения с вашего Канни изображения с шага 1, и вы закончите.

просто приложение для 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;
    }
}

вы пробовали ContourFinding над изображением Cannyied?

cvFindContours создает своего рода дерево, в котором внешние графы являются родителями внутренних контуров ("отверстия"). Смотри contours.py образец. Из контуров можно было извлечь семена


недавно я также нахожу решение этой проблемы. Здесь я реализовал Амро идея следующим образом:

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

надеюсь, это будет полезно.


Если у вас есть точки от краев, вы можете использовать fillConvexPoly () или fillPoly () (Если Поли не выпуклый).

один из способов получить точки от ребер-это сделать findContours() -> approxPolyDP().