DIY DeepFake [Teil 1]

Trotz aller Freuden des Internets hat es viele Nachteile, und einer der schrecklichsten ist die Irreführung von Menschen. Clickbait, Fotobearbeitung, falsche Nachrichten - all diese Tools werden aktiv verwendet, um normale Benutzer im globalen Netzwerk zu täuschen. In den letzten Jahren hat jedoch ein neues potenziell gefährliches Tool namens DeepFake an Dynamik gewonnen.

Ich habe mich kürzlich für diese Technologie interessiert. Zum ersten Mal erfuhr ich davon aus dem Bericht eines der Redner auf der „AI Conference 2018“. Dort wurde ein Video gezeigt, in dem der Algorithmus durch Audioaufnahme ein Video mit der Anziehungskraft von Barack Obama erzeugte. Link zu einer Auswahl von Videos, die mit dieser Technologie erstellt wurden . Die Ergebnisse haben mich sehr inspiriert und ich habe mich entschlossen, diese Technologie besser zu verstehen, um sie in Zukunft abzulehnen. Dafür habe ich beschlossen, DeepFake in C # zu schreiben. Als Ergebnis habe ich ein solches Ergebnis erhalten.

Bild

Viel Spaß beim Lesen!

Allgemeine Grundsätze

Ausgangspunkt war dieses Projekt. Daraus habe ich genau gelernt, wie der Gesichtsersatz in Videos funktioniert.

  1. Laden eines Bildes, mit dem wir ein Gesicht machen werden
  2. Gesichtsextraktion
  3. 3D-Maskenerstellung
  4. Video ist in Frames unterteilt
  5. Der Bereich der Gesichtslokalisierung im Rahmen wird berechnet
  6. Der Winkel und der Gesichtsausdruck werden berechnet
  7. Übertragen Sie Rotation und Mimik auf ein 3D-Modell
  8. Rendern
  9. Ersetzen einer realen Person im Rahmen durch das Ergebnis des Renderns

Video zur Demonstration der Arbeit des FaceSwap-Projekts :


Ich beschloss, die Arbeit in drei Teile zu teilen:

1.) Ersetzen eines Gesichts in einem Foto durch ein Gesicht eines anderen ohne Verwendung einer 3D-Maske
2.) Abschluss des Austauschs mit der 3D-Maske
3.) Videoverarbeitung

Der Gesichtsersatz auf dem Foto kann in folgende Punkte zerlegt werden:

  1. Laden eines Bildes, mit dem wir ein Gesicht machen werden
  2. Laden des Bildes, auf das das Gesicht projiziert wird
  3. Gesichtsextraktion
  4. Skalieren des von Bild 2 aufgenommenen Gesichts auf das Seitenverhältnis in Bild 1
  5. Ersetzen Sie das Gesicht in Bild 1 durch das Gesicht in Bild 2

Betten Sie ein Bild in ein anderes ein

Als erstes habe ich ein Bild in ein anderes eingebettet. Das Skript zad1.py wird verwendet, um die Einbettung in das ursprüngliche Projekt zu demonstrieren.
Als Ergebnis wird die Datei „eyeHandBlend.jpg“ erstellt, in der das Auge in die Hand eingebettet ist.

eyeHandBlend.jpg

Dieser Algorithmus besteht aus 2 Teilen. Der erste Teil überträgt die Farbe aus dem Bereich mit dem Gesicht im Originalbild auf das Gesicht, das eingefügt werden muss. Die zweite Option macht die Bildränder mit der gewünschten Fläche transparent und verringert die Transparenz, wenn sie sich der Bildmitte nähert.

Ich habe den ersten Teil komplett aus dem ursprünglichen Projekt übernommen.

Python-Code
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 auf C # portiert
  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); } 


Um die Kanten transparenter als der zentrale Teil des Bildes zu machen, wurde zur Berechnung des Alphakanals eine radiale Basisfunktion der folgenden Form eingeführt:
k=190n=3r=( fraciCw1.2 cdotW)2+( fracjChH)2 alpha=255 cdotexp(k cdotrn)

k und n wurden empirisch ausgewählt.
i - Pixelindex entlang der OX-Achse
j - Pixelindex entlang der OY-Achse
Cw- Komponente x der Bildmitte
Ch- Komponente y der Bildmitte

Als Ergebnis habe ich folgendes Ergebnis erhalten:

eyeHandBlend.jpg

Gesichtssuche

Um nach Gesichtern auf dem Foto zu suchen, gibt es viele Algorithmen:

  • Viola-Jones-Algorithmus (Haar Cascades)
  • Schwein + svm
  • R-CNN
  • Schnelle r-cnn
  • Schneller r-cnn
  • Yolo

Ursprünglich wurde der Viola-Jones-Algorithmus verwendet, der sich jedoch als nicht genau genug herausstellte, weil hervorgehobene Gesichter nicht genau. Der Auswahlbereich einer Person stimmte nicht mit dem Auswahlbereich der zweiten Person überein, aufgrund dessen der Austausch durch Fehler erfolgte. Ein Beispiel für die Auswahl von Gesichtern unter Verwendung dieses Algorithmus ist unten gezeigt. Gesichter können verschoben werden, d.h. In einem Bild werden beide Ohren erfasst, in dem anderen nur eines. Solche Fehler wirken sich ziemlich stark auf das Endergebnis aus (auf dem Foto hat die vorherige Bibliothek bei der Arbeit mit DLib nicht immer das Gesicht gefunden, aber die Screenshots wurden leider nicht gespeichert).



Als nächstes entschied ich mich, Orientierungspunkte aus der Dlib-Bibliothek zu verwenden. DlibDotNet gefunden , das auf .Net Core geschrieben ist. Zur Verwendung im .Net Framework wurde ein Zwischenprojekt zu .Net Standard 2.0 mit den Hauptfunktionen, Gesichtssuche und Hervorhebung von Orientierungspunkten erstellt.

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


Dann schrieb er eine Bibliothek auf .Net Framework 4.6.1, in der er die gesamte Logik implementierte.

Ein Beispiel für das Erhalten von Langmarks:



Eine Person kann genauer unterschieden werden, indem die Punkte ganz links, rechts, oben und unten gefunden und Rahmen darauf aufgebaut werden.



Dann wurde das Gesicht in der unteren rechten Ecke aus dem Bild herausgeschnitten und unter Verwendung des oben beschriebenen Algorithmus in das Bild eingefügt: „Caballero de la mano en el pecho“.

Das folgende Ergebnis wurde erhalten.

Bild


Im nächsten Artikel möchte ich eine 3D-Maske aus einem Foto erstellen.

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


All Articles