OpenCV|C++|詳細註釋代碼實踐之圖像去除畸變的幾種方法

1.使用cv::goodFeaturesToTrack()函數檢測圖像特徵點css

2.校訂先後圖像和矩形對比
c++

使用公式對圖像去畸變
web

使用函數對特徵點去畸變
算法

矩形框去畸變
數組

3.使用getOptimalNewCameraMatrix()函數生成去畸變圖像的內參矩陣

下面是參數alpha = 1去除畸變的效果

下面是參數alpha = 0去除畸變的效果

4.使用函數initUndistortRectifyMap獲得去除畸變後的像素矩陣

5.使用函數remap重映射生成去畸變後的圖像bash

6.另外兩種去畸變的方法
微信

7.OpenCV|C++可執行詳細註釋代碼

https://chunqiushenye.blog.csdn.net/article/details/112641824網絡

#include <opencv2/opencv.hpp>#include <string>
using namespace std;
int main() { /********************** * 讀取相機內參數和畸變係數 *********************/ const string strSettingPath = "../undistort.yaml"; // 讀取相機參數 cv::FileStorage fSettings(strSettingPath, cv::FileStorage::READ); float fx = fSettings["Camera.fx"]; float fy = fSettings["Camera.fy"]; float cx = fSettings["Camera.cx"]; float cy = fSettings["Camera.cy"]; cv::Mat K = cv::Mat::eye(3,3,CV_32F);// 構造內參數矩陣 K.at<float>(0,0) = fx; K.at<float>(1,1) = fy; K.at<float>(0,2) = cx; K.at<float>(1,2) = cy;
// cv::Matx33f intrinsic_matrix = cv::Matx33f::eye();// 另外一種定義的方法// intrinsic_matrix(0,0) = fx;// intrinsic_matrix(1,1) = fy;// intrinsic_matrix(0,2) = cx;// intrinsic_matrix(1,2) = cy;
float k1 = fSettings["Camera.k1"];// 畸變係數 k1, k2, p1, p2 float k2 = fSettings["Camera.k2"]; float p1 = fSettings["Camera.p1"]; float p2= fSettings["Camera.p2"]; cv::Mat DistCoef(4,1,CV_32F);// 構造畸變係數矩陣 DistCoef.at<float>(0) = k1; DistCoef.at<float>(1) = k2; DistCoef.at<float>(2) = p1; DistCoef.at<float>(3) = p2;
// cv::Vec4f distortion_coeffs;// 另外一種定義畸變係數矩陣的方法// distortion_coeffs(0) = k1;// distortion_coeffs(1) = k2;// distortion_coeffs(2) = p1;// distortion_coeffs(3) = p2;
cv::Mat image_distorted = cv::imread("../distorted.png", CV_LOAD_IMAGE_GRAYSCALE);// 讀取帶畸變的圖像 cv::imshow("image_distorted", image_distorted); cv::waitKey();

/**************************************** * 使用cv::goodFeaturesToTrack提取圖像特徵點 ***************************************/ const int MAX_CNT = 150; const int MIN_DIST = 30; vector<cv::Point2f> pts; cv::goodFeaturesToTrack(image_distorted, pts, MAX_CNT, 0.01, MIN_DIST);// 提取特徵點的圖像要是灰度圖 /** * cv::goodFeaturesToTrack 角點檢測 * 第一個參數是輸入圖像(8位或32位單通道圖) * 第二個參數是檢測到的全部角點,類型爲vector或數組,由實際給定的參數類型而定。若是是vector * 那麼它應該是一個包含cv::Point2f的vector對象;若是類型是cv::Mat,那麼它的每一行對應一個角點,點的x、y位置分別是兩列 * 第三個參數用於限定檢測到的點數的最大值 * 第四個參數表示檢測到的角點的質量水平(一般是0.10到0.01之間的數值,不能大於1.0) * 第五個參數用於區分相鄰兩個角點的最小距離(小於這個距離得點將進行合併) * 第六個參數是mask,若是指定,它的維度必須和輸入圖像一致,且在mask值爲0處不進行角點檢測 * 第七個參數是blockSize,表示在計算角點時參與運算的區域大小,經常使用值爲3,可是若是圖像的分辨率較高則能夠考慮使用較大一點的值 * 第八個參數用於指定角點檢測的方法,若是是true則使用Harris角點檢測,false則使用Shi Tomasi算法 * 第九個參數是在使用Harris算法時使用,最好使用默認值0.04。 */ cv::Mat image_distorted_pts = image_distorted.clone();// 在圖像中畫出檢測到的特徵點 for(auto& pt:pts){ circle(image_distorted_pts, pt, 2, cv::Scalar(255, 0, 0), 2); /** * 在圖像上面畫出檢測的到的特徵點 * cvCircle(CvArr* img, CvPoint center, int radius, CvScalar color, int thickness=1, int lineType=8, int shift=0) * img爲源圖像指針 * center爲畫圓的圓心座標 * radius爲圓的半徑 * color爲設定圓的顏色,規則根據B(藍)G(綠)R(紅) * thickness 若是是正數,表示組成圓的線條的粗細程度。不然,-1表示圓是否被填充 * line_type 線條的類型。默認是8 * shift 圓心座標點和半徑值的小數點位數 */ } cv::imshow("image_distorted_pts", image_distorted_pts); cv::waitKey();
cv::Mat image_distorted_rectangle = image_distorted.clone();// 畫出方框 cv::Rect Bbox{338, 141, 23, 57}; cv::rectangle(image_distorted_rectangle, Bbox, cv::Scalar(255, 0, 0), 2, 1); cv::imshow("image_distorted_rectangle", image_distorted_rectangle); cv::waitKey();
int image_Width, image_Height; image_Width = fSettings["image.Width"]; cout << image_Width << endl; // 752 image_Height = fSettings["image.Height"]; cout << image_Height << endl; // 480 cv::Mat image_undistort_diy = cv::Mat(image_Height, image_Width, CV_8UC1); for (int v = 0; v < image_Height; v++) {// 計算去畸變後圖像的內容 for (int u = 0; u < image_Width; u++) { double x = (u - K.at<float>(0,2)) / K.at<float>(0,0); // 按照公式計算點(u,v)對應到畸變圖像中的座標(u_image_undistort_diy, v_image_undistort_diy) double y = (v - K.at<float>(1,2)) / K.at<float>(1,1); double r = sqrt(x * x + y * y); double x_distorted = x * (1 + DistCoef.at<float>(0) * r * r + DistCoef.at<float>(1) * r * r * r * r) + 2 * DistCoef.at<float>(2) * x * y + DistCoef.at<float>(3) * (r * r + 2 * x * x); double y_distorted = y * (1 + DistCoef.at<float>(0) * r * r + DistCoef.at<float>(1) * r * r * r * r) + DistCoef.at<float>(2) * (r * r + 2 * y * y) + 2 * DistCoef.at<float>(3) * x * y; double u_distorted = K.at<float>(0,0) * x_distorted + K.at<float>(0,2); double v_distorted = K.at<float>(1,1) * y_distorted + K.at<float>(1,2); image_undistort_diy.at<uchar>(v, u) = image_distorted.at<uchar>((int) v_distorted, (int) u_distorted); } } cv::imshow("image_undistort_diy", image_undistort_diy); cv::waitKey();
uint N = pts.size();// 對特徵點去畸變,並顯示N爲提取的特徵點數量,將N個特徵點保存在N*2的mat中 cv::Mat mat_pts(N,2,CV_32F); for(int i=0; i<N; i++){ mat_pts.at<float>(i,0)=pts[i].x; mat_pts.at<float>(i,1)=pts[i].y; } mat_pts=mat_pts.reshape(2);// 調整mat的通道爲2,矩陣的行列形狀不變 cv::undistortPoints(mat_pts, mat_pts, K, DistCoef, cv::Mat(), K);// 對每個檢測到的特徵點進行去畸變 /** * void undistortPoints( InputArray src, OutputArray dst, InputArray cameraMatrix, InputArray distCoeffs, * InputArray R = noArray(), InputArray P = noArray()); */ mat_pts=mat_pts.reshape(1);
for(int i=0; i<N; i++){// 存儲去幾遍校訂後的特徵點 cv::Point2f kp = pts[i]; kp.x=mat_pts.at<float>(i,0); kp.y=mat_pts.at<float>(i,1); pts[i] = kp; }
cv::Mat image_undistort_diy_pts = image_undistort_diy.clone(); for(auto& pt:pts){// 將去畸變的特徵點畫在去畸變的圖像上 circle(image_undistort_diy_pts, pt, 2, cv::Scalar(0, 0, 255), 2); } cv::imshow("image_undistort_diy_pts", image_undistort_diy_pts); cv::waitKey();
cv::Mat mat_rectangle(4, 2, CV_32F); mat_rectangle.at<float>(0, 0) = Bbox.x; mat_rectangle.at<float>(0, 1) = Bbox.y;
mat_rectangle.at<float>(1, 0) = Bbox.x + Bbox.width; mat_rectangle.at<float>(1, 1) = Bbox.y;
mat_rectangle.at<float>(2, 0) = Bbox.x; mat_rectangle.at<float>(2, 1) = Bbox.y + Bbox.height;
mat_rectangle.at<float>(3, 0) = Bbox.x + Bbox.width; mat_rectangle.at<float>(3, 1) = Bbox.y + Bbox.height;
mat_rectangle = mat_rectangle.reshape(2); // 2通道,行列不變 cv::undistortPoints(mat_rectangle, mat_rectangle, K, DistCoef, cv::Mat(), K); // 一樣也是調用點的去畸變函數,對矩形方框的四個頂點去畸變並顯示 mat_rectangle = mat_rectangle.reshape(1); // 單通道,行列不變
double MaxX, MaxY; Bbox.x = min(mat_rectangle.at<float>(0, 0), mat_rectangle.at<float>(2, 0)); MaxX = max(mat_rectangle.at<float>(1, 0), mat_rectangle.at<float>(3, 0)); Bbox.y = min(mat_rectangle.at<float>(0, 1), mat_rectangle.at<float>(1, 1)); MaxY = max(mat_rectangle.at<float>(2, 1), mat_rectangle.at<float>(3, 1)); Bbox.width = MaxX - Bbox.x; Bbox.height = MaxY - Bbox.y; cv::Mat image_undistort_diy_rectangle = image_undistort_diy.clone(); cv::rectangle(image_undistort_diy_rectangle, Bbox, cv::Scalar(0, 0, 255), 2, 1); cv::imshow("image_undistort_diy_rectangle", image_undistort_diy_rectangle); cv::waitKey();

cv::Mat mapx = cv::Mat(image_distorted.size(), CV_32FC1); cv::Mat mapy = cv::Mat(image_distorted.size(), CV_32FC1); cv::Size imageSize(image_Width, image_Height); // const double alpha = 1; const double alpha = 0; cv::Mat NewCameraMatrix = getOptimalNewCameraMatrix(K, DistCoef, imageSize, alpha, imageSize, 0); // 調用opencv函數getOptimalNewCameraMatrix initUndistortRectifyMap remap 去畸變  /** * Mat cv::getOptimalNewCameraMatrix()函數的功能是"Return the new camera matrix based on the free scaling parameter" * 參數含義 * InputArray cameraMatrix, // 相機內參矩陣 * InputArray distCoeffs, // 相機畸變參數 * Size imageSize, // 圖像尺寸 * double alpha, // 縮放比例 * 當alpha=1 原圖像中的全部像素可以獲得保留,所以這個時候獲得的矯正後的圖像是帶黑框的 * 當alpha=0 獲得的圖像是不帶黑色邊框的,相對於原圖像,此時的圖像損失了部分像素 * Size newImgSize = Size(), // 校訂後的圖像尺寸 * Rect * validPixROI = 0, // 輸出感興趣區域設置 * bool centerPrincipalPoint = false // 可選標誌 * * 單目相機 newCameraMatrix能夠用cv::getOptimalNewCameraMatrix計算,或者直接與cameraMatrix相等 * 雙目相機 newCameraMatrix通常是用cv::stereoRectify計算 * 也能夠和原圖內參數相同,這時候圖像比alpha=0的時候仍是要損失更多像素,看上去圖像更不清晰了 */
initUndistortRectifyMap(K, DistCoef, cv::Mat(), NewCameraMatrix, imageSize, CV_16SC2, mapx, mapy); /** * void cv::initUndistortRectifyMap()函數用於計算原始圖像和矯正圖像之間的轉換關係,將結果以映射的形式表達映射關係存儲在map1和map2中 * 參數含義 * InputArray cameraMatrix, // 原相機內參矩陣 * InputArray distCoeffs, // 原相機畸變參數 * InputArray R, // 可選的修正變換矩陣 * InputArray newCameraMatrix, // 新相機內參矩陣 * Size size, // 去畸變後圖像的尺寸 * int m1type, // 第一個輸出的映射(map1)的類型,CV_32FC1 or CV_16SC2 * OutputArray map1, // 第一個輸出映射,存儲去畸變像素的橫座標 * OutputArray map2 // 第二個輸出映射,存儲去畸變像素的縱座標 */ cv::Mat image_undistort_NewCameraMatrix_remap; remap(image_distorted, image_undistort_NewCameraMatrix_remap, mapx, mapy, cv::INTER_LINEAR); /** * void cv::remap()函數功能:把原始圖像中某位置的像素映射到矯正後的圖像指定位置。 * 這裏的map1和map2就是上面cv::initUndistortRectifyMap()計算出來的結果。 * 參數含義 * InputArray src, // 原始圖像 * OutputArray dst, // 矯正圖像 * InputArray map1, // 第一個映射 * InputArray map2, // 第二個映射 * int interpolation, // 插值方式 * int borderMode=BORDER_CONSTANT, // 邊界模式 * const Scalar& borderValue=Scalar() // 邊界顏色,默認Scalar()黑色 */ cv::imshow("image_undistort_NewCameraMatrix_remap", image_undistort_NewCameraMatrix_remap); cv::waitKey();
initUndistortRectifyMap(K, DistCoef, cv::Mat(), K, imageSize, CV_16SC2, mapx, mapy); cv::Mat image_undistort_K_remap; remap(image_distorted, image_undistort_K_remap, mapx, mapy, cv::INTER_LINEAR); cv::imshow("image_undistort_K_remap", image_undistort_K_remap); cv::waitKey();

cv::Mat image_undistort_undistort; cv::undistort(image_distorted, image_undistort_undistort, K, DistCoef, NewCameraMatrix);// 調用opencv函數 undistort 去畸變 /** * void cv::undistort()函數功能對圖像進行畸變矯正,上面是去畸變分步驟的方法 * 若是undistort函數的最後一個參數使用原相機內參,那麼獲得的結果至關於alpha=0的狀況。 * 若是undistort函數的最後一個參數使用getOptimalNewCameraMatrix計算出來的新矩陣,那麼獲得損失像素後的圖像,當alpha=1時 * 有多個圖片須要矯正推薦組合的方法,這種方法適合圖片少的狀況 * 由於initUndistortRectifyMap函數只須要計算一次就行,不須要每次循環都計算 * undistort函數內部調用了initUndistortRectifyMap和remap * 函數參數 * InputArray src, // 原始圖像 * OutputArray dst, // 矯正圖像 * InputArray cameraMatrix, // 原相機內參矩陣 * InputArray distCoeffs, // 相機畸變參數 * InputArray newCameraMatrix = noArray() // 新相機內參矩陣 */ cv::imshow("image_undistort_undistort", image_undistort_undistort); cv::waitKey(); return 0;}
//https://zhuanlan.zhihu.com/p/74133719//https://zhuanlan.zhihu.com/p/137053640//https://blog.csdn.net/u013341645/article/details/78710740// 設置角點檢測參數// std::vector<cv::Point2f> corners;// int max_corners = 100;// double quality_level = 0.01;// double min_distance = 10;// int block_size = 3;// bool use_harris = false;// double k = 0.04;// // 指定亞像素計算迭代標註// cv::TermCriteria criteria = cv::TermCriteria(cv::TermCriteria::MAX_ITER + cv::TermCriteria::EPS,40,0.01);// // 亞像素檢測// cv::cornerSubPix(RawImage_Gray, corners, cv::Size(5, 5), cv::Size(-1, -1), criteria);//    // 將檢測到的亞像素角點繪製到複製的原圖上
8.CMakeLists.txt編譯文件
cmake_minimum_required(VERSION 2.8)project(imageUndistort)
set(CMAKE_BUILD_TYPE "Debug")
set(CMAKE_CXX_STANDARD 11)
# 添加c++ 11標準支持set(CMAKE_CXX_FLAGS "-std=c++11 -O2")# 尋找OpenCV庫find_package(OpenCV REQUIRED)include_directories(${OpenCV_INCLUDE_DIRS})
add_executable(b-undisort b-undisort.cpp)target_link_libraries(b-undisort ${OpenCV_LIBS})
9.代碼執行環境
Ubuntu16.04Clionopencv-3.4.0

10.代碼執行原圖app

添加小秋微信「xiaoqiuslambiji」獲取函數

11.相機參數yaml文件

%YAML:1.0
# Camera calibration and distortion parameters (OpenCV) Camera.fx: 458.654Camera.fy: 457.296Camera.cx: 367.215Camera.cy: 248.375
Camera.k1: -0.28340811Camera.k2: 0.07395907Camera.p1: 0.00019359Camera.p2: 1.76187114e-05
image.Width: 752image.Height: 480

往期教程彙總

Cmake編譯教程

源代碼是如何變成可執行程序的 ?(一)

源代碼是如何變成可執行程序的 ?(二)

源代碼是如何變成可執行程序的 ?(三)

源代碼是如何變成可執行程序的 ?(四)

源代碼是如何變成可執行程序的 ?(五)

源代碼是如何變成可執行程序的 ?(六)

GDB調試教程

GDB調試源碼以及出現段錯誤核心已轉存的錯誤如何藉助core dump文件去解決問題

Clion編譯和調試教程

Ubuntu16.04系統下安裝和配置CLion編譯器詳細教程

Ubuntu16.04系統下用CLion編譯器調試C++代碼詳細圖文視頻教程

Ubuntu16.04系統下用CLion編譯器調試ROS代碼詳細教程

OpenCV|C++代碼實踐

OpenCV|C++代碼實踐之圖像基礎操做

OpenCV|C++代碼實踐之圖像的旋轉平移仿射和透視變換

OpenCV|C++代碼實踐之圖像混合和同時顯示多張圖像

OpenCV|C++代碼實踐之圖像的濾波操做和添加噪聲

OpenCV|C++代碼實踐之圖像上繪製文本空心和實心圖形

OpenCV|C++代碼實踐之從文件或終端讀寫yaml和txt文件

OpenCV|C++代碼實踐之兩種圖像批量重命名方法

OpenCV|C++代碼實踐之圖像提取過濾特徵

點擊下方閱讀原文可在CSDN得到更好閱讀體驗,掃描或者長按下方二維碼添加小秋私人微信,可答疑並邀請加入微信QQ交流羣,本文部份內容整理來自網絡若有侵權請聯繫刪除。

 
    
    
    
     
     
              
     
 
    

本文分享自微信公衆號 - 小秋SLAM筆記(gh_df091a0565ac)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索