Мой рецепт достижения этих последних процентов (ac)cu(re)teness

Фон

Я столкнулся с проблемой рукописных сумм, которые нужно было распознать как можно точнее. Трудность заключается в удержании ложных срабатываний ниже 0,01%. Количество выборок в наборе данных было фиксированным, поэтому логично перейти к дополнению данных. Быстрый поиск не выявил стандартного метода оптического распознавания символов (OCR). Поэтому я засучил рукава и сам создал процедуру увеличения данных. Он использовался во время обучения и помог моей модели достичь цели. Читайте дальше, чтобы узнать, как это сделать.

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

Тестовая настройка

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

Я буду использовать O penCV и библиотеку albumentations для трех видов изменений: морфологических, шумовых и трансформаций.

OpenCV — известная библиотека компьютерного зрения. Albumentations — это относительно новая библиотека Python для простой, но мощной аугментации изображений.

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

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

1-я техника аугментации: морфологические изменения

Они относятся к форме конструкции. Проще говоря: их можно использовать для того, чтобы текстовые строки выглядели написанными более тонкой или толстой ручкой. Эрозия и расширение они называются. К сожалению, они не являются (пока?) частью библиотеки альбументаций, поэтому для этого мне приходится прибегать к opencv.

Чтобы создать эффект, что кто-то использовал перо с более толстой линией, мы можем расширить оригинал:

Эрозия, с другой стороны (каламбур), имитирует текст, написанный более тонким пером:

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

cv2.dilate(img, kernel,iterations=random.randint(1, 3))

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

2-й метод увеличения: введение шума

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

RandomRain с черным цветом капли очень вреден. Даже мне тяжело до сих пор читать текст. Вот почему я решил установить очень низкую вероятность этого:

RandomShadow будет размазывать текст линиями различной интенсивности:

PixelDropout аккуратно превращает случайные пиксели в черный цвет:

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

В меньшей степени то же самое делает PixelDropoutto white. Но это приводит к более общему блеклому изображению:

3-я техника аугментации: трансформации

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

Размытие. Старый (но золотой) надежный. Будет выполняться с разной интенсивностью.

Большой финал: объединение их всех вместе:

Вот где сила. Мы можем случайным образом комбинировать эти эффекты для создания уникальных изображений, которые будут включены в каждую тренировочную эпоху. Необходимо внимательно следить за тем, чтобы не использовать слишком много методов одного и того же типа. Мы можем сделать это с помощью функции в альбоме OneOf. OneOf содержит список возможных преобразований и, как следует из названия, выполнит только одно из них с вероятностью P. Поэтому имеет смысл сгруппировать преобразования, которые делают более или менее одинаковые, чтобы не переусердствовать. Вот функция:

import random
import cv2
import numpy as np
import albumentations as A

#gets PIL image and returns augmented PIL image
def augment_img(img):
  #only augment 3/4th the images
  if random.randint(1, 4) > 3:
      return img
  
  img = np.asarray(img)     #convert to numpy for opencv

  # morphological alterations
  kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))
  if random.randint(1, 5) == 1:
    # dilation because the image is not inverted
    img = cv2.erode(img, kernel, iterations=random.randint(1, 2))
  if random.randint(1, 6) == 1:
    # erosion because the image is not inverted
    img = cv2.dilate(img, kernel,iterations=random.randint(1, 1))
  
  transform = A.Compose([
      
    A.OneOf([
      #add black pixels noise
      A.OneOf([
             A.RandomRain(brightness_coefficient=1.0, drop_length=2, drop_width=2, drop_color = (0, 0, 0), blur_value=1, rain_type = 'drizzle', p=0.05), 
              A.RandomShadow(p=1),
              A.PixelDropout(p=1),
         ], p=0.9),

      #add white pixels noise
      A.OneOf([
              A.PixelDropout(dropout_prob=0.5,drop_value=255,p=1),
             A.RandomRain(brightness_coefficient=1.0, drop_length=2, drop_width=2, drop_color = (255, 255, 255), blur_value=1, rain_type = None, p=1), 
        ], p=0.9),
    ], p=1),

    #transformations
    A.OneOf([
            A.ShiftScaleRotate(shift_limit=0, scale_limit=0.25, rotate_limit=2, border_mode=cv2.BORDER_CONSTANT, value=(255,255,255),p=1),
            A.ShiftScaleRotate(shift_limit=0.1, scale_limit=0, rotate_limit=8, border_mode=cv2.BORDER_CONSTANT, value=(255,255,255),p=1),
            A.ShiftScaleRotate(shift_limit=0.02, scale_limit=0.15, rotate_limit=11, border_mode=cv2.BORDER_CONSTANT, value=(255,255,255),p=1),  
            A.Affine(shear=random.randint(-5, 5),mode=cv2.BORDER_CONSTANT, cval=(255,255,255), p=1)          
       ], p=0.5),
    A.Blur(blur_limit=5,p=0.25),
  ])
  img = transform(image=img)['image']  
  image = Image.fromarray(img)   
  return image

P означает вероятность того, что что-то произойдет. Это значение от 0 до 1, где 1 означает, что это происходит всегда, а 0 никогда.

Итак, давайте посмотрим на это в действии:

Выглядит довольно аккуратно, не так ли?

альтернативный подход:🌮

В статье ПАСХА 2.0 они придумали технику TACo. Это расшифровывается как Tiling and Corruption. (🌮 ха-ха)
На это он способен:

Я не пробовал это, потому что моя интуиция подсказывает мне, что слишком много разрушено по сравнению с оригиналом. На мой взгляд, если я не могу это прочитать, то и компьютер тоже. Однако я могу ошибаться, если вы считаете это человеком, вы можете догадаться, что это «ТАКО», если увидите «ТА█О». Мы смотрели на окружающие буквы. а тако - это обычное слово. Но компьютер со словарем за ним может сделать его «ТАМО», что является английским словом, означающим «японский ясень».

Заключение

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

Я сделал технику общедоступной в этой записной книжке Jupyter.

Вам также может понравиться:



Использованная литература: