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 代码实现。我个人觉得,矩阵这一步是承上启下的关键——理解了矩阵,后面学旋转、平移就一通百通了。
总结一下:
- 缩放变换就是坐标乘以缩放因子
- 用矩阵可以统一处理所有顶点
- Python 里用 NumPy 做矩阵运算,又快又准
- 缩放因子为负数时变成镜像,这个技巧很实用
嗯,缩放变换就讲到这里。代码你拿去跑一跑,改改 Sx 和 Sy 的值,看看图形怎么变。动手试过的东西,记得才牢。
可可精品资料库