前言
相機標定,重映射可以進行插值映射從而矯正圖像,這是一種方法,也有矩陣映射方法,本篇使用重映射方式解說畸變矯正的計算原理。
橫向縱向區域固定拉伸:
橫向縱向拉伸:
右下角拉伸:
標定相機需要做兩件事:
- 糾正畸變的影響
- 根據圖像重構三位場景
Opencv提供了可以直接使用的矯正算法,即通過輸入原始圖像和由函數cv::calibrateCamera()得到的畸變係數,生成校正後的圖像。(注意:這裏可使用用cv::undistort()使用該算法直接完成所需任務,也可以使用函數cv::iniitUndistorRectifyMap()和cv::remap()來更有效的處理。
當進行圖像矯正時,必須指定輸入圖像的每個像素在輸出圖像中移動到的位置,成爲“矯正映射”(畸變映射)。
N x M的矩陣A中,重映射由雙通道浮點數的N x M的矩陣B表示,對於圖像A中的任意一點aPoint(i, j),映射爲b1Point(i’, j’)和b2Point(i’, j’),在A中假設i=2,j=3,那麼(假設重映射之後4.5,5.5)在B1中b1Point(i’, j’)值爲4.5,b2Point(i’, j’)值爲5.5,由於座標是浮點數,那麼需要插值得到整數位置以及中間過渡的區域顏色(平滑處理)。
雙矩陣浮點數表示,N x M的矩陣A中,重映射由一對N x M的矩陣B和C描述,這裏所有的N x M矩陣都是單通道浮點矩陣,在A中的點aPoint(i, j),重映射矩陣B中的點bPoint(i,j)存儲了重映射後的i’ (映射後的i座標), 重映射矩陣C中的點cPoint(i,j)存儲了重映射後的j’(映射後的j座標)。
映射由雙通道有符號整數矩陣(即CV_16SC2類型)表示。該方式與雙通道浮點數表示方式相同,但使用此格式要快得多(筆者理解:由浮點數插值改爲整數插值,會要快一些,但是肯定雙通道浮點數的表示方式圖像效果會稍微好一些)。
在於得到插值的座標系來映射新位置的x和y位置,要漸近等,所以本方法的核心關鍵在於得到標定後的矩陣,得到映射矩陣的方式可以自己寫算法,也可以使用其他方式,後續文章繼續深入這塊。
void remap( InputArray src,
OutputArray dst,
InputArray map1,
InputArray map2,
int interpolation,
int borderMode = BORDER_CONSTANT,
const Scalar& borderValue = Scalar());
- 參數一:InputArray類型的src,一般爲cv::Mat;
- 參數二:OutputArray類型的dst,目標圖像。它的大小與map1相同,類型與src相同。
- 參數三:InputArray類型的map1,它有兩種可能的表示對象:表示點(x,y)的第一個映射或者表示CV_16SC2 , CV_32FC1 或CV_32FC2類型的x值。
- 參數四:InputArray類型的map2,它也有兩種可能的表示對象,而且他是根據map1來確定表示哪種對象。若map1表示點(x,y)時,這個參數不代表任何值,否則,表示CV_16UC1 , rCV_32FC1類型的y值(第二個值)。
- 參數五:int類型的interpolation,使用的插值方法;
- 參數六:int類型的borderMode,邊界處理方式;
- 參數七:Scalar類型的borderValue,重映射後,離羣點的背景,需要broderMode設置爲BORDER_CONSTRANT時纔有效。(離羣點:當圖片大小爲400x300,那麼對應的map1和map2範圍爲0399、0299,小於0或者大於299的則爲離散點,使用該顏色填充);
void OpenCVManager::testRemap2()
{
std::string srcFilePath = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/25.jpg";
// 步驟一:讀取文件
cv::Mat srcMat = cv::imread(srcFilePath);
// 縮放一下
int width = 400;
int height = 400;
cv::resize(srcMat, srcMat, cv::Size(width, height));
// 步驟二:映射矩陣
cv::Mat mapX;
cv::Mat mapY;
mapX.create(srcMat.size(), CV_32FC1);
mapY.create(srcMat.size(), CV_32FC1);
// 算法:這裏400x400,將0~100放大至0~200,將100~400映射爲200~400
// 算法:這裏400x400,將0~100放大至0~200,將100~400映射爲200~400
#if 0
for(int row = 0; row < srcMat.rows; row++)
{
for(int col = 0; col < srcMat.cols; col++)
{
// if(true)
if(col < 200)
{
mapX.at<float>(row, col) = static_cast<float>(col * 1.0f / 2);
}else{
mapX.at<float>(row, col) = static_cast<float>(100 + (col - 200) * 1.0f / 2 * 3);
}
// if(true)
if(row < 200)
{
mapY.at<float>(row, col) = static_cast<float>(row * 1.0f / 2);
}else{
mapY.at<float>(row, col) = static_cast<float>(100 + (row - 200) * 1.0f / 2 * 3);
}
}
}
#endif
#if 0
for(int row