Часть 1. Введение
Вы когда-нибудь задумывались, как на самом деле дешифруются капчи? Что ж, довольно пугающе осознавать, что он включает в себя сложные алгоритмы машинного обучения наряду с методами глубокого обучения, основанными на современной математике.
Шучу ... Это довольно просто, но требует небольшого понимания области компьютерного зрения и обработки изображений и, конечно же, чайной ложки машинного обучения. Чтобы быть более конкретным, декодирование капчи является примером OCR (оптического распознавания символов).
Итак, давайте сразу перейдем к основам: Изображения
В отличие от людей, компьютеры не могут переваривать визуальную информацию. Они говорят в терминах чисел, что означает пиксели в случае изображений.
Пиксели лежат в основе обработки изображений, и их значения зависят от изображения. Наиболее популярные типы изображений, встречающиеся при обработке изображений:
- Оттенки серого: значения пикселей в диапазоне [0,255]
- Черно-белое: значения пикселей {0,1}
- RGB: изображение имеет 3 измерения - ширину, высоту и количество каналов (в данном случае 3).
Итак, задача следующая: Для набора следующих изображений обучите алгоритм машинного обучения и предсказывайте слово, написанное на новом тестовом изображении.
Итак, с чего и как нам начать? Ниже приведены шаги, которые мы предпримем, чтобы прийти к решению:
- Часть 1 Исследование и предварительная обработка
- Часть 2 Тренировочные данные и извлечение функций
- Часть 3 Обучение и классификация
Часть 1: Исследование и предварительная обработка
Шаги, которые мы будем выполнять, следующие:
- Преобразование изображения в оттенки серого
- Выявление интересующей области
- Обрезка нужного региона
- Извлечение символов изображений
- Сохранение символов на диске
Преобразование изображения в оттенки серого
Начнем с импорта библиотек. Библиотека, которую я считаю наиболее полезной в задачах обработки изображений, - это PIL (Python Image Library).
import os import numpy as np import matplotlib.pyplot as plt from matplotlib.pyplot import * from IPython.display import clear_output from PIL import Image, ImageChops, ImageDraw, ImageFilter def readImage(index): image=Image.open(os.getcwd()+'/Images/'+str(index)+'.jpg') return image def showImage(image, h=3): figure(figsize = (10,h)) plt.imshow(image) plt.show() image = readImage() def convertImage(image): convertedImage = image.convert('1') return convertedImage
Создание рамки вокруг интересующей области
def boundingBox(image): pixelData = image.load() width, height = image.size #Finding coordinates of bounding rectangle x_min=width y_min=height x_max=0 y_max=0 for i in range(width): for j in range(height): if (pixelData[i,j][0]==0): # For getting the corners of bounding rectangle if(i<x_min): x_min=i if(j<y_min): y_min=j if(i>x_max): x_max=i if(j>y_max): y_max=j xy = [x_min, y_min, x_max, y_max] return xy
Обрезка области, ограниченной рамкой
def crop(image, xy): image = image.crop(xy) return image
Извлечение отдельных символов
def showGaps(image): separatorLine = image.copy() width,height = separatorLine.size pixels = np.asarray(separatorLine).transpose() draw = ImageDraw.Draw(separatorLine) for i in range(width): if (np.mean(pixels[i])!=1.0): draw.line((i,0,i,height), fill=0)
Эта функция представляет собой алгоритм рисования вертикальных линий, разделяющих символы, как показано ниже:
#lower most and upper most coordinates of any character def getCharacterHeight(image, start, end): pixelData = image.load() y_min = image.size[1] y_max = 0 i = start while( i < end): for j in range(image.size[1]): if (pixelData[i,j]==0): if(j<y_min): y_min=j if(j>y_max): y_max=j i+=1 return (y_min, y_max) #extracting character by cropping along identified coordinates def extract(image): indexes = [] start = 0 # this is the left-most x-coordinate of any character separatorLine = image.copy() width,height = separatorLine.size pixels = np.asarray(separatorLine).transpose() draw = ImageDraw.Draw(separatorLine) i=0 while i < width: if (np.mean((pixels[i]))==1.0): y_min, y_max = getCharacterHeight(image, start, i) indexes.append((start,y_min,i,y_max)) start=i while(np.mean(pixels[i])==1.0): i+=1 start+=1 else: i+=1 y_min, y_max = getCharacterHeight(image, start, i) indexes.append((start,y_min,width-1,y_max)) return indexes indexes = extract(croppedImage) print "Following are the extracted characters" for i in range(len(indexes)): image = crop(croppedImage, indexes[i]) showImage(image,1)
Вот и все, мы извлекли символы из всех таких изображений. Можем ли мы теперь приступить к извлечению функций? Точно нет. Как видно, размеры каждого извлеченного символа различны, которые вместо этого должны быть одинаковыми, чтобы иметь одинаковое количество функций для каждого символа - обязательное требование для любой модели машинного обучения.
Посетите Часть 2: тренировочные данные и извлечение признаков из серии.
Вот ссылка на полное решение на github