Python Opencv: фильтр изображения для обнаружения текста - PullRequest
3 голосов
/ 20 февраля 2020

unfiltered image

У меня есть этот набор изображений, которые я хочу снять шум, чтобы запустить OCR на:

Я пытаюсь прочитать 1973 с картинки.

Я пробовал

import cv2,numpy as np


img=cv2.imread('uxWbP.png',0)
img = cv2.resize(img, (0, 0), fx=2, fy=2)
copy_img=np.copy(img)
#adaptive threshold as the image has different lighting conditions in different areas
thresh = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 21, 2)

contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#kill small contours
for i_cnt, cnt in enumerate(sorted(contours, key=lambda x: cv2.boundingRect(x)[0])):
    _area = cv2.contourArea(cnt)
    x, y, w, h = cv2.boundingRect(cnt)
    x_y_area = w * h
    if 10000 < x_y_area and x_y_area < 400000:
        pass
        # cv2.rectangle(copy_img, (x, y), (x + w, y + h), (255, 0, 255), 2)
        # cv2.putText(copy_img, str(int(x_y_area)) + ' , ' + str(w) + ' , ' + str(h), (x, y + 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
        # cv2.drawContours(copy_img, [cnt], 0, (0, 255, 0), 1)
    elif 10000 > x_y_area:
        #write over small contours
        cv2.drawContours(thresh, [cnt], -1, 255, -1)

cv2.imshow('img',copy_img)
cv2.imshow('thresh',thresh)
cv2.waitKey(0)

, что значительно улучшает изображение до:

filtered image

Любые рекомендации о том, как отфильтровать это изображение в достаточной степени, либо в отношении улучшений отфильтрованного изображения, либо полного изменения с самого начала, чтобы я мог запустить OCR или некоторые сценарии обнаружения ML для этого? Я хотел бы разделить числа для обнаружения, но открыть и для других методов.

Ответы [ 2 ]

3 голосов
/ 20 февраля 2020

Моя первая мысль - использовать размытие по Гауссу для своего рода «фильтра нерезкости». (Я думаю, моя вторая идея лучше ; она сочетает в себе это размытие и добавление с игрой с эрозией / расширением. Я опубликовал ее в качестве отдельного ответа, потому что я думаю, что это стратегия, которая по-другому заслуживает это.) @eldesgraciado отметил частоту, которая в основном и состоит в том, что мы здесь делаем. Я надену код и объяснение. ( Здесь - это один ответ на сообщение SO, в котором много говорится о повышении резкости - связанный ответ представляет собой более переменную маску нерезкости, написанную в Python. Не торопитесь, чтобы посмотреть другие ответы, включая this , одна из многих простых реализаций, которые выглядят так же, как у меня - хотя некоторые написаны на разных языках программирования.) Вам нужно будет возиться с параметрами. Возможно, это не сработает, но это первое, о чем я подумал.

>>> import cv2
>>> im_0 = cv2.imread("FWM8b.png")
>>> cv2.imshow("FWM8b.png", im_0)
>>> cv2.waitKey(0)
## Press any key.
>>> ## Here's where we get to frequency. We'll use a Gaussian Blur.
    ## We want to take out the "frequency" of changes from white to black
    ## and back to white that are less than the thickness of the "1973"
>>> k_size = 0 ## This is the kernal size - the "width frequency",
               ## if you will. Using zero gives a width based on sigmas in
               ## the Gaussian function.
               ## You'll want to experiment with this and the other
               ## parameters, perhaps trying to run OCR over the image
               ## after each combination of parameters.
               ## Hint, avoid even numbers, and think of it as a radius
>>> gs_border = 3
>>> im_blurred = cv2.GaussianBlur(im_0, (k_size, k_size), gs_border)
>>> cv2.imshow("gauss", im_blurred)
>>> cv2.waitKey(0)

Gaussian blur with kernel size determined by sigmas

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

Мы собираемся умножить исходное изображение на значение, умножить размытое изображение на значение и вычтите value*blurry из value*orig. Надеюсь, код будет понятнее.

>>> orig_img_multiplier = 1.5
>>> blur_subtraction_factor = -0.5
>>> gamma = 0
>>> im_better = cv2.addWeighted(im_0, orig_img_multiplier, im_blurred, blur_subtraction_factor, gamma)
>>> cv2.imshow("First shot at fixing", im_better)

First attempted fix

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

Редактировать Это отличный вопрос. Отвечая на насмешливую критику @ eldesgraciado

Ах, непослушный, непослушный. Пытаетесь взломать их коды CAPTCHA, а? Их трудно сломать по причине. Сегментация текста, как видите, нетривиальна. В вашем конкретном изображении много высокочастотного шума, вы можете сначала попробовать какую-то частотную фильтрацию и посмотреть, какой результат вы получите.

Я отправляю следующее из статьи Википедии о reCAPTCHA ( в архиве ).

reCAPTCHA полностью оцифровала архивы New York Times и книги из Google Книг по состоянию на 2011 год. три Поиск в архиве можно найти в статье New York Times Archive.four Благодаря массовому сотрудничеству reCAPTCHA помогала оцифровывать книги, которые слишком неразборчивы для сканирования компьютерами, а также переводить книги на разные языки с 2015 года. *

Также посмотрите эту статью (в архиве).

Я не думаю, что эта CAPTCHA является частью массового онлайн-сотрудничества.

Редактировать: Потребуется другой тип заточки. Я только что понял, что применяю множители 1,5 и -0,5 к пикселям, которые обычно имеют значения, очень близкие к 0 или 255, то есть, вероятно, я просто восстанавливаю исходное изображение после повышения резкости. Я приветствую любые отзывы по этому поводу.

Кроме того, из комментариев с @eldesgracio:

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

Просто чтобы дать вам несколько идей ...

Вот размытие с k_size = 5

The Gaussian-blurred image

Вот размытие с k_size = 25

enter image description here

Обратите внимание, что это BLURS , а не исправления. Скорее всего, вам придется возиться с orig_img_multiplier и blur_subtraction_factor в зависимости от частоты (я точно не помню, как, поэтому я не могу точно сказать, как это делается.) Не стесняйтесь возиться с gs_border, gamma и все, что вы можете найти в документации для методов, которые я показал.

Удачи с этим.

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

2 голосов
/ 20 февраля 2020

Еще одна вещь, которую можно попробовать - отдельно от размытия (или в сочетании с ним) - игра эрозии / расширения , как намекается в комментарии @eldesgraciado, которому, я думаю, следует отдать должное за эти ответы go.

Эти два (эрозия и расширение) могут применяться один за другим, неоднократно. Я думаю, уловка это изменить размер ядра. Во всяком случае, я знаю, что я использовал это, чтобы уменьшить шум в прошлом. Вот один пример расширения:

>>> import cv2
>>> import numpy as np
>>> im_0 = cv2.imread("FWM8b.png")
>>> k_size = 3
>>> kernel = np.ones((k_size, k_size), np.uint8)
>>> im_dilated = cv2.dilate(im_0, kernel, iterations=1)
>>> cv2.imshow("d", im_dilated)
>>> cv2.waitKey(0)

Quick Dilation

Сделайте любое ядро, которое вы хотите для размывания, и проверьте эффекты.

>>> im_eroded = cv2.erode(im_0, kernel, iterations=1)
>>> cv2.imshow("erosion", im_eroded)
>>> cv2.waitKey(0)

Редактировать с возможными улучшениями:

>>> im_blurred = cv2.GaussianBlur(im_dilated, (0, 0), 3)
>>> im_better = cv2.addWeighted(im_0, 0.5, im_blurred, 1.2, 0) 
# Getting closer.

Dilated, blurred, and combined with original, 1st

^ d ilated, b выложено и объединено ( a dded) с оригиналом, 1 st way

# Even better, I think.
im_better2 = cv2.addWeighted(im_0, 0.9, im_blurred, 1.7, 0)

Dilated, blurred, and combined with original, 2

^ d подкладка, b выложенная и комбинированная ( a dded) с оригинальным, 2 nd way

Вы можете сделать удаление артефактов, но будьте осторожны, чтобы не избавиться от стебля 7. Если вы можете держать 7 вместе, вы можете проводить анализ связанных компонентов и хранить самые большие связанные компоненты.

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

Sums of rows and columns

Обратите внимание, что это больше сумма (255 - pixel_value). Это может найти вас прямоугольники, где должны быть ваши глифы (цифры), которые будут найдены. Вы можете сделать двумерную карту column_pixel_sum + row_pixel_sum, или просто сделать некоторое приближение, как я сделал ниже.

Sums and rectangles

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

Множество других вещей, чтобы попробовать ... предложение от @eldesgraciado из Модель шума особенно интригует.

Еще одна вещь, которую вы можете попробовать - создать «модель шума» и вычесть ее из исходного изображения. Сначала возьмите изображение и примените Gaussian Blur с очень низкими параметрами, едва размывая его, затем вычтите эту маску из изображения. Отсюда, шаги являются экспериментальными: разница должна быть снова размыта и пороговая. Сохраните это изображение. Вы выполняете эту предварительную обработку с различными параметрами и каждый раз сохраняете конечное двоичное изображение, а затем усредняете полученные маски. Постоянные капли должны быть именно теми, которые вы ищете ... вроде какой-то пространственной полосы, я думаю ...

Продолжайте экспериментировать.

Нерезкая маска ( другой мой ответ) на этот результат изображение . Больше шума ушло, но болит 7.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...