相機模型和變形
這一節里的函數都使用攝像機模型,這就是說,一幅視圖是通過透視變換將三維空間中的點投影到圖像平面。投影公式如下:

或者

這里(X, Y, Z)是一個點的世界坐標,(u, v)是點投影在圖像平面的坐標,以像素為單位。A被稱作攝像機矩陣,或者內參數矩陣。(cx, cy)是基准點(通常在圖像的中心),fx, fy是以像素為單位的焦距。所以如果因為某些因素對來自於攝像機的一幅圖像升采樣或者降采樣,所有這些參數(fx, fy, cx和cy)都將被縮放(乘或者除)同樣的尺度。內參數矩陣不依賴場景的視圖,一旦計算出,可以被重復使用(只要焦距固定)。旋轉-平移矩陣[R|t]被稱作外參數矩陣,它用來描述相機相對於一個固定場景的運動,或者相反,物體圍繞相機的的剛性運動。也就是[R|t]將點(X, Y, Z)的坐標變換到某個坐標系,這個坐標系相對於攝像機來說是固定不變的。上面的變換等價與下面的形式(z≠0):

x' = x / z
y' = y / z

![]()
真正的鏡頭通常有一些形變,主要的變形為徑向形變,也會有輕微的切向形變。所以上面的模型可以擴展為:

x' = x / z
y' = y / z


這里 r2 = x'2 + y'2

k1和k2是徑向形變系數,p1和p1是切向形變系數。OpenCV中沒有考慮高階系數。形變系數跟拍攝的場景無關,因此它們是內參數,而且與拍攝圖像的分辨率無關。
后面的函數使用上面提到的模型來做如下事情:
- 給定內參數和外參數,投影三維點到圖像平面。
- 給定內參數、幾個三維點坐標和其對應的圖像坐標,來計算外參數。
- 根據已知的定標模式,從幾個角度(每個角度都有幾個對應好的3D-2D點對)的照片來計算相機的外參數和內參數。
照相機定標
ProjectPoints2
投影三維點到圖像平面
1 void cvProjectPoints2( const CvMat* object_points, const CvMat* rotation_vector, 2 const CvMat* translation_vector, const CvMat* intrinsic_matrix, 3 const CvMat* distortion_coeffs, CvMat* image_points, 4 CvMat* dpdrot=NULL, CvMat* dpdt=NULL, CvMat* dpdf=NULL, 5 CvMat* dpdc=NULL, CvMat* dpddist=NULL );
- object_points
- 物體點的坐標,為3xN或者Nx3的矩陣,這兒N是視圖中的所有所有點的數目。
- rotation_vector
- 旋轉向量,1x3或者3x1。
- translation_vector
- 平移向量,1x3或者3x1。
- intrinsic_matrix
-
攝像機內參數矩陣A:
- distortion_coeffs
- 形變參數向量,4x1或者1x4,為[k1,k2,p1,p2]。如果是NULL,所有形變系數都設為0。
- image_points
- 輸出數組,存儲圖像點坐標。大小為2xN或者Nx2,這兒N是視圖中的所有點的數目。
- dpdrot
- 可選參數,關於旋轉向量部分的圖像上點的導數,Nx3矩陣。
- dpdt
- 可選參數,關於平移向量部分的圖像上點的導數,Nx3矩陣。
- dpdf
- 可選參數,關於fx和fy的圖像上點的導數,Nx2矩陣。
- dpdc
- 可選參數,關於cx和cy的圖像上點的導數,Nx2矩陣。
- dpddist
- 可選參數,關於形變系數的圖像上點的導數,Nx4矩陣。
函數cvProjectPoints2通過給定的內參數和外參數計算三維點投影到二維圖像平面上的坐標。另外,這個函數可以計算關於投影參數的圖像點偏導數的雅可比矩陣。雅可比矩陣可以用在cvCalibrateCamera2和cvFindExtrinsicCameraParams2函數的全局優化中。這個函數也可以用來計算內參數和外參數的反投影誤差。 注意,將內參數和(或)外參數設置為特定值,這個函數可以用來計算外變換(或內變換)。
FindHomography
計算兩個平面之間的透視變換
1 void cvFindHomography( const CvMat* src_points, 2 const CvMat* dst_points, 3 CvMat* homography );
- src_points
- 原始平面的點坐標,大小為2xN,Nx2,3xN或者 Nx3矩陣(后兩個表示齊次坐標),這兒N表示點的數目。
- dst_points
- 目標平面的點坐標大小為2xN,Nx2,3xN或者 Nx3矩陣(后兩個表示齊次坐標)。
- homography
- 輸出的3x3的homography矩陣。
函數cvFindHomography計算源平面和目標平面之間的透視變換

