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

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

Вот мое изображение с дырками:

введите описание изображения здесь

РЕДАКТИРОВАТЬ: функция типа floodfill должна заполнять дыры в больших компонентах, не запрашивая координаты X, Y в качестве начальных значений ...

РЕДАКТИРОВАТЬ: Я пытался использовать функцию 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);

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

введите описание изображения здесь


person Zaur Guliyev    schedule 25.04.2012    source источник


Ответы (7)


Сделать это можно двумя способами:

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)

Полученное изображение:

введите описание изображения здесь

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

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

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

введите описание изображения здесь

Как видите, особой разницы в обоих случаях нет.

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

person Abid Rahman K    schedule 25.04.2012
comment
Хорошо, у меня возникла проблема с параметрами, которую я должен использовать: cvDrawContours (серый, ptr, color, color, 0, CV_FILLED, 8); с 0 в качестве параметра max_level. Хм, теперь он наполнен успехом :) - person Zaur Guliyev; 25.04.2012
comment
Попробуйте установить более крупный StructuringElement, например 15,15. Это может улучшить вашу процедуру «открытия». - person Gilad; 31.10.2012
comment
Классное решение - Спасибо! Для меня метод открытия изображения дал время выполнения ~ 6 мс, а метод заполнения контура дал время выполнения ~ 3,5 мс. Работа с видео, снятым с помощью cv2.VideoCapture (0) и моего MacBook Pro. Любой способ ускорить это с помощью numpy, или это уже используется opencv? - person user391339; 06.02.2014
comment
@ user391339: Я не думаю, что с этим можно что-то сделать, чтобы улучшить его скорость, потому что он использует готовую функцию opencv, и они оптимизированы как можно лучше. ой .. подождите пожалуйста !!! - person Abid Rahman K; 06.02.2014
comment
@ user391339: если подумать, я думаю, что использование cv2.RETR_ExTERNAL вместо cv2.RETR_CCOMP может немного улучшить. Не уверен, надо это проверить. Но все равно толку от этого не будет. Вместо этого вы можете проверить, что занимает больше времени в вашем коде, и попытаться его оптимизировать. Кстати, всего 3,5 мс. Зачем вообще нужно увеличивать его скорость? - person Abid Rahman K; 06.02.2014
comment
Я попытаюсь. Позже я хочу выполнить более дорогостоящую обработку, чтобы сэкономить время на мелочах. - person user391339; 06.02.2014

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

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

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

person Elias Vasylenko    schedule 25.04.2012

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

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

person thiton    schedule 25.04.2012
comment
но для opencv не для Matlab - person Zaur Guliyev; 25.04.2012
comment
@lbstr: Эта страница в основном объясняет теорию. Я не могу выбросить вам код OpenCV из головы. - person thiton; 25.04.2012
comment
Ссылка мертвая. - person Reunanen; 03.01.2018
comment
@Reunanen Ссылка все еще работает на момент написания этой статьи. Эта ссылка относится к официальной функции imfill в MATLAB's Image Processing Toolbox. Он должен быть надежным, пока существует MATLAB. - person rayryeng; 08.04.2020
comment
@ rayryeng-ReinstateMonica Он работает, потому что Крис Луенго исправил его после того, как я сделал свое замечание: stackoverflow.com/posts/10318004/revisions - person Reunanen; 09.04.2020

Если кто-то ищет реализацию cpp -

            std::vector<std::vector<cv::Point> > contours_vector;

            cv::findContours(input_image, contours_vector, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);

            cv::Mat contourImage(input_image.size(), CV_8UC1, cv::Scalar(0));
            for ( ushort contour_index = 0; contour_index < contours_vector.size(); contour_index++) {
                cv::drawContours(contourImage, contours_vector, contour_index, cv::Scalar(255), -1);
            }

            cv::imshow("con", contourImage);
            cv::waitKey(0);

введите описание изображения здесь

введите описание изображения здесь

person infoclogged    schedule 27.09.2018

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

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

person bubble    schedule 25.04.2012

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

вот шаги (оптимизированные), как показано в 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.

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

person Faroq AL-Tam    schedule 04.06.2012

Я искал в Интернете, чтобы найти подходящую функцию 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, contour, white, white, 0, CV_FILLED);

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

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

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

person Jeremy.S    schedule 15.04.2013