DIY DeepFake [部分1]

尽管Internet带来了许多乐趣,但它也有许多缺点,其中最可怕的就是误导人们。 Clickbait,照片编辑,虚假新闻-所有这些工具都被用来欺骗全球网络上的普通用户,但是近年来,一种称为DeepFake的潜在新危险工具获得了发展。

我最近对这项技术感兴趣。 我第一次从“ 2018 AI大会”的一位发言人的报告中了解到这一点。 在那里显示了一个视频,其中通过录音,该算法产生了巴拉克·奥巴马(Barack Obama)的吸引力的视频。 链接到使用此技术创建的精选视频 。 结果极大地鼓舞了我,因此我决定更好地理解这项技术,以便将来反对它。 为此,我决定用C#编写DeepFake。 结果,我得到了这样的结果。

图片

祝您阅读愉快!

一般原则

起点是这个项目。 通过它,我确切地了解了视频中面部替换的工作原理。

  1. 加载一张我们将要面对的照片
  2. 人脸提取
  3. 3D遮罩创建
  4. 视频分为帧
  5. 计算框架中的人脸定位区域
  6. 计算角度和面部表情
  7. 将旋转和面部表情转移到3D模型中
  8. 渲染图
  9. 用渲染结果替换框架上的真实人物

演示FaceSwap项目工作的视频:


我决定将工作分为三个部分:

1st)在不使用3D蒙版的情况下用另一张照片替换一张照片中的一张脸
2)使用3D遮罩完成替换
3)视频处理

照片中的人脸替换可以分解为以下几点:

  1. 加载一张我们将要面对的照片
  2. 加载我们要在其上投影脸部的图像
  3. 人脸提取
  4. 将从图像2拍摄的脸部缩放为图像1中的宽高比
  5. 将图片1中的脸替换为图片2中的脸

将一个图像嵌入另一个

我首先要做的是将一个图像嵌入另一个图像。 zad1.py脚本用于演示在原始项目中的嵌入。
结果,创建了文件“ eyeHandBlend.jpg”,眼睛被嵌入到手中。

eyeHandBlend.jpg

该算法由两部分组成,第一部分将颜色从原始图片中带有脸部的区域转移到需要插入的脸部。 第二种方法使具有所需面部的图像边缘透明,从而降低了接近图像中心时的透明度。

我完全从原始项目转移了第一部分。

Python代码
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 = rX; i < rX + r.Width-2; i+=3) { for (int j = rY; j < rY + 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); } 


为了使边缘比图像的中心部分更透明,为了计算alpha通道,引入了以下形式的径向基函数:
k=190n=3r= fraciCw1.2 cdotW2+ fracjChH2 alpha=255 cdotexpk cdotrn

k和n是根据经验选择的。
i-沿OX轴的像素索引
j-沿OY轴的像素索引
Cw-图像中心的分量x
Ch-图像中心的分量y

结果,我得到以下结果:

eyeHandBlend.jpg

脸部搜寻

要搜索照片中的人脸,有很多算法:

  • Viola-Jones算法(Haar级联)
  • 猪+ SVM
  • 神经网络
  • 快速r-cnn
  • 更快的r-cnn
  • 尤洛

最初,使用了Viola-Jones算法,但结果证明不够准确,因为 突出的面孔不完全一样。 一个人的选择区域与第二个人的选择区域不一致,由于该替换发生了缺陷,下面显示了使用此算法选择脸部的示例。 脸部可能会移位,即 在一张图像中,它捕获了两只耳朵,而另一只只捕获了一只耳朵。 这样的缺陷会严重影响最终结果(在照片中,与DLib一起使用时,以前的库并不总是能找到面孔,但是不幸的是,屏幕快照并未保存)。



接下来,我决定使用Dlib库中的Landmarks。 找到了在.Net Core上编写的DlibDotNet 。 为了在.Net Framework中使用,在.Net Standard 2.0上创建了一个中间项目,该项目具有主要功能,面部搜索和突出显示地标。

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”。

得到以下结果。

图片


在下一篇文章中,我打算考虑根据照片创建3D蒙版。

Source: https://habr.com/ru/post/zh-CN470323/


All Articles