使得反投影錯誤最小:

這個函數可以用來計算初始的內參數和外參數矩陣。由於Homography矩陣的尺度可變,所以它被規一化使得h33 = 1
CalibrateCamera2
利用定標來計算攝像機的內參數和外參數
1 void cvCalibrateCamera2( const CvMat* object_points, const CvMat* image_points, 2 const CvMat* point_counts, CvSize image_size, 3 CvMat* intrinsic_matrix, CvMat* distortion_coeffs, 4 CvMat* rotation_vectors=NULL, 5 CvMat* translation_vectors=NULL, 6 int flags=0 );
- object_points
- 定標點的世界坐標,為3xN或者Nx3的矩陣,這里N是所有視圖中點的總數。
- image_points
- 定標點的圖像坐標,為2xN或者Nx2的矩陣,這里N是所有視圖中點的總數。
- point_counts
- 向量,指定不同視圖里點的數目,1xM或者Mx1向量,M是視圖數目。
- image_size
- 圖像大小,只用在初始化內參數時。
-
intrinsic_matrix輸出內參矩陣(A)
,如果指定CV_CALIB_USE_INTRINSIC_GUESS和(或)CV_CALIB_FIX_ASPECT_RATION,fx、 fy、 cx和cy部分或者全部必須被初始化。
- distortion_coeffs
- 輸出大小為4x1或者1x4的向量,里面為形變參數[k1, k2, p1, p2]。
- rotation_vectors
- 輸出大小為3xM或者Mx3的矩陣,里面為旋轉向量(旋轉矩陣的緊湊表示方式,具體參考函數cvRodrigues2)
- translation_vectors
- 輸出大小為3xM或Mx3的矩陣,里面為平移向量。
- flags
-
不同的標志,可以是0,或者下面值的組合:
- CV_CALIB_USE_INTRINSIC_GUESS - 內參數矩陣包含fx,fy,cx和cy的初始值。否則,(cx, cy)被初始化到圖像中心(這兒用到圖像大小),焦距用最小平方差方式計算得到。注意,如果內部參數已知,沒有必要使用這個函數,使用cvFindExtrinsicCameraParams2則可。
- CV_CALIB_FIX_PRINCIPAL_POINT - 主點在全局優化過程中不變,一直在中心位置或者在其他指定的位置(當CV_CALIB_USE_INTRINSIC_GUESS設置的時候)。
- CV_CALIB_FIX_ASPECT_RATIO - 優化過程中認為fx和fy中只有一個獨立變量,保持比例fx/fy不變,fx/fy的值跟內參數矩陣初始化時的值一樣。在這種情況下, (fx, fy)的實際初始值或者從輸入內存矩陣中讀取(當CV_CALIB_USE_INTRINSIC_GUESS被指定時),或者采用估計值(后者情況中fx和fy可能被設置為任意值,只有比值被使用)。
- CV_CALIB_ZERO_TANGENT_DIST – 切向形變參數(p1, p2)被設置為0,其值在優化過程中保持為0。
函數cvCalibrateCamera2從每個視圖中估計相機的內參數和外參數。3維物體上的點和它們對應的在每個視圖的2維投影必須被指定。這些可以通過使用一個已知幾何形狀且具有容易檢測的特征點的物體來實現。這樣的一個物體被稱作定標設備或者定標模式,OpenCV有內建的把棋盤當作定標設備方法(參考cvFindChessboardCorners)。目前,傳入初始化的內參數(當CV_CALIB_USE_INTRINSIC_GUESS不被設置時)只支持平面定標設備(物體點的Z坐標必須為全0或者全1)。不過3維定標設備依然可以用在提供初始內參數矩陣情況。在內參數和外參數矩陣的初始值都計算出之后,它們會被優化用來減小反投影誤差(圖像上的實際坐標跟cvProjectPoints2計算出的圖像坐標的差的平方和)。
FindExtrinsicCameraParams2
計算指定視圖的攝像機外參數
1 void cvFindExtrinsicCameraParams2( const CvMat* object_points, 2 const CvMat* image_points, 3 const CvMat* intrinsic_matrix, 4 const CvMat* distortion_coeffs, 5 CvMat* rotation_vector, 6 CvMat* translation_vector );
- object_points
- 定標點的坐標,為3xN或者Nx3的矩陣,這里N是視圖中的個數。
- image_points
- 定標點在圖像內的坐標,為2xN或者Nx2的矩陣,這里N是視圖中的個數。
-
intrinsic_matrix內參矩陣(A)
。
- distortion_coeffs
- 大小為4x1或者1x4的向量,里面為形變參數[k1,k2,p1,p2]。如果是NULL,所有的形變系數都為0。
- rotation_vector
- 輸出大小為3x1或者1x3的矩陣,里面為旋轉向量(旋轉矩陣的緊湊表示方式,具體參考函數cvRodrigues2)。
- translation_vector
- 大小為3x1或1x3的矩陣,里面為平移向量。
函數cvFindExtrinsicCameraParams2使用已知的內參數和某個視圖的外參數來估計相機的外參數。3維物體上的點坐標和相應的2維投影必須被指定。這個函數也可以用來最小化反投影誤差。
Rodrigues2
進行旋轉矩陣和旋轉向量間的轉換
int cvRodrigues2( const CvMat* src, CvMat* dst, CvMat* jacobian=0 );
- src
- 輸入的旋轉向量(3x1或者1x3)或者旋轉矩陣(3x3)。
- dst
- 輸出的旋轉矩陣(3x3)或者旋轉向量(3x1或者1x3)
- jacobian
- 可選的輸出雅可比矩陣(3x9或者9x3),關於輸入部分的輸出數組的偏導數。
函數轉換旋轉向量到旋轉矩陣,或者相反。旋轉向量是旋轉矩陣的緊湊表示形式。旋轉向量的方向是旋轉軸,向量的長度是圍繞旋轉軸的旋轉角。旋轉矩陣R,與其對應的旋轉向量r,通過下面公式轉換:



