DIY DeepFake [Parte 1]

Apesar de todas as delícias da Internet, ela tem muitas desvantagens, e uma das mais terríveis é enganar as pessoas. Clickbait, edição de fotos, notícias falsas - todas essas ferramentas são ativamente usadas para enganar usuários comuns na rede global, mas nos últimos anos uma nova ferramenta potencialmente perigosa conhecida como DeepFake vem ganhando força.

Eu estava interessado nesta tecnologia recentemente. Pela primeira vez, aprendi sobre isso com o relatório de um dos palestrantes da “AI Conference 2018”. Um vídeo foi exibido lá, no qual, por gravação de áudio, o algoritmo gerou um vídeo com o apelo de Barack Obama. Link para uma seleção de vídeos criados usando esta tecnologia . Os resultados me inspiraram bastante e decidi entender melhor essa tecnologia para me opor a ela no futuro. Para isso, decidi escrever o DeepFake em C #. Como resultado, obtive esse resultado.

imagem

Boa leitura!

Princípios gerais

O ponto de partida foi este projeto. Com isso, aprendi exatamente como funciona a substituição de rosto no vídeo.

  1. Carregando uma foto com a qual vamos fazer uma careta
  2. Extração de rosto
  3. Criação de máscara 3D
  4. O vídeo é dividido em quadros
  5. A área de localização da face no quadro é calculada
  6. O ângulo e a expressão facial são calculados
  7. Transferir rotação e expressões faciais para um modelo 3D
  8. Renderização
  9. Substituindo uma pessoa real no quadro pelo resultado da renderização

Vídeo demonstrativo do trabalho do projeto FaceSwap :


Decidi dividir o trabalho em 3 partes:

1) Substituir um rosto em uma foto por um rosto de outra, sem usar uma máscara 3D
2º) Finalização da substituição usando a máscara 3D
3º) Processamento de Vídeo

A substituição do rosto na foto pode ser decomposta nos seguintes pontos:

  1. Carregando uma foto com a qual vamos fazer uma careta
  2. Carregando a imagem na qual projetaremos o rosto
  3. Extração de rosto
  4. Dimensionar o rosto tirado da imagem 2 para a proporção da imagem 1
  5. Substituindo a face na figura 1 pela face na figura 2

Incorporar uma imagem em outra

A primeira coisa que comecei foi incorporar uma imagem em outra. O script zad1.py é usado para demonstrar a incorporação no projeto original.
Como resultado, o arquivo "eyeHandBlend.jpg" é criado, onde o olho está incorporado na mão.

eyeHandBlend.jpg

Esse algoritmo consiste em 2 partes, a primeira transfere a cor da área com a face da imagem original para a face que precisa ser inserida. O segundo torna transparente as bordas da imagem com a face desejada, reduzindo a transparência à medida que se aproxima do centro da imagem.

Transferi completamente a primeira parte do projeto original.

Código 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ódigo portado para 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); } 


Para tornar as bordas mais transparentes que a parte central da imagem, para o cálculo do canal alfa, foi introduzida uma função de base radial da seguinte forma:
k=190n=3r=( fraciCw1,2 cdotW)2+( fracjChH)2 alpha=255 cdotexp(k cdotrn)

k e n foram selecionados empiricamente.
i - índice de pixels ao longo do eixo OX
j - índice de pixels ao longo do eixo OY
Cw- componente x do centro da imagem
Ch- componente y do centro da imagem

Como resultado, obtive o seguinte resultado:

eyeHandBlend.jpg

Pesquisa de rosto

Para procurar rostos na foto, existem muitos algoritmos:

  • Algoritmo de Viola-Jones (cascatas de Haar)
  • Porco + svm
  • R-CNN
  • R-cnn rápido
  • R-cnn mais rápido
  • Yolo

Inicialmente, o algoritmo Viola-Jones foi usado, mas acabou não sendo preciso o suficiente, porque rostos destacados não exatamente. A área de seleção de uma pessoa não coincidiu com a área de seleção da segunda, devido à qual a substituição ocorreu com defeitos, um exemplo da seleção de faces usando esse algoritmo é mostrado abaixo. As faces podem ser deslocadas, ou seja, em uma imagem, captura as duas orelhas, na outra, apenas uma. Tais defeitos afetam bastante o resultado final (na foto, trabalhando com o DLib, a biblioteca anterior nem sempre encontrou o rosto, mas infelizmente as capturas de tela não foram salvas).



Em seguida, decidi usar os Marcos da biblioteca Dlib. DlibDotNet encontrado, escrito em .Net Core. Para uso no .Net Framework, foi criado um projeto intermediário no .Net Standard 2.0 com as principais funções, pesquisa de faces e destaque de Marcos.

Código 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; } 


Em seguida, ele escreveu uma biblioteca no .Net Framework 4.6.1, na qual implementou toda a lógica.

Um exemplo de como obter Langmarks:



Uma pessoa pode ser distinguida com mais precisão encontrando os pontos mais à esquerda, direito, superior e inferior e construindo estruturas sobre eles.



Em seguida, o rosto foi cortado da figura no canto inferior direito e inserido, usando o algoritmo descrito acima, na figura: “Caballero de la mano en el pecho”.

O seguinte resultado foi obtido.

imagem


No próximo artigo, pretendo considerar a criação de uma máscara 3D a partir de uma fotografia.

Source: https://habr.com/ru/post/pt470323/


All Articles