镜头畸变现象及其校正方法
????? 攝像機校準一般采用小孔成像模型,理想的小孔模型是線性模型,但是由于存在鏡頭畸變等原因,線性模型通常要加上一些內部參數,變成非線性模型。現對產生這一現象的原理以及解決方法進行整理,如下:
一、鏡頭畸變現象介紹
???? 相機的成像過程實質上是坐標系的轉換。首先空間中的點由 “世界坐標系” 轉換到 “像機坐標系”,然后再將其投影到成像平面 ( 圖像物理坐標系 ) ,最后再將成像平面上的數據轉換到 圖像像素坐標系。但是由于透鏡制造精度以及組裝工藝的偏差會引入畸變,導致原始圖像的失真。鏡頭的畸變分為徑向畸變和切向畸變兩類。參見:
http://blog.csdn.net/dcrmg/article/details/52950141
http://blog.csdn.net/waeceo/article/details/50580808
1. 徑向畸變
徑向畸變是沿著透鏡半徑方向分布的畸變,產生原因是光線在原理透鏡中心的地方比靠近中心的地方更加彎曲,這種畸變在普通廉價的鏡頭中表現更加明顯,徑向畸變主要包括桶形畸變和枕形畸變兩種。
成像儀光軸中心的畸變為0,沿著鏡頭半徑方向向邊緣移動,畸變越來越嚴重。畸變的數學模型可以用主點(principle point)周圍的泰勒級數展開式的前幾項進行描述,通常使用前兩項,即k1和k2,對于畸變很大的鏡頭,如魚眼鏡頭,可以增加使用第三項k3來進行描述,成像儀上某點根據其在徑向方向上的分布位置,調節公式為:
式里(x0,y0)是畸變點在成像儀上的原始位置,(x,y)是畸變較真后新的位置,下圖是距離光心不同距離上的點經過透鏡徑向畸變后點位的偏移示意圖,距離光心越遠,徑向位移越大,表示畸變也越大,在光心附近,幾乎沒有偏移。
2. 切向畸變
切向畸變是由于透鏡本身與相機傳感器平面(成像平面)或圖像平面不平行而產生的,這種情況多是由于透鏡被粘貼到鏡頭模組上的安裝偏差導致。畸變模型可以用兩個額外的參數p1和p2來描述:
大體上畸變位移相對于左下——右上角的連線是對稱的,說明該鏡頭在垂直于該方向上有一個旋轉角度。
二、物象坐標映射轉換
opencv中,函數findhomography可以找到這一單應性矩陣。
2、method:0表示使用所有點的常規方法;CV_RANSAC 基于RANSAC魯棒性的方法;CV_LMEDS 最小中值魯棒性方法
3、ransacReprojThreshod 僅在RANSAC方法中使用,一個點對被認為是內層圍值(非異常值)所允許的最大投影誤差。如果srcPoints和dstPoints單位是像素,通常意味著在某些情況下這個參數的范圍在1到10之間。
4、status,可選的輸出掩碼,用在CV_RANSAC或者CV_LMEDS方法中。注意輸入掩碼將被忽略。
如果參數method設置為默認值0,該函數使用一個簡單的最小二乘方案來計算初始的單應性估計。然而,如果不是所有的點對(srcPoints,dstPoints)都適應這個嚴格的透視變換。(也就是說,有一些異常值),這個初始估計值將很差。在這種情況下,我們可以使用兩個魯棒性算法中的一個。RANSCA和LMEDS這兩個方法都嘗試不同的隨機的相對應點對的子集,每四對點集一組,使用這個子集和一個簡單的最小二乘算法來估計單應性矩陣,然后計算得到單應性矩陣的質量quality/goodness。(對于RANSAC方法是內層圍點的數量,對于LMeDs是中間的重投影誤差)。然后最好的子集用來產生單應性矩陣的初始化估計和inliers/outliers的掩碼。
三、相機校正
在第二部分,求得的單應性矩陣,是由兩部分構成的:內參矩陣和外參矩陣,在OpenCV的3D重建中,對攝像機的內參外參有講解:外參:攝像機的旋轉平移屬于外參,用于描述相機在靜態場景下相機的運動,或者在相機固定時,運動物體的剛性運動。因此,在圖像拼接或者三維重建中,就需要使用外參來求幾幅圖像之間的相對運動,從而將其注冊到同一個坐標系下面來。
內參:下面給出了內參矩陣,需要注意的是,真實的鏡頭還會有徑向和切向畸變,而這些畸變是屬于相機的內參的。
攝像機內參矩陣:
fx s x0K = 0 fy y00 0 1 其中,fx,fy為焦距,一般情況下,二者相等,x0、y0為主點坐標(相對于成像平面),s為坐標軸傾斜參數,理想情況下為0。
攝像機外參矩陣:包括旋轉矩陣和平移矩陣,旋轉矩陣和平移矩陣共同描述了如何把點從世界坐標系轉換到攝像機坐標系。
旋轉矩陣:描述了世界坐標系的坐標軸相對于攝像機坐標軸的方向。
平移矩陣:描述了在攝像機坐標系下,空間原點的位置。
博文http://blog.csdn.net/dcrmg/article/details/52939318中給出了opencv校正流程,表示感謝.
另外還可以參見:
http://www.cnblogs.com/tianya2543/p/3894644.html,http://www.eepw.com.cn/article/201706/352360.htm
http://blog.csdn.net/dcrmg/article/details/52929669
1. 準備標定圖片
標定圖片需要使用標定板在不同位置、不同角度、不同姿態下拍攝,最少需要3張,以10~20張為宜。標定板需要是黑白相間的矩形構成的棋盤圖,制作精度要求較高,如下圖所示:
2.對每一張標定圖片,提取角點信息
需要使用findChessboardCorners函數提取角點,這里的角點專指的是標定板上的內角點,這些角點與標定板的邊緣不接觸。
?findChessboardCorners函數原型:
//! finds checkerboard pattern of the specified size in the image CV_EXPORTS_W bool findChessboardCorners( InputArray image, Size patternSize, OutputArray corners, int flags=CALIB_CB_ADAPTIVE_THRESH+CALIB_CB_NORMALIZE_IMAGE );第一個參數Image,傳入拍攝的棋盤圖Mat圖像,必須是8位的灰度或者彩色圖像;
第二個參數patternSize,每個棋盤圖上內角點的行列數,一般情況下,行列數不要相同,便于后續標定程序識別標定板的方向;
第三個參數corners,用于存儲檢測到的內角點圖像坐標位置,一般用元素是Point2f的向量來表示:vector<Point2f> image_points_buf;
第四個參數flage:用于定義棋盤圖上內角點查找的不同處理方式,有默認值。
3. 對每一張標定圖片,進一步提取亞像素角點信息
為了提高標定精度,需要在初步提取的角點信息上進一步提取亞像素信息,降低相機標定偏差,常用的方法是cornerSubPix,另一個方法是使用find4QuadCornerSubpix函數,這個方法是專門用來獲取棋盤圖上內角點的精確位置的,或許在相機標定的這個特殊場合下它的檢測精度會比cornerSubPix更高?
cornerSubPix函數原型:
CV_EXPORTS_W void cornerSubPix( InputArray image, InputOutputArray corners, Size winSize, Size zeroZone, TermCriteria criteria );
第一個參數image,輸入的Mat矩陣,最好是8位灰度圖像,檢測效率更高;
第二個參數corners,初始的角點坐標向量,同時作為亞像素坐標位置的輸出,所以需要是浮點型數據,一般用元素是Pointf2f/Point2d的向量來表示:vector<Point2f/Point2d> iamgePointsBuf;
第三個參數winSize,大小為搜索窗口的一半;
第四個參數zeroZone,死區的一半尺寸,死區為不對搜索區的中央位置做求和運算的區域。它是用來避免自相關矩陣出現某些可能的奇異性。當值為(-1,-1)時表示沒有死區;
第五個參數criteria,定義求角點的迭代過程的終止條件,可以為迭代次數和角點精度兩者的組合;
find4QuadCornerSubpix函數原型:
第一個參數img,輸入的Mat矩陣,最好是8位灰度圖像,檢測效率更高;
第二個參數corners,初始的角點坐標向量,同時作為亞像素坐標位置的輸出,所以需要是浮點型數據,一般用元素是Pointf2f/Point2d的向量來表示:vector<Point2f/Point2d> iamgePointsBuf;
第三個參數region_size,角點搜索窗口的尺寸;
在其中一個標定的棋盤圖上分別運行cornerSubPix和find4QuadCornerSubpix尋找亞像素角點,兩者定位到的亞像素角點坐標分別為:
? ?cornerSubPix: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??find4QuadCornerSubpix:
? ? ? ? ? ? ? ? ? ? ? ?
4. 在棋盤標定圖上繪制找到的內角點(非必須,僅為了顯示)
drawChessboardCorners函數用于繪制被成功標定的角點,函數原型:
//! draws the checkerboard pattern (found or partly found) in the image CV_EXPORTS_W void drawChessboardCorners( InputOutputArray image, Size patternSize, InputArray corners, bool patternWasFound ); 第一個參數image,8位灰度或者彩色圖像;
第二個參數patternSize,每張標定棋盤上內角點的行列數;
第三個參數corners,初始的角點坐標向量,同時作為亞像素坐標位置的輸出,所以需要是浮點型數據,一般用元素是Pointf2f/Point2d的向量來表示:vector<Point2f/Point2d> iamgePointsBuf;
第四個參數patternWasFound,標志位,用來指示定義的棋盤內角點是否被完整的探測到,true表示別完整的探測到,函數會用直線依次連接所有的內角點,作為一個整體,false表示有未被探測到的內角點,這時候函數會以(紅色)圓圈標記處檢測到的內角點;
以下是drawChessboardCorners函數中第四個參數patternWasFound設置為true和false時內角點的繪制效果:
patternWasFound=ture時,依次連接各個內角點:
patternWasFound=false時,以(紅色)圓圈標記處角點位置:
5. 相機標定
獲取到棋盤標定圖的內角點圖像坐標之后,就可以使用calibrateCamera函數進行標定,計算相機內參和外參系數,
calibrateCamera函數原型:
第一個參數objectPoints,為世界坐標系中的三維點。在使用時,應該輸入一個三維坐標點的向量的向量,即vector<vector<Point3f>> object_points。需要依據棋盤上單個黑白矩陣的大小,計算出(初始化)每一個內角點的世界坐標。
第二個參數imagePoints,為每一個內角點對應的圖像坐標點。和objectPoints一樣,應該輸入vector<vector<Point2f>> image_points_seq形式的變量;
第三個參數imageSize,為圖像的像素尺寸大小,在計算相機的內參和畸變矩陣時需要使用到該參數;
第四個參數cameraMatrix為相機的內參矩陣。輸入一個Mat cameraMatrix即可,如Mat cameraMatrix=Mat(3,3,CV_32FC1,Scalar::all(0));
第五個參數distCoeffs為畸變矩陣。輸入一個Mat distCoeffs=Mat(1,5,CV_32FC1,Scalar::all(0))即可;
第六個參數rvecs為旋轉向量;應該輸入一個Mat類型的vector,即vector<Mat>rvecs;
第七個參數tvecs為位移向量,和rvecs一樣,應該為vector<Mat> tvecs;
第八個參數flags為標定時所采用的算法。有如下幾個參數:
CV_CALIB_USE_INTRINSIC_GUESS:使用該參數時,在cameraMatrix矩陣中應該有fx,fy,u0,v0的估計值。否則的話,將初始化(u0,v0)圖像的中心點,使用最小二乘估算出fx,fy。?
CV_CALIB_FIX_PRINCIPAL_POINT:在進行優化時會固定光軸點。當CV_CALIB_USE_INTRINSIC_GUESS參數被設置,光軸點將保持在中心或者某個輸入的值。?
CV_CALIB_FIX_ASPECT_RATIO:固定fx/fy的比值,只將fy作為可變量,進行優化計算。當CV_CALIB_USE_INTRINSIC_GUESS沒有被設置,fx和fy將會被忽略。只有fx/fy的比值在計算中會被用到。?
CV_CALIB_ZERO_TANGENT_DIST:設定切向畸變參數(p1,p2)為零。?
CV_CALIB_FIX_K1,…,CV_CALIB_FIX_K6:對應的徑向畸變在優化中保持不變。?
CV_CALIB_RATIONAL_MODEL:計算k4,k5,k6三個畸變參數。如果沒有設置,則只計算其它5個畸變參數。
第九個參數criteria是最優迭代終止條件設定。
在使用該函數進行標定運算之前,需要對棋盤上每一個內角點的空間坐標系的位置坐標進行初始化,標定的結果是生成相機的內參矩陣cameraMatrix、相機的5個畸變系數distCoeffs,另外每張圖像都會生成屬于自己的平移向量和旋轉向量。
6. 對標定結果進行評價
對標定結果進行評價的方法是通過得到的攝像機內外參數,對空間的三維點進行重新投影計算,得到空間三維點在圖像上新的投影點的坐標,計算投影坐標和亞像素角點坐標之間的偏差,偏差越小,標定結果越好。
對空間三維坐標點進行反向投影的函數是projectPoints,函數原型是:
//! projects points from the model coordinate space to the image coordinates. Also computes derivatives of the image coordinates w.r.t the intrinsic and extrinsic camera parameters CV_EXPORTS_W void projectPoints( InputArray objectPoints, InputArray rvec, InputArray tvec, InputArray cameraMatrix, InputArray distCoeffs, OutputArray imagePoints, OutputArray jacobian=noArray(), double aspectRatio=0 );
第一個參數objectPoints,為相機坐標系中的三維點坐標;
第二個參數rvec為旋轉向量,每一張圖像都有自己的選擇向量;
第三個參數tvec為位移向量,每一張圖像都有自己的平移向量;
第四個參數cameraMatrix為求得的相機的內參數矩陣;
第五個參數distCoeffs為相機的畸變矩陣;
第六個參數iamgePoints為每一個內角點對應的圖像上的坐標點;
第七個參數jacobian是雅可比行列式;
第八個參數aspectRatio是跟相機傳感器的感光單元有關的可選參數,如果設置為非0,則函數默認感光單元的dx/dy是固定的,會依此對雅可比矩陣進行調整;
下邊顯示了某一張標定圖片上的亞像素角點坐標和根據標定結果把空間三維坐標點映射回圖像坐標點的對比:
find4QuadCornerSubpix查找到的亞像素點坐標: ? ? ? ? ? ? ? ? ? ? ? ? ??projectPoints映射的坐標:
? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ???
以下是每一幅圖像上24個內角點的平均誤差統計數據:
7. 查看標定效果——利用標定結果對棋盤圖進行矯正
利用求得的相機的內參和外參數據,可以對圖像進行畸變的矯正,這里有兩種方法可以達到矯正的目的,分別說明一下。
方法一:使用initUndistortRectifyMap和remap兩個函數配合實現。
initUndistortRectifyMap用來計算畸變映射,remap把求得的映射應用到圖像上。
initUndistortRectifyMap的函數原型:
//! initializes maps for cv::remap() to correct lens distortion and optionally rectify the image CV_EXPORTS_W void initUndistortRectifyMap( InputArray cameraMatrix, InputArray distCoeffs, InputArray R, InputArray newCameraMatrix, Size size, int m1type, OutputArray map1, OutputArray map2 );
第一個參數cameraMatrix為之前求得的相機的內參矩陣;
第二個參數distCoeffs為之前求得的相機畸變矩陣;
第三個參數R,可選的輸入,是第一和第二相機坐標之間的旋轉矩陣;
第四個參數newCameraMatrix,輸入的校正后的3X3攝像機矩陣;
第五個參數size,攝像機采集的無失真的圖像尺寸;
第六個參數m1type,定義map1的數據類型,可以是CV_32FC1或者CV_16SC2;
第七個參數map1和第八個參數map2,輸出的X/Y坐標重映射參數;
remap函數原型:
//! warps the image using the precomputed maps. The maps are stored in either floating-point or integer fixed-point format CV_EXPORTS_W void remap( InputArray src, OutputArray dst, InputArray map1, InputArray map2, int interpolation, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar());第一個參數src,輸入參數,代表畸變的原始圖像;
第二個參數dst,矯正后的輸出圖像,跟輸入圖像具有相同的類型和大小;
第三個參數map1和第四個參數map2,X坐標和Y坐標的映射;
第五個參數interpolation,定義圖像的插值方式;
第六個參數borderMode,定義邊界填充方式;
方法二:使用undistort函數實現
undistort函數原型:
//! corrects lens distortion for the given camera matrix and distortion coefficients CV_EXPORTS_W void undistort( InputArray src, OutputArray dst, InputArray cameraMatrix, InputArray distCoeffs, InputArray newCameraMatrix=noArray() );第一個參數src,輸入參數,代表畸變的原始圖像;
第二個參數dst,矯正后的輸出圖像,跟輸入圖像具有相同的類型和大小;
第三個參數cameraMatrix為之前求得的相機的內參矩陣;
第四個參數distCoeffs為之前求得的相機畸變矩陣;
第五個參數newCameraMatrix,默認跟cameraMatrix保持一致;
方法一相比方法二執行效率更高一些,推薦使用。
工程代碼如下,親測,可用,再次感謝該博文的博主~
http://blog.csdn.net/Loser__Wang/article/details/51811347,
http://www.cnblogs.com/li-yao7758258/p/5929145.html
總結
以上是生活随笔為你收集整理的镜头畸变现象及其校正方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在PPT中插入组织结构图ppt模板大全
- 下一篇: 字符串压缩 java_如何在Java中压