Я очень верю в междисциплинарные вещи, когда разные области знаний могут смешиваться вместе, чтобы создать что-то уникальное. Так как моя дочь любит искусство, а я люблю технологии — я часто смотрю на пересечение этих двух областей или так называемое Science Art.

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

Эти изображения были созданы путем выравнивания нескольких портретов в соответствии с их глазами, создавая таким образом что-то похожее на комбинированное смешанное лицо. Хотя вы, безусловно, можете сделать это вручную в PhotoShop, это отнимет много времени, и ваша способность проводить быстрые эксперименты будет серьезно ограничена. Я покажу вам, как довольно легко создавать такие картинки, используя Microsoft Cognitive Services и немного творчества. Вы можете выполнить процедуру, которую я описываю ниже, используя код, доступный здесь. Если вы используете код для создания своих собственных изображений, используйте метод Cognitive People Blending и ссылку: http://bit.do/peopleblending.

Главная идея

Процесс начинается с нескольких портретов — это могут быть изображения одного и того же человека или разных людей, которых мы хотим совместить. Чем больше у вас будет картинок — тем интереснее получится результат. Начиная с 10 фотографий, вы можете создать что-то интересное.

Чтобы узнать, где находятся глаза на каждой картинке, воспользуемся Face API — он умеет извлекать координаты ключевых точек лица, так называемых Facial Landmarks. Затем мы воспользуемся умной штукой под названием аффинное преобразование и библиотекой OpenCV, чтобы повернуть изображение в соответствии с ключевыми точками. Наконец, нам нужно смешать изображения вместе, используя простое усреднение.

Извлечение лицевых ориентиров

Давайте начнем с изучения того, как извлекать ориентиры лица из изображения. Microsoft Face API предоставляет простой REST API, который может извлекать много полезной информации из изображения лица, включая следующие ориентиры:

Вызвать Face API напрямую через REST довольно просто, но еще лучше использовать существующий SDK, который доступен для Python в составе Azure SDK library. Вы можете посетить Microsoft Docs для получения более подробной информации о Face API и его использовании на языках, отличных от Python.

Мы устанавливаем SDK (вместе с библиотекой OpenCV, которая нам понадобится) с помощью следующей команды:

pip install azure-cognitiveservices-vision-face opencv-python

Чтобы использовать Face API, нам нужно предоставить ключ и URL-адрес конечной точки (поскольку он доступен в разных регионах, URL-адрес может быть другим). Есть много способов получить Face API Key:

Важно! Если вы используете пробный ключ, ваш Face API будет иметь некоторые ограничения с точки зрения количества изображений, которые вы можете обработать, и частоты вызовов API (не более 20 вызовов в минуту). Чтобы все работало с более чем 20 изображениями, вам нужно будет вставлять некоторые паузы между вызовами.

После того, как вы получите ключ и конечную точку, мы поместим их в наш код:

key = ‘ — INSERT YOUR KEY HERE — ‘ 
endpoint = ‘https://westus2.api.cognitive.microsoft.com'

Большинство вызовов Face API выполняются через статический модуль cognitive_face, который для краткости будем называть cf:

import azure.cognitiveservices.vision.face as cf 
from msrest.authentication import CognitiveServicesCredentials 
cli = cf.FaceClient(endpoint,CognitiveServicesCredentials(key))

Основная функция распознавания лиц называется face.detect_with_url или face.detect_with_stream. Он может извлечь много полезной информации из лица, в зависимости от указанных вами параметров — в нашем случае нам понадобятся ориентиры лица:

im_url='https://site.com/path/to/image.jpg' 
res = cli.face.detect_with_url(im_url,return_face_landmarks=True) print(res[0])

В этом коде res будет массивом, каждый элемент которого соответствует лицу, найденному на картинке. Мы всегда будем предполагать, что имеем дело с портретами, содержащими одно и только одно лицо, поэтому использование res[0] даст нам информацию об этом лице:

{‘face_rectangle’: <azure.cognitiveservices.vision.face.models._models_py3.FaceRectangle object at 0x7f72f23570b8>, ‘additional_properties’: {}, ‘face_attributes’: None, ‘face_id’: ‘59dc97ef-b5e2–4c83–99c0–75cdb69048fa’, ‘face_landmarks’: <azure.cognitiveservices.vision.face.models._models_py3.FaceLandmarks object at 0x7f72f2357080>, ‘recognition_model’: None}

Чтобы получить словарь лицевых ориентиров, мы будем использовать res[0].facial_landmarks.as_dict():

{'nose_left_alar_top': {'y': 147.4, 'x': 131.9}, 'eyebrow_right_inner': {'y': 106.3, 'x': 157.2}, 
'pupil_right': {'y': 118.9, 'x': 170.9}, 
'eye_right_outer': {'y': 118.5, 'x': 181.5}, 
...}

Обработка изображений

