Быстрое формирование изображения
Что такое быстрый и надежный способ создания пороговых изображений с возможной размытой и неравномерной яркостью?
Пример (размытие, но равномерная яркость):
![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]()
Ответы
Ответ 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));
может ускорить ваше достаточно решение с низким воздействием на точность.