Часть 1. Введение

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

Шучу ... Это довольно просто, но требует небольшого понимания области компьютерного зрения и обработки изображений и, конечно же, чайной ложки машинного обучения. Чтобы быть более конкретным, декодирование капчи является примером OCR (оптического распознавания символов).

Итак, давайте сразу перейдем к основам: Изображения

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

Пиксели лежат в основе обработки изображений, и их значения зависят от изображения. Наиболее популярные типы изображений, встречающиеся при обработке изображений:

  1. Оттенки серого: значения пикселей в диапазоне [0,255]
  2. Черно-белое: значения пикселей {0,1}
  3. RGB: изображение имеет 3 измерения - ширину, высоту и количество каналов (в данном случае 3).

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

Итак, с чего и как нам начать? Ниже приведены шаги, которые мы предпримем, чтобы прийти к решению:

  1. Часть 1 Исследование и предварительная обработка
  2. Часть 2 Тренировочные данные и извлечение функций
  3. Часть 3 Обучение и классификация

Часть 1: Исследование и предварительная обработка

Шаги, которые мы будем выполнять, следующие:

  1. Преобразование изображения в оттенки серого
  2. Выявление интересующей области
  3. Обрезка нужного региона
  4. Извлечение символов изображений
  5. Сохранение символов на диске

Преобразование изображения в оттенки серого

Начнем с импорта библиотек. Библиотека, которую я считаю наиболее полезной в задачах обработки изображений, - это 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