К этому времени нам понадобится коллекция изображений. Я предлагаю вам начать с 15–20 собственных фотографий. Однако, если вам не терпится попробовать и слишком лень собирать фотографии, вы можете скачать несколько изображений Билла Гейтса, полученных из Bing Image Search. Мы поместим их в каталог images:

mkdir images wget https://github.com/shwars/NeuroWorkshopData   
                         /raw/master/Data/Gates50.zip 
unzip -q Gates50.zip -d images rm Gates50.zip

Если вы используете Блокнот Azure и хотите свои картинки — создайте каталог images в своем проекте и загрузите туда свои фотографии вручную.

Теперь пришло время повеселиться! Мы загрузим все изображения и вызовем Face API, чтобы получить лицевые ориентиры:

import glob 
filenames = [] 
images = [] 
imagepoints = [] 
for fn in glob.glob(“images/*”): 
 print(“Processing {}”.format(fn)) 
 with open(fn,’rb’) as f: 
  res=cli.face.detect_with_stream(f,return_face_landmarks=True)    
 if len(res)>0: 
  filenames.append(fn) 
  images.append(cv2.cvtColor(cv2.imread(fn),cv2.COLOR_BGR2RGB))     
  imagepoints.append(res[0].face_landmarks.as_dict())

Чтобы увидеть результат, давайте нанесем ориентиры на изображения:

def decorate(i): 
   img = images[i].copy() 
   for k,v in imagepoints[i].items(): 
     cv2.circle(img,(int(v[‘x’]),int(v[‘y’])),7,(255,255,0),5)     
   return img 
display_images([decorate(i) for i in range(1,5)])

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

Аффинные преобразования

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

В нашем случае мы знаем положение глаз и знаем, что хотим переместить их в места (130,120) и (170,120) — это звучит как хорошее место, если мы нацеливаемся на размер изображения 300x300. Однако нам нужна еще одна точка помимо глаз, чтобы полностью определить преобразование.

Хотя мы можем выбрать любую точку, удобно брать середину рта — потому что она как бы противоположна глазам, а треугольник глаза-середина рта покрывает довольно большую площадь лица. У нас нет лицевого ориентира для середины рта, но вместо этого мы можем взять среднюю точку между mouth_left и mouth_right.

Аффинное преобразование определяется в 2D-пространстве с помощью матрицы. OpenCV содержит функцию getAffineTransform, которая может вычислить такую ​​матрицу с учетом координат 3 точек до и после преобразования, как мы описали выше. Затем мы используем warpAffine, чтобы применить преобразование к исходному изображению — оно также вырезает оставшиеся части изображения, чтобы оно соответствовало прямоугольнику заданного размера.

target_triangle = np.float32(
                      [[130.0,120.0],[170.0,120.0],[150.0,160.0]]) 
size = 300 
def affine_transform(img,attrs): 
  mc_x=(attrs['mouth_left']['x']+attrs['mouth_right']['x'])/2.0    
  mc_y=(attrs['mouth_left']['y']+attrs['mouth_right']['y'])/2.0
  tr = cv2.getAffineTransform(np.float32([
   (attrs['pupil_left']['x'],attrs['pupil_left']['y']),   
   (attrs['pupil_right']['x'],attrs['pupil_right']['y']), 
      (mc_x,mc_y)]), target_triangle) 
  return cv2.warpAffine(img,tr,(size,size))

Как только мы определили эту функцию, мы можем преобразовать все наши изображения:

img_aligned = [affine_transform(i,a) 
                             for i,a in zip(images,imagepoints)] 
display_images(img_aligned[:5])

И… вуаля!

Чтобы получить окончательный результат, нам в основном нужно смешать изображения вместе. Для этого нам просто нужно усреднить соответствующие numpy-массивы, что можно сделать с помощью одной простой операции:

imgs = np.array(img_aligned,dtype=np.float32)/255. plt.imshow(np.average(imgs,axis=0))

Один трюк здесь заключается в том, что нам нужно преобразовать данные изображения из целочисленной матрицы в числа с плавающей запятой (в диапазоне 0..1), чтобы получить правильное усреднение. Как только мы это сделаем — вот результат:

Теперь пришло время PeopleBlend!

Теперь вы знаете все, что нужно для создания своих собственных PeopleBlends! У вас есть пример кода здесь, и вам даже не нужен установленный Python, потому что вы можете использовать Azure Notebooks. Так что у вас нет оправдания, чтобы не попробовать это самостоятельно!

Чтобы дать вам еще один повод попробовать (помимо любопытства и творчества), я объявляю небольшой Рождественский конкурс! Пожалуйста, оставьте свои результаты (например, в виде ссылок на посты в социальных сетях) в комментариях или пришлите их мне до 1 января, и я опубликую лучший снимок в этом блоге, а победителю вручу копия моей книги по F# (которая сейчас редкость, так как сильно устарела).

Счастливые люди!

Первоначально опубликовано на https://soshnikov.com 21 ноября 2019 г.