Не смотря на все прелести интернета, у него есть много минусов, и один из самых ужасных – это введения людей в заблуждение. Кликбейт, монтаж фотографий, ложные новости – все эти инструменты активно используются для обмана обычных пользователей в мировой сети, но в последние годы набирает обороты новый потенциально опасный инструмент, известный как DeepFake.
Меня данная технология заинтересовала недавно. Впервые о ней я узнал из доклада одного из спикеров на “AI Conference 2018”. Там демонстрировалось видео, в котором по аудиозаписи алгоритм сгенерировал видео с обращением Барака Обамы. Ссылка на подборку видео созданных с помощью этой технологии. Результаты меня сильно вдохновили, и мною было принято решение лучше разобраться с данной технологией, чтобы в будущем противодействовать ей. Для этого я решил написать DeepFake на языке C#. В итоге получил такой результат.
Приятного чтения!
Общие принципы
Отправной точкой стал этот проект. Из него я узнал как именно работает замена лица на видео.
Видео с демонстрацией работы проекта «FaceSwap»:
Работу я решил разбить на 3 части:
1-я) Замена лица на одном фото лицом с другого, без использования 3D маски
2-я) Доработка замены с применением 3D маски
3-я) Обработка видео
Замену лица на фото можно разложить на следующие пункты:
Встраивание одного изображения в другое
Первое с чего я начал работу — это встраивание одного изображения в другое. Для демонстрации встраивания в оригинальном проекте используется скрипт zad1.py.
В результате создается файл «eyeHandBlend.jpg», где глаз встраивается в руку.
Данный алгоритм состоит из 2х частей, первая переносит цвет из участка с лицом на исходной картинке, на то лицо, которое необходимо вставить. Вторая делает края картинки с нужным лицом прозрачными уменьшая прозрачность по мере приближения к центру изображения.
Первую часть я полностью перенес из оригинального проекта.
Чтобы края были более прозрачными, чем центральная часть изображения, для расчета альфа канала, была введена радиально-базисная функция следующего вида:
k и n были подобраны эмпирически.
i — индекс пикселя по оси OX
j — индекс пикселя по оси OY
— компонента x центра изображения
— компонента y центра изображения
В итоге я получил следующий результат:
Поиск лица
Для поиска лица на фото существует множество алгоритмов:
Изначально использовался алгоритм Виолы-Джонса, но он оказался не достаточно точным, т.к. выделял лица не точно. Область выделения одного лица не совпадала с областью выделения второго, из-за чего замена происходила с дефектами, пример выделения лиц с помощь данного алгоритма показан ниже. Лица могут быть смещены, т.е. в на одном изображении оно захватывает оба уха, на другом только одно. Такие дефекты довольно плохо сказываются на конечном результате (на фото работа с DLib, предыдущая библиотека не всегда находила лицо, но к сожалению скриншоты не сохранились).
Далее я решил использовать Landmarks из библиотеки Dlib. Нашел DlibDotNet, который написан на .Net Core. Для использования в .Net Framework был создан промежуточный проект на .Net Standard 2.0 с основными функциями, поиска лица и выделения Landmarks.
После чего написал библиотеку на .Net Framework 4.6.1, в которой реализовал всю логику.
Пример получения Langmarks:
Лицо можно выделить точнее, находя самую левую, правую, верхнюю и нижнюю точки и строя рамки по ним.
Потом лицо вырезалось из картинки в правом нижнем углу и вставлялось, с помощью описанного выше алгоритма, в картину: «Caballero de la mano en el pecho».
Был получен следующий результат.
В следующей статье я планирую рассмотреть создание 3D маски по фотографии.
Меня данная технология заинтересовала недавно. Впервые о ней я узнал из доклада одного из спикеров на “AI Conference 2018”. Там демонстрировалось видео, в котором по аудиозаписи алгоритм сгенерировал видео с обращением Барака Обамы. Ссылка на подборку видео созданных с помощью этой технологии. Результаты меня сильно вдохновили, и мною было принято решение лучше разобраться с данной технологией, чтобы в будущем противодействовать ей. Для этого я решил написать DeepFake на языке C#. В итоге получил такой результат.
Приятного чтения!
Общие принципы
Отправной точкой стал этот проект. Из него я узнал как именно работает замена лица на видео.
- Загрузка картинки с которой мы будем брать лицо
- Извлечение лица
- Создание 3D маски
- Видео разбивается на кадры
- Вычисляется область локализации лица в кадре
- Вычисляется ракурс и выражение лица
- Перенос поворота и выражения лица на 3D модель
- Рендеринг
- Замена реального лица на кадре результатом рендеринга
Видео с демонстрацией работы проекта «FaceSwap»:
Работу я решил разбить на 3 части:
1-я) Замена лица на одном фото лицом с другого, без использования 3D маски
2-я) Доработка замены с применением 3D маски
3-я) Обработка видео
Замену лица на фото можно разложить на следующие пункты:
- Загрузка картинки с которой мы будем брать лицо
- Загрузка картинки на которую будем проецировать лицо
- Извлечение лиц
- Масштабирование лица взятого с изображения 2 к пропорциям в изображении 1
- Замена лица в картинке 1 на лицо в картинке 2
Встраивание одного изображения в другое
Первое с чего я начал работу — это встраивание одного изображения в другое. Для демонстрации встраивания в оригинальном проекте используется скрипт zad1.py.
В результате создается файл «eyeHandBlend.jpg», где глаз встраивается в руку.
Данный алгоритм состоит из 2х частей, первая переносит цвет из участка с лицом на исходной картинке, на то лицо, которое необходимо вставить. Вторая делает края картинки с нужным лицом прозрачными уменьшая прозрачность по мере приближения к центру изображения.
Первую часть я полностью перенес из оригинального проекта.
Код на питоне
def colorTransfer(src, dst, mask):
transferredDst = np.copy(dst)
#indeksy nie czarnych pikseli maski
maskIndices = np.where(mask != 0)
#src[maskIndices[0], maskIndices[1]] zwraca piksele w nie czarnym obszarze maski
maskedSrc = src[maskIndices[0], maskIndices[1]].astype(np.int32)
maskedDst = dst[maskIndices[0], maskIndices[1]].astype(np.int32)
meanSrc = np.mean(maskedSrc, axis=0)
meanDst = np.mean(maskedDst, axis=0)
maskedDst = maskedDst - meanDst
maskedDst = maskedDst + meanSrc
maskedDst = np.clip(maskedDst, 0, 255)
transferredDst[maskIndices[0], maskIndices[1]] = maskedDst
return transferredDst
Код перенесенный на C#
static public Bitmap NewColor(Bitmap src, Bitmap ins, Rectangle r)
{
List<Vector> srV = new List<Vector>();
List<Vector> inV = new List<Vector>(); ;
for (int i = r.X; i < r.X + r.Width-2; i+=3)
{
for (int j = r.Y; j < r.Y + r.Height-3; j+=4)
{
Color color = src.GetPixel(i, j);
Color color2 = ins.GetPixel(i, j);
srV.Add(new double[] { color.R, color.G, color.B }.ToVector());
inV.Add(new double[] { color2.R, color2.G, color2.B }.ToVector());
}
}
Vector meanSrc = Vector.Mean(srV.ToArray()) / 255;
Vector meanInk = Vector.Mean(inV.ToArray()) / 255;
Tensor tensor = ImgConverter.BmpToTensor
(ins.Clone(r, PixelFormat.Format32bppArgb));
tensor = tensor.DivD(meanInk);
tensor = tensor.PlusD(meanSrc);
tensor = tensor.TransformTensor(x =>
{
if (x < 0) x = 0;
if (x > 1) x = 1;
return x;
});
return ImgConverter.TensorToBitmap(tensor);
}
Чтобы края были более прозрачными, чем центральная часть изображения, для расчета альфа канала, была введена радиально-базисная функция следующего вида:
k и n были подобраны эмпирически.
i — индекс пикселя по оси OX
j — индекс пикселя по оси OY
— компонента x центра изображения
— компонента y центра изображения
В итоге я получил следующий результат:
Поиск лица
Для поиска лица на фото существует множество алгоритмов:
- Алгоритм Виолы-Джонса(каскады Хаара)
- Hog+SVM
- R-CNN
- Fast R-CNN
- Faster R-CNN
- Yolo
Изначально использовался алгоритм Виолы-Джонса, но он оказался не достаточно точным, т.к. выделял лица не точно. Область выделения одного лица не совпадала с областью выделения второго, из-за чего замена происходила с дефектами, пример выделения лиц с помощь данного алгоритма показан ниже. Лица могут быть смещены, т.е. в на одном изображении оно захватывает оба уха, на другом только одно. Такие дефекты довольно плохо сказываются на конечном результате (на фото работа с DLib, предыдущая библиотека не всегда находила лицо, но к сожалению скриншоты не сохранились).
Далее я решил использовать Landmarks из библиотеки Dlib. Нашел DlibDotNet, который написан на .Net Core. Для использования в .Net Framework был создан промежуточный проект на .Net Standard 2.0 с основными функциями, поиска лица и выделения Landmarks.
Код на C#
public int[] Face(byte[] bts, int row, int col, int st)
{
var img = Dlib.LoadImageData<RgbPixel>
(ImagePixelFormat.Bgr, bts, (uint)row, (uint)col, (uint)st );
var face = faceDetector.Operator(img)[0];
int[] rect = { face.Left, face.Top, (int)face.Width, (int)face.Height};
return rect;
}
public List<int[]> FacePoints(byte[] bts, int row, int col, int st)
{
List<int[]> points = new List<int[]>();
var img = Dlib.LoadImageData<RgbPixel>
(ImagePixelFormat.Bgr, bts, (uint)row, (uint)col, (uint)st);
var face = faceDetector.Operator(img)[0];
var shape = shapePredictor.Detect(img, face);
for (var i = 0; i < shape.Parts; i++)
{
var point = shape.GetPart((uint)i);
points.Add(new int[] { point.X, point.Y });
}
return points;
}
После чего написал библиотеку на .Net Framework 4.6.1, в которой реализовал всю логику.
Пример получения Langmarks:
Лицо можно выделить точнее, находя самую левую, правую, верхнюю и нижнюю точки и строя рамки по ним.
Потом лицо вырезалось из картинки в правом нижнем углу и вставлялось, с помощью описанного выше алгоритма, в картину: «Caballero de la mano en el pecho».
Был получен следующий результат.
- Лица используемые для демонстрации алгоритмов взяты из этого проекта.
- Используемые библиотеки.
- Shape predictor(68 точек).
- Ссылка на проект.
В следующей статье я планирую рассмотреть создание 3D маски по фотографии.
Комментарии (13)
anshev0
07.10.2019 17:09+1Спасибо автору. Жду продолжений с Neural Network на C#. Гуглил несколько дней назад примеры NN+GPU+C#, что-то не густо было. С интересом посмотрю, что выйдет.
Zachar_5 Автор
07.10.2019 17:19Кстати, что касается использования NN+GPU+C#, есть такие проекты:
- Сверточная нейронная сеть под .Net Лицензия MIT
- Tensor Flow под .Net Лицензия MIT
- LSTM под .Net Лицензия BSD 3-Clause
lostmsu
10.10.2019 19:15
Petr2009
07.10.2019 22:19+1Мне кажется, что надо еще и фильтр для кожи использовать, типа что-нибудь вроде YUCIHighPassSkinSmoothing (https://github.com/YuAo/YUCIHighPassSkinSmoothing).
vladkorotnev
Разрешите
допоинтересоваться, в итоге цикл статей прийдёт к использованию нейросетей?Потому что в самом названии DeepFake подразумевается использование нейросети и методов глубокого обучения, а здесь пока что обычный face swap реализован
Zachar_5 Автор
Да, Вы правы, сейчас это FaceSwap. В первых 3х статьях я планирую повторить на языке C# этот проект.
Первая нейронная сеть появится в 4-й части, там будет замена лица конкретного человека на видео. Нейросеть будет использоваться для распознавания лиц, до этого момента буду работать с одним лицом.
В 5-й части планирую сделать "оживление" портрета. Т.е. синтезировать мимику по голосу, думаю использовать LSTM, на вход принимать спектрограмму голоса за короткий промежуток времени, на выходе выдавать изменения Landmarks.
vladkorotnev
Не знаю, кто влепил минус на коммент, но буду ждать, звучит интересно
DeepFakescovery
поясните а зачем вообще что-то повторять на C#?
я сам фанат и C# и питона, но область дипфейков постоянно развивается, и если вы хотите развивать проект, то его нужно писать на питоне, потому что только на питоне есть обилие самых разных готовых библиотек с которыми легко быстро воплощать любую задумку, чего не скажешь про другие языки.
ProX_Alex
Теперь Вы мой кумир! Главное чтобы всё это воплотилось в жизнь. Побольше Вам свободного времени, чтобы эти запланированные статьи увидели свет!
Zachar_5 Автор
Спасибо!)
subcommande
Zachar_5 Автор
Да, на данный момент это Face Swap. Но конечная цель — полноценный DeepFake.