Заполнение отверстий внутри двоичного объекта

У меня проблема с заполнением белых отверстий внутри черных монет, так что у меня может быть только двоичное изображение 0-255 с заполненными черными монетами.. Я использовал фильтр медиан для его выполнения, но в этом случае соединительный мост между монетами растет и это становится невозможным распознать их после нескольких раз эрозии... Поэтому мне нужен простой метод заливки флуда в opencv

Вот мое изображение с отверстиями:

enter image description here

EDIT: Функция заливки, заполняющая заливку, должна заполнять отверстия большими компонентами, не запрашивая координаты X, Y в качестве семени...

EDIT: я попытался использовать функцию cvDrawContours, но я не заполняю контуры внутри больших.

Вот мой код:

        CvMemStorage mem = cvCreateMemStorage(0);
        CvSeq contours = new CvSeq();
        CvSeq ptr = new CvSeq();
        int sizeofCvContour = Loader.sizeof(CvContour.class);

        cvThreshold(gray, gray, 150, 255, CV_THRESH_BINARY_INV);

        int numOfContours = cvFindContours(gray, mem, contours, sizeofCvContour, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
        System.out.println("The num of contours: "+numOfContours); //prints 87, ok

        Random rand = new Random();
        for (ptr = contours; ptr != null; ptr = ptr.h_next()) {
            Color randomColor = new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat());
            CvScalar color = CV_RGB( randomColor.getRed(), randomColor.getGreen(), randomColor.getBlue());
            cvDrawContours(gray, ptr, color, color, -1, CV_FILLED, 8);
        }
        CanvasFrame canvas6  = new CanvasFrame("drawContours");
        canvas6.showImage(gray);

Результат: (вы можете видеть черные дыры внутри каждой монеты)

enter image description here

Ответ 1

Есть два способа сделать это:

1) Заполнение контуров:

Сначала инвертируйте изображение, найдите контуры в изображении, заполните его черным и инвертируйте назад.

des = cv2.bitwise_not(gray)
contour,hier = cv2.findContours(des,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)

for cnt in contour:
    cv2.drawContours(des,[cnt],0,255,-1)

gray = cv2.bitwise_not(des)

Результирующее изображение:

enter image description here

2) Открытие изображения:

kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))
res = cv2.morphologyEx(gray,cv2.MORPH_OPEN,kernel)

Полученное изображение выглядит следующим образом:

enter image description here

Вы можете видеть, что в обоих случаях нет большой разницы.

NB: серо-серое изображение, все коды находятся в OpenCV-Python

Ответ 2

Я просто думаю, что простой разветвление и эрозия достаточно хорошо закрывают пробелы. Я думаю, возможно, это то, что вы ищете.

Более надежным решением было бы сделать обнаружение края на всем изображении, а затем преобразовать хруст для кругов. Быстрый google показывает, что есть примеры кода, доступные на разных языках для определения инвариантности по размеру окружностей с использованием преобразования hough, поэтому, надеюсь, это даст вам что-то, что нужно продолжить.

Преимущество использования преобразования hough заключается в том, что алгоритм фактически даст вам оценку размера и местоположения каждого круга, поэтому вы можете восстановить идеальное изображение на основе этой модели. Также должно быть очень надежно перекрываться, особенно учитывая качество входного изображения здесь (т.е. Меньше беспокоиться о ложных срабатываниях, поэтому можно снизить порог для результатов).

Ответ 3

Возможно, вы ищете Fillhole transform, приложение морфологической реконструкции изображений.

Это преобразование заполнит отверстия в ваших монетах, хотя за счет того, что они также заполняют все отверстия между группами соседних монет. Пространственные или открытые решения Hough, предлагаемые другими плакатами, вероятно, дадут вам лучшие результаты распознавания на высоком уровне.

Ответ 4

Попробуйте использовать функцию cvFindContours(). Вы можете использовать его для поиска подключенных компонентов. С помощью правильных параметров эта функция возвращает список с контурами всех подключенных компонентов.

Найдите контуры, представляющие отверстие. Затем используйте cvDrawContours(), чтобы заполнить выбранный контур цветом переднего плана, тем самым закрыв отверстия.

Ответ 5

Я думаю, что если объекты будут затронуты или переполнены, будут возникать проблемы с использованием контуров и открытия математической мофологии. Вместо этого найдено и протестировано следующее простое решение. Он работает очень хорошо, и не только для этих изображений, но и для любых других изображений.

Вот шаги (оптимизированные), как показано в http://blogs.mathworks.com/steve/2008/08/05/filling-small-holes/

let I: входное изображение

1. filled_I = floodfill(I). // fill every hole in the image.
2. inverted_I = invert(I)`.   
3. holes_I = filled_I AND inverted_I. // finds all holes 
4. cc_list = connectedcomponent(holes_I) // list of all connected component in holes_I.
5. holes_I = remove(cc_list,holes_I, smallholes_threshold_size) // remove all holes from holes_I having size > smallholes_threshold_size.
6. out_I = I OR holes_I. // fill only the small holes.

Короче говоря, алгоритм состоит только в том, чтобы найти все отверстия, удалить большие, а затем записать маленькие только на исходное изображение.

Ответ 6

Я просматривал интернет, чтобы найти правильную 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 с двоичными значениями.