DIY DeepFake [Partie 1]

Malgré tous les plaisirs d'Internet, il présente de nombreux inconvénients, et l'un des plus terribles est celui d'induire en erreur les gens. Clickbait, retouche photo, fausses nouvelles - tous ces outils sont activement utilisés pour tromper les utilisateurs ordinaires sur le réseau mondial, mais ces dernières années, un nouvel outil potentiellement dangereux connu sous le nom de DeepFake a pris de l'ampleur.

Je me suis intéressé récemment à cette technologie. Pour la première fois, je l'ai appris du rapport d'un des orateurs de la «Conférence AI 2018». Une vidéo y a été montrée, dans laquelle, par enregistrement audio, l'algorithme a généré une vidéo à l'attrait de Barack Obama. Lien vers une sélection de vidéos créées à l'aide de cette technologie . Les résultats m'ont beaucoup inspiré et j'ai décidé de mieux comprendre cette technologie afin de m'y opposer à l'avenir. Pour cela, j'ai décidé d'écrire DeepFake en C #. En conséquence, j'ai obtenu un tel résultat.

image

Bonne lecture!

Principes généraux

Le point de départ était ce projet. De là, j'ai appris exactement comment fonctionne le remplacement du visage dans la vidéo.

  1. Chargement d'une photo avec laquelle nous prendrons un visage
  2. Extraction du visage
  3. Création de masques 3D
  4. La vidéo est divisée en images
  5. La zone de localisation du visage dans le cadre est calculée
  6. L'angle et l'expression faciale sont calculés
  7. Transférer la rotation et les expressions faciales dans un modèle 3D
  8. Rendu
  9. Remplacement d'une personne réelle sur le cadre avec le résultat du rendu

Vidéo montrant le travail du projet FaceSwap :


J'ai décidé de diviser le travail en 3 parties:

1er) Remplacer un visage sur une photo par un visage sur une autre, sans utiliser de masque 3D
2ème) Finalisation du remplacement à l'aide du masque 3D
3e) Traitement vidéo

Le remplacement du visage sur la photo peut être décomposé en les points suivants:

  1. Chargement d'une photo avec laquelle nous prendrons un visage
  2. Chargement de l'image sur laquelle nous projetterons le visage
  3. Extraction du visage
  4. Mise à l'échelle du visage pris de l'image 2 au rapport d'aspect de l'image 1
  5. Remplacement du visage de l'image 1 par le visage de l'image 2

Intégrer une image dans une autre

La première chose que j'ai commencée a été d'incorporer une image dans une autre. Le script zad1.py est utilisé pour illustrer l'intégration dans le projet d'origine.
En conséquence, le fichier «eyeHandBlend.jpg» est créé, où l'œil est intégré dans la main.

eyeHandBlend.jpg

Cet algorithme se compose de 2 parties, la première transfère la couleur de la zone avec le visage dans l'image d'origine vers le visage qui doit être inséré. La seconde rend les bords de l'image avec le visage souhaité transparents, réduisant la transparence à l'approche du centre de l'image.

J'ai complètement transféré la première partie du projet d'origine.

Code 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 


Code porté en 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); } 


Afin de rendre les bords plus transparents que la partie centrale de l'image, pour le calcul du canal alpha, une fonction de base radiale de la forme suivante a été introduite:
k=190n=3r=( fraciCw1.2 cdotW)2+( fracjChH)2 alpha=255 cdotexp(k cdotrn)

k et n ont été sélectionnés empiriquement.
i - indice de pixels le long de l'axe OX
j - indice de pixels le long de l'axe OY
Cw- composante x du centre de l'image
Ch- composante y du centre de l'image

En conséquence, j'ai obtenu le résultat suivant:

eyeHandBlend.jpg

Recherche de visage

Pour rechercher des visages sur la photo, il existe de nombreux algorithmes:

  • Algorithme de Viola-Jones (Haar Cascades)
  • Porc + svm
  • R-CNN
  • R-cnn rapide
  • R-cnn plus rapide
  • Yolo

Initialement, l'algorithme Viola-Jones a été utilisé, mais il s'est avéré ne pas être suffisamment précis, car visages mis en évidence pas exactement. La zone de sélection d'une personne ne coïncidait pas avec la zone de sélection de la seconde, en raison de laquelle le remplacement s'est produit avec des défauts, un exemple de sélection de visages utilisant cet algorithme est illustré ci-dessous. Les visages peuvent être déplacés, c'est-à-dire dans une image, il capture les deux oreilles, de l'autre une seule. De tels défauts affectent assez mal le résultat final (sur la photo, en travaillant avec DLib, la bibliothèque précédente n'a pas toujours trouvé le visage, mais malheureusement les captures d'écran n'ont pas été enregistrées).



Ensuite, j'ai décidé d'utiliser des repères de la bibliothèque Dlib. Trouvé DlibDotNet , qui est écrit sur .Net Core. Pour une utilisation dans le .Net Framework, un projet intermédiaire sur .Net Standard 2.0 a été créé avec les fonctions principales, la recherche de visages et la mise en évidence des repères.

Code 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; } 


Il a ensuite écrit une bibliothèque sur .Net Framework 4.6.1, dans laquelle il a implémenté toute la logique.

Un exemple d'obtention de Langmarks:



Une personne peut être distinguée plus précisément en trouvant les points les plus à gauche, à droite, en haut et en bas et en construisant des cadres sur eux.



Ensuite, le visage a été découpé de l'image dans le coin inférieur droit et inséré, en utilisant l'algorithme décrit ci-dessus, dans l'image: "Caballero de la mano en el pecho".

Le résultat suivant a été obtenu.

image


Dans le prochain article, je prévois d'envisager de créer un masque 3D à partir d'une photographie.

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


All Articles