Порог размытого изображения - часть 2
Как я могу порочить это размытое изображение, чтобы сделать цифры как можно более ясными?
В предыдущем посте, я попытался адаптивно порождать размытое изображение (слева), что привело к искаженным и отключенным цифрам (справа):
![enter image description here]()
С тех пор я попытался использовать морфологическую операцию закрытия, описанную в этом сообщении, чтобы сделать яркость изображения равномерным:
![enter image description here]()
Если я адаптивно порою это изображение, я не получаю значительно лучших результатов. Однако, поскольку яркость примерно одинаковая, я могу теперь использовать обычный порог:
![enter image description here]()
Это намного лучше, чем раньше, но у меня две проблемы:
- Мне пришлось вручную выбрать пороговое значение. Хотя операция закрытия приводит к равномерной яркости, уровень яркости может отличаться для других изображений.
- Различные части изображения будут лучше с небольшими изменениями порогового уровня. Например, 9 и 7 в верхнем левом углу частично выцветают и должны иметь более низкий порог, в то время как некоторые из 6s слились в 8s и должны иметь более высокий порог.
Я думал, что возврат к адаптивному порогу, но с очень большим размером блока (1/9 изображения), решит обе проблемы. Вместо этого я получаю странный "эффект ореола", где центр изображения намного ярче, но края примерно такие же, как у обычного изображения:
![enter image description here]()
Изменить: remi предложил морфологически открывать пороговое изображение в правом верхнем углу этой публикации. Это не работает слишком хорошо. Используя эллиптические ядра, только 3x3 достаточно мала, чтобы полностью не стереть изображение, и даже тогда в цифрах имеются значительные разрывы:
![enter image description here]()
Edit2: mmgp предложил, используя фильтр Винера, чтобы удалить размытие. Я адаптировал этот код для фильтрации Винера в OpenCV в OpenCV4Android, но он делает изображение еще более размытым! Здесь изображение перед (слева) и после фильтрации с моим кодом и ядром 5x5:
![enter image description here]()
Вот мой адаптированный код, который фильтрует на месте:
private void wiener(Mat input, int nRows, int nCols) { // I tried nRows=5 and nCols=5
Mat localMean = new Mat(input.rows(), input.cols(), input.type());
Mat temp = new Mat(input.rows(), input.cols(), input.type());
Mat temp2 = new Mat(input.rows(), input.cols(), input.type());
// Create the kernel for convolution: a constant matrix with nRows rows
// and nCols cols, normalized so that the sum of the pixels is 1.
Mat kernel = new Mat(nRows, nCols, CvType.CV_32F, new Scalar(1.0 / (double) (nRows * nCols)));
// Get the local mean of the input. localMean = convolution(input, kernel)
Imgproc.filter2D(input, localMean, -1, kernel, new Point(nCols/2, nRows/2), 0);
// Get the local variance of the input. localVariance = convolution(input^2, kernel) - localMean^2
Core.multiply(input, input, temp); // temp = input^2
Imgproc.filter2D(temp, temp, -1, kernel, new Point(nCols/2, nRows/2), 0); // temp = convolution(input^2, kernel)
Core.multiply(localMean, localMean, temp2); //temp2 = localMean^2
Core.subtract(temp, temp2, temp); // temp = localVariance = convolution(input^2, kernel) - localMean^2
// Estimate the noise as mean(localVariance)
Scalar noise = Core.mean(temp);
// Compute the result. result = localMean + max(0, localVariance - noise) / max(localVariance, noise) * (input - localMean)
Core.max(temp, noise, temp2); // temp2 = max(localVariance, noise)
Core.subtract(temp, noise, temp); // temp = localVariance - noise
Core.max(temp, new Scalar(0), temp); // temp = max(0, localVariance - noise)
Core.divide(temp, temp2, temp); // temp = max(0, localVar-noise) / max(localVariance, noise)
Core.subtract(input, localMean, input); // input = input - localMean
Core.multiply(temp, input, input); // input = max(0, localVariance - noise) / max(localVariance, noise) * (input - localMean)
Core.add(input, localMean, input); // input = localMean + max(0, localVariance - noise) / max(localVariance, noise) * (input - localMean)
}
Ответы
Ответ 1
Некоторые подсказки, которые вы можете попробовать:
-
Примените морфологическое открытие в исходном пороговом изображении (том, что шумно справа от первого изображения). Вы должны избавиться от большинства фоновых шумов и сможете повторно подключить цифры.
-
Используйте другую предварительную обработку исходного изображения вместо закрытия морфования, например медианный фильтр (имеет тенденцию размывать края) или двусторонняя фильтрация, который будет лучше сохранять ребра, но медленнее вычислять.
-
Что касается порога, вы можете использовать флаг CV_OTSU в пороге cv::, чтобы определить оптимальное значение для глобального порога. Локальное пороговое значение может все же быть лучше, но должно работать лучше с двусторонним или медианным фильтром.
Ответ 2
Я пробовал порождать каждый блок 3x3 отдельно, используя алгоритм Otsu (CV_OTSU - спасибо remi!), чтобы определить оптимальное пороговое значение для каждого окна. Это работает немного лучше, чем пороговое изображение всего изображения, и, вероятно, немного более надежное.
![enter image description here]()
Однако лучшие решения приветствуются.
Ответ 3
Если вы готовы потратить на это несколько циклов, есть методы размытия, которые можно использовать для резкости изображения перед обработкой. Ничего в OpenCV пока нет, но если это что-то вроде make-or-break, вы можете добавить его.
Там есть куча литературы по этому вопросу:
http://www.cse.cuhk.edu.hk/~leojia/projects/motion_deblurring/index.html
http://www.google.com/search?q=motion+deblurring
И какая-то болтовня в списке рассылки OpenCV:
http://tech.groups.yahoo.com/group/OpenCV/message/20938
Странный "эффект ореола", который вы видите, скорее всего, из-за того, что OpenCV принимает черный цвет, когда адаптивный порог находится на/вблизи края изображения, а окно, в котором оно использует "зависает" по краю, территория без изображения. Есть способы исправить это, скорее всего, вы сделаете временный образ, по крайней мере, два полных размера блока выше и выше изображения с камеры. Затем скопируйте изображение камеры в середину. Затем установите окружающую "пустую" часть временного изображения как средний цвет изображения с камеры. Теперь, когда вы выполняете адаптивный порог, данные на/вблизи краев будут намного ближе к точности. Это не будет совершенным, поскольку его не реальная картина, но она даст лучшие результаты, чем черный, который предполагает OpenCV.
Ответ 4
Мое предложение предполагает, что вы можете определить ячейки sudoku, которые, я думаю, не слишком много спрашивают. По-моему, попытка применить морфологические операторы (хотя они мне действительно нравятся) и/или методы бинаризации в качестве первого шага является неправильным способом. Ваше изображение по крайней мере частично размыто по любой причине (исходный угол камеры и/или движение, среди других причин). Итак, вам нужно вернуть это, выполнив деконволюцию. Конечно, просить идеальной деконволюции слишком много, но мы можем попробовать кое-что.
Одной из этих "вещей" является фильтр Винера, а в Matlab, например, функция называется deconvwnr
. Я заметил, что размытость находится в вертикальном направлении, поэтому мы можем выполнить деконволюцию с вертикальным ядром определенной длины (10 в следующем примере), а также предположить, что вход не является шумовым (предположение 5%) - я ' я просто пытаюсь дать здесь очень поверхностное представление, успокойся. В Matlab ваша проблема, по крайней мере, частично решена путем:
f = imread('some_sudoku_cell.png');
g = deconvwnr(f, fspecial('motion', 10, 90), 0.05));
h = im2bw(g, graythresh(g)); % graythresh is the Otsu method
Вот результаты некоторых из ваших клеток (оригинал, otsu, otsu развития региона, морфологическое улучшенное изображение, otsu от морфологического увеличенного изображения с ростом области, otsu деконволюции):
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
![enter image description here]()
Улучшенное изображение было создано путем выполнения оригинального + tophat (оригинал) - bottomhat (оригинал) с плоским диском радиуса 3. Я вручную выбрал начальную точку для роста области и вручную выбрал лучший порог.
Для пустых ячеек вы получаете странные результаты (оригинал и отцу деконволюции):
![enter image description here]()
![enter image description here]()
Но я не думаю, что вам будет сложно определить, является ли ячейка пустой или нет (глобальный порог уже разрешает ее).
EDIT:
Добавлены лучшие результаты, которые я мог бы получить с другим подходом: рост региона. Я также попытался использовать некоторые другие подходы, но это был второй лучший вариант.
Ответ 5
Вероятно, вам нужно локальное пороговое значение. для этого существуют некоторые общие подходы.
Проверьте алгоритм niblack. См. Также пороговое значение niblack. fooobar.com/questions/437574/...
Мы успешно использовали это для сегментации документа.
Ответ 6
Другой вариант - использовать методы выращивания в регионе. Мы даем это нашим ученикам на домашнее задание, которое можно найти по адресу:
http://www.cs205.org/resources/hw4.pdf
(проблема 5)
Цель состоит в том, чтобы распространять информацию из набора семян. Вам все равно потребуется порог (и в этом случае у вас теперь будет установлен 2 порога!), Но вы можете получить гораздо лучшие результаты.