3. 二维平移变换:平移变换的数学原理、平移矩阵、Python实现平移变换

好,咱们今天聊聊二维图形里最基础、也最直观的一个变换——平移。

说白了,平移就是让一个点或者一个图形,在平面上「挪个地方」。你想想看,把一个正方形从屏幕左边拖到右边,这就是平移。听起来简单吧?但背后的数学原理,以及怎么用代码实现,还是有不少细节值得琢磨的。

3.1 平移变换的数学原理

先从一个点说起。假设平面上有一个点 P,它的坐标是 (x, y)。我想把它往右移动 tx 个单位,往上移动 ty 个单位。那移动后的新坐标 (x', y') 是多少?

这个太直观了:

x' = x + tx
y' = y + ty

嗯,就这么简单。tx 和 ty 就是平移的量。如果 tx 是正数,就往右移;负数就往左移。ty 同理,正数往上,负数往下。

但问题来了——我们后面要学旋转、缩放、错切这些变换。如果每个变换都单独写一套公式,代码会变得很乱。有没有一种统一的方式,能把所有变换都表达成「矩阵乘法」?

有。这就是引入齐次坐标的原因。

核心思想:把二维点 (x, y) 扩展成三维向量 (x, y, 1)。这样平移就能用矩阵乘法表示了。

为什么非要加一个 1?我刚开始学的时候也觉得这有点多余。但你想,如果不加这个 1,平移的加法操作永远没法写成矩阵乘法——因为矩阵乘法本质是线性变换,而线性变换必须过原点。平移不过原点,所以必须升维。

用齐次坐标表示平移:

[x']   [1  0  tx] [x]
[y'] = [0  1  ty] [y]
[1 ]   [0  0   1] [1]

展开矩阵乘法,得到:

x' = 1*x + 0*y + tx*1 = x + tx
y' = 0*x + 1*y + ty*1 = y + ty
1  = 0*x + 0*y + 1*1  = 1

你看,结果完全一致。但形式上,我们统一成了矩阵乘法。后面学旋转、缩放时,你会发现这个「加一维」的技巧有多好用。

3.2 平移矩阵

我们把上面那个 3x3 的矩阵叫做平移矩阵,记作 T(tx, ty):

T(tx, ty) = 
[1  0  tx]
[0  1  ty]
[0  0   1]

这个矩阵有几个特点:

  • 左上角 2x2 是单位矩阵,表示「不旋转、不缩放」
  • 第三列前两行是平移量 tx 和 ty
  • 第三行永远是 [0, 0, 1],这是齐次坐标的「保底」行

小技巧:如果你想把一个图形先平移再平移,连续两次平移的结果,等于把平移量相加。即:T(tx1, ty1) * T(tx2, ty2) = T(tx1+tx2, ty1+ty2)。这个性质在实际编程中很常用。

另外要注意,矩阵乘法不满足交换律。但平移矩阵之间是交换的——先右移再上移,和先上移再右移,结果一样。这算是平移的一个「特权」吧。旋转和缩放可没这么友好。

3.3 Python实现平移变换

好,理论说完了,咱们动手写代码。我个人习惯用 NumPy 来处理矩阵运算,配合 Matplotlib 来可视化。

先定义一个平移矩阵的生成函数:

import numpy as np

def translation_matrix(tx, ty):
    """
    生成二维平移矩阵(齐次坐标形式)
    tx: x方向平移量
    ty: y方向平移量
    """
    return np.array([
        [1, 0, tx],
        [0, 1, ty],
        [0, 0,  1]
    ])

然后定义一个函数,对一组点应用平移变换:

def transform_points(points, matrix):
    """
    对点集应用变换矩阵
    points: 形状为 (N, 2) 的数组,每行是一个点的 (x, y)
    matrix: 3x3 变换矩阵
    返回: 变换后的点集 (N, 2)
    """
    # 转为齐次坐标:加一列 1
    ones = np.ones((points.shape[0], 1))
    homogeneous = np.hstack([points, ones])  # (N, 3)
    
    # 应用变换
    transformed = homogeneous @ matrix.T  # (N, 3)
    
    # 转回二维坐标:去掉最后一列
    return transformed[:, :2]

我曾经踩过的一个坑:这里用的是 @ matrix.T 而不是 matrix @ homogeneous.T。因为我们的点是按行排列的,每行一个点。如果搞反了维度,会得到形状错误的结果。嗯,这个细节调试了我半小时。

来,我们画个三角形,把它平移一下看看效果:

import matplotlib.pyplot as plt

# 定义一个三角形(三个顶点)
triangle = np.array([
    [0, 0],
    [2, 0],
    [1, 1.5]
])

# 生成平移矩阵:右移3,上移2
T = translation_matrix(3, 2)

# 应用变换
triangle_moved = transform_points(triangle, T)

# 可视化
plt.figure(figsize=(6, 6))
# 画原始三角形
plt.fill(triangle[:, 0], triangle[:, 1], 
         alpha=0.5, label='原始', color='skyblue')
# 画平移后的三角形
plt.fill(triangle_moved[:, 0], triangle_moved[:, 1], 
         alpha=0.5, label='平移后', color='salmon')

plt.axhline(0, color='gray', linewidth=0.5)
plt.axvline(0, color='gray', linewidth=0.5)
plt.grid(True, linestyle='--', alpha=0.3)
plt.axis('equal')
plt.legend()
plt.title('二维平移变换示例')
plt.show()

运行这段代码,你会看到两个三角形——蓝色的在原点附近,红色的跑到了右上方。形状完全没变,只是位置变了。这就是平移的本质:保持形状和朝向,只改变位置。

3.4 知识体系总览

为了让你对整个平移变换有个全局认识,我画了一张结构图:

二维平移变换 数学原理 x' = x + tx y' = y + ty 平移矩阵 [1 0 tx] [0 1 ty] [0 0 1] Python实现 生成矩阵 应用变换 核心:齐次坐标 → 矩阵乘法 → 统一变换框架

这张图把平移变换的三个核心模块串起来了。从左到右:数学公式是基础,矩阵是表达形式,Python代码是落地工具。三者缺一不可。

3.5 实际应用中的注意事项

最后,分享几个我在项目中积累的经验:

  • 坐标系方向:屏幕坐标系和数学坐标系 y 轴是反的。在屏幕上,y 正方向朝下。所以「上移」在屏幕上是减小 y 值。写代码时一定要确认你用的库是什么坐标系。
  • 浮点精度:多次平移后,坐标可能会有微小误差。比如平移 0.1 十次,理论上应该回到原位,但实际可能是 0.9999999。必要时做四舍五入。
  • 性能优化:如果你要对成千上万个点做平移,别用循环。用 NumPy 的向量化操作,一次矩阵乘法搞定。我见过有人用 for 循环平移 10 万个点,跑了 3 秒——改成矩阵乘法后只要 0.01 秒。

一个小练习:试着把上面的三角形先右移 2,再上移 3,然后左移 1。看看最终位置在哪里。再用矩阵乘法验证一下:T(-1, 0) * T(0, 3) * T(2, 0) 的结果矩阵是什么?

好了,平移变换就讲到这里。它虽然简单,但却是理解后面所有变换的基石。你想想看,如果没有齐次坐标这个「升维」的技巧,我们后面要讲的旋转、缩放、复合变换,都会变得非常别扭。所以,请务必把平移矩阵的推导和代码实现都亲手过一遍。

可可精品资料库