Tło: Próbuję wypaczyć twarz do innej o innym kształcie.Zwiększenie wydajności obliczania współrzędnych barycentrycznych w pythonie
Aby wypaczyć jedno zdjęcie na drugie, używam delangadnej triangulacji charakterystycznych rysów twarzy i wypaczając trójkąty jednego portretu do odpowiednich trójkątów drugiego portretu. Używam barycentrycznego układu współrzędnych do mapowania punktu w trójkącie do odpowiadającej mu wypaczonej lokalizacji w drugim trójkącie.
Moje pierwsze podejście polegało na rozwiązaniu układu Ax = b metodą odwrotnego mnożenia, gdzie A składa się z trzech rogów trójkąta, b reprezentuje bieżący punkt, a x reprezentuje barycentryczne współrzędne tego punktu (alfa, beta i gamma). Znalazłem odwrotność macierzy A raz na każdy trójkąt, a następnie dla każdego punktu w tym trójkącie obliczono współrzędne barycentryczne, znajdując iloczyn punktowy A^-1 i punkt b. Zauważyłem, że jest to bardzo powolne (wykonanie tej funkcji zajmuje 36 sekund).
Zgodnie z zaleceniami innych stanowisk, próbowałem użyć rozwiązania najmniejszych kwadratów, aby poprawić efektywność tego procesu. Jednak czas ten wzrósł do 154 sekund, gdy użyłem metody numsu w lsq. Sądzę, że wynika to z faktu, że matryca A jest uwzględniana za każdym razem, gdy pętla wewnętrzna działa, podczas gdy zanim udało mi się znaleźć odwrotność tylko jeden raz, zanim zaczną się obie pętle.
Moje pytanie brzmi: jak mogę poprawić efektywność tej funkcji? Czy istnieje sposób na przechowywanie faktoryzacji A, aby za każdym razem, gdy obliczono najmniejsze kwadratowe rozwiązanie dla nowego punktu, nie powtórzyło się to samo?
Pseudokod dla tej funkcji:
# 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
Kluczem do zwiększenia wydajności jest wyeliminowanie pętli i wykorzystanie możliwości wektoryzacji. Na dodatek mam wrażenie, że możesz zrobić dla ciebie scipy.spatial wiele ciężkiego podnoszenia. Czy mógłbyś dodać opis swojego problemu o wyższym poziomie spodziewanego typu input-ouput? –
Prostą optymalizacją byłoby najpierw wektoryzować pętle ponad xiy. Po prostu utwórz listę punktów używając np.mgrid, przekształć wszystkie te punkty w przestrzeń barycentryczną i odfiltruj wszystkie punkty, które nie mają wyłącznie dodatnich współrzędnych. To powinno przynieść wydajność rzędu. Jednak nadal uważam, że to rozwiązanie nie jest optymalne, jeśli spojrzymy na problem z wyższego poziomu. –
btw; lstsq nie oblicza współrzędnych barycentrycznych jako takich. Rozważmy trójkąt z jego COM na początku. "Współrzędne barycentryczne" dla [0,0] obliczone dla lstsq wynoszą [0,0,0]; które nie sumują się do jednego. Znalezienie przekształcenia w kable barycentryczne powinno zasadniczo obejmować ograniczenie sumy do jednego. –