【深度好文】3D座標系下的點的轉換矩陣(平移、縮放、旋轉、錯切)
上一節中往我們介紹了2D座標系下點的轉換矩陣,本節將2D座標系擴充套件到3D座標系,來研究對應的轉換矩陣。
1。 平移 (Translation)
在3D空間中,假設我們需要將一個點平移到另一個位置。假設空間中的一點P,其用座標表示為(x,y,z);將其向 x方向平移 tx,向y方向平移ty, 向z方向平移tz, 設平移後點的座標為(x’,y’,z‘),則上述點的平移操作可以歸納為如下公式:
使用齊次矩陣表示如下:
將上述過程用程式碼實現如下:
%
matplotlib
inline
import
matplotlib
import
matplotlib。pyplot
as
plt
from
mpl_toolkits。mplot3d
import
Axes3D
import
numpy
as
np
X
,
Y
,
Z
=
np
。
mgrid
[
0
:
1
:
5
j
,
0
:
1
:
5
j
,
0
:
1
:
5
j
]
x
,
y
,
z
=
X
。
ravel
(),
Y
。
ravel
(),
Z
。
ravel
()
def
trans_translate
(
x
,
y
,
z
,
tx
,
ty
,
tz
):
T
=
[[
1
,
0
,
0
,
tx
],
[
0
,
1
,
0
,
ty
],
[
0
,
0
,
1
,
tz
],
[
0
,
0
,
0
,
1
]]
T
=
np
。
array
(
T
)
P
=
np
。
array
([
x
,
y
,
z
,
[
1
]
*
x
。
size
])
return
np
。
dot
(
T
,
P
)
fig
,
ax
=
plt
。
subplots
(
1
,
4
,
subplot_kw
=
{
’projection‘
:
’3d‘
})
T_
=
[[
2。3
,
0
,
0
],
[
0
,
1。7
,
0
],
[
0
,
0
,
2。5
],
[
2
,
2
,
2
]]
for
i
in
range
(
4
):
tx
,
ty
,
tz
=
T_
[
i
]
x_
,
y_
,
z_
,
_
=
trans_translate
(
x
,
y
,
z
,
tx
,
ty
,
tz
)
ax
[
i
]
。
view_init
(
20
,
-
30
)
ax
[
i
]
。
scatter
(
x_
,
y_
,
z_
)
ax
[
i
]
。
set_title
(
r
’$t_x=
{0:。2f}
$ , $t_y=
{1:。2f}
$ , $t_z=
{2:。2f}
$‘
。
format
(
tx
,
ty
,
tz
))
ax
[
i
]
。
set_xlim
([
-
0。5
,
4
])
ax
[
i
]
。
set_ylim
([
-
0。5
,
4
])
ax
[
i
]
。
set_zlim
([
-
0。5
,
4
])
plt
。
show
()
效果如下:
動態效果如下:
2。 縮放 (Scaling)
在3D空間中,對點(x,y,z)常用的另一種操作為相對於另一點(px,py,pz)進行縮放操作,我們不妨x方向的縮放因子為sx,y方向的縮放因子為sy,z方向的縮放因子為sz, 則上述點(x,y,z)相對於點(px,py,pz)的縮放操作可以歸納為如下公式:
使用齊次矩陣表示如下:
將上述過程用程式碼實現如下:
def
trans_scale
(
x
,
y
,
z
,
px
,
py
,
pz
,
sx
,
sy
,
sz
):
T
=
[[
sx
,
0
,
0
,
px
*
(
1
-
sx
)],
[
0
,
sy
,
0
,
py
*
(
1
-
sy
)],
[
0
,
0
,
sz
,
pz
*
(
1
-
sz
)],
[
0
,
0
,
0
,
1
]]
T
=
np
。
array
(
T
)
P
=
np
。
array
([
x
,
y
,
z
,
[
1
]
*
x
。
size
])
return
np
。
dot
(
T
,
P
)
fig
,
ax
=
plt
。
subplots
(
1
,
4
,
subplot_kw
=
{
’projection‘
:
’3d‘
})
S_
=
[[
1。8
,
1
,
1
],
[
1
,
1。7
,
1
],
[
1
,
1
,
1。9
],
[
2
,
2
,
2
]]
P_
=
[[
0
,
0
,
0
],
[
0
,
0
,
0
],
[
0。45
,
0。45
,
0。45
],
[
1。1
,
1。1
,
1。1
]]
for
i
in
range
(
4
):
sx
,
sy
,
sz
=
S_
[
i
];
px
,
py
,
pz
=
P_
[
i
]
x_
,
y_
,
z_
,
_
=
trans_scale
(
x
,
y
,
z
,
px
,
py
,
pz
,
sx
,
sy
,
sz
)
ax
[
i
]
。
view_init
(
20
,
-
30
)
ax
[
i
]
。
scatter
(
x_
,
y_
,
z_
)
ax
[
i
]
。
scatter
(
px
,
py
,
pz
,
s
=
50
)
ax
[
i
]
。
set_title
(
r
’$p_x=
{0:。2f}
$ , $p_y=
{1:。2f}
$ , $p_z=
{2:。2f}
$‘
。
format
(
px
,
py
,
pz
)
+
’
\n
‘
r
’$s_x=
{0:。2f}
$ , $s_y=
{1:。2f}
$ , $s_z=
{2:。2f}
$‘
。
format
(
sx
,
sy
,
sz
)
)
ax
[
i
]
。
set_xlim
([
-
2
,
2
])
ax
[
i
]
。
set_ylim
([
-
2
,
2
])
ax
[
i
]
。
set_zlim
([
-
2
,
2
])
plt
。
show
()
效果如下:
動態效果如下:
3。 旋轉(Rotation)
在3D空間中,對點(x,y,z)常用的另一種操作為相對於另一點(px,py,pz)進行旋轉操作,我們依舊採用右手座標系,即旋轉角的正方向為逆時針方向。旋轉我們可分為繞x軸、y軸、z軸旋轉。假設繞x軸旋轉角度為alpha,繞y軸旋轉角度為beta,繞z軸旋轉的角度為gamma。則相應的變換如下:
1)繞x軸旋轉
公式如下:
使用齊次矩陣表示如下:
2)繞y軸旋轉
公式如下:
使用齊次矩陣表示如下:
3)繞z軸旋轉
公式如下:
使用齊次矩陣表示如下:
將上述過程用程式碼實現如下:
def
trans_rotate
(
x
,
y
,
z
,
px
,
py
,
pz
,
alpha
,
beta
,
gamma
):
alpha
,
beta
,
gamma
=
np
。
deg2rad
(
alpha
),
np
。
deg2rad
(
beta
),
np
。
deg2rad
(
gamma
)
Rx
=
[[
1
,
0
,
0
,
0
],
[
0
,
np
。
cos
(
alpha
),
-
np
。
sin
(
alpha
),
py
*
(
1
-
np
。
cos
(
alpha
))
+
pz
*
np
。
sin
(
alpha
)],
[
0
,
np
。
sin
(
alpha
),
np
。
cos
(
alpha
),
pz
*
(
1
-
np
。
cos
(
alpha
))
-
py
*
np
。
sin
(
alpha
)],
[
0
,
0
,
0
,
1
]]
Ry
=
[[
np
。
cos
(
beta
),
0
,
np
。
sin
(
beta
),
px
*
(
1
-
np
。
cos
(
beta
))
-
pz
*
np
。
sin
(
beta
)],
[
0
,
1
,
0
,
0
],
[
-
np
。
sin
(
beta
),
0
,
np
。
cos
(
beta
),
pz
*
(
1
-
np
。
cos
(
beta
))
+
px
*
np
。
sin
(
beta
)],
[
0
,
0
,
0
,
1
]]
Rz
=
[[
np
。
cos
(
gamma
),
-
np
。
sin
(
gamma
),
0
,
px
*
(
1
-
np
。
cos
(
gamma
))
+
py
*
np
。
sin
(
gamma
)],
[
np
。
sin
(
gamma
),
np
。
cos
(
gamma
),
0
,
py
*
(
1
-
np
。
cos
(
gamma
))
-
px
*
np
。
sin
(
gamma
)],
[
0
,
0
,
1
,
0
],
[
0
,
0
,
0
,
1
]]
Rx
=
np
。
array
(
Rx
);
Ry
=
np
。
array
(
Ry
);
Rz
=
np
。
array
(
Rz
)
P
=
np
。
array
([
x
,
y
,
z
,
[
1
]
*
x
。
size
])
return
np
。
dot
(
np
。
dot
(
np
。
dot
(
Rx
,
Ry
),
Rz
),
P
)
fig
,
ax
=
plt
。
subplots
(
1
,
4
,
subplot_kw
=
{
’projection‘
:
’3d‘
})
R_
=
[[
45
,
0
,
0
],
[
0
,
45
,
0
],
[
0
,
0
,
45
],
[
0
,
0
,
0
]]
P_
=
[[
0
,
0
,
0
],
[
0
,
0
,
0
],
[
0。5
,
-
0。5
,
0。5
],
[
1。1
,
1。1
,
1。1
]]
for
i
in
range
(
4
):
alpha
,
beta
,
gamma
=
R_
[
i
];
px
,
py
,
pz
=
P_
[
i
]
x_
,
y_
,
z_
,
_
=
trans_rotate
(
x
,
y
,
z
,
px
,
py
,
pz
,
alpha
,
beta
,
gamma
)
ax
[
i
]
。
view_init
(
20
,
-
30
)
ax
[
i
]
。
scatter
(
x_
,
y_
,
z_
)
ax
[
i
]
。
scatter
(
px
,
py
,
pz
)
ax
[
i
]
。
set_title
(
r
’$p_x=
{0:。2f}
$ , $p_y=
{1:。2f}
$ , $p_z=
{2:。2f}
$‘
。
format
(
px
,
py
,
pz
)
+
’
\n
‘
r
’$\alpha=
{0:03d}
^o$ , $\beta=
{1:03d}
^o$ , $\gamma=
{2:03d}
^o$‘
。
format
(
alpha
,
beta
,
gamma
)
)
ax
[
i
]
。
set_xlim
([
-
2
,
2
])
ax
[
i
]
。
set_ylim
([
-
2
,
2
])
ax
[
i
]
。
set_zlim
([
-
2
,
2
])
plt
。
show
()
效果如下:
動態效果如下:
4。 錯切(Shearing)
在3D空間中,對點(x,y,z)常用的另一種操作為相對於另一點(px,py,pz)進行錯切操作。不妨假設在yz平面的投影相對於y軸的錯切引數為lambaxy,相對於z軸的錯切引數為lambaxz;在xz平面的投影相對於x軸的錯切引數為lambayx,相對於z軸的錯切引數為lambayz;在xy平面的投影相對於x軸的錯切引數為lambazx,相對於y軸的錯切引數為lambazy。則上述點(x,y,z)相對於點(px,py,pz)的錯切操作可以歸納為如下公式:
使用齊次矩陣表示如下:
將上述過程用程式碼實現如下:
def
trans_shear
(
x
,
y
,
z
,
px
,
py
,
pz
,
lambdaxy
,
lambdaxz
,
lambdayx
,
lambdayz
,
lambdazx
,
lambdazy
):
T
=
[[
1
,
lambdaxy
,
lambdaxz
,
-
(
lambdaxy
+
lambdaxz
)
*
px
],
[
lambdayx
,
1
,
lambdayz
,
-
(
lambdayx
+
lambdayz
)
*
py
],
[
lambdazx
,
lambdazy
,
1
,
-
(
lambdazx
+
lambdazy
)
*
py
],
[
0
,
0
,
0
,
1
]]
T
=
np
。
array
(
T
)
P
=
np
。
array
([
x
,
y
,
z
,
[
1
]
*
x
。
size
])
return
np
。
dot
(
T
,
P
)
fig
,
ax
=
plt
。
subplots
(
1
,
4
,
subplot_kw
=
{
’projection‘
:
’3d‘
})
L_
=
[[[
2
,
0
],
[
0
,
0
],
[
0
,
0
]],
[[
0
,
0
],
[
2
,
0
],
[
1
,
0
]],
[[
0
,
1
],
[
0
,
0
],
[
0
,
2
]],
[[
2
,
0
],
[
0
,
2
],
[
2
,
0
]]]
P_
=
[[
0
,
0
,
0
],
[
0
,
0
,
0
],
[
0
,
1。5
,
0
],
[
1。1
,
1。1
,
1。1
]]
for
i
in
range
(
4
):
lambdax
,
lambday
,
lambdaz
=
L_
[
i
];
px
,
py
,
pz
=
P_
[
i
]
x_
,
y_
,
z_
,
_
=
trans_shear
(
x
,
y
,
z
,
px
,
py
,
pz
,
*
lambdax
,
*
lambday
,
*
lambdaz
)
ax
[
i
]
。
view_init
(
20
,
-
30
)
ax
[
i
]
。
scatter
(
x_
,
y_
,
z_
)
ax
[
i
]
。
scatter
(
px
,
py
)
ax
[
i
]
。
set_title
(
r
’$p_x=
{0:。2f}
$ , $p_y=
{1:。2f}
$ , $p_z=
{2:。2f}
$‘
。
format
(
px
,
py
,
pz
)
+
’
\n
‘
r
’$\lambda_x^y=
{0:。2f}
$ , $\lambda_y^x=
{1:。2f}
$ , $\lambda_z^x=
{2:。2f}
$‘
。
format
(
lambdax
[
0
],
lambday
[
0
],
lambdaz
[
0
])
+
’
\n
‘
r
’$\lambda_x^z=
{0:。2f}
$ , $\lambda_y^z=
{1:。2f}
$ , $\lambda_z^y=
{2:。2f}
$‘
。
format
(
lambdax
[
1
],
lambday
[
1
],
lambdaz
[
1
])
)
ax
[
i
]
。
set_xlim
([
-
3
,
3
])
ax
[
i
]
。
set_ylim
([
-
3
,
3
])
ax
[
i
]
。
set_zlim
([
-
3
,
3
])
plt
。
show
()
效果如下:
動態效果如下:
5。 總結
有了以上平移、旋轉、縮放和錯切矩陣後,我們就可以透過矩陣乘法求得3D空間下點P任意變化後坐標。