反變換也可以很容易的通過如下公式實現:

旋轉向量是只有3個自由度的旋轉矩陣一個方便的表示,這種表示方式被用在函數cvFindExtrinsicCameraParams2和cvCalibrateCamera2內部的全局最優化中。
Undistort2
校正圖像因相機鏡頭引起的變形
1 void cvUndistort2( const CvArr* src, CvArr* dst, 2 const CvMat* intrinsic_matrix, 3 const CvMat* distortion_coeffs );
- src
- 原始圖像(已經變形的圖像)。只能變換32fC1的圖像。
- dst
- 結果圖像(已經校正的圖像)。
-
intrinsic_matrix相機內參數矩陣,格式為
。
- distortion_coeffs
- 四個變形系數組成的向量,大小為4x1或者1x4,格式為[k1,k2,p1,p2]。
函數cvUndistort2對圖像進行變換來抵消徑向和切向鏡頭變形。相機參數和變形參數可以通過函數cvCalibrateCamera2取得。使用本節開始時提到的公式,對每個輸出圖像像素計算其在輸入圖像中的位置,然后輸出圖像的像素值通過雙線性插值來計算。如果圖像得分辨率跟定標時用得圖像分辨率不一樣,fx、fy、cx和cy需要相應調整,因為形變並沒有變化。
InitUndistortMap
計算形變和非形變圖像的對應(map)
1 void cvInitUndistortMap( const CvMat* intrinsic_matrix, 2 const CvMat* distortion_coeffs, 3 CvArr* mapx, CvArr* mapy );
- intrinsic_matrix
- 攝像機內參數矩陣(A) [fx 0 cx; 0 fy cy; 0 0 1].
- distortion_coeffs
- 形變系數向量[k1, k2, p1, p2],大小為4x1或者1x4。
- mapx
- x坐標的對應矩陣。
- mapy
- y坐標的對應矩陣。
函數cvInitUndistortMap預先計算非形變對應-正確圖像的每個像素在形變圖像里的坐標。這個對應可以傳遞給cvRemap函數(跟輸入和輸出圖像一起)。
FindChessboardCorners
尋找棋盤圖的內角點位置
1 int cvFindChessboardCorners( const void* image, CvSize pattern_size, 2 CvPoint2D32f* corners, int* corner_count=NULL, 3 int flags=CV_CALIB_CB_ADAPTIVE_THRESH );
- image
- 輸入的棋盤圖,必須是8位的灰度或者彩色圖像。
- pattern_size
- 棋盤圖中每行和每列角點的個數。
- corners
- 檢測到的角點
- corner_count
- 輸出,角點的個數。如果不是NULL,函數將檢測到的角點的個數存儲於此變量。
- flags
-
各種操作標志,可以是0或者下面值的組合:
- CV_CALIB_CB_ADAPTIVE_THRESH - 使用自適應閾值(通過平均圖像亮度計算得到)將圖像轉換為黑白圖,而不是一個固定的閾值。
- CV_CALIB_CB_NORMALIZE_IMAGE - 在利用固定閾值或者自適應的閾值進行二值化之前,先使用cvNormalizeHist來均衡化圖像亮度。
- CV_CALIB_CB_FILTER_QUADS - 使用其他的准則(如輪廓面積,周長,方形形狀)來去除在輪廓檢測階段檢測到的錯誤方塊。
函數cvFindChessboardCorners試圖確定輸入圖像是否是棋盤模式,並確定角點的位置。如果所有角點都被檢測到且它們都被以一定順序排布(一行一行地,每行從左到右),函數返回非零值,否則在函數不能發現所有角點或者記錄它們地情況下,函數返回0。例如一個正常地棋盤圖右8x8個方塊和7x7個內角點,內角點是黑色方塊相互聯通地位置。這個函數檢測到地坐標只是一個大約地值,如果要精確地確定它們的位置,可以使用函數cvFindCornerSubPix。
DrawChessBoardCorners
繪制檢測到的棋盤角點
1 void cvDrawChessboardCorners( CvArr* image, CvSize pattern_size, 2 CvPoint2D32f* corners, int count, 3 int pattern_was_found );
- image
- 結果圖像,必須是8位彩色圖像。
- pattern_size
- 每行和每列地內角點數目。
- corners
- 檢測到地角點數組。
- count
- 角點數目。
- pattern_was_found
- 指示完整地棋盤被發現(≠0)還是沒有發現(=0)。可以傳輸cvFindChessboardCorners函數的返回值。
當棋盤沒有完全檢測出時,函數cvDrawChessboardCorners以紅色圓圈繪制檢測到的棋盤角點;如果整個棋盤都檢測到,則用直線連接所有的角點。
姿態估計
CreatePOSITObject
初始化包含對象信息的結構
CvPOSITObject* cvCreatePOSITObject( CvPoint3D32f* points, int point_count );
- points
- 指向三維對象模型的指針
- point_count
- 對象的點數
函數 cvCreatePOSITObject 為對象結構分配內存並計算對象的逆矩陣。
預處理的對象數據存儲在結構CvPOSITObject中,只能在OpenCV內部被調用,即用戶不能直接讀寫數據結構。用戶只可以創建這個結構並將指針傳遞給函數。
對象是在某坐標系內的一系列點的集合,函數 cvPOSIT計算從照相機坐標系中心到目標點points[0] 之間的向量。
一旦完成對給定對象的所有操作,必須使用函數cvReleasePOSITObject釋放內存。
POSIT
執行POSIT算法
1 void cvPOSIT( CvPOSITObject* posit_object, CvPoint2D32f* image_points, 2 double focal_length, 3 CvTermCriteria criteria, CvMatr32f rotation_matrix, 4 CvVect32f translation_vector );
- posit_object
- 指向對象結構的指針
- image_points
- 指針,指向目標像素點在二維平面圖上的投影。
- focal_length
- 使用的攝像機的焦距
- criteria
- POSIT迭代算法程序終止的條件
- rotation_matrix
- 旋轉矩陣
- translation_vector
- 平移矩陣.
函數 cvPOSIT 執行POSIT算法。圖像坐標在攝像機坐標系統中給出。焦距可以通過攝像機標定得到。算法每一次迭代都會重新計算在估計位置的透視投影。
兩次投影之間的范式差值是對應點中的最大距離。如果差值過小,參數criteria.epsilon就會終止程序。
ReleasePOSITObject
釋放3D對象結構
void cvReleasePOSITObject( CvPOSITObject** posit_object );
- posit_object
- 指向 CvPOSIT 結構指針的指針。
函數 cvReleasePOSITObject 釋放函數 cvCreatePOSITObject分配的內存。
CalcImageHomography
計算長方形或橢圓形平面對象(例如胳膊)的Homography矩陣
void cvCalcImageHomography( float* line, CvPoint3D32f* center, float* intrinsic, float* homography );
- line
- 對象的主要軸方向,為向量(dx,dy,dz).
- center
- 對象坐標中心 ((cx,cy,cz)).
- intrinsic
- 攝像機內參數 (3x3 matrix).
- homography
- 輸出的Homography矩陣(3x3).
函數 cvCalcImageHomography 為從圖像平面到圖像平面的初始圖像變化(defined by 3D oblong object line)計算Homography矩陣。
對極幾何(雙視幾何)
FindFundamentalMat
由兩幅圖像中對應點計算出基本矩陣
1 int cvFindFundamentalMat( const CvMat* points1, 2 const CvMat* points2, 3 CvMat* fundamental_matrix, 4 int method=CV_FM_RANSAC, 5 double param1=1., 6 double param2=0.99, 7 CvMat* status=NULL);
- points1
- 第一幅圖像點的數組,大小為2xN/Nx2 或 3xN/Nx3 (N 點的個數),多通道的1xN或Nx1也可以。點坐標應該是浮點數(雙精度或單精度)。:
- points2
- 第二副圖像的點的數組,格式、大小與第一幅圖像相同。
- fundamental_matrix
- 輸出的基本矩陣。大小是 3x3 或者 9x3 ,(7-點法最多可返回三個矩陣).
- method
-
計算基本矩陣的方法
- CV_FM_7POINT – 7-點算法,點數目= 7
- CV_FM_8POINT – 8-點算法,點數目 >= 8
- CV_FM_RANSAC – RANSAC 算法,點數目 >= 8
- CV_FM_LMEDS - LMedS 算法,點數目 >= 8
- param1
- 這個參數只用於方法RANSAC 或 LMedS 。它是點到對極線的最大距離,超過這個值的點將被舍棄,不用於后面的計算。通常這個值的設定是0.5 or 1.0 。
- param2
- 這個參數只用於方法RANSAC 或 LMedS 。 它表示矩陣正確的可信度。例如可以被設為0.99 。
- status
- 具有N個元素的輸出數組,在計算過程中沒有被舍棄的點,元素被被置為1;否則置為0。這個數組只可以在方法RANSAC and LMedS 情況下使用;在其它方法的情況下,status一律被置為1。這個參數是可選參數。
對極幾何可以用下面的等式描述:

