同次座標と変換行列
Homogeneous Coordinates and Transformation Matrices
同次座標とは
同次座標(Homogeneous Coordinates)は、射影幾何学で使われる座標系で、通常の座標に1次元を追加したものである。厳密には $(kx, ky, kw) \sim (x, y, w)$($k \neq 0$)のように比例関係にある座標を同じ点とみなす同値類であり、これにより平行移動を含むすべての変換を行列の積で表現できる。
同次座標の定義
- 2D点 $(x, y)$ の同次座標: $(x, y, 1)$ または一般に $(kx, ky, k)$ for $k \neq 0$
- 3D点 $(X, Y, Z)$ の同次座標: $(X, Y, Z, 1)$ または $(kX, kY, kZ, k)$
- 通常座標への変換: $(x, y, w) \to (x/w, y/w)$
なぜ同次座標を使うのか?
- 平行移動を行列の積で表現できる
- 複数の変換を1つの行列に合成できる
- 射影変換(透視投影)を線形に扱える
- 無限遠点を有限の座標で表現できる($w = 0$)
平行移動はなぜ特別か
通常座標では平行移動は加算 $(x', y') = (x, y) + (t_x, t_y)$ であり、原点を固定する線形変換(1つの行列の積)では書けない。同次座標で次元を1つ増やすと、平行移動も $(x', y', 1) = T\,(x, y, 1)$ という単一の行列積で表せる。これにより回転・スケール・平行移動を同じ枠組みで合成できるのが最大の利点である。
2D変換行列
ベクトルの向き: 本記事ではベクトルを列ベクトルとして扱い、$p' = T\,p$ と行列を左から掛ける。$(x, y, 1)$ とカンマ区切りで横書きするのは、列ベクトル $(x, y, 1)^\top$ を縦に積む代わりに省スペースで書いた省略表記である。
平行移動(Translation)
回転(Rotation)
スケーリング(Scaling)
変換の合成
複数の変換は行列の積で合成できる。右から順に適用される点に注意。
変換の合成
スケール → 回転 → 平行移動 の順に適用する場合:
$$M = T \cdot R \cdot S$$ $$\mathbf{p}' = M \mathbf{p} = T(R(S \mathbf{p}))$$重要: 行列の積は非可換($AB \neq BA$)。変換の順序が異なると結果も異なる。
3D変換行列
3D空間での変換は $4 \times 4$ 行列で表現される。
3D平行移動
$$T = \begin{pmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 & 1 & t_z \\ 0 & 0 & 0 & 1 \end{pmatrix}$$3D回転行列
X軸周り(ロール):
$$R_x(\theta) = \begin{pmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos\theta & -\sin\theta & 0 \\ 0 & \sin\theta & \cos\theta & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}$$Y軸周り(ピッチ):
$$R_y(\theta) = \begin{pmatrix} \cos\theta & 0 & \sin\theta & 0 \\ 0 & 1 & 0 & 0 \\ -\sin\theta & 0 & \cos\theta & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}$$Z軸周り(ヨー):
$$R_z(\theta) = \begin{pmatrix} \cos\theta & -\sin\theta & 0 & 0 \\ \sin\theta & \cos\theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix}$$
Pitch(Y軸)
Roll(X軸)
Yaw(Z軸)
注意(オイラー角の限界): Roll・Pitch・Yaw は航空宇宙分野由来の呼び方で、CV/CG では単に X 軸・Y 軸・Z 軸回りの回転と呼ぶことも多い。3つの回転を合成する順序(例: $R = R_z(\text{yaw})\,R_y(\text{pitch})\,R_x(\text{roll})$)は慣例により異なる。またオイラー角による表現にはジンバルロック(特定の姿勢で回転の自由度が1つ失われる現象)があり、滑らかな補間や特異点回避が必要な場面ではクォータニオンや回転行列を直接扱うことが多い。
剛体変換と相似変換
剛体変換(Rigid Transformation / Euclidean)
回転+平行移動。形状と大きさを保存(6自由度: 3回転 + 3並進)
$$\begin{pmatrix} \mathbf{R} & \mathbf{t} \\ \mathbf{0}^T & 1 \end{pmatrix}, \quad \mathbf{R} \in SO(3), \quad \mathbf{t} \in \mathbb{R}^3$$相似変換(Similarity Transformation)
剛体変換+等方スケール。角度を保存(3Dで7自由度: 剛体6 + 等方スケール1)
$$\begin{pmatrix} s\mathbf{R} & \mathbf{t} \\ \mathbf{0}^T & 1 \end{pmatrix}, \quad s > 0$$アフィン変換(Affine Transformation)
平行性を保存(3Dで12自由度: 線形部 $\mathbf{A}$ の9 + 並進 $\mathbf{t}$ の3)
$$\begin{pmatrix} \mathbf{A} & \mathbf{t} \\ \mathbf{0}^T & 1 \end{pmatrix}, \quad \mathbf{A} \in \mathbb{R}^{3 \times 3}$$コード例(Python)
import numpy as np
def translation_matrix_2d(tx, ty):
"""2D平行移動行列"""
return np.array([
[1, 0, tx],
[0, 1, ty],
[0, 0, 1]
])
def rotation_matrix_2d(theta):
"""2D回転行列(ラジアン)"""
c, s = np.cos(theta), np.sin(theta)
return np.array([
[c, -s, 0],
[s, c, 0],
[0, 0, 1]
])
def scale_matrix_2d(sx, sy):
"""2Dスケール行列"""
return np.array([
[sx, 0, 0],
[0, sy, 0],
[0, 0, 1]
])
# 変換の合成: スケール → 回転 → 平行移動
S = scale_matrix_2d(2, 2)
R = rotation_matrix_2d(np.radians(45))
T = translation_matrix_2d(100, 50)
M = T @ R @ S # 右から順に適用
# 点の変換
p = np.array([10, 20, 1]) # 同次座標
p_transformed = M @ p
print(f"変換後: ({p_transformed[0]:.1f}, {p_transformed[1]:.1f})")
# OpenCVでの使用
import cv2
# img: 入力画像(cv2.imread で読み込む)
height, width = img.shape[:2]
# 2x3のアフィン行列に変換
M_cv = M[:2, :]
img_transformed = cv2.warpAffine(img, M_cv, (width, height))
まとめ
- 同次座標により、平行移動を含む変換を行列の積で表現
- 2D変換は $3 \times 3$ 行列、3D変換は $4 \times 4$ 行列
- 変換の合成は行列の積(右から順に適用)
- 剛体変換は回転+平行移動(形状保存)
- カメラの外部パラメータは剛体変換で表される