图形变换
举一个例子将第一象限的坐标 ( 1 , 4 ) 点顺时针旋转 π 2 后对应点的坐标 ? 首先告诉你一个矩阵( 2 D 图形顺时针旋转矩阵,不用管怎么来的) [ cos ( θ ) sin ( θ ) 0 − sin ( θ ) cos ( θ ) 0 0 0 1 ] 将坐标 ( 1 , 4 ) → [ 1 4 1 ] 计算变换后的坐标 [ cos ( π 2 ) sin ( π 2 ) 0 − sin ( π 2 ) cos ( π 2 ) 0 0 0 1 ] [ 1 4 1 ] = [ 0 1 0 − 1 0 0 0 0 1 ] [ 1 4 1 ] = [ 4 − 1 1 ] ⟹ ( 4 , − 1 ) . 这就是矩阵的乘法在几何中妙用,接下来我们就看看有哪些类似 − 2 D 图形顺时针旋转矩阵 − 的矩阵。 举一个例子将第一象限的坐标(1,4)点顺时针旋转\frac{\pi}{2}后对应点的坐标?\\ 首先告诉你一个矩阵(2D图形顺时针旋转矩阵,不用管怎么来的)\\ \left[\begin{matrix}{\cos(\theta)}&{\sin(\theta)}&{0}\\{-\sin(\theta)}&{\cos(\theta)}&{0}\\{0}&{0}&{1}\\\end{matrix}\right]\\ 将坐标(1,4)\rightarrow\begin{bmatrix}1\\4\\1\end{bmatrix}\\ 计算变换后的坐标\begin{bmatrix}\cos(\frac\pi2)&\sin(\frac\pi2)&0\\-\sin(\frac\pi2)&\cos(\frac\pi2)&0\\0&0&1\end{bmatrix}\begin{bmatrix}1\\4\\1\end{bmatrix}=\begin{bmatrix}0&1&0\\-1&0&0\\0&0&1\end{bmatrix}\begin{bmatrix}1\\4\\1\end{bmatrix}=\begin{bmatrix}4\\-1\\1\end{bmatrix}\implies(4,-1).\\ 这就是矩阵的乘法在几何中妙用,接下来我们就看看有哪些类似-2D图形顺时针旋转矩阵-的矩阵。 举一个例子将第一象限的坐标(1,4)点顺时针旋转2π后对应点的坐标?首先告诉你一个矩阵(2D图形顺时针旋转矩阵,不用管怎么来的) cos(θ)−sin(θ)0sin(θ)cos(θ)0001 将坐标(1,4)→ 141 计算变换后的坐标 cos(2π)−sin(2π)0sin(2π)cos(2π)0001 141 = 0−10100001 141 = 4−11 ⟹(4,−1).这就是矩阵的乘法在几何中妙用,接下来我们就看看有哪些类似−2D图形顺时针旋转矩阵−的矩阵。
2D图形各种变换矩阵
平移
dx:左移距离
dy:右移距离
注:当俩个都存在的时候表名可以左移矩阵乘右移矩阵(可以试试),也可以理解左右移动复合(这里没有谁先谁后的概念)。
[ 1 0 d x 0 1 d y 0 0 1 ] \left[\begin{array}{ccc}1&0&dx\\0&1&dy\\0&0&1\end{array}\right] 100010dxdy1
顺时针旋转
为了方便这里角度为正
[ cos ( θ ) sin ( θ ) 0 − sin ( θ ) cos ( θ ) 0 0 0 1 ] \left[\begin{matrix}{\cos(\theta)}&{\sin(\theta)}&{0}\\{-\sin(\theta)}&{\cos(\theta)}&{0}\\{0}&{0}&{1}\\\end{matrix}\right] cos(θ)−sin(θ)0sin(θ)cos(θ)0001
逆时针旋转
为了方便这里角度也为正
[ cos ( θ ) − sin ( θ ) 0 sin ( θ ) cos ( θ ) 0 0 0 1 ] \left[\begin{array}{ccc}{\cos(\theta)}&{-\sin(\theta)}&{0}\\{\sin(\theta)}&{\cos(\theta)}&{0}\\{0}&{0}&{1}\\\end{array}\right] cos(θ)sin(θ)0−sin(θ)cos(θ)0001
放缩与镜像
Sx:Sx>1表示x方向放大,0<Sx<1表示x方向缩小。-1<Sx<0表示x方向缩小且镜像,Sx=-1在x方向仅仅镜像,Sx<-1在x方向放大且镜像
Sy:Sy>1表示x方向放大,0<Sy<1表示y方向缩小。-1<Sy<0表示y方向缩小且镜像,Sy=-1在x方向仅仅镜像,Sy<-1在y方向放大且镜像
[ S x 0 0 0 S y 0 0 0 1 ] \left.\left[\begin{matrix}{Sx}&{0}&{0}\\{0}&{Sy}&{0}\\{0}&{0}&{1}\\\end{matrix}\right.\right] Sx000Sy0001
裁剪
shx:y轴不变沿着x轴移动(虽然和平移差不多,但是不同y移动的幅度是不一样,这就导致整个图形是倾斜的。矩形会变成平行四边形)
shy:x轴不变沿着y轴移动(同理)
[ 1 s h x 0 s h y 1 0 0 0 1 ] \begin{bmatrix}1&shx&0\\shy&1&0\\0&0&1\end{bmatrix} 1shy0shx10001
3D图形各种变换矩阵
平移
参考2D平移的参数
[ 1 0 0 d x 0 1 0 d y 0 0 1 d z 0 0 0 1 ] \left[\begin{array}{cccc}1&0&0&dx\\0&1&0&dy\\0&0&1&dz\\0&0&0&1\end{array}\right] 100001000010dxdydz1
在x轴顺、逆时针旋转
参考2D旋转的参数。这里的顺、逆时针,是让对应坐标轴的箭头插入眼睛。然后左顺,右逆。
顺 : [ 1 0 0 0 0 cos ( θ ) sin ( θ ) 0 0 − sin ( θ ) cos ( θ ) 0 0 0 0 1 ] 逆 : [ 1 0 0 0 0 cos ( θ ) − sin ( θ ) 0 0 sin ( θ ) cos ( θ ) 0 0 0 0 1 ] 顺:\left.\left[\begin{array}{cccc}1&0&0&0\\0&\cos(\theta)&\sin(\theta)&0\\0&-\sin(\theta)&\cos(\theta)&0\\0&0&0&1\end{array}\right.\right] 逆:\begin{bmatrix}1&0&0&0\\0&\cos(\theta)&-\sin(\theta)&0\\0&\sin(\theta)&\cos(\theta)&0\\0&0&0&1\end{bmatrix} 顺: 10000cos(θ)−sin(θ)00sin(θ)cos(θ)00001 逆: 10000cos(θ)sin(θ)00−sin(θ)cos(θ)00001
在y轴顺、逆时针旋转
顺 : [ cos ( θ ) 0 sin ( θ ) 0 0 1 0 0 − sin ( θ ) 0 cos ( θ ) 0 0 0 0 1 ] 逆 : [ cos ( θ ) 0 − sin ( θ ) 0 0 1 0 0 sin ( θ ) 0 cos ( θ ) 0 0 0 0 1 ] 顺:\begin{bmatrix}\cos(\theta)&0&\sin(\theta)&0\\0&1&0&0\\-\sin(\theta)&0&\cos(\theta)&0\\0&0&0&1\end{bmatrix} 逆:\begin{bmatrix}\cos(\theta)&0&-\sin(\theta)&0\\0&1&0&0\\\sin(\theta)&0&\cos(\theta)&0\\0&0&0&1\end{bmatrix} 顺: cos(θ)0−sin(θ)00100sin(θ)0cos(θ)00001 逆: cos(θ)0sin(θ)00100−sin(θ)0cos(θ)00001
在z轴顺、逆时针旋转
顺 : [ cos ( θ ) sin ( θ ) 0 0 − sin ( θ ) cos ( θ ) 0 0 0 0 1 0 0 0 0 1 ] 逆 : [ cos ( θ ) − sin ( θ ) 0 0 sin ( θ ) cos ( θ ) 0 0 0 0 1 0 0 0 0 1 ] 顺:\left[\begin{array}{cccc}\cos\left(\theta\right)&\sin\left(\theta\right)&0&0\\-\sin\left(\theta\right)&\cos\left(\theta\right)&0&0\\0&0&1&0\\0&0&0&1\end{array}\right] 逆:\begin{bmatrix}\cos\left(\theta\right)&-\sin\left(\theta\right)&0&0\\\sin\left(\theta\right)&\cos\left(\theta\right)&0&0\\0&0&1&0\\0&0&0&1\end{bmatrix} 顺: cos(θ)−sin(θ)00sin(θ)cos(θ)0000100001 逆: cos(θ)sin(θ)00−sin(θ)cos(θ)0000100001
放缩和镜像
参考2D放缩和镜像
[ S x 0 0 0 0 S y 0 0 0 0 S z 0 0 0 0 1 ] \begin{bmatrix}Sx&0&0&0\\0&Sy&0&0\\0&0&Sz&0\\0&0&0&1\end{bmatrix} Sx0000Sy0000Sz00001
透视投影
模拟人眼或相机在观察三维场景时所看到的景象,考虑了物体距离观察者的远近关系。透视投影使离观察者更远的物体看起来较小,与现实中的效果相似。
fov: 视野角度(Field of View Angle)
aspect: 屏幕纵横比(Aspect Ratio)
near: 近剪裁面(Near Clipping Plane)
far: 远剪裁面(Far Clipping Plane)
[ 1 aspect ⋅ tan ( fov 2 ) 0 0 0 0 1 tan ( fov 2 ) 0 0 0 0 − far + near far − near − 2 ⋅ far ⋅ near far − near 0 0 − 1 0 ] \begin{bmatrix} \frac{1}{{\text{{aspect}} \cdot \tan(\frac{{\text{{fov}}}}{2})}} & 0 & 0 & 0 \\ 0 & \frac{1}{{\tan(\frac{{\text{{fov}}}}{2})}} & 0 & 0 \\ 0 & 0 & -\frac{{\text{{far}} + \text{{near}}}}{{\text{{far}} - \text{{near}}}} & -\frac{{2 \cdot \text{{far}} \cdot \text{{near}}}}{{\text{{far}} - \text{{near}}}} \\ 0 & 0 & -1 & 0 \\ \end{bmatrix} aspect⋅tan(2fov)10000tan(2fov)10000−far−nearfar+near−100−far−near2⋅far⋅near0
正交投影
将物体投影到一个平行的二维平面上(三视图的每一视都属于正交投影),不考虑物体的远近效果(投影面积与实际物理面积一致),因此在投影中不会发生透视效果。
left, right: 这些参数定义了平行投影平面的左右截断平面,决定了投影的宽度。
bottom, top: 这些参数定义了平行投影平面的底部和顶部截断平面,决定了投影的高度。
near, far: 近剪裁面和远剪裁面定义了可见空间的深度范围,物体在这个范围之外的部分将不可见。这些参数决定了投影的深度
[ 2 ( r i g h t − l e f t ) 0 0 − ( r i g h t + l e f t ) ( r i g h t − l e f t ) 0 2 ( t o p − b o t t o m ) 0 − ( t o p + b o t t o m ) ( t o p − b o t t o m ) 0 0 2 ( f a r − n e a r ) − ( f a r + n e a r ) ( f a r − n e a r ) 0 0 0 1 ] \begin{bmatrix}\frac2{(right-left)}&0&0&-\frac{(right+left)}{(right-left)}\\0&\frac2{(top-bottom)}&0&-\frac{(top+bottom)}{(top-bottom)}\\0&0&\frac2{(far-near)}&-\frac{(far+near)}{(far-near)}\\0&0&0&1\end{bmatrix} (right−left)20000(top−bottom)20000(far−near)20−(right−left)(right+left)−(top−bottom)(top+bottom)−(far−near)(far+near)1
2D和3D变换的代码
2D变换
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
# 打开图片
image = Image.open("img.png")
# 将图像转换为NumPy数组
image_array = np.array(image)
# 获取图像的宽度和高度
width, height, channels = image_array.shape
# 创建一个空白的NumPy数组,用于保存转化后的图像数据
transformational_image_array = np.zeros_like(image_array, dtype=np.uint8)
# 定义新的change_array用于移动、旋转、放缩、镜像图片,这里的矩阵和图片中的每个像素实际位置进行乘法运算
change_array = np.array([
[-1, 0, 300],
[0, -1, 300],
[0, 0, 1]
])
# 遍历图像的每个像素位置
for x in range(width):
for y in range(height):
# 获取当前像素的颜色值
pixel_color = image_array[x, y]
# 计算新位置的像素坐标
final_x, final_y, _ = np.dot(change_array, [x - width / 2, y - height / 2, 1]).astype(int)
final_x = int(final_x + width / 2) # 转为整数
final_y = int(final_y + height / 2)
# 确保镜像后的坐标在合法范围内
if 0 <= final_x < width and 0 <= final_y < height:
transformational_image_array[x, y] = image_array[final_x, final_y]
# 创建一个包含两个子图的图像
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 4))
# 在第一个子图中显示原始图像
ax1.imshow(image_array)
ax1.set_title('Original Image')
ax1.axis('off') # 不显示坐标轴
# 在第二个子图中显示变换后的图像
ax2.imshow(transformational_image_array)
ax2.set_title('Transformational Image')
ax2.axis('off') # 不显示坐标轴
# 显示图像
plt.show()
# 关闭图片对象
image.close()
3D变换
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
import numpy as np
# 创建一个Figure对象和3D坐标轴
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# 定义正方体的顶点坐标,恰好是xyz单位向量为边的正方体。
vertices = np.array([
[0, 0, 0],
[1, 0, 0],
[1, 1, 0],
[0, 1, 0],
[0, 0, 1],
[1, 0, 1],
[1, 1, 1],
[0, 1, 1]
])
# 创建复合变换矩阵
composite_matrix = np.array([
[-1, 0, 0, -0.5], # 镜像、不放缩和平移(X轴上向负方向移动0.5个距离)
[0, -2, 0, 0], # 镜像、放大(Y轴上负方向放大2倍)和没有在Y轴上平移
[0, 0, -2, 0], # 镜像、放大(Z轴上负方向放大2倍)和没有在Z轴上平移
[0, 0, 0, 1]
])
# 给vertices(空间点坐标)增加最后全1列(为了和复合变换矩阵可以作乘法运算补充出来的)
num_rows, num_cols = vertices.shape
new_column = np.ones((num_rows, 1), dtype=int)
vertices_new = np.hstack((vertices, new_column))
# 应用复合变换到正方体的顶点,注意要转置矩阵
transformetional_vertices = np.dot(vertices_new, composite_matrix.T)
# 定义正方体的面
faces = [
[vertices[0], vertices[1], vertices[2], vertices[3]],
[vertices[4], vertices[5], vertices[6], vertices[7]],
[vertices[0], vertices[1], vertices[5], vertices[4]],
[vertices[2], vertices[3], vertices[7], vertices[6]],
[vertices[1], vertices[2], vertices[6], vertices[5]],
[vertices[0], vertices[3], vertices[7], vertices[4]]
]
# 绘制原始正方体的面
ax.add_collection3d(Poly3DCollection(faces, facecolors='cyan', linewidths=1, edgecolors='r', alpha=.25))
# 去掉最后一行
mirrored_vertices = transformetional_vertices[:, :-1]
# 绘制镜像后的正方体的面
mirrored_faces = [
[mirrored_vertices[0], mirrored_vertices[1], mirrored_vertices[2], mirrored_vertices[3]],
[mirrored_vertices[4], mirrored_vertices[5], mirrored_vertices[6], mirrored_vertices[7]],
[mirrored_vertices[0], mirrored_vertices[1], mirrored_vertices[5], mirrored_vertices[4]],
[mirrored_vertices[2], mirrored_vertices[3], mirrored_vertices[7], mirrored_vertices[6]],
[mirrored_vertices[1], mirrored_vertices[2], mirrored_vertices[6], mirrored_vertices[5]],
[mirrored_vertices[0], mirrored_vertices[3], mirrored_vertices[7], mirrored_vertices[4]]
]
# 绘制镜像后的正方体的面
ax.add_collection3d(Poly3DCollection(mirrored_faces, facecolors='magenta', linewidths=1, edgecolors='k', alpha=.25))
# 绘制X、Y、Z轴
ax.quiver(0, 0, 0, 1.2, 0, 0, color='red', label='X')
ax.quiver(0, 0, 0, 0, 1.2, 0, color='green', label='Y')
ax.quiver(0, 0, 0, 0, 0, 1.2, color='blue', label='Z')
# 设置X轴、Y轴和Z轴的刻度位置和标签
ax.set_xticks(np.arange(-2, 2, step=1)) # 设置X轴刻度
ax.set_yticks(np.arange(-2, 2, step=1)) # 设置Y轴刻度
ax.set_zticks(np.arange(-2, 2, step=1)) # 设置Z轴刻度
# 设置坐标轴标签
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
# 显示图例
ax.legend()
# 显示图形
plt.show()