之前的文章為瞭推導的便利是完全沒有考慮鏡頭引起的畸變情形,但實際上,任何鏡頭都不可能是沒有畸變的。畸變是在鏡頭的制造過程中產生的。透鏡的畸變主要分為兩類:徑向畸變和切向畸變

徑向畸變(Radial distortion)

徑向畸變是由於透鏡表面的弧度引起的光線折射角不同造成的,因此越靠鏡頭的邊緣畸變越嚴重,光心處零畸變。如下圖所示:

對於徑向畸變,可以用光心(optical center,下式的 (x, y) )周圍距離為r的泰勒級數展開式的前幾項進行描述,通常使用前兩項就足夠瞭,即 k_1k_2 ,對於畸變更大的鏡頭,如魚眼鏡頭,可以增加使用第三項 k_3 來進行描述:

left{ begin{aligned} x_{corrected} &= x cdot left( 1+ k_1r^2 +k_2r^4+k_3r^6 right) \ y_{corrected} &= y cdot left( 1+ k_1r^2 +k_2r^4+k_3r^6 right) \ end{aligned} right. tag{1}

可能讀者會好奇為什麼隻有偶數次冪的項,這裡稍作解釋。r0 點的泰勒展開形式為:

f(r) = a_0 +a_1r + a_2r^2+ ldots \

f(r=0)=0 , 即 a_0=0 , 又鏡頭是對稱的,故僅剩餘偶數次冪的項。

徑向畸變的結果可參考下圖:

切向畸變(tangential distortion)

切向畸變的產生主要歸結於裝配的瑕疵,使鏡頭與成像平面不平行,如下圖:

結果如圖:

徑向畸變可以用兩個參數表示:

left{ begin{aligned} x_{corrected} &= x + left[ 2p_1xy + p_2 left(r^2 +2x^2 right) right] \ y_{corrected} &= y + left[ p_1 left( r^2 + 2y^2 right) + 2p_2xy right] \ end{aligned} right. tag{2}

這個推導有興趣的讀者可查閱《Decentering Distortion of Lenses – D.C. Brown, Photometric Engineering, pages 444-462, Vol. 32, No. 3, 1966》。

圖片矯正

這樣一來,我們就有5個參數來定義鏡頭的畸變 (k_1,k_2,p_1,p_2,k_3) ,在OpenCV中可以通過cv::calibrateCamera同內參一起得出。接下來,我們看一看圖片矯正後的效果:

#include <opencv2/opencv.hpp>

int main()
{
const cv::Mat M = (cv::Mat_<double>(3, 3) << 5.3398795245975896e+02, 0., 3.2838647449406972e+02,
0., 5.2871082110006125e+02, 2.3684272831168110e+02,
0., 0., 1.);
const cv::Mat D = (cv::Mat_<double>(5, 1) << -2.5896599091487332e-01, -1.2618381088945435e-01, 0., 0., 0.);

const int ImgWidth = 640;
const int ImgHeight = 480;

cv::Mat map1, map2;
cv::Size imageSize(ImgWidth, ImgHeight);

cv::initUndistortRectifyMap(M, D, cv::Mat(), M, imageSize, CV_16SC2, map1, map2);

std::string InputPath = "test/projection/left01.jpg";
cv::Mat RawImage = cv::imread(InputPath);

cv::Mat UndistortImage;
remap(RawImage, UndistortImage, map1, map2, cv::INTER_LINEAR);

cv::Mat img_concat;
cv::hconcat(RawImage, UndistortImage, img_concat);
cv::imwrite("test/calibration/left01_raw_undistorted.jpg",img_concat);

return 0;
}