Как использовать предварительную обработку изображений для повышения точности Tesseract

1656604932 kak ispolzovat predvaritelnuyu obrabotku izobrazhenij dlya povysheniya tochnosti tesseract

Берк Каан Кугуоглу

XhjeB7WoijTZNzIBKRqTnAMIZmqXtYnhV5sy

Ранее, в статье «Как начать работу с Tesseract», я дал вам практическое руководство по быстрому началу работы с Tesseract с помощью Python. Это достаточно простой обзор, но он должен помочь вам начать работу с Tesseract и устранить некоторые препятствия, с которыми я столкнулся, когда был на вашем месте. Теперь я хочу показать вам еще несколько приёмов и вещей, которые вы можете сделать с помощью Tesseract и OpenCV, чтобы повысить общую точность.

Где мы остановились в прошлый раз?

В предыдущей истории я в большинстве своем не вдавался в детали. Но если вам понравилась первая история, вот продолжение! Где мы остановились?

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

Изменение масштаба

Изменение масштабируемых изображений уменьшается или увеличивается. Если вы хотите уменьшить свое изображение, INTER_AREA это путь для вас. (Кстати, параметры fx и fy отметьте коэффициент масштабирования в функции ниже.)

img = cv2.resize(img, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)

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

img = cv2.resize(img, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)

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

img = cv2.resize(img, None, fx=2, fy=2, interpolation=cv2.INTER_LINEAR)

Размывание

Следует отметить, что в библиотеке OpenCV есть несколько фильтров размытия. Размывание изображения обычно достигается путём свертывания изображения с помощью ядра фильтра низких частот. Хотя фильтры обычно используются для размытия изображения или уменьшения шума, между ними есть несколько отличий.

1. Усреднение

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

img = cv.blur(img,(5,5))

2. Гауссовое размытие

Это работает так же, как усреднение, но для свертки используется ядро ​​Гаусса вместо нормализованного фильтра. Здесь размеры ядра и стандартные отклонения по обоим направлениям можно определить независимо. Размывание по Гауссу очень полезно для удаления – угадайте что? гауссовый шум с изображения. Напротив, Размывание по Гаусс не сохраняет края во введенных данных.

img = cv2.GaussianBlur(img, (5, 5), 0)

3. Среднее размытие

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

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

img = cv2.medianBlur(img, 3)

4. Двухсторонняя фильтрация

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

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

img = cv.bilateralFilter(img,9,75,75)

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

Опять же ты делаешь.

Пороговое значение изображения

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

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

1. Простой порог

Вы можете вспомнить, как ваш друг дал вам несколько советов по вашей жизни, сказав «не всегда все черно-белое». Что ж, для простого порога все достаточно просто.

cv.threshold(img,127,255,cv.THRESH_BINARY)

Сначала вы выбираете пороговое значение, скажем, 127. Если значение пикселя превышает порог, оно становится черным. Если меньше, он становится белым. OpenCV предоставляет нам разные типы методов порогового значения, которые можно передать как четвертый параметр. Я часто использую двоичный порог для большинства задач, но для других методов порогового значения вы можете посетить официальную документацию.

2. Адаптивный порог

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

cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)

Есть два адаптивных метода для расчета порогового значения. Хотя Адаптивный пороговый средний возвращает среднее значение области окрестностей, Адаптивное среднее Гаусса вычисляет взвешенную сумму значений окрестностей.

У нас есть еще два параметра, которые определяют размер области окрестности и постоянное значение, которое вычитается из результата: пятый и шестой параметры соответственно.

3. Порог Оцу

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

cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]

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

Виды порогов

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

Что дальше?

Пока мы обсуждали некоторые приемы предварительной обработки изображений. Вы можете задаться вопросом, когда именно вы собираетесь испачкать руки. Ну, время пришло. Прежде чем вы вернетесь к своей любимой IDE Python – мой PyCharm, кстати, – я собираюсь показать вам несколько строк кода, которые сэкономят вам время, пытаясь найти, какая комбинация фильтров и манипуляций с изображениями хорошо работает с вашими документами.

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

Здесь я создал 20 разных комбинаций методов порогового значения изображения, методов размывания и размеров ядра. Функция переключателя, apply_threshold, принимает два аргумента, а именно изображение OpenCV и целое число, обозначаемое фильтром. Так же, поскольку эта функция в результате возвращает изображение OpenCV, ее можно легко интегрировать в наш get_string функция по предварительной публикации.

def apply_threshold(img, argument):    switcher = {        1: cv2.threshold(cv2.GaussianBlur(img, (9, 9), 0), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1],        2: cv2.threshold(cv2.GaussianBlur(img, (7, 7), 0), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1],        3: cv2.threshold(cv2.GaussianBlur(img, (5, 5), 0), 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1],
                              ...              
        18: cv2.adaptiveThreshold(cv2.medianBlur(img, 7), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2),        19: cv2.adaptiveThreshold(cv2.medianBlur(img, 5), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2),        20: cv2.adaptiveThreshold(cv2.medianBlur(img, 3), 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 2)    }    return switcher.get(argument, "Invalid method")

И вот оно.

def get_string(img_path, method):    # Read image using opencv    img = cv2.imread(img_path)    # Extract the file name without the file extension    file_name = os.path.basename(img_path).split('.')[0]    file_name = file_name.split()[0]    # Create a directory for outputs    output_path = os.path.join(output_dir, file_name)    if not os.path.exists(output_path):        os.makedirs(output_path)
    # Rescale the image, if needed.    img = cv2.resize(img, None, fx=1.5, fy=1.5, interpolation=cv2.INTER_CUBIC)
    # Convert to gray    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)    # Apply dilation and erosion to remove some noise    kernel = np.ones((1, 1), np.uint8)    img = cv2.dilate(img, kernel, iterations=1)    img = cv2.erode(img, kernel, iterations=1)
    # Apply threshold to get image with only black and white    img = apply_threshold(img, method)
    # Save the filtered image in the output directory    save_path = os.path.join(output_path, file_name + "_filter_" + str(method) + ".jpg")    cv2.imwrite(save_path, img)    # Recognize text with tesseract for python    result = pytesseract.image_to_string(img, lang="eng")
    return result

Последние слова

Теперь все, что нам нужно сделать, это написать простой цикл for, который перебирает входящий каталог для сбора изображений и применяет каждый фильтр к собранным изображениям. Я предпочитаю использовать глобили osдля сбора изображений из каталогов, и argparse для передачи аргументов через терминал, как и любой другой здоровый человек.

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

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

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

Добавить комментарий

Ваш адрес email не будет опубликован.