Порог размытого изображения - часть 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 hereenter image description hereenter image description hereenter image description hereenter image description hereenter image description here
enter image description hereenter image description hereenter image description hereenter image description hereenter image description hereenter image description here
enter image description hereenter image description hereenter image description hereenter image description hereenter image description hereenter image description here
enter image description hereenter image description hereenter image description hereenter image description hereenter image description hereenter image description here
enter image description hereenter image description hereenter image description hereenter image description hereenter image description hereenter image description here
enter image description hereenter image description hereenter image description hereenter image description hereenter image description hereenter image description here

Улучшенное изображение было создано путем выполнения оригинального + tophat (оригинал) - bottomhat (оригинал) с плоским диском радиуса 3. Я вручную выбрал начальную точку для роста области и вручную выбрал лучший порог.

Для пустых ячеек вы получаете странные результаты (оригинал и отцу деконволюции):

enter image description hereenter image description here

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

EDIT:

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

Ответ 5

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

Проверьте алгоритм niblack. См. Также пороговое значение niblack. fooobar.com/questions/437574/... Мы успешно использовали это для сегментации документа.

Ответ 6

Другой вариант - использовать методы выращивания в регионе. Мы даем это нашим ученикам на домашнее задание, которое можно найти по адресу:

http://www.cs205.org/resources/hw4.pdf
(проблема 5)

Цель состоит в том, чтобы распространять информацию из набора семян. Вам все равно потребуется порог (и в этом случае у вас теперь будет установлен 2 порога!), Но вы можете получить гораздо лучшие результаты.