将一个三角形变换为另一个三角形

14

大家好,我正在尝试创建仿射变换,以便将一个三角形转换为另一个三角形。我有两个三角形的坐标,你能帮我吗?

根据Adam Rosenfield的答案,我编写了以下代码,以便在任何人不想亲自解决方程时可以使用:

public static AffineTransform createTransform(ThreePointSystem source,
            ThreePointSystem dest) {        
    double x11 = source.point1.getX();
    double x12 = source.point1.getY();
    double x21 = source.point2.getX();
    double x22 = source.point2.getY();
    double x31 = source.point3.getX();
    double x32 = source.point3.getY();
    double y11 = dest.point1.getX();
    double y12 = dest.point1.getY();
    double y21 = dest.point2.getX();
    double y22 = dest.point2.getY();
    double y31 = dest.point3.getX();
    double y32 = dest.point3.getY();

    double a1 = ((y11-y21)*(x12-x32)-(y11-y31)*(x12-x22))/
                ((x11-x21)*(x12-x32)-(x11-x31)*(x12-x22));
    double a2 = ((y11-y21)*(x11-x31)-(y11-y31)*(x11-x21))/
                ((x12-x22)*(x11-x31)-(x12-x32)*(x11-x21));
    double a3 = y11-a1*x11-a2*x12;
    double a4 = ((y12-y22)*(x12-x32)-(y12-y32)*(x12-x22))/
                ((x11-x21)*(x12-x32)-(x11-x31)*(x12-x22));
    double a5 = ((y12-y22)*(x11-x31)-(y12-y32)*(x11-x21))/
                ((x12-x22)*(x11-x31)-(x12-x32)*(x11-x21));
    double a6 = y12-a4*x11-a5*x12;
    return new AffineTransform(a1, a4, a2, a5, a3, a6);
}

所以你需要以某种方式“移动”一个三角形,使其坐标与第二个坐标匹配,而不改变三角形的外观?你有什么样的坐标? - Janusz
两个完全不同的三角形(无论是形状还是位置)。它们都存在于同一个坐标系中。 - Savvas Dalkitsis
1
非常有用,谢谢!只是一个快速提醒,如果使用JavaScript HTML5画布,context.setTransform()函数需要输入您的函数的确切输出,因此当您拥有每个三角形点的注册时,这是将图像从一个坐标系映射到另一个坐标系的绝佳工具。 - JayCrossler
4个回答

14

假设你在讨论二维变换。一个仿射变换矩阵有9个值:

    | a1 a2 a3 |
A = | a4 a5 a6 |
    | a7 a8 a9 |

有3个输入顶点 x1, x2, 和 x3,当它们被变换后应该变成 y1, y2, 和 y3。然而,因为我们正在使用齐次坐标,将 A 应用于 x1 并不一定会得到 y1 -- 它会给出 y1 的倍数。因此,我们还有未知的乘数 k1, k2, 和 k3,其方程组如下:

A*x1 = k1*y1
A*x2 = k2*y2
A*x3 = k3*y3

每个方程都是一个向量,因此我们实际上有12个未知数中的9个方程,所以解决方案将不完全确定。如果我们要求 a7=0, a8=0, 和 a9=1,那么解决方案将是唯一的(这个选择是自然的,因为它意味着如果输入点是 (x, y, 1),那么输出点将始终具有齐次坐标1,因此得到的变换就是一个2x2的变换加上一个平移)。

因此,这将方程组简化为:

a1*x11 + a2*x12 + a3 = k1*y11
a4*x11 + a5*x12 + a6 = k1*y12
                   1 = k1
a1*x21 + a2*x22 + a3 = k2*y21
a4*x21 + a5*x22 + a6 = k2*y22
                   1 = k2
a1*x31 + a2*x32 + a3 = k3*y31
a4*x31 + a5*x32 + a6 = k3*y32 
                   1 = k3
a4*x31 + a5*x32 + a6 = k3*y32 1 = k3

因此,将k1k2k3替换并转换为矩阵形式得到:

| x11 x12   1   0   0   0 |   | a1 |   | y11 |
| x21 x22   1   0   0   0 |   | a2 |   | y21 |
| x31 x32   1   0   0   0 | * | a3 | = | y31 |
|   0   0   0 x11 x12   1 |   | a4 |   | y12 |
|   0   0   0 x21 x22   1 |   | a5 |   | y22 |
|   0   0   0 x31 x32   1 |   | a6 |   | y32 |

解决这个6x6方程组可以得到仿射变换矩阵A。仅当源三角形的3个点不共线时,它才具有唯一解。


一个6x6的系统不是有点过于笨重了吗? - Rhythmic Fistman
2
既然问题指定了“仿射变换”,那么这是否意味着 [a7, a8, a9] == [0, 0, 1]?我知道这是你最终得出的结果,但似乎你做了一些不必要的扭曲来达到这个结果。而且,k 倍增量也似乎过于普遍化了。 - Laurence Gonsalves

2

大家好,为了方便起见,我们假设两个三角形的一个顶点是原点(您可以稍后添加仿射变换),因此它们由点0、a、b、c、d定义,然后将您的点x乘以矩阵NM

其中

M = inverse(a b) <--- 这是2x2矩阵,其列为点ab

而且

N = (c d)

就这样。


1

只需将问题公式化为一组方程,然后解决它:

P1 * M = P1'
P2 * M = P2'
P3 * M = P3'

M是一个3x3的矩阵,形式如下:

[m00, m01, m02;
 m10, m11, m12;
 0  ,   0,   1]

P_i 是一个元组 [k*x_i, k*y_i, k](齐次坐标)...

现在,您可以尝试扩展上述三个矩阵方程并创建一个新系统,其中 m_ij 为未知数并解决它,但如果我没有漏掉什么(也许我有),您需要一个以上的点来完全指定转换,否则您将拥有额外的自由度(当然,您可以修复它)。


1

如果我理解正确,你的三角形大小和角度相同,因此你应该能够对它们进行变换,使它们至少有一个共同点。之后,它们只应在旋转方向或者镜像方面不同,所以你可以通过获取三角形线之间的角度并尝试旋转这些角度来镜像其中的一个三角形,如果这些角度都没有效果。

编辑:好吧,这还不够,仿射变换也可以包含错切和缩放... 缩放可以很容易地完成,只需将线条的长度除以比例系数,这也会为你提供有关三角形对应线的一些信息,但是错切就会更困难...

另一方面,你难道不能为此解决一些方程组吗?毕竟,应该有一个变换矩阵和3个点(新的和旧的)...


1
不是的。我想要将一个三角形镜像到另一个完全不同的三角形上(相同的坐标系)。 - Savvas Dalkitsis

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接