【发布时间】:2015-07-15 23:15:15
【问题描述】:
背景:我正在尝试将一张脸变形为另一张不同形状的脸。
为了将一张图像变形为另一张图像,我使用面部标志的 delaunay 三角剖分,并将一张肖像的三角形变形为第二张肖像的相应三角形。我正在使用重心坐标系将三角形内的一个点映射到另一个三角形上相应的扭曲位置。
我的第一种方法是用逆乘法求解系统Ax = b,其中A由三角形的三个角组成,b代表当前点,x代表该点的重心坐标(α,β和伽玛)。我在每个三角形中找到了矩阵 A 的逆矩阵,然后对于该三角形中的每个点,通过找到 A^-1 和点 b 的点积来计算重心坐标。我发现这很慢(该功能需要 36 秒才能完成)。
根据其他帖子的建议,我尝试使用最小二乘解决方案来提高此过程的效率。但是,当我使用 numpy 的 lsq 方法时,时间增加到了 154 秒。我相信这是因为 A 矩阵在每次运行内部循环时都会被分解,而在两个循环开始之前,我只能找到一次逆矩阵。
我的问题是,如何提高这个功能的效率?有没有办法存储 A 的分解,以便每次为新点计算最小二乘解时,它不会重复相同的工作?
这个函数的伪代码:
# 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
【问题讨论】:
-
这里提高效率的本质是消除循环,并利用矢量化的可能性。最重要的是,我觉得你可以让 scipy.spatial 为你做很多繁重的工作。您能否为您的问题添加更高级的预期输入输出类型描述?
-
一个简单的优化是首先对 x 和 y 上的循环进行矢量化。只需使用 np.mgrid 创建一个点列表,将所有这些点转换为重心空间,并过滤掉所有不具有唯一正坐标的点。这应该很容易产生一个数量级的性能。但是,如果我们从更高层次的角度看待问题,我认为这个解决方案并不是最优的。
-
顺便说一句; lstsq 本身不计算重心坐标。考虑一个以 COM 为原点的三角形。用 lstsq 计算的 [0,0] 的“重心坐标”是 [0,0,0];不等于一。找到重心索的转换应该从根本上包括总和到一的约束。
-
barytransform = np.linalg.inv([[ax,bx,cx], [ay,by,cy], [1,1,1]]) 这应该给你一个矩阵转换为重心空间;然后得到重心坐标: barycoord = np.dot(barytransform, [x,y,1])
标签: python numpy linear-algebra delaunay