OpenCV開發筆記(七十五):相機標定矯正中使用remap重映射進行畸變矯正

前言

  相機標定,重映射可以進行插值映射從而矯正圖像,這是一種方法,也有矩陣映射方法,本篇使用重映射方式解說畸變矯正的計算原理。

 

Demo

  橫向縱向區域固定拉伸:
  在這裏插入圖片描述

  橫向縱向拉伸:
  在這裏插入圖片描述

  右下角拉伸:
  在這裏插入圖片描述

 

相機畸變矯正

  標定相機需要做兩件事:

  • 糾正畸變的影響
  • 根據圖像重構三位場景

糾正畸變的影響

  Opencv提供了可以直接使用的矯正算法,即通過輸入原始圖像和由函數cv::calibrateCamera()得到的畸變係數,生成校正後的圖像。(注意:這裏可使用用cv::undistort()使用該算法直接完成所需任務,也可以使用函數cv::iniitUndistorRectifyMap()和cv::remap()來更有效的處理。

 

矯正映射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類型)表示。該方式與雙通道浮點數表示方式相同,但使用此格式要快得多(筆者理解:由浮點數插值改爲整數插值,會要快一些,但是肯定雙通道浮點數的表示方式圖像效果會稍微好一些)。
  在這裏插入圖片描述

 

remap核心關鍵

  在於得到插值的座標系來映射新位置的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的則爲離散點,使用該顏色填充);
 

Demo源碼

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 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章