Snapchat, Instagram, а теперь и Apple практически одновременно выпустили видео-эффекты с наложением на лица в реальном времени.
В сегодняшней статье я мы попробуем создать метод для отслеживания и изменения нашего лица в реальном времени. Также, как это делают в перечисленных приложениях.
Инструменты для определения лиц в Python
Мы воспользуемся двумя самыми главными и удивительными библиотеками обработки изображений для Python 3: Dlib и OpenCV.
Есть вопросы по Python?
На нашем форуме вы можете задать любой вопрос и получить ответ от всего нашего сообщества!
Паблик VK
Одно из самых больших сообществ по Python в социальной сети ВК. Видео уроки и книги для вас!
Установка Dlib – это достаточно просто, спасибо за доступность на большей части платформ.
1 |
pip install dlib |
Однако для OpenCV установка несколько сложнее. Если вы работаете в MacOS, в интернете легко можно найти информацию по установке OpenCV. Это касается и Ubuntu. Пользователи Windows могут воспользоваться неофициальным источником.
После установки OpenCV, можете расслабиться до конца статьи
Архитектура Эффекта Линз
Мы используем OpenCV для получения необработанного стрима видео с веб-камеры. Далее, мы изменим размер этого стрима, при помощи специальной функции imutils, чтобы получить приличную частоту кадров для определения лиц.
После получения частоты кадров, мы конвертируем кадр видео веб-камеры в черно-белый, затем передадим его Dlib для определения лица.
get_frontal_face_detector возвращает набор ограненных прямоугольников для каждого обнаруженного лица на изображении. Таким образом мы можем использовать это как модель (в нашем случае, shape_predictor_68_face_landmarks на Github), и получить набор из 68-и точек с нашей лицевой ориентацией.
Из точек возле глаз мы можем сформировать полигон аналогичной формы в новом канале.
Таким образом, выполняем bitwise_and и копируем наши глаза из кадра.
Далее создаем объект для отслеживания n числа позиций, в которых были наши глаза. Функция OpenCV под названием boundingRect дает нам базовую координату x и y для рисования.
Наконец, мы создаем маску для воссоздания всех предыдущих местах, в которых были наши глаза, более того, bitwise_and копирует предыдущее изображение глаз в кадре перед показом.
Для запуска своих Python приложений вам нужно воспользоваться провайдером firstvds который предоставляет качественные сервера и быструю тех. поддержку. FirstVDS предоставляет отличные VPS для начинающих (от 90 руб в месяц) где вы сможете тестировать свои Python приложения на реальном сервере. Для более больших проектов, FirstVDS предоставляет выделенные сервера по лучшей цене на русском рынке.
Пишем Код!
Так как наша концепция предельно ясна, написание настоящего детектора и манипулятора для глаз будет проходить достаточно прямолинейно. Воспользуемся модулем numpy.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
import argparse import cv2 from imutils.video import VideoStream from imutils import face_utils, translate, resize import time import dlib import numpy as np parser = argparse.ArgumentParser() parser.add_argument("-predictor", required=True, help="path to predictor") args = parser.parse_args() print("starting program.") print("'s' starts drawing eyes.") print("'r' to toggle recording image, and 'q' to quit") vs = VideoStream().start() time.sleep(1.5) # эта часть обнаруживает наше лицо detector = dlib.get_frontal_face_detector() # эта определяет расположение нашего лица predictor = dlib.shape_predictor(args.predictor) recording = False counter = 0 class EyeList(object): def __init__(self, length): self.length = length self.eyes = [] def push(self, newcoords): if len(self.eyes) < self.length: self.eyes.append(newcoords) else: self.eyes.pop(0) self.eyes.append(newcoords) def clear(self): self.eyes = [] # начинает от 10 предыдущих позиций глаз eyelist = EyeList(10) eyeSnake = False # получаем первый кадр вне цикла, так что мы можем увидеть как # вебкамера изменила свое расширение, которое теперь составляет w / np.shape frame = vs.read() frame = resize(frame, width=800) eyelayer = np.zeros(frame.shape, dtype='uint8') eyemask = eyelayer.copy() eyemask = cv2.cvtColor(eyemask, cv2.COLOR_BGR2GRAY) translated = np.zeros(frame.shape, dtype='uint8') translated_mask = eyemask.copy() while True: # получаем кадр из камеры, уменьшаем размер frame = vs.read() frame = resize(frame, width=800) # заливаем наши маски и кадры 0 (черный цвет) в каждом цикле рисования eyelayer.fill(0) eyemask.fill(0) translated.fill(0) translated_mask.fill(0) # детектор ждет серые изображения gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) rects = detector(gray, 0) # для случая запуска цикла eyesnake (нажмите «s» во время работы для активации) if eyeSnake: for rect in rects: # предсказатель это наша модель из 68 точек, которую мы загрузили shape = predictor(gray, rect) shape = face_utils.shape_to_np(shape) # наша модель dlib возвращает 68 точек, которые формируют лицо. # левый глаз расположен между 36й и 42й точками. # правый глаз – между 42й и 48й точками. leftEye = shape[36:42] rightEye = shape[42:48] # заливаем маску в форме наших глаз cv2.fillPoly(eyemask, [leftEye], 255) cv2.fillPoly(eyemask, [rightEye], 255) # копируем изображение из кадра в eyelayer при помощи этой маски eyelayer = cv2.bitwise_and(frame, frame, mask=eyemask) # это мы используем для получения координат х и y для вставки глаз x, y, w, h = cv2.boundingRect(eyemask) # добавляем это в наш список eyelist.push([x, y]) # наконец, рисуем наши глаза в обратном порядке for i in reversed(eyelist.eyes): # сначала меняем название eyelayer на eyes translated1 = translate(eyelayer, i[0] - x, i[1] - y) # далее, переводим присвоенную маску translated1_mask = translate(eyemask, i[0] - x, i[1] - y) # добавляем ее в существующую переведенную маску eyes (не буквально, так как рискуем перегрузить) translated_mask = np.maximum(translated_mask, translated1_mask) # вырезаем новую переведенную маску translated = cv2.bitwise_and(translated, translated, mask=255 - translated1_mask) # вставляем в только-что указанную позицию глаза translated += translated1 # вырезаем переведенную маску еще раз frame = cv2.bitwise_and(frame, frame, mask=255 - translated_mask) # и вставляем в переведенное изображение глаза frame += translated # показ текущего кадра, проверяем, нажал ли пользователь на клавишу cv2.imshow("eye glitch", frame) key = cv2.waitKey(1) & 0xFF if recording: # создаем папку под названием "image_seq", теперь мы можем создавать гифки # в ffmpeg с последовательными изображениями cv2.imwrite("image_seq/%05d.png" % counter, frame) counter += 1 if key == ord("q"): break if key == ord("s"): eyeSnake = not eyeSnake eyelist.clear() if key == ord("r"): recording = not recording cv2.destroyAllWindows() vs.stop() |
Запускаем код!
Для запуска этого кода, нам нужно скачать предсказатель dlib, работающий с 68-и точками. Мы можем скачать его, после чего извлечь в нашу папку, в которой хранится наша программа Python. Из нее мы можем выполнить следующее:
1 |
python3 eye-glitch.py -predictor shape_predictor_68_face_landmarks.dat |
Мы должны сделать наш кадр движущимся. Здесь, нажав на клавишу “S» мы запускаем эффект змеиных глаз. Нажав “R”, мы записываем кадры на диск, для сохранения в качестве цельного видео в будущем. Если вы хотите сделать это, вам нужно для начала создать папку под названием image_seq в той же папке, в которой лежит ваша программа Python.
Пример работы
Исходный код на Github: https://github.com/burningion/snapchat-lens-effect-in-python
Вторая часть:
Подведем итоги
Если вам понравилась данная статья, и если вы охотитесь за годными и креативными статьями о программировании, вы в правильном месте! Буду благодарен, если вы разместите этот текст в соц.сетях.
Являюсь администратором нескольких порталов по обучению языков программирования Python, Golang и Kotlin. В составе небольшой команды единомышленников, мы занимаемся популяризацией языков программирования на русскоязычную аудиторию. Большая часть статей была адаптирована нами на русский язык и распространяется бесплатно.
E-mail: vasile.buldumac@ati.utm.md
Образование
Universitatea Tehnică a Moldovei (utm.md)
- 2014 — 2018 Технический Университет Молдовы, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Технический Университет Молдовы, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»