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

К счастью, благодаря XXX и команде YYY были выполнены самые сложные этапы предварительной обработки изображений, результатом которых стали классификаторы каскада Хаара. И, честно говоря, приятно знать, что мы можем использовать это на разных языках программирования (C++, Java и Python), каждый из которых является одинаково (спорным) мощным инструментом для машинного обучения. В качестве языка программирования для этого руководства выбран Python. Продолжайте загрузку предварительно обученных классификаторов из репозитория Github и поместите их в папку пути. Вы можете заметить, что существуют разные классификаторы для разных целей, но мы не будем рассматривать их все в этом руководстве.

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

import imageio
from matplotlib.pyplot import imshow
import matplotlib.pyplot as plt

Библиотека imageio предоставляет нам функциональность для чтения и записи изображений, как мы скоро увидим в следующих разделах. Я использую imshow из библиотеки matplotlib для отображения изображений. Таким образом, в приведенном ниже коде imageio.imread считывает изображение aliko.JPG в дескриптор файла sample_image, который затем передается в imshow для отображения, как показано ниже в коде.

sample_image = imageio.imread(“location/aliko.JPG”)
imshow(sample_image)

Стоит отметить, что указанную выше задачу можно выполнить и с помощью OpenCV (import cv2) — мы можем отобразить изображение, написав cv2.imshow(filehandle). Библиотека matplotlib также позволяет нам просматривать наши изображения рядом, написав следующий код:

sample_image = imageio.imread("location/aliko.JPG")
sample_image2 = imageio.imread("location/elon.JPG")
fig = plt.figure()
ax1 = fig.add_subplot(1, 2, 1)
plt.title('Aliko')
ax1.imshow(sample_image)
ax2 = fig.add_subplot(1, 2, 2)
plt.title('Elon')
ax2.imshow(sample_image2)

Мы можем исследовать цветовые каналы RGB наших изображений, передав аргумент cmap в imshow, как показано ниже. Это позволяет нам просматривать наши изображения в красном, зеленом и синем каналах. Вы также можете изучить другие цветовые каналы — вы можете прочитать документацию для получения дополнительной информации о цветовых каналах.

fig, ax = plt.subplots(2,3)
ax[0,0].imshow(sample_image[:, :, 2], cmap='Reds_r')
ax[0,1].imshow(sample_image[:, :, 2], cmap='Greens_r')
ax[0,2].imshow(sample_image[:, :, 2], cmap='Blues_r')
ax[1,0].imshow(sample_image2[:, :, 2], cmap='Reds_r')
ax[1,1].imshow(sample_image2[:, :, 2], cmap='Greens_r')
ax[1,2].imshow(sample_image2[:, :, 2], cmap='Blues_r')

Хотя это и не обязательно, вы можете увеличить контрастность и нормализовать яркость изображений. Мы можем добиться этого, применив алгоритм equalizeHist() для нормализации гистограммы каждого изображения. Исходное изображение для алгоритма equalizeHist() должно быть 8-битным одноканальным изображением, а именно изображением в градациях серого. Таким образом, мы будем читать наше изображение непосредственно в оттенках серого, как показано ниже. Вы также можете сначала прочитать, а затем преобразовать в оттенки серого. Здесь мы передаем pilmode = ‘L’ в imageio.imread, чтобы получить изображение в оттенках серого в дескриптор файла image_grey.

image_grey = imageio.imread("filepath", pilmode = 'L')

image_grey теперь будет передано в cv2.equalizeHist(). Помните, что перед применением equalizeHist() нам нужно будет импортировать cv2. После выравнивания мы попытаемся обнаружить (а) лица на нашем изображении, используя haarcascade_frontalface_alt.xml. Центр лица равен (xf, yf), а размеры прямоугольной ограничивающей рамки равны (xf + wf, yf + hf). Мы устанавливаем десятичный цветовой код RGB на (0, 0, 0) для черной ограничивающей рамки, а толщину ограничивающих линий устанавливаем на 1.

import cv2
image_grey = imageio.imread("filepath", pilmode = 'L')
image_grey = cv2.equalizeHist(image_grey)
face_cascade = cv2.CascadeClassifier('opencv/data/haarcascades/haarcascade_frontalface_alt.xml')
faces = face_cascade.detectMultiScale(image_grey, scaleFactor=1.1, minNeighbors=5)
for (xf, yf, wf, hf) in faces:
    cv2.rectangle(image_grey, (xf, yf), (xf + wf, yf + hf), (0, 0, 0), 1);
