超實用!圖像去畸變矯正及雙線性內插法

點擊上方“3D視覺工坊”,選擇“星標”

乾貨第一時間送達

通過採集的圖像我們可以得到畸變後的圖像,要得到沒有畸變的圖像要通過畸變模型推導其映射關係。

真實圖像 imgR 與 畸變圖像 imgD 之間的關係爲: imgR(U, V) = imgD(Ud, Vd) 。遍歷所有(U,V)填充爲映射對應的(Ud,Vd)即可實現圖像去畸變處理。

前提條件是:已經得知相機內參K以及畸變參數k1,k2,k3,p1,p2,這部分可以由matlab工具箱實現。

接下來,我們將詳細介紹如何去除圖像的畸變。

1. 圖像去畸變代碼

#include <opencv2/opencv.hpp>
#include <string>
#include <math.h>
 
using namespace std;
using namespace cv;
 
 
int main(int argc, char **argv) {
 
  // 內參
  double k1 = -0.3630, k2 = 0.1100, p1 = 0, p2 = 0;
  double fx = 1026, fy = 1019.2, cx = 922.9716, cy = 589.6080;
  char image_name[100];
  for (int num = 1; num < 302; num  )
  {
    sprintf_s(image_name,"./image/%d.jpg",num);
    Mat image = cv::imread(image_name, 0);   // 圖像是灰度圖,CV_8UC1
    int rows = image.rows, cols = image.cols;
    Mat image_undistort = Mat(rows, cols, CV_8UC1);   // 去畸變以後的圖
 
    // 計算去畸變後圖像的內容image_undistort
    for (int v = 0; v < rows; v  )
      for (int u = 0; u < cols; u  ) {
      double u_distorted = 0, v_distorted = 0;
 
      double x1, y1, x2, y2;
      //未畸變像素平面到成像平面
      x1 = (u - cx) / fx;
      y1 = (v - cy) / fy;
      double r2;
      //成像平面畸變後
      r2 = pow(x1, 2)   pow(y1, 2);
      x2 = x1*(1   k1*r2   k2*pow(r2, 2))   2 * p1*x1*y1   p2*(r2   2 * x1*x1);
      y2 = y1*(1   k1*r2   k2*pow(r2, 2))   p1*(r2   2 * y1*y1)   2 * p2*x1*y1;
      //畸變的成像平面到像素平面
      u_distorted = fx*x2   cx;
      v_distorted = fy*y2   cy;
      // 賦值 (最近鄰插值)
      if (u_distorted >= 0 && v_distorted >= 0 && u_distorted < cols && v_distorted < rows) {
        image_undistort.at<uchar>(v, u) = image.at<uchar>((int)v_distorted, (int)u_distorted);
      }
      else {
        image_undistort.at<uchar>(v, u) = 0;
      }
      }
    // 畫圖去畸變後圖像
    char outname[100];
    sprintf_s(outname, "./image1/%d.jpg", num);
    cv::imwrite(outname,image_undistort);
  }
 
  cv::waitKey();
  return 0;
 
}

方法二:調用 initUndistortRectifyMap和remap函數

#include <opencv2/opencv.hpp>
#include <string>
#include <math.h>
 
using namespace std;
using namespace cv;
 
 
int main(int argc, char **argv) {
 
  // 內參
  double k1 = -0.4183, k2 = 0.1584, p1 = 0, p2 = 0;
  double fx = 1047.8, fy = 1028.6, cx = 885.1422, cy = 579.8241;
  Mat cameraMatrix = (Mat_<double>(3, 3) <<
    1047.8, 0, 885.1422,
    0, 1028.6, 579.8241,
    0,0,1);
  Mat distCoeffs = (Mat_<double>(1, 4) << -0.4183, 0.1584, 0, 0);
 
  char image_name[100];
  for (int num = 1; num < 500; num  )
  {
    sprintf_s(image_name, "./image_indoor/%d.jpg", num);
    Mat image = cv::imread(image_name, 0);   // 原圖image是灰度圖,CV_8UC1
    int rows = image.rows, cols = image.cols;
    Mat image_undistort = Mat(rows, cols, CV_8UC1);   // 去畸變以後的圖
 
    // 計算去畸變後圖像的內容image_undistort
    Mat map1, map2;
    //initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(), getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, image.size(), 1, image.size(), 0), image.size(), CV_16SC2, map1, map2);
    initUndistortRectifyMap(cameraMatrix, distCoeffs, Mat(), Mat(),image.size(), CV_16SC2, map1, map2);
    remap(image, image_undistort, map1, map2, INTER_LINEAR);
    // 畫圖去畸變後圖像
    char outname[100];
    sprintf_s(outname, "./image_indoor2/%d.jpg", num);
    cv::imwrite(outname, image_undistort);
  }
 
  cv::waitKey();
  return 0;
 
}

效果對比:

2. 雙線性內插法去畸變

雙線性內插法利用待求像素四個相鄰像素的灰度在兩個方向上做線性內插,在光流法求取某像素位置的灰度值時同樣用到了二維線性插值。

光流法這裏返回的是某個像素位置的灰度值。

protected:
    // get a gray scale value from reference image (bilinear interpolated)
    //取得變換後的圖中對應像素座標處的灰度值,這裏並不是返回一張圖像的灰度值,而是就是寫死了,就是類構造裏傳入的那張圖
    inline float getPixelValue ( float x, float y )
    {
        //這裏先說一下各個參數的類型:
        //image_爲Mat*類型,圖像指針,所以調用data時用->符號,
        //data爲圖像矩陣首地址,支持數組形式訪問,data[]就是訪問到像素的值了,此處爲像素的灰度值,類型爲uchar
        //關於step有點複雜,data[]中括號的式子有點複雜,總的意思就是y行乘上每行內存數,定位到行,然後在加上x,定位到像素
        //step具體解釋在最後面有一些資料
        //image_->data[int(y)*image_->step   int(x)]這一步讀到了x,y處的灰度值,類型爲uchar,
        //但是後面由於線性插值,需要定位這個像素的位置,而不是他的灰度值,所以取其地址,賦值給data_ptr,記住它的位置,後面使用
        uchar* data_ptr = & image_->data[int(y)*image_->step   int(x)];
 
        //由於x,y這裏有可能帶小數,但是像素位置肯定是整數,所以,問題來了,(1.2, 4.5)像素座標處的灰度值爲多少呢?OK,線性插值!
        //說一下floor(),std中的cmath函數。向下取整,返回不大於x的整數。例floor(4.9)=4
        //xx和yy,就是取到小數部分。例:x=4.9的話,xx=x-floor(x)就爲0.9。y同理
        float xx = x - floor ( x );
        float yy = y - floor ( y );
 
        //這整個return一個float值包含了線性插值,二維線性差值,這裏後文有具體高清無碼解釋
        return float (
                   (1-xx) * ( 1-yy ) * data_ptr[0]  
                   xx* ( 1-yy ) * data_ptr[1]  
                   ( 1-xx ) *yy*data_ptr[ image_->step ]  
                   xx*yy*data_ptr[image_->step 1]
               );
    }

參考:

1.https://blog.csdn.net/weixin_41074793/article/details/88886153

2.https://blog.csdn.net/a472609409/article/details/90515742?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

推薦閱讀

重磅!3DCVer-學術論文寫作投稿 交流羣已成立

掃碼添加小助手微信,可申請加入3D視覺工坊-學術論文寫作與投稿 微信交流羣,旨在交流頂會、頂刊、SCI、EI等寫作與投稿事宜。

同時也可申請加入我們的細分方向交流羣,目前主要有3D視覺CV&深度學習SLAM三維重建點雲後處理自動駕駛、CV入門、三維測量、VR/AR、3D人臉識別、醫療影像、缺陷檢測、行人重識別、目標跟蹤、視覺產品落地、視覺競賽、車牌識別、硬件選型、學術交流、求職交流等微信羣,請掃描下面微信號加羣,備註:”研究方向+學校/公司+暱稱“,例如:”3D視覺 + 上海交大 + 靜靜“。請按照格式備註,否則不予通過。添加成功後會根據研究方向邀請進去相關微信羣。原創投稿也請聯繫。

▲長按加微信羣或投稿

▲長按關注公衆號

3D視覺從入門到精通知識星球:針對3D視覺領域的知識點彙總、入門進階學習路線、最新paper分享、疑問解答四個方面進行深耕,更有各類大廠的算法工程人員進行技術指導。與此同時,星球將聯合知名企業發佈3D視覺相關算法開發崗位以及項目對接信息,打造成集技術與就業爲一體的鐵桿粉絲聚集區,近1000+星球成員爲創造更好的AI世界共同進步,知識星球入口:

學習3D視覺核心技術,掃描查看介紹,3天內無條件退款

 圈裏有高質量教程資料、可答疑解惑、助你高效解決問題

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