Подтвердить что ты не робот

Быстрое формирование изображения

Что такое быстрый и надежный способ создания пороговых изображений с возможной размытой и неравномерной яркостью?

Пример (размытие, но равномерная яркость):

enter image description here

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

enter image description here

Я также попытался использовать выравнивание гистограммы (используя функцию OpenCV equalizeHist). Это увеличивает контраст, не уменьшая различия в яркости.

Лучшее решение, которое я нашел, состоит в том, чтобы разделить изображение на его морфологическое закрытие (кредит этот пост), чтобы сделать однородность яркости, затем перенормировать, а затем использовать фиксированный порог (с использованием алгоритма Otsu для выбора оптимального порогового уровня):

enter image description here

Вот код для этого в OpenCV для Android:

Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(19,19));
Mat closed = new Mat(); // closed will have type CV_32F
Imgproc.morphologyEx(image, closed, Imgproc.MORPH_CLOSE, kernel);
Core.divide(image, closed, closed, 1, CvType.CV_32F);
Core.normalize(closed, image, 0, 255, Core.NORM_MINMAX, CvType.CV_8U);
Imgproc.threshold(image, image, -1, 255, Imgproc.THRESH_BINARY_INV
    +Imgproc.THRESH_OTSU); 

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

Изменить: на основе предложения DCS я попытался использовать фильтр верхних частот. Я выбрал фильтр Лапласа, но ожидал бы аналогичных результатов с фильтрами Sobel и Scharr. Фильтр захватывает высокочастотный шум в областях, которые не содержат признаков, и страдает от подобного искажения адаптивного порога из-за размытия. это также занимает примерно столько же времени, сколько и операция закрытия. Вот пример с фильтром 15x15:

enter image description here

Изменить 2: на основе ответа AruniRC я использовал обнаружение края Canny на изображении с предлагаемыми параметрами:

double mean = Core.mean(image).val[0];
Imgproc.Canny(image, image, 0.66*mean, 1.33*mean);

Я не уверен, как надежно автоматически настроить параметры для подключения цифр.

enter image description here

4b9b3361

Ответ 1

Используя предложения Vaughn Cato и Theraot, я уменьшил изображение до его закрытия, затем масштабировал закрытое изображение до обычного размера. Я также уменьшил размер ядра пропорционально.

Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(5,5));
Mat temp = new Mat(); 

Imgproc.resize(image, temp, new Size(image.cols()/4, image.rows()/4));
Imgproc.morphologyEx(temp, temp, Imgproc.MORPH_CLOSE, kernel);
Imgproc.resize(temp, temp, new Size(image.cols(), image.rows()));

Core.divide(image, temp, temp, 1, CvType.CV_32F); // temp will now have type CV_32F
Core.normalize(temp, image, 0, 255, Core.NORM_MINMAX, CvType.CV_8U);

Imgproc.threshold(image, image, -1, 255, 
    Imgproc.THRESH_BINARY_INV+Imgproc.THRESH_OTSU);

Изображение ниже показывает результаты бок о бок для 3 разных методов:

Левая - закрытие обычного размера (432 пикселя), размер 19 ядро ​​

Среднее - полуразмерное закрытие (216 пикселей), размер 9 ядро ​​

Закрытие вправо - четверть размера (108 пикселей), размер 5 ядро ​​

enter image description here

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

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

Ответ 2

Альтернативный подход:

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

Здесь довольно простой подход:

  • Сделайте изображение на Canny edgemap. Сначала попробуйте с параметрами функции Canny в диапазоне нижнего порога до 0,66 * [среднее значение], а высокий порог - 1,33 * [среднее значение]. (что означает среднее значение уровня greylevel).
  • Вам нужно немного поиграть с параметрами, чтобы получить изображение, в котором основные компоненты/цифры четко видны как отдельные компоненты. Рядом с идеальным было бы достаточно хорошо на этом этапе.
  • Учитывая, что каждый ребро Canny является связующим компонентом (т.е. использует cvFindContours() или его экземпляр С++, в зависимости от того, какой из них), можно оценить передние и задние уровни greilevels и достигнуть порога.

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

    Надеюсь, что это помогло!

Изменить 1:

На основании пороговых значений Canny edge здесь очень грубая идея, достаточная для точной настройки значений. Элемент high_threshold определяет, насколько сильным является край, прежде чем он будет обнаружен. В принципе, край должен иметь величину градиента, превышающую high_threshold, которая должна быть обнаружена в первую очередь. Таким образом, это первоначальное обнаружение ребер.

Теперь low_threshold имеет дело с подключением соседних ребер. Он контролирует, насколько близкие разъединенные края будут объединены вместе в один край. Для лучшей идеи прочитайте "Шаг 6" эту веб-страницу. Попробуйте установить очень маленький low_threshold и посмотреть, как все происходит. Вы можете отбросить это значение 0,66 * [среднее значение], если оно не работает на этих изображениях - это просто эмпирическое правило.

Ответ 3

Мы используем алгоритм Брэдли для очень сходной задачи (для отсечения букв фона, с неравномерным светом и неравномерным цветом фона), описанный здесь: http://people.scs.carleton.ca:8008/~roth/iit-publications-iti/docs/gerh-50002.pdf, здесь код С#: http://code.google.com/p/aforge/source/browse/trunk/Sources/Imaging/Filters/Adaptive+Binarization/BradleyLocalThresholding.cs?r=1360. Он работает с интегральным изображением, которое можно вычислить с помощью функции integral OpenCV. Он очень надежный и быстрый, но сам он не реализован в OpenCV, но его легко переносить.

Другой вариант - метод adaptiveThreshold в openCV, но мы не дали ему попробовать: http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html#adaptivethreshold. Версия MEAN такая же, как и bradleys, за исключением того, что она использует константу для изменения среднего значения вместо процента, который, я думаю, лучше.

Кроме того, хорошая статья здесь: https://dsp.stackexchange.com/a/2504

Ответ 4

Вы можете попробовать работать на основе каждой плитки, если знаете, что у вас хороший урожай сетки. Работа над 9 субимагами, а не всей картиной, скорее всего, приведет к более равномерной яркости на каждом субимаге. Если ваша обрезка идеальна, вы даже можете попробовать каждую цифровую ячейку индивидуально; но все зависит от того, насколько надежна ваша культура.

Ответ 5

Форма эллипса сложна для расчета по сравнению с плоской формой. Попробуйте изменить:

Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(19,19));

в

Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(19,19));

может ускорить ваше достаточно решение с низким воздействием на точность.