imshow(image_grey, cmap = 'Greys_r')

Теперь, когда мы зафиксировали «наше лицо», давайте теперь займемся глазами. Мы определим новый дескриптор файла (хотя и не обязательно) и установим его в image_grey. Аналогичным образом мы напишем код для обнаружения глаз, передавая путь к нашему файлу haarcascade_eye.xml:

img = image_grey
eyes_cascade = cv2.CascadeClassifier('opencv/data/haarcascades/haarcascade_eye.xml')
eyes = eyes_cascade.detectMultiScale(img[yf:yf+hf, xf:xf+wf])
for (xe, ye, we, he) in eyes:
    cv2.rectangle(img, (xf+xe, yf+ye), (xf+xe+we, yf+ye+he), (255, 255, 255), 1)
imshow(img, cmap = 'Greys_r')

Теперь мы можем обрезать ту часть фотографии, которая нам нужна для нашей нейронной сети. Мы установим параметр с именем padding, аналогично тому, что вы можете сделать в сверточных нейронных сетях. Если вы не знакомы с ConvNets, отступы — это просто расстояние, которое мы хотим вокруг обнаруженного лица — расстояние наружу или внутрь от границы. У нас могут быть разные отступы для направлений x и y отдельно (Px и Py), и они могут быть отрицательными или положительными значениями или даже нулевыми. Здесь мы установим отступ равным 5 как для x, так и для y, но не стесняйтесь экспериментировать с этим параметром с другими значениями.

p = 5
cropped_image = img[yf-p+1 : yf+hf+p, xf-p+1 : xf+wf+p]
imshow(cropped_image, cmap = 'Greys_r')
#We can write the cropped image to file:
cv2.imwrite('croped.jpg', img_rotated[yf-p+1 : yf+hf+p, xf-p+1 : xf+wf+p])

Изображение, над которым мы работали до сих пор, имеет несколько вертикально выровненное лицо. Но «жизнь не идеальна». При работе с наборами данных из реальной жизни мы столкнемся с разными сценариями с различными проблемами, такими как наклонные лица, и крайне важно, чтобы мы их «исправили». Это помогает повысить точность задач распознавания лиц нейросетями.

Я повернул фотографию «Элси Эффа Кауфманн» на угол = -18, как показано в коде ниже. Результат этого вращения следует коду. Положительный угол поворота соответствует вращению изображения против часовой стрелки, тогда как отрицательный угол поворота соответствует направлению по часовой стрелке.

import numpy as np
angle = -18
img1 = imageio.imread("filepath")
image_center = tuple(np.array(img1.shape[1::-1]) / 2)   #--to rotate w.r.t. the image center
M = cv2.getRotationMatrix2D(image_center, angle, 1)
img1 = cv2.warpAffine(img1, M, img1.shape[1::-1])
imshow(img1)

Последний момент, который следует обсудить в этом руководстве, прежде чем я закончу, — это нормализация. В типичном наборе изображений фотографии, скорее всего, были сделаны в разные дни, в разное время суток и при разном освещении. Более яркие изображения заставят определенные нейроны «загораться» намного сильнее, чем должны, что приведет к значительным различиям в весах, что, следовательно, повлияет на совместное использование функций. Чтобы улучшить этот эффект, мы нормализуем набор данных, чтобы наш градиент не выходил за пределы во время обратного распространения и чтобы все функции оставались в одинаковом диапазоне значений.

Объединяем все вместе

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

import cv2
import imageio
from matplotlib.pyplot import imshow
import numpy as np
angle = -18
img1 = imageio.imread("filepath", pilmode = "L")
image_center = tuple(np.array(img1.shape[1::-1]) / 2)
M = cv2.getRotationMatrix2D(image_center, angle, 1)
img1 = cv2.warpAffine(img1, M, img1.shape[1::-1])
img1 = cv2.equalizeHist(img1)
face_cascade = cv2.CascadeClassifier('opencv/data/haarcascades/haarcascade_frontalface_alt.xml')
faces = face_cascade.detectMultiScale(img1, scaleFactor=1.1, minNeighbors=5)
img1 = cv2.normalize(img1, None, alpha=0, beta=1, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_32F)
p = 5
for (xf, yf, wf, hf) in faces:
    cropped_image = img1[yf-p+1 : yf+hf+p, xf-p+1 : xf+wf+p]
    imshow(cropped_image, cmap = 'Greys_r')

Результат показан ниже: