Отсоединенные контуры после адаптивного порога

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

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

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

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

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

Я смог повторно соединить края после адаптивного определения порога, вручную заполнив все одно- и двухпиксельные промежутки в изображении (на самом деле я пороговую для уменьшенного изображения, чтобы сэкономить время выполнения; промежутки больше в приведенном выше полном размер изображения). Вот код OpenCV, который я использовал (написанный для привязок Android). 0 - черный, -1 - белый.

private void fillGaps(Mat image) {

    int size = image.rows() * image.cols();
    byte[] src = new byte[size], dst = new byte[size];
    image.get(0, 0, src);

    int c = image.cols();
    int start = 2 * c + 2;
    int end = size - start;
    for (int i = start; i < end; i++) {
        if (src[i+1] == -1 && src[i-1] == -1 || src[i+c] == -1 && src[i-c] ==-1){
            // 1-pixel gap
            dst[i] = -1;
        } else if (src[i+1] == 0 && src[i+2  ] == -1 && src[i-1] == -1) {
            // 2-pixel horizontal gap
            dst[i] = -1; dst[i+1] = -1;
        } else if (src[i+c] == 0 && src[i+2*c] == -1 && src[i-c] == -1) { 
            // 2-pixel vertical gap
            dst[i] = -1; dst[i+c] = -1;
        }
    }

    image.put(0, 0, dst);
}

Вот уменьшенное изображение до и после заполнения пробелов:

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

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

Как можно надежно избежать отсоединения контуров после адаптивного порога?


person 1''    schedule 14.08.2013    source источник


Ответы (2)


Подход с адаптивным порогом, который вы применяете, хорош, но в качестве следующего шага вам следует выполнить некоторые морфологические операции: эрозию, расширение, открытие, закрытие. Для вашего конкретного случая подойдет операция закрытия.
Есть также встроенные в Open-CV методы cvDilate и cvErode. Форма структурирующего элемента не имеет большого значения, но сохраняйте его небольшие размеры.

Я видел вашу реализацию заполнения пробелов. Здесь вы не рассматриваете диагональные элементы. Лучше взять окно 3x3 или 5x5 вокруг каждого пикселя и сравнить каждый элемент, а затем принять решение о результате.

counter=0;
for (int k=i-radius; k<=i+radius; k++)
{
  for (int l=j-radius; l<=j+radius; l++)
  {
    if (src[k][l] == -1)
    counter++;
  }
}

if (counter > 0)
  dest[k][l] = -1;
else
  dest[k][l] = 0;

Это пример кода, который я использую для расширения (или заполнения пробелов). Радиус может быть 1 (3x3) или 2 (5x5).

person Soumyajit    schedule 14.08.2013
comment
Вы абсолютно правы: закрытие маленьким окошком (прямоугольник 3x3) - лучший вариант. Я сознательно не рассматривал диагональные элементы, потому что предполагал вертикальную и горизонтальную сетку, но закрытие выполняется достаточно осторожно, чтобы включить диагонали тоже. - person 1''; 14.08.2013
comment
Извините, на самом деле с круглым окном лучше работать. Я использовал Imgproc.morphologyEx(image, image, Imgproc.MORPH_CLOSE, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(3,3))); - person 1''; 14.08.2013
comment
Да, круглое окно дает лучшую границу, так как сложность вычисления такого окна увеличивается (при ручной реализации), я не использовал его ... попробую это когда-нибудь позже. - person Soumyajit; 16.08.2013

Попробуйте расширить изображение после AdaptiveThreshold.

person Hamidreza Vakilian    schedule 28.11.2016