?引言
以三維投影介紹大多比較分散,不少小伙伴再面對諸多的坐標系轉換中容易弄混,特別是再寫代碼的時候可能搞錯,所有這篇文章幫大家完整的梳理3D視覺中的投影變換的全流程,一文弄清楚這個過程,幫助大家搞清坐標系轉換。
想象你正站在一個空房間里,腳下是地板,面前是墻。
- ?在紙上畫一個直角坐標系,標記原點為墻角(左下角),X向右,Y向前,Z向上。
- 相機位于[2, 4, 1.5]位置。
- ?在[4, 2, 1.5]處點一個點——這是我們要投影到相機圖像的三維點。
接下來,我會帶你一步步推導:?這個點如何變成相機照片中的一個像素。
第一章:從3D到2D的四大步驟
在計算機視覺和三維重建領域,將三維空間中的點映射到二維圖像平面是相機成像過程的核心。這個過程可以抽象為一個投影模型(Projection Model),它描述了如何將真實世界中的點通過一系列變換最終落在圖像傳感器上,成為我們看到的像素點。
整個過程可以分為四個關鍵步驟:
步驟一:世界坐標系 → 相機坐標系(剛體變換)
三維空間中的點通常以**世界坐標系(World Coordinate System)表示,記為,為了模擬相機的視角,我們需要將其轉換到相機坐標系(Camera Coordinate System)中。這一步是通過剛體變換(Rigid Transformation)**完成的,包括旋轉和平移操作:
投影公式如下:
中:
R
?是旋轉矩陣(3x3);t
?是平移向量(3x1);P_c
?是點在相機坐標系下的表示。
也可以用齊次坐標寫成一個變換矩陣:
步驟二:相機坐標系 → 歸一化圖像坐標系(透視投影)
接下來,我們將三維相機坐標系中的點通過透視投影(Perspective Projection)映射到二維的歸一化圖像坐標系中。這一步模擬了光線穿過針孔相機的過程。
設相機焦距為 f
,則投影公式如下:
這里的 (x, y)
表示歸一化圖像平面上的坐標,與實際像素無關。
步驟三:歸一化圖像坐標 → 像素坐標(內參變換)
為了得到最終的像素坐標,還需要考慮相機的內部參數,例如像素尺寸、主點偏移等。這些信息被包含在一個稱為相機內參矩陣(Intrinsic Matrix)的矩陣中:
其中:
f_x
,?f_y
?是以像素為單位的焦距;(c_x, c_y)
?是圖像主點(principal point),通常是圖像中心。
于是,歸一化坐標 (x, y)
到像素坐標 (u, v)
的變換為:
步驟四:像素坐標 → 圖像坐標系(圖像裁剪與翻轉)
最后一步是將像素坐標 (u, v)
映射到圖像的實際坐標系統中。通常圖像坐標原點位于左上角,而之前計算的 (u, v)
是浮點數,需要進行取整或插值處理。
此外,還要注意圖像可能會有縮放、裁剪、翻轉等后處理操作,這會影響最終的像素位置。
第二章:從世界坐標系到相機坐標系的剛體變換詳解
——坐標系定義、旋轉矩陣與平移矩陣的構建全過程
一、相機坐標系(CCS)的定義與物理意義
在計算機視覺中,相機坐標系(Camera Coordinate System, CCS) 是以相機為中心建立的右手坐標系。其定義如下:
- 原點(O?):位于相機光心(光學中心);
- Z軸(Z?):指向相機拍攝方向(即光軸方向);
- X軸(X?):指向圖像右側;
- Y軸(Y?):指向圖像下方,滿足右手系規則:X? × Y? = Z?。
二、世界坐標系(WCS)與相機坐標系的關系
我們考慮一個具體例子:
- 世界坐標系(WCS):
- 原點位于房間角落;
- X軸向右;
- Y軸向前;
- Z軸向上。
- 相機位置:
- 在 WCS 中的位置為:t =[2, 4, 1.5]?;
- 表示相機在房間中位于 x=2, y=4, z=1.5 的位置(單位:米);
- 相機朝向:
- 光軸(Z?)指向 WCS 的??Y 方向;
- 即相機背對世界前方(Y軸正方向),面朝后方。
這個設定常見于室內監控場景中,例如相機安裝在房間中央,面朝墻壁。
三、剛體變換公式
將一個三維點 P_w 從世界坐標系轉換到相機坐標系的過程如下:
四、分步構建變換過程
步驟 1:平移變換(Translation)
目標:將世界坐標點“移動”到以相機為原點的相對位置。
公式:
示例:
P_w = np.array([4, 2, 1.5]) # 修改后的世界點位置
t = np.array([2, 4, 1.5])
則:
?
物理意義:該點相對于相機的位置是向右 2 米、向前 -2 米、下方 0 米(相機沒有旋轉前)。
步驟 2:旋轉變換(Rotation)
目標:將相對向量 PrelPrel? 轉換到相機坐標系的方向中去。
(1)確定相機坐標系的三個單位向量
根據設定,相機光軸指向 WCS 的 ?Y 方向:
- Z?(光軸方向):[0, ?1, 0]
- X?(右側方向):選擇與 Z? 垂直的世界 X 軸方向:[1, 0, 0]
- Y?(下方方向):由叉積得到:Y? = Z? × X? = [0, 0, ?1]
這三個向量構成了相機坐標系的基底。
(2)構造旋轉矩陣 R
將上述三個單位向量作為列向量,組成旋轉矩陣:
?
解釋每一列含義:
- 第1列:世界 X 軸在 CCS 中的方向;
- 第2列:世界 Y 軸在 CCS 中的方向;
- 第3列:世界 Z 軸在 CCS 中的方向。
五、完整變換計算
繼續使用上面的例子:
import numpy as npP_w = np.array([2, 3, 0.5])
t = np.array([1, 2, 1.5])R = np.array([[1, 0, 0],[0, 0, -1],[0, -1, 0]
])P_rel = P_w - t
P_c = R @ P_rel
結果:
解釋:
- Xc = 2:點在相機右側 2 米;
- Yc = 0:點在相機下方 0 米(Y軸向下);
- Zc = 2:點在相機前方2米。
六、為什么是 ?Y 方向而不是 +Y?
這是由以下兩個因素決定的:
1. 相機坐標系的定義要求
為了保持右手系結構(X? × Y? = Z?),如果 X? 對齊世界 X 軸、Y? 對齊世界 Z 軸,則 Z? 必須指向世界 ?Y。
2. 圖像坐標系的一致性
在圖像坐標系中,通常定義:
- u 軸(列)向右;
- v 軸(行)向下;
這與相機坐標系中 Y? 向下一致,因此相機朝向 ?Y 可以自然地映射到圖像平面。
第三章:旋轉矩陣 R 的數學構建與相機坐標系的構建原理詳解
在第二章中,我們已經介紹了如何從世界坐標系(WCS)到相機坐標系(CCS)進行剛體變換,并通過一個具體示例演示了平移和旋轉的具體計算過程。本章將深入講解旋轉矩陣 R 的數學構造方法、其物理意義以及相機坐標系構建背后的幾何邏輯。
一、旋轉矩陣 R 的數學定義與性質
1.1 基本定義
旋轉矩陣是一個 3×3 的正交矩陣,滿足以下兩個重要性質:
- 正交性
?
- 行列式為 1:
這兩個條件保證了旋轉操作只改變方向而不改變長度或形狀。
1.2 旋轉矩陣的列向量解釋
設世界坐標系中的三個標準基向量為:
而相機坐標系的三個單位基向量為:
?則旋轉矩陣 R 可以表示為:
換句話說,旋轉矩陣的每一列是相機坐標系在世界坐標系下的方向向量。
? 通俗理解:旋轉矩陣 R 描述了“世界坐標軸在相機坐標系中的方向”。
1.3 構造 R 的通用步驟
步驟 1:確定相機光軸方向(Z?)
這是最重要的一步,決定了相機的拍攝方向。例如:
- 若希望相機朝向世界 ?Y 方向,則:
- 若希望相機朝向世界 +Z 方向,則:
步驟 2:選擇 X? 向量(與 Z? 垂直)
通常優先對齊世界坐標系中的某個軸(如 X 軸),但必須確保與 Z? 垂直:
- 設定:
- 或者使用 Gram-Schmidt 正交化法調整方向。
步驟 3:由叉積得到 Y? 向量
根據右手系規則
這確保了整個坐標系保持右手結構。
步驟 4:歸一化所有向量
確保每個向量都是單位向量:
步驟 5:構造旋轉矩陣 R
將三個單位向量作為列向量排列成矩陣:
二、相機坐標系為什么這么構建?
相機坐標系的設計并不是隨意的,而是為了滿足以下幾個關鍵目標:
2.1 圖像坐標的自然映射
圖像坐標系通常定義如下:
- u 軸(列方向)→ 相機右側;
- v 軸(行方向)→ 相機下方;
- 原點位于圖像左上角。
因此,相機坐標系的 X? 向右、Y? 向下可以自然地映射到圖像坐標。
2.2 保持右手系結構
三維空間建模和渲染中廣泛使用右手系,這樣便于統一處理旋轉、投影等操作。如果 Y? 指向上方,則會導致圖像坐標的行方向與 Y? 方向相反,增加轉換復雜度。
2.3 簡化后續投影模型
相機坐標系的 Z? 軸指向拍攝方向,使得后續的透視投影公式簡潔且直觀:
其中 f?是焦距。
三、構建相機坐標系的完整數學流程示例
假設:
- 相機位置:t=[1,2,1.5]T,表示相機在世界坐標系中的位置為 x=1, y=2, z=1.5。
- 相機光軸指向:世界 ?Y 方向(即面朝房間后方)。
步驟 1:確定 Z?
Z? 是相機坐標系的 Z 軸方向,表示相機的拍攝方向。在這個例子中
? 相機朝向世界 ?Y 方向,因此:
?
這是單位向量,無需歸一化。
📌 注意:Z? 并不是相機的位置向量,而是指向相機“看的方向”。在這里,“看向 ?Y”意味著相機背對世界 +Y 軸。
步驟 2:確定 X?
X? 應該與 Z? 垂直,并且盡可能與世界坐標系的某個軸對齊(通常選 X 或 Y),以便簡化后續計算和理解。
在這個例子中,我們可以選擇:
因為:
- 它是單位向量;
- 與 Z? 垂直(點積為零):
步驟 3:由叉積得 Y?
步驟 4:歸一化各向量(此處已是單位向量)
步驟 5:構造旋轉矩陣 R
第四章:相機坐標系 → 歸一化圖像坐標系(透視投影)
在第三章中,我們完成了從世界坐標系到相機坐標系的變換,即通過旋轉和平移將一個點 PwPw? 轉換為相機坐標 Pc=[Xc,Yc,Zc]TPc?=[Xc?,Yc?,Zc?]T。
本章將繼續這一流程,介紹如何將三維空間中的點 投影到二維歸一化圖像平面,完成:
從 相機坐標系(CCS) 到 歸一化圖像坐標系(Normalized Image Coordinates) 的變換。
一、什么是歸一化圖像坐標?
歸一化圖像坐標是針孔相機模型下的中間圖像坐標系統。它是一個無單位的二維坐標系,原點位于圖像中心,Z 軸與相機光軸重合。
它的定義如下:
設相機焦距為 f,則對于相機坐標系下的點 Pc=[Xc,Yc,Zc]T,其對應的歸一化圖像坐標 (x,y)為:
這個過程稱為透視投影(Perspective Projection),它模擬了真實相機成像的基本原理 —— 遠小近大。
? 注意:這里的 (x,y)是歸一化的坐標,還沒有考慮圖像分辨率、主點偏移等參數。這是后續章節會討論的內容。
二、沿用之前的示例進行推導
示例設定:
- 相機位置:
- 旋轉矩陣:
- 焦距: f=1
該旋轉矩陣表示相機的光軸指向世界 ?Z 方向(向下看),Y? 向下,X? 向右。
舉例說明:選取幾個3D點進行投影
我們在相機坐標系中選取了五個點,它們分別是:
點編號 | X_c | Y_c | Z_c |
---|---|---|---|
P0 | 0.5 | 0.5 | 1.5 |
P1 | -0.5 | 0.3 | 2 |
P2 | 0.2 | -0.4 | 3 |
P3 | 0 | 0 | 5 |
P4 | 0.8 | 0.8 | 0.8 |
這些點都位于相機前方(Z? > 0),因此可以被正確投影到圖像平面上。
投影計算示例(以 P0 為例)
對于點 P0:
Xc=0.5,Yc=0.5,Zc=1.5
代入公式得:
??
所以歸一化圖像坐標為:?
類似地,我們可以計算其他點的歸一化圖像坐標。
投影結果匯總(部分)
點編號 | X_c | Y_c | Z_c | x | y |
---|---|---|---|---|---|
P0 | 0.5 | 0.5 | 1.5 | 0.333 | 0.333 |
P1 | -0.5 | 0.3 | 2 | -0.25 | 0.15 |
P2 | 0.2 | -0.4 | 3 | 0.067 | -0.133 |
P3 | 0 | 0 | 5 | 0 | 0 |
P4 | 0.8 | 0.8 | 0.8 | 1.0 | 1.0 |
可以看到,隨著 Z 值變小(靠近相機),投影點變得更大;而遠離相機的點(如 P3)則更接近圖像中心。
三、物理意義解釋
參數 | 數值范圍 | 物理意義 |
---|---|---|
Z_c > 0 | 所有點都在相機前方 | 可以被正常投影到圖像上 |
Z_c < 0 | 未出現在示例中 | 表示點在相機背后,不會出現在圖像中 |
Z_c = 0 | 未出現 | 表示點在相機正前方無窮遠處,無法投影 |
x/y 接近 0 | 如 P3 | 表示點位于圖像中心附近 |
x/y 絕對值較大 | 如 P4 | 表示點靠近圖像邊緣或超出視野 |
四、注意事項
- 當?Zc=0時,表示點在相機正前方無窮遠處,無法投影;
- 當?Zc<0時,點在相機背后,不會出現在圖像中;
- 實際圖像坐標還需結合內參矩陣(焦距、主點、畸變)進一步映射;
- 本章只討論理想情況下的歸一化圖像坐標。
第五章:歸一化圖像坐標 → 像素坐標(內參變換)
在第四章中,我們完成了從相機坐標系到歸一化圖像坐標系的投影過程,得到了一個無單位的二維坐標 (x,y)。
本章將介紹如何將這個歸一化圖像坐標進一步轉換為像素坐標 (u,v),這一步是通過引入**相機內參矩陣(Intrinsic Camera Matrix)**來實現的。
一、什么是相機內參矩陣?
相機內參矩陣 KK 是一個 3×3的上三角矩陣,它包含了以下物理參數:
參數 | 物理意義 |
---|---|
fx,fy | 焦距(以像素為單位),通常?fx=fy=f,但在非正方形像素時可以不同 |
cx,cy | 主點(Principal Point),即圖像中心在像素坐標中的位置(通常為圖像分辨率的一半) |
s | 圖像的斜切因子(Skew Factor),表示像素是否正交排列,默認為 0 |
?完整的相機內參矩陣形式如下:
實際使用中,大多數情況下 s=0s=0,所以常簡化為:
二、歸一化圖像坐標 → 像素坐標的公式
設歸一化圖像坐標為 (x,y),則對應的像素坐標 (u,v)可由下式計算:
展開后得到:
這個過程稱為 “內參變換”,它不依賴于相機的位置或朝向,只與相機本身的成像特性有關。
三、延續最開始的例子進行推導
示例設定:
- 相機位置:
- 點的世界坐標:
- 旋轉矩陣(沿用之前例子):
步驟 1:計算相機坐標
先求相對坐標:
再應用旋轉:
所以:
步驟 2:透視投影(歸一化圖像坐標)
焦距設為 f=200
所以歸一化圖像坐標為:
步驟 3:假設一個內參矩陣
設相機分辨率為 640×480,則主點為:
構建內參矩陣:
步驟 4:計算像素坐標
代入公式:
所以最終像素坐標為:
? 注意事項
- 若像素坐標超出圖像范圍則該點不在圖像視野范圍內;
- 調整焦距?ff或相機姿態可以改變點是否出現在圖像中;
- 如果?Zc<0,即使計算出像素坐標也無法顯示,因為該點在相機背后。
四、參數總結與物理意義
參數 | 單位 | 物理意義 |
---|---|---|
ff(焦距) | 像素 | 控制圖像放大倍數,越大視角越小,物體顯得更大 |
cx,cycx?,cy? | 像素 | 主點偏移量,通常是圖像中心,控制圖像原點位置 |
Xc,Yc,ZcXc?,Yc?,Zc? | 米或任意單位 | 點在相機坐標系下的三維坐標 |
x,y | 無量綱 | 歸一化圖像坐標,反映點相對于圖像中心的位置 |
u,v | 像素 | 最終圖像坐標,可用于繪制或識別目標位置 |
第六章:更復雜的案例 —— 相機與世界坐標系存在夾角(完整流程詳解)
在本章中,我們將以一個更復雜的案例為例,詳細推導從世界坐標 → 像素坐標的完整變換流程。這個案例的特點是:
? 相機姿態不平行于世界坐標軸 —— 即相機與世界坐標系之間存在夾角。
🎯 示例設定
1. 點的世界坐標:
P_w = np.array([4, 2, 1.5])
表示某個物體在世界坐標中的位置。
2. 相機參數
(1)相機位置(平移向量):
t = np.array([2, 4, 1.5])
相機位于世界坐標系中的 (2, 4, 1.5)
位置。
(2)原始旋轉矩陣(將世界坐標轉換為相機坐標):
R_original = np.array([[1, 0, 0],[0, 0, -1],[0, -1, 0]
])
3. 新增旋轉:繞相機 Y? 軸向右旋轉 30°
我們希望讓相機繞其自身的 Z軸偏轉 30°(X->Y),從而看到右側空間。
在三維空間中,繞 Y 軸 旋轉一個角度 θ 的標準旋轉矩陣為:
物理意義:
- 表示繞?Y 軸旋轉角度?θ;
- 相當于“轉頭”動作:向左或向右看;
- X 和 Z 坐標發生變化,Y 不變
繞 X 軸旋轉(Pitch)一個角度 θ 的標準旋轉矩陣為:
物理意義:
- 表示繞?X 軸旋轉角度?θ;
- 相當于“點頭”動作:向上或向下看;
- Y 和 Z 坐標發生變化,X 不變。
繞 Z 軸旋轉(Roll)一個角度 θ 的標準旋轉矩陣為:
物理意義:
- 表示繞?Y 軸旋轉角度?θ;
- 相當于“轉頭”動作:向左或向右看;
- X 和 Z 坐標發生變化,Y 不變。
構造繞相機 z? 軸的旋轉矩陣:
theta = np.radians(30)
R_y_camera = np.array([[np.cos(theta), -np.sin(theta), 0],[np.sin(theta), np.cos(theta), 0],[0, 0, 1]
])
代入數值近似為:
array([[ 0.8660254, -0.5 , 0. ],[ 0.5 , 0.8660254, 0. ],[ 0. , 0. , 1. ]])
4. 新的旋轉矩陣(合成后的相機姿態)
我們將新增的局部旋轉應用到原旋轉上,得到新的整體旋轉矩陣,實際應該過程中,會很容易忽略掉原始相機坐標系R_original從而導致出錯:
R_new = R_y_camera @ R_original
代入計算得:
array([[ 0.8660254, 0. , 0.5 ],[ 0.5 , 0. , -0.8660254],[ 0. , -1. , 0. ]])
🔁 完整變換流程(公式代碼格式)
? 步驟 1:世界坐標 → 相機坐標
?代入數值:
P_rel = P_w - t
P_c = R_new @ P_rel
結果為:
p_c = array([1.73205081, 1. , 2. ])
即:
X_w = np.array([1, 0, 0]) # 世界X軸
Y_w = np.array([0, 1, 0]) # 世界Y軸
Z_w = np.array([0, 0, 1]) # 世界Z軸t = np.array([2, 4, 1.5]) # 新相機位置(Y值更大,更靠后)
P_w = np.array([4, 2, 1.5])
# 旋轉矩陣(光軸 Z_c = -Y_w)
R = np.array([[1, 0,0],[0, 0, -1],[0, -1, 0]
])theta = np.radians(30)
R_z = np.array([[np.cos(theta), -np.sin(theta), 0],[np.sin(theta), np.cos(theta), 0],[0, 0, 1]
])R = R_z @ R# 轉換到相機坐標系
P_c = R @ (P_w - t)
? 步驟 2:透視投影(相機坐標 → 歸一化圖像坐標)
設焦距 f=200:
歸一化圖像坐標為:
(x, y) = (718, 0)
? 步驟 3:應用內參矩陣(歸一化圖像坐標 → 像素坐標)
設相機分辨率為 640×480,則主點為:
內參矩陣為:
?像素坐標為:
?最終像素坐標為: