【问题标题】:Increasing efficiency of barycentric coordinate calculation in python提高python中重心坐标计算的效率
【发布时间】: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


【解决方案1】:

这是我的建议,用您的伪代码表达。请注意,对三角形上的循环进行矢量化也不应该更难。

# 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

    # 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)

    barytransform = np.linalg.inv([[ax,bx,cx], [ay,by,cy], [1,1,1]])     

    grid = np.mgrid[xleft:xright, ytop:ybottom].reshape(2,-1)
    grid = np.vstack((grid, np.ones((1, grid.shape[1]))))

    barycoords = np.dot(barytransform, grid)
    barycoords = barycoords[:,np.all(barycoords>=0, axis=0)]

【讨论】:

  • 在计算出三角形的所有重心坐标后,在不使用任何循环的情况下执行实际变换的最有效方法是什么?
  • 我不理解您试图解决的更高级别的问题,无法具体回答该问题。就个人而言,我猜你的问题最好通过现有的高级功能来解决,比如 scipy.spatial.Delaunay.find_simplex
猜你喜欢
  • 1970-01-01
  • 2021-04-05
  • 2016-07-11
  • 2020-01-11
  • 1970-01-01
  • 2021-09-02
  • 1970-01-01
  • 1970-01-01
  • 2013-11-10
相关资源
最近更新 更多