其中 F 是基本矩陣,p1 和 p2 分別是兩幅圖上的對應點。
函數 FindFundamentalMat 利用上面列出的四種方法之一計算基本矩陣,並返回基本矩陣的值:沒有找到矩陣,返回0,找到一個矩陣返回1,多個矩陣返回3。 計算出的基本矩陣可以傳遞給函數cvComputeCorrespondEpilines來計算指定點的對極線。
1 例子1:使用 RANSAC 算法估算基本矩陣。 2 int numPoints = 100; 3 CvMat* points1; 4 CvMat* points2; 5 CvMat* status; 6 CvMat* fundMatr; 7 points1 = cvCreateMat(2,numPoints,CV_32F); 8 points2 = cvCreateMat(2,numPoints,CV_32F); 9 status = cvCreateMat(1,numPoints,CV_32F); 10
11 /* 在這里裝入對應點的數據... */
12
13 fundMatr = cvCreateMat(3,3,CV_32F); 14 int num = cvFindFundamentalMat(points1,points2,fundMatr,CV_FM_RANSAC,1.0,0.99,status); 15 if( num == 1 ) 16 printf("Fundamental matrix was found\n"); 17 else
18 printf("Fundamental matrix was not found\n"); 19
20
21 例子2:7點算法(3個矩陣)的情況。 22 CvMat* points1; 23 CvMat* points2; 24 CvMat* fundMatr; 25 points1 = cvCreateMat(2,7,CV_32F); 26 points2 = cvCreateMat(2,7,CV_32F); 27
28 /* 在這里裝入對應點的數據... */
29
30 fundMatr = cvCreateMat(9,3,CV_32F); 31 int num = cvFindFundamentalMat(points1,points2,fundMatr,CV_FM_7POINT,0,0,0); 32 printf("Found %d matrixes\n",num);
ComputeCorrespondEpilines
為一幅圖像中的點計算其在另一幅圖像中對應的對極線。
1 void cvComputeCorrespondEpilines( const CvMat* points, 2 int which_image, 3 const CvMat* fundamental_matrix, 4 CvMat* correspondent_lines);
- points
- 輸入點,是2xN 或者 3xN 數組 (N為點的個數)
- which_image
- 包含點的圖像指數(1 or 2)
- fundamental_matrix
- 基本矩陣
- correspondent_lines
- 計算對極點, 3xN數組
函數 ComputeCorrespondEpilines 根據外級線幾何的基本方程計算每個輸入點的對應外級線。如果點位於第一幅圖像(which_image=1),對應的對極線可以如下計算 :

