4、二维缩放变换:缩放变换的数学原理、缩放矩阵、Python实现缩放变换

好,咱们今天聊聊缩放变换。说白了,就是把一个图形拉宽、压扁,或者放大、缩小。你想想看,平时用PS或者PPT拖拽图片的四个角,那就是缩放。但在计算机图形学里,我们得用数学把这件事说清楚。

4.1 缩放变换的数学原理

缩放变换的核心思想很简单:把每个点的坐标,乘以一个缩放因子。

假设我们有一个点 P(x, y),想把它沿着 x 轴缩放 Sx 倍,沿着 y 轴缩放 Sy 倍。那么新坐标 P'(x', y') 就是:

x' = x * Sx
y' = y * Sy

举个例子:点 (2, 3),如果 Sx=2, Sy=0.5,那么新坐标就是 (4, 1.5)。x 方向拉长了一倍,y 方向压缩了一半。

关键点:缩放是相对于原点 (0,0) 进行的。如果图形不在原点,缩放后它还会跑偏。这个问题我们后面讲复合变换时会处理。

我在项目中遇到过一个问题:缩放一个不规则多边形时,如果缩放因子不一样(比如 Sx=2, Sy=1),图形会变形。嗯,这其实不是 bug,而是非均匀缩放的正常表现。你想想看,一个正方形,x 方向拉长两倍,不就变成长方形了吗?

4.2 缩放矩阵

为了用统一的方式处理变换,我们引入矩阵。缩放变换的矩阵形式长这样:

[x']   [Sx   0] [x]
[y'] = [0   Sy] [y]

展开来算就是:

x' = Sx * x + 0 * y = Sx * x
y' = 0 * x + Sy * y = Sy * y

跟前面的公式完全一致。用矩阵的好处是,我们可以把多个变换(缩放、旋转、平移)组合成一个矩阵,一次计算搞定。

缩放类型 Sx Sy 效果
均匀放大 2 2 图形等比例放大两倍
均匀缩小 0.5 0.5 图形等比例缩小一半
水平拉伸 3 1 宽度变为三倍,高度不变
垂直压缩 1 0.3 高度压缩到30%,宽度不变
镜像翻转 -1 1 沿 y 轴镜像(左右翻转)

个人小技巧:当 Sx 或 Sy 为负数时,缩放就变成了镜像。我曾经用 Sx=-1 实现过一个水平翻转效果,省去了单独写镜像函数的麻烦。

4.3 Python实现缩放变换

理论说完了,咱们直接上代码。我习惯用 NumPy 来处理矩阵运算,干净利落。

import numpy as np
import matplotlib.pyplot as plt

def scale_point(point, sx, sy):
    """
    对单个点进行缩放变换
    point: [x, y] 格式的坐标
    sx: x 方向缩放因子
    sy: y 方向缩放因子
    """
    # 构建缩放矩阵
    scale_matrix = np.array([
        [sx, 0],
        [0, sy]
    ])
    
    # 应用变换
    new_point = scale_matrix @ point
    return new_point

# 测试一下
point = np.array([2, 3])
result = scale_point(point, 2, 0.5)
print(f"原始点: {point}")
print(f"缩放后: {result}")

输出结果:

原始点: [2 3]
缩放后: [4.  1.5]

跟手算的结果完全一致。接下来,我们试试对一个多边形进行缩放:

def scale_polygon(points, sx, sy):
    """
    对多边形所有顶点进行缩放
    points: 形状为 (n, 2) 的数组,每行一个顶点
    """
    scale_matrix = np.array([
        [sx, 0],
        [0, sy]
    ])
    
    # 矩阵乘法,一次处理所有点
    new_points = (scale_matrix @ points.T).T
    return new_points

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

# 缩放
scaled_triangle = scale_polygon(triangle, 1.5, 0.8)

print("原始三角形顶点:")
print(triangle)
print("\n缩放后三角形顶点:")
print(scaled_triangle)

我曾经踩过的坑:一开始我直接用 for 循环遍历每个点做矩阵乘法,数据量大了之后慢得不行。后来改成用矩阵一次性计算所有点,速度提升了上百倍。记住:能用矩阵运算就别用循环。

咱们再画个图直观感受一下:

# 绘制原始图形和缩放后的图形
plt.figure(figsize=(8, 6))

# 原始三角形
original = np.vstack([triangle, triangle[0]])  # 闭合图形
plt.plot(original[:, 0], original[:, 1], 'b-', label='原始', linewidth=2)

# 缩放后的三角形
scaled = np.vstack([scaled_triangle, scaled_triangle[0]])
plt.plot(scaled[:, 0], scaled[:, 1], 'r--', label='缩放后', linewidth=2)

plt.axhline(0, color='gray', linestyle=':', alpha=0.5)
plt.axvline(0, color='gray', linestyle=':', alpha=0.5)
plt.grid(True, alpha=0.3)
plt.legend()
plt.axis('equal')
plt.title('二维缩放变换效果')
plt.show()

运行这段代码,你会看到蓝色实线是原始三角形,红色虚线是缩放后的结果。x 方向拉伸了 1.5 倍,y 方向压缩到 0.8 倍,形状明显变了。

4.4 知识体系总览

为了让你对整个缩放变换有个全局认识,我画了张图:

二维缩放变换知识体系 缩放变换 数学原理 缩放矩阵 Python实现 x' = x * Sx y' = y * Sy [Sx 0; 0 Sy] NumPy矩阵运算 核心:用矩阵乘法统一处理所有顶点坐标

这张图把缩放变换的三个核心部分串起来了。从左到右:先理解数学公式,再学会用矩阵表达,最后用 Python 代码实现。我个人觉得,矩阵这一步是承上启下的关键——理解了矩阵,后面学旋转、平移就一通百通了。

总结一下:

  • 缩放变换就是坐标乘以缩放因子
  • 用矩阵可以统一处理所有顶点
  • Python 里用 NumPy 做矩阵运算,又快又准
  • 缩放因子为负数时变成镜像,这个技巧很实用

嗯,缩放变换就讲到这里。代码你拿去跑一跑,改改 Sx 和 Sy 的值,看看图形怎么变。动手试过的东西,记得才牢。

可可精品资料库