Hintergrund: Ich versuche, ein Gesicht zu einem anderen mit einer anderen Form zu verzerren.Erhöhung der Effizienz der baryzentrischen Koordinatenberechnung in Python
Um ein Bild zu einem anderen zu verzerren, verwende ich eine Delaunay-Triangulation von Gesichtsmarkierungen und verforme die Dreiecke eines Porträts zu den entsprechenden Dreiecken des zweiten Portraits. Ich verwende ein baryzentrisches Koordinatensystem, um einen Punkt innerhalb eines Dreiecks auf die entsprechende verzogene Position auf dem anderen Dreieck abzubilden.
Meine erste Annäherung war, das System Ax = b mit der inversen Multiplikationsmethode zu lösen, wobei A aus den drei Ecken des Dreiecks besteht, b den aktuellen Punkt darstellt und x die baryzentrischen Koordinaten dieses Punktes darstellt (Alpha, Beta und Gamma). Ich fand das Inverse der Matrix A einmal pro Dreieck und berechnete dann für jeden Punkt innerhalb dieses Dreiecks die baryzentrischen Koordinaten, indem das Punktprodukt von A^-1 und der Punkt b gefunden wurden. Ich fand das sehr langsam (die Funktion dauert 36 Sekunden).
Nach der Empfehlung anderer Beiträge, versuchte ich eine Kleinste-Quadrate-Lösung zu verwenden, um die Effizienz dieses Prozesses zu verbessern. Die Zeit stieg jedoch auf 154 Sekunden, als ich die lsq-Methode von numpy anwendete. Ich glaube, das liegt an der Tatsache, dass die A-Matrix jedes Mal, wenn die innere Schleife läuft, berücksichtigt wird, während ich zuvor nur einmal die Umkehrung finden konnte, bevor die beiden Schleifen beginnen.
Meine Frage ist, wie kann ich die Effizienz dieser Funktion verbessern? Gibt es eine Möglichkeit, die Faktorisierung von A zu speichern, so dass jedes Mal, wenn die Lösung der kleinsten Quadrate für einen neuen Punkt berechnet wird, nicht dieselbe Arbeit wiederholt wird?
Pseudocode für diese Funktion:
# Iterate through each triangle (and get corresponding warp triangle)
for triangle in triangulation:
# Extract corners of the unwarped triangle
a = firstCornerUW
b = secondCornerUW
c = thirdCornerUW
# Extract corners of the warp triangle
a_prime = firstCornerW
b_prime = secondCornerW
c_prime = thirdCornerW
# This matrix will be the same for all points within the triangle
triMatrix = matrix of a, b, and c
# Bounding box of the triangle
xleft = min(ax, bx, cx)
xright = max(ax, bx, cx)
ytop = min(ay, by, cy)
ybottom = max(ay, by, cy)
for x in range(xleft, xright):
for y in range(ytop, ybottom):
# Store the current point as a matrix
p = np.array([[x], [y], [1]])
# Solve for least squares solution to get barycentric coordinates
barycoor = np.linalg.lstsq(triMatrix, p)
# Pull individual coordinates from the array
alpha = barycoor[0]
beta = barycoor[1]
gamma = barycoor[2]
# If any of these conditions are not met, the point is not inside the triangle
if alpha, beta, gamma > 0 and alpha + beta + gamma <= 1:
# Now calculate the warped point by multiplying by alpha, beta, and gamma
# Warp the point from image to warped image
Das Wesentliche zur Steigerung der Effizienz ist hier, die Schleifen zu eliminieren und Vektorisierungsmöglichkeiten zu nutzen. Darüber hinaus habe ich den Eindruck, dass Sie mit scipy.spatial viel schweres Heben für Sie erledigen können. Könnten Sie eine detailliertere Beschreibung des Problems mit dem erwarteten Eingabe-Ausgabe-Typ hinzufügen? –
Eine einfache Optimierung wäre, zuerst die Schleifen über x und y zu vektorisieren. Erstellen Sie einfach eine Liste von Punkten mit np.mgrid, transformieren Sie all diese Punkte in baryzentrischen Raum und filtern Sie alle Punkte heraus, die nicht ausschließlich positive Koordinaten haben. Das sollte eine Größenordnung in der Leistung leicht ergeben. Aber ich glaube nicht, dass diese Lösung optimal ist, wenn wir das Problem auf einer höheren Ebene betrachten. –
btw; lstsq berechnet baryzentrische Koordinaten nicht per se. Betrachten Sie ein Dreieck mit seinem COM am Ursprung. Die 'baryzentrischen Koordinaten' für [0,0] wie mit lstsq berechnet sind [0,0,0]; welche summieren sich nicht zu eins. Das Finden der Transformation zu baryzentrischen Korden sollte grundsätzlich die Summe-zu-Eins-Beschränkung beinhalten. –