其中F是基本矩陣,p1 是第一幅圖像中的點, l2 - 是與第二幅對應的對極線。如果點位於第二副圖像中 which_image=2),計算如下:

其中p2 是第二幅圖像中的點,l1 是對應於第一幅圖像的對極線,每條對極線都可以用三個系數表示 a, b, c:

歸一化后的對極線系數存儲在correspondent_lines 中。
ConvertPointsHomogenious
Convert points to/from homogenious coordinates
void cvConvertPointsHomogenious( const CvMat* src, CvMat* dst );
- src
- The input point array, 2xN, Nx2, 3xN, Nx3, 4xN or Nx4 (where N is the number of points). Multi-channel 1xN or Nx1 array is also acceptable.
- dst
- The output point array, must contain the same number of points as the input; The dimensionality must be the same, 1 less or 1 more than the input, and also within 2..4.
The function cvConvertPointsHomogenious converts 2D or 3D points from/to homogenious coordinates, or simply copies or transposes the array. In case if the input array dimensionality is larger than the output, each point coordinates are divided by the last coordinate:
- (x,y[,z],w) -> (x',y'[,z'])
-
其中
- x' = x/w
- y' = y/w
- z' = z/w (if output is 3D)
If the output array dimensionality is larger, an extra 1 is appended to each point.
(x,y[,z]) -> (x,y[,z],1)
Otherwise, the input array is simply copied (with optional tranposition) to the output. Note that, because the function accepts a large variety of array layouts, it may report an error when input/output array dimensionality is ambiguous. It is always safe to use the function with number of points N>=5, or to use multi-channel Nx1 or 1xN arrays.
