Как научить компьютер распознавать цветные изображения букв и цифр (captcha)?
Работа с изображениями является одной из самых распространенных задач в машинном обучении. Обычная картинка, воспринимаемая человеком однозначно, для компьютера не имеет никакого смысла и интерпретации. Только если нет предварительно обученной нейронной сети, которая способна отнести изображение к одному определенному классу.
Для работы такой нейронной сети необходимо ее обучение на тренировочных данных, изображениях предварительно обработанных и поданных на вход нейронной сети в виде матрицы чисел, характеризующих определенный тон (цвет) на определенной позиции в изображении. В этой статье приводится пример обработки изображения, получение матриц (тензоров) чисел, подготовка данных обучающего множества, пример архитектуры нейронной сети.
Наша задача: имеются цветные изображения букв и цифр (captcha). Необходимо распознать буквы и цифры, находящиеся на изображениях. Рассмотрим последовательность решения задачи:
- анализ изображений;
- подготовка данных;
- генерация данных;
- тренировка нейронной сети, предсказание ответов.
Проводим предварительный анализ 100 изображений в формате «.png». Изображения состоят из 29 уникальных символов «12345789абвгдежзиклмнопрстуфя». Символы могут быть повернуты на определенный угол (от -1° –+15°), могут быть смещены по горизонтали и вертикали, символы могут накладываться друг на друга. Символы представлены в цвете, все три символа могут иметь различные цвета. На фоне имеются более мелкие символы в светлых оттенках (шум на изображении). Для анализа, вывода и обработки изображений понадобятся библиотеки языка python 3 opencv, matplotlib, pillow. Определим положение символов на изображении путем подбора разделяющих линий:
Также для вывода удобно использовать matplotlib, особенность библиотеки в работе с моделью RGB: передав данные модели BGR вывод будет отличаться, интенсивный красный станет интенсивным синим и наоборот, зеленый не изменится. Для корректного вывода в matplotlib (если цвета имеют значения) необходимо поменять местами матрицы цветов синего и красного.
Перебором изображений с расстановкой разделяющих линий, определим местоположение символов. На изображениях каждый символ находится в своем диапазоне. Для всех символов верхняя граница находится на 3 строке, нижняя на 47. Вертикальные границы (за которые символы не выходят): для первого символа 14–44 колонка, для второго символа: 32–62 колонка, для третьего символа: 48 –72. Изображение при чтении библиотекой opencv представляется в виде тензора numpy array, размерностью (50, 100, 3). Изображение представлено в 3 массивах, состоящих из 50 строк и 100 столбцов. Каждый из трех массивов отвечает за свой цвет BGR (blue синий, green зеленый, red красный), каждый из 3-х массивов находится в диапазоне от 0-255.
Данные такого вида не совсем удобны для дальнейшей обработки цветов, так как символ имеет не четкий цвет, а сумму цветов, к краям более светлый тон, сумма цветов меняет свои значения. Для выделения символа определенного цвета необходимо будет указывать диапазоны для трех цветов B(n-m) G(k-l) R(y-z). Вместо этого проще представить изображение в другой цветовой модели HSV (Hue, Saturation, Value — тон, насыщенность, яркость). В библиотеке opencv единицы измерения Heu 0 – 179, S 0 – 255, V 0 –255. При данной цветовой модели достаточно указать сектор цвета Heu и для всех символов указать постоянные значения S 10 – 255, V 0 – 234, отсекая тем самым фон и шумовые изображения, представленные в более светлых тонах.
HSV представляет собой тензор (50, 100, 3) (3 матрицы numpy array размерностью (50, 100), 50 одномерных массивов, содержащие 100 значений). Индексы матриц — [:,: , 0] Hue, [:,: , 1] Saturation, [:,: , 2] Value.
Отобразим матрицы в градиентах серого (0 – черный 255 – белый).
[,: , 0] Тон, отсутствует белый т.к. диапазон до 179, приблизительный разброс цветов 160 – 179 и 0~30 красный цвет, 60 ~ 100 зеленый, 110 ~ 150 синий. На первом изображении цифра 9 более светлая т.к. оттенок красного попал в диапазон от 160, на втором изображении буква «м» черная т.к. оттенок попал в диапазон 0~30.
[,: , 1] Насыщенность, фон и шум на изображении представлен в светлых, пастельных тонах значения 0~10, символы имеют более выраженный цвет >10
[,: , 2] Яркость, изображение светлое следовательно яркость фона 240 ~ 255, символы более темнее имеют значения < 240.
На основании матриц S и V (насыщенность и яркость), можно получить фильтры для отделения символов от фона и от шума в матрице Hue (тон).
Результат: две матрицы булевых значений (50, 100) [[True, False,. , ],… , [..]]. Значению истина соответствует фон и шум. Если применить эти фильтры к матрице тон (Hue) и придать значениям, соответствующим истина 255, матрица будет содержать значения 0-179, 255 – удобно для дальнейшей фильтрации (дальнейшая работа только с матрицей тон, Hue).
Следующий этап — отделение символов друг от друга. Задаем диапазон, в котором находится символ.
Для выделения каждого символа и удаления части соседнего символа, отсекаем область нахождения соседнего символа, находим количество значений соответствующих тонам в матрице (максимальное количество значений имеет число 255 фон (500 – 800 значений в матрице), следующее по количеству значений будет основной тон символа, единичные значения будет иметь оставшийся не отфильтрованный шум). На основании основного тона символа находим диапазон оттенков N -10, N + 10.
Оставляем часть 1 и 3 символа не попадающее в область 2 символа. Получаем матрицы, отвечающие за тон.
На матрицы Hue для символов 1, 3 накладываем фильтр, значения где тон попадает в указанный диапазон равны 0, иначе 255.
Необходимо привести значения к 0 и 1 и инвертировать матрицу.
Из 2-го символа, находящегося в центре изображения, удаляем части 1 и 3 символа путем определения частей матриц, пересекающих 2-й символ и приравнивании к 255 тех значений матрицы 2, где матрицы 1 и 3 не равны нулю.
Проводим аналогичные преобразования матрицы символа 2. Результат матрицы символов 1, 2, 3 – со значениями 0, 1. При выводе изображения имеют пропуски, остатки шума. Проводим фильтрацию, дополнительное преобразование инструментами opencv, указываем ядро (окно матрицы) которое проходит по матрице символов и при нахождении рядом заполненных пикселей и пропусков, заполняет пропуски.
Определяем центры символов, перемещаем символы в центр массива добавляя и удаляя строки, столбцы, заполненные нулями.
Разработан алгоритм выделения символов из изображения. Для дальнейшего определения значения символа построим и обучим нейронную сеть. Для обучения нейронной сети скачиваем ~100 экземпляров картинок выделяем из них символы, размечаем картинки вручную. Получаем 300 экземпляров размеченных данных (массивы 44×30 содержащие числа 0 и 1). Этого количества данных недостаточно. Определяем и скачиваем шрифт, которым отображаются символы на картинках. Воспользовавшись библиотекой pillow языка python, размещаем символы шрифта на изображении 44×30, задаем смещение и поворот символов случайным выбором из заданных значений, преобразуем в массив nympy array. Формируем выборку данных из сгенерированных данных и данных размеченных вручную.
Тренировочный набор данных представляет собой тензор размерностью (50000, 44, 30, 1), дополнительная размерность (1) для нейронной сети.
Каждому экземпляру тренировочной выборки соответствует экземпляр из массива ответов: char_y = [0, 4,… , 29] – 50_000 (цифры 0-29 ключи словаря символов).
Приведем данные предсказания к виду унитарный код (one-hot encoding). Преобразуем численные значения в массив, имеющий длину 29. Массив состоит из нулей и одной единицы. Позиция единицы соответствует кодируемому символу. Например, буква «а» будет иметь вид ‘000000000100000000000000000000’.
Разбиваем полученные данные на тренировочный набор и тестовый.
Для распознавания символов необходимо построить нейронную сеть, так как данные похожи на учебный набор mnist (рукописные цифры 28×28) на ресурсе kaggle находим примеры архитектуры нейронной сети дающие хороший результат. Архитектура нейронной сети:
Зададим параметр, при улучшении которого модель сохраняется (val_accuracy).
После тренировки сохраняются веса модели с наилучшими показателями val_accuracy, доли верных ответов. Дальнейшая работа программы: на вход подается изображение, которое обрабатывается ранее описанным алгоритмом. Результат обработки — numpy array (массив). Далее инициализируется модель, загружаются веса ранее обученной модели. На вход модели подают данные (1, 2, 3 символ). В результате модель выдает вероятность того или иного символа. Приняв за верный ответ позицию с наибольшей вероятностью из словаря «значение – символ» получаем символьное предсказание модели.
Данный алгоритм обрабатывает цветные изображения, на которых находятся символы букв и цифр, результат распознавания символов нейронной сетью 95% (точность), распознавание каптчи 82% (точность). На примере разбора алгоритма распознавания символов можно заметить, что основную долю разработки занимает подготовка, обработка и генерация данных. Выбор архитектуры и обучение нейронной сети является важней частью задачи, но не самой затратной по времени. Вариантов решения задачи распознавания цифр, букв, изображений предметов и т.п. множество, в данной статье приведен лишь один из примеров решения, показаны этапы решения, трудности, с которыми можно столкнуться в результате работы и примеры их преодоления. А как Вы работаете с каптчами?