Очистка изображения для распознавания текста


9

Я пытался очистить изображения для распознавания текста: (строки)

введите описание изображения здесь

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

    copy = img.copy()
    blur = cv2.GaussianBlur(copy, (9,9), 0)
    thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,11,30)

    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
    dilate = cv2.dilate(thresh, kernel, iterations=2)

    cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]

    for c in cnts:
        area = cv2.contourArea(c)
        if area > 300:
            x,y,w,h = cv2.boundingRect(c)
            cv2.rectangle(copy, (x, y), (x + w, y + h), (36,255,12), 3)

Изменить: Кроме того, использование постоянных чисел не будет работать в случае изменения шрифта. Есть ли общий способ сделать это?


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

@YvesDaoust Как можно найти близость символов? (поскольку фильтрация только по размеру часто смешивается с персонажами)
K41F4r

1
Для каждого сгустка можно найти расстояние до ближайшего соседа. Затем с помощью гистограммного анализа расстояний вы найдете порог между «близко» и «обособленно» (что-то вроде режима распределения) или между «окруженным» и «изолированным».
Ив Дауст

В случае нескольких маленьких линий рядом друг с другом, не будет ли их ближайший сосед другой маленькой линией? Будет ли вычисление среднего расстояния до всех остальных объектов слишком дорогим?
K41F4r

«Не будет ли их ближайший сосед другой маленькой линией?» - хорошее возражение, ваша честь. На самом деле куча близких коротких сегментов не отличается от легального текста, хотя и совершенно маловероятно. Возможно, вам придется перегруппировать фрагменты из ломаных линий. Я не уверен, что среднее расстояние до всех вас спасет.
Ив Дауст

Ответы:


14

Вот идея. Мы разбиваем эту проблему на несколько этапов:

  1. Определить среднюю площадь прямоугольного контура. Затем мы пороговые значения, затем находим контуры и фильтруем, используя ограничивающую прямоугольную область контура. Причина, по которой мы это делаем, заключается в том, что любой типичный символ будет таким большим, тогда как большой шум будет охватывать большую прямоугольную область. Затем мы определяем среднюю площадь.

  2. Удалить большие контуры выбросов. Мы повторяем контуры снова и удаляем большие контуры, если они 5xбольше, чем средняя площадь контура, заполняя контур. Вместо использования фиксированной области порога, мы используем этот динамический порог для большей устойчивости.

  3. Расширьте вертикальное ядро, чтобы соединить символы . Идея состоит в том, чтобы воспользоваться наблюдением, что символы выровнены в столбцах. Расширяя вертикальное ядро, мы соединяем текст, чтобы шум не включался в этот комбинированный контур.

  4. Удалить небольшой шум . Теперь, когда сохраняемый текст связан, мы находим контуры и удаляем все контуры, меньшие, чем 4xсредняя площадь контура.

  5. Побитовый - и реконструировать образ . Поскольку у нас есть только желаемые контуры, чтобы сохранить нашу маску, мы побитовые - и сохранить текст и получить наш результат.


Вот визуализация процесса:

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

введите описание изображения здесь введите описание изображения здесь

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

введите описание изображения здесь

Теперь мы находим контуры и фильтруем, используя область контура, чтобы удалить небольшой шум

введите описание изображения здесь

Здесь все удаленные шумовые частицы выделены зеленым

введите описание изображения здесь

Результат

введите описание изображения здесь

Код

import cv2

# Load image, grayscale, and Otsu's threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Determine average contour area
average_area = [] 
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    area = w * h
    average_area.append(area)

average = sum(average_area) / len(average_area)

# Remove large lines if contour area is 5x bigger then average contour area
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    area = w * h
    if area > average * 5:  
        cv2.drawContours(thresh, [c], -1, (0,0,0), -1)

# Dilate with vertical kernel to connect characters
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,5))
dilate = cv2.dilate(thresh, kernel, iterations=3)

# Remove small noise if contour area is smaller than 4x average
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    area = cv2.contourArea(c)
    if area < average * 4:
        cv2.drawContours(dilate, [c], -1, (0,0,0), -1)

# Bitwise mask with input image
result = cv2.bitwise_and(image, image, mask=dilate)
result[dilate==0] = (255,255,255)

cv2.imshow('result', result)
cv2.imshow('dilate', dilate)
cv2.imshow('thresh', thresh)
cv2.waitKey()

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


1
В случае более крупного шрифта это тоже не удалит текст?
K41F4r

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

Кажется, слишком специфичным для примера, использование средней области будет по-прежнему удалять текст в
большинстве

У вас есть другой пример входного изображения, которое вы можете добавить в пост?
Натанси

1
Найти решение, которое работает во всех ситуациях с использованием традиционных методов обработки изображений, довольно сложно. Возможно, вы захотите изучить свой собственный классификатор с использованием глубокого обучения. Удачи!
Натанси
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.