接上一篇文章讲解的相机标定的基本概念,这篇文章主要阐述一下如何使用Matlab或者Opencv等软件得的Camera内参之后,如何保存调用内参完成对应Camera的标定流程。
一、Matlab标定鱼眼镜头实践
1. 这里我使用的Camera是Jetson Nano专用的鱼眼摄像机,具体参数如下所示:
我使用的畸变较大的鱼眼镜头。
由于我采用的是红外夜视的Camera,结果在光线稍微不好的时候就会出现图片偏红色的情况(解决方案:微雪Camera-wiki)
如果大家没有必要的夜视应用场景,建议不要使用红外夜视的摄像头,如果在后期处理的过程中还要对畸变图像矫正,建议就不要使用广角鱼眼摄像头了,因为矫正之后的视野区域也会有所减少,这一点可以在后面矫正的程序中看到(在畸变矫正的过程中,也需要消耗部分CPU资源,这样对于嵌入式板卡来说也许就不划算了)
2. 基于JetsonNano板卡,CSI摄像头的标定图采集Demo程序如下:
1 import cv2 2 import sys 3 import glob 4 import time 5 import threading 6 import numpy as np 7 8 if len(sys.argv) == 1: 9 print 'Please Choose the CSI-Camera id for image sample.Try again!' 10 exit(0) 11 12 Camera_ID = int(sys.argv[1]) 13 14 def gstreamer_pipeline( 15 csi_camera_id = 0, 16 capture_width=1280, 17 capture_height=720, 18 display_width=1280, 19 display_height=720, 20 framerate=60, 21 flip_method=0, 22 ): 23 return ( 24 "nvarguscamerasrc sensor_id=%d ! " 25 "video/x-raw(memory:NVMM), " 26 "width=(int)%d, height=(int)%d, " 27 "format=(string)NV12, framerate=(fraction)%d/1 ! " 28 "nvvidconv flip-method=%d ! " 29 "video/x-raw, width=(int)%d, height=(int)%d, format=(string)BGRx ! " 30 "videoconvert ! " 31 "video/x-raw, format=(string)BGR ! appsink" 32 % ( 33 csi_camera_id, 34 capture_width, 35 capture_height, 36 framerate, 37 flip_method, 38 display_width, 39 display_height, 40 ) 41 ) 42 43 print 'Starting Capture...' 44 csi_pipeline_str = gstreamer_pipeline(csi_camera_id=Camera_ID,flip_method=0) 45 print csi_pipeline_str 46 cap = cv2.VideoCapture(csi_pipeline_str, cv2.CAP_GSTREAMER) 47 count = 0 48 while not cap.isOpened(): 49 time.sleep(0.5) 50 print('Camera is Initialize...') 51 count += 1 52 if count == 10: 53 print 'CSICamera Initial Failed!' 54 cap.release() 55 exit(0) 56 57 ret, frame = cap.read() 58 59 Key_val = 0 60 61 def Keybo_Moni(): 62 count = 0 63 while True: 64 global Key_val, frame, process_flag, cap 65 if Key_val == ord('r'): 66 Key_val= 0 67 cv2.imwrite('CSICamera' + str(Camera_ID) + '-' + str(count) + '.jpg', frame) 68 count += 1 69 print('Get new pic %d' % count) 70 if Key_val == ord('q'): 71 break 72 73 try: 74 Keybo_Moni_Thread = threading.Thread(target=Keybo_Moni, name='Keyboard-Thread') 75 Keybo_Moni_Thread.start() 76 except: 77 print('Error:uqnable to start the thread!') 78 79 while True: 80 ret, frame = cap.read() 81 cv2.imshow('Video_Show', frame) 82 Key_val = cv2.waitKey(1) 83 if Key_val == ord('q'): 84 cv2.destroyAllWindows() 85 cap.release() 86 print('Pic Sample Finished!') 87 break 88 89 print('Finished CalibrateCamera Image Sample...')
程序中涉及到了Gstreamer等多媒体pipeline框架内容,请参考这里,Python脚本的使用方法:
python CSICAM_Calibraton_Sample.py 1 # 采集CSI-Camera1摄像头的内容 python CSICAM_Calibraton_Sample.py 0 # 采集CSI-Camera0摄像头的内容
如果你使用的其他的USB Camera,可以参考使用如下程序:
1 import cv2 2 import glob 3 import time 4 import threading 5 import numpy as np 6 7 print('Starting Capture...') 8 cap = cv2.VideoCapture(2) 9 while not cap.isOpened(): 10 time.sleep(100) 11 print('Camera is Initialize...') 12 13 width = int(cap.get(3)) 14 height = int(cap.get(4)) 15 16 frame = np.zeros((width,height,3),dtype=np.uint8) 17 ret, frame = cap.read() 18 19 Key_val = 0 20 21 def Keybo_Moni(): 22 count = 0 23 while True: 24 global Key_val, frame, process_flag, cap 25 if Key_val == ord('r'): 26 Key_val= 0 27 cv2.imwrite('ResPic' + str(count) + '.jpg', frame) 28 count += 1 29 print('Get new pic %d' % count) 30 if Key_val == ord('q'): 31 Key_val = 'q' 32 break; 33 34 try: 35 Keybo_Moni_Thread = threading.Thread(target=Keybo_Moni, name='Keyboard-Thread') 36 Keybo_Moni_Thread.start() 37 except: 38 print('Error:uqnable to start the thread!') 39 40 while True: 41 ret, frame = cap.read() 42 cv2.imshow('Video_Show', frame) 43 Key_val = cv2.waitKey(1) 44 if Key_val == ord('q'): 45 cv2.destroyAllWindows() 46 cap.release() 47 print('Pic Sample Finished!') 48 break 49 50 print('Finished CalibrateCamera Image Sample...')
3. 接下来使用采集图像进行Matlab进行标定
能够看到,矫正之后的图像还是粉粉的...(问题待解决!)
具体matlab进行相机标定流程请参看我前一篇内容,下面是矫正前后的效果图:
从两图对比中,我们可以看出,section_a区域的部分以及Section_b的部分都发生了明显的变化,原来应该显示的内容由于图像在矫正的过程中根据内插法消失了,那么我们如何获取Camera的内参呢?
对于Matlab之前的版本直接参看上一篇教程,由于我使用的是2019b版本的Matlab,使用操作方式有所不同,在这里说明如下:
a) 点击工具栏位置的 Export Camera Parameters,这样会输出相机的基本内参以及外参到Matlab的工作空间中:
这样就会将相机参数保存在工作空间,名称为cameraParams。
在工作区中,可以找到cameraParams变量,单机右键,打开所选内容,如下所示:
将图中颜色标出的参数保存即可。
同时这里我们有必要将工作空间中cameraParams mat变量保存下来为下一次矫正的过程使用提供相机的内参数据,单机右键点击另存为,将相机的参数数据保存即可(参考.mat数据保存使用)。
b) 点击工具栏位置的 Export Camera Parameters下拉箭头,选择Generate Matalab script:
% Auto-generated by cameraCalibrator app on 15-Apr-2020 %------------------------------------------------------- % Define images to process imageFileNames = {'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-0.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-3.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-4.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-5.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-7.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-8.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-9.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-10.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-11.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-12.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-13.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-14.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-15.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-16.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-17.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-18.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-19.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-20.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-21.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-22.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-23.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-24.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-25.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-26.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-27.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-28.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-29.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-30.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-31.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-32.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-33.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-34.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-35.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-36.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-37.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-40.jpg',... 'C:UsersAdministratorDesktopImageDataCAM0CSICamera0-41.jpg',... }; % Detect checkerboards in images [imagePoints, boardSize, imagesUsed] = detectCheckerboardPoints(imageFileNames); imageFileNames = imageFileNames(imagesUsed); % Read the first image to obtain image size originalImage = imread(imageFileNames{1}); [mrows, ncols, ~] = size(originalImage); % Generate world coordinates of the corners of the squares squareSize = 40; % in units of 'millimeters' worldPoints = generateCheckerboardPoints(boardSize, squareSize); % Calibrate the camera [cameraParams, imagesUsed, estimationErrors] = estimateCameraParameters(imagePoints, worldPoints, ... 'EstimateSkew', false, 'EstimateTangentialDistortion', false, ... 'NumRadialDistortionCoefficients', 2, 'WorldUnits', 'millimeters', ... 'InitialIntrinsicMatrix', [], 'InitialRadialDistortion', [], ... 'ImageSize', [mrows, ncols]); % View reprojection errors h1=figure; showReprojectionErrors(cameraParams); % Visualize pattern locations h2=figure; showExtrinsics(cameraParams, 'CameraCentric'); % Display parameter estimation errors displayErrors(estimationErrors, cameraParams); % For example, you can use the calibration data to remove effects of lens distortion. undistortedImage = undistortImage(originalImage, cameraParams); % See additional examples of how to use the calibration data. At the prompt type: % showdemo('MeasuringPlanarObjectsExample') % showdemo('StructureFromMotionExample')
运行刚刚生成保存的代码,得到如下的结果:
Standard Errors of Estimated Camera Parameters ---------------------------------------------- Intrinsics ---------- Focal length (pixels): [ 794.4361 +/- 1.3790 796.3933 +/- 1.3197 ] Principal point (pixels):[ 606.8329 +/- 0.8403 393.2514 +/- 0.9863 ] Radial distortion: [ -0.3111 +/- 0.0010 0.0837 +/- 0.0007 ] Extrinsics ---------- Rotation vectors: [ -0.2723 +/- 0.0024 0.0407 +/- 0.0022 0.0792 +/- 0.0005 ] ... Translation vectors (millimeters): [ -90.3841 +/- 0.5212 -121.9121 +/- 0.6037 485.5467 +/- 0.8995 ] ...
根据上述结果,我们可以得到此鱼眼相机的畸变参数为(k1=-0.3111 k2=0.0837),使用这个参数进行图像的畸变矫正即可。
4. 使用畸变参数对Camera采集的图像进行矫正:
将3步骤中保存下来的cameraParams Mat数据加载到Matlab当中:
load cameraParams
注意,此时必须添加cameraParams文件保存的文件空间到Matlab工作空间中来,加载完成之后就可以使用参数进行相机采集图片的矫正了:
I = imread('C:UsersAdministratorDesktopImageDataCAM0CSICamera0-41.jpg'); I_R = undistortImage(I, cameraParams); figure, imshow(I_R) figure, imshow(I)
运行结果如下所示:
至此,关于Matlab这端的图像畸变矫正过程就算是完成了,如何使用畸变以及相机内参完成畸变矫正参看下面链接:
相机畸变校正详解:https://blog.csdn.net/hanxiaoyong_/article/details/82888946
二、Cpp-Opencv标定鱼眼镜头实践
1. 依然使用之前采集的图片源,使用Opencv官方代码进行Camera的矫正:
首先下载官方源代码:https://github.com/opencv/opencv/tree/master/samples/cpp/tutorial_code/calib3d/camera_calibration
参考官方标定算法手册:https://docs.opencv.org/master/d4/d94/tutorial_camera_calibration.html
下载很慢的可以直接拷贝我的程序:
camera_calibration.cpp (主程序文件,包括了图像输入,图像角点提取,图形标定,相机参数保存等功能模块)
1 #include <iostream> 2 #include <sstream> 3 #include <string> 4 #include <ctime> 5 #include <cstdio> 6 7 #include <opencv2/core.hpp> 8 #include <opencv2/core/utility.hpp> 9 #include <opencv2/imgproc.hpp> 10 #include <opencv2/calib3d.hpp> 11 #include <opencv2/imgcodecs.hpp> 12 #include <opencv2/videoio.hpp> 13 #include <opencv2/highgui.hpp> 14 15 using namespace cv; 16 using namespace std; 17 18 class Settings 19 { 20 public: 21 Settings() : goodInput(false) {} 22 enum Pattern { NOT_EXISTING, CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID }; 23 enum InputType { INVALID, CAMERA, VIDEO_FILE, IMAGE_LIST }; 24 25 void write(FileStorage& fs) const //Write serialization for this class 26 { 27 fs << "{" 28 << "BoardSize_Width" << boardSize.width 29 << "BoardSize_Height" << boardSize.height 30 << "Square_Size" << squareSize 31 << "Calibrate_Pattern" << patternToUse 32 << "Calibrate_NrOfFrameToUse" << nrFrames 33 << "Calibrate_FixAspectRatio" << aspectRatio 34 << "Calibrate_AssumeZeroTangentialDistortion" << calibZeroTangentDist 35 << "Calibrate_FixPrincipalPointAtTheCenter" << calibFixPrincipalPoint 36 37 << "Write_DetectedFeaturePoints" << writePoints 38 << "Write_extrinsicParameters" << writeExtrinsics 39 << "Write_gridPoints" << writeGrid 40 << "Write_outputFileName" << outputFileName 41 42 << "Show_UndistortedImage" << showUndistorsed 43 44 << "Input_FlipAroundHorizontalAxis" << flipVertical 45 << "Input_Delay" << delay 46 << "Input" << input 47 << "}"; 48 } 49 void read(const FileNode& node) //Read serialization for this class 50 { 51 node["BoardSize_Width" ] >> boardSize.width; 52 node["BoardSize_Height"] >> boardSize.height; 53 node["Calibrate_Pattern"] >> patternToUse; 54 node["Square_Size"] >> squareSize; 55 node["Calibrate_NrOfFrameToUse"] >> nrFrames; 56 node["Calibrate_FixAspectRatio"] >> aspectRatio; 57 node["Write_DetectedFeaturePoints"] >> writePoints; 58 node["Write_extrinsicParameters"] >> writeExtrinsics; 59 node["Write_gridPoints"] >> writeGrid; 60 node["Write_outputFileName"] >> outputFileName; 61 node["Calibrate_AssumeZeroTangentialDistortion"] >> calibZeroTangentDist; 62 node["Calibrate_FixPrincipalPointAtTheCenter"] >> calibFixPrincipalPoint; 63 node["Calibrate_UseFisheyeModel"] >> useFisheye; 64 node["Input_FlipAroundHorizontalAxis"] >> flipVertical; 65 node["Show_UndistortedImage"] >> showUndistorsed; 66 node["Input"] >> input; 67 node["Input_Delay"] >> delay; 68 node["Fix_K1"] >> fixK1; 69 node["Fix_K2"] >> fixK2; 70 node["Fix_K3"] >> fixK3; 71 node["Fix_K4"] >> fixK4; 72 node["Fix_K5"] >> fixK5; 73 74 validate(); 75 } 76 void validate() 77 { 78 goodInput = true; 79 if (boardSize.width <= 0 || boardSize.height <= 0) 80 { 81 cerr << "Invalid Board size: " << boardSize.width << " " << boardSize.height << endl; 82 goodInput = false; 83 } 84 if (squareSize <= 10e-6) 85 { 86 cerr << "Invalid square size " << squareSize << endl; 87 goodInput = false; 88 } 89 if (nrFrames <= 0) 90 { 91 cerr << "Invalid number of frames " << nrFrames << endl; 92 goodInput = false; 93 } 94 95 if (input.empty()) // Check for valid input 96 inputType = INVALID; 97 else 98 { 99 if (input[0] >= '0' && input[0] <= '9') 100 { 101 stringstream ss(input); 102 ss >> cameraID; 103 inputType = CAMERA; 104 } 105 else 106 { 107 if (isListOfImages(input) && readStringList(input, imageList)) 108 { 109 inputType = IMAGE_LIST; 110 nrFrames = (nrFrames < (int)imageList.size()) ? nrFrames : (int)imageList.size(); 111 } 112 else 113 inputType = VIDEO_FILE; 114 } 115 if (inputType == CAMERA) 116 inputCapture.open(cameraID); 117 if (inputType == VIDEO_FILE) 118 inputCapture.open(input); 119 if (inputType != IMAGE_LIST && !inputCapture.isOpened()) 120 inputType = INVALID; 121 } 122 if (inputType == INVALID) 123 { 124 cerr << " Input does not exist: " << input; 125 goodInput = false; 126 } 127 128 flag = 0; 129 if(calibFixPrincipalPoint) flag |= CALIB_FIX_PRINCIPAL_POINT; 130 if(calibZeroTangentDist) flag |= CALIB_ZERO_TANGENT_DIST; 131 if(aspectRatio) flag |= CALIB_FIX_ASPECT_RATIO; 132 if(fixK1) flag |= CALIB_FIX_K1; 133 if(fixK2) flag |= CALIB_FIX_K2; 134 if(fixK3) flag |= CALIB_FIX_K3; 135 if(fixK4) flag |= CALIB_FIX_K4; 136 if(fixK5) flag |= CALIB_FIX_K5; 137 138 if (useFisheye) { 139 // the fisheye model has its own enum, so overwrite the flags 140 flag = fisheye::CALIB_FIX_SKEW | fisheye::CALIB_RECOMPUTE_EXTRINSIC; 141 if(fixK1) flag |= fisheye::CALIB_FIX_K1; 142 if(fixK2) flag |= fisheye::CALIB_FIX_K2; 143 if(fixK3) flag |= fisheye::CALIB_FIX_K3; 144 if(fixK4) flag |= fisheye::CALIB_FIX_K4; 145 if (calibFixPrincipalPoint) flag |= fisheye::CALIB_FIX_PRINCIPAL_POINT; 146 } 147 148 calibrationPattern = NOT_EXISTING; 149 if (!patternToUse.compare("CHESSBOARD")) calibrationPattern = CHESSBOARD; 150 if (!patternToUse.compare("CIRCLES_GRID")) calibrationPattern = CIRCLES_GRID; 151 if (!patternToUse.compare("ASYMMETRIC_CIRCLES_GRID")) calibrationPattern = ASYMMETRIC_CIRCLES_GRID; 152 if (calibrationPattern == NOT_EXISTING) 153 { 154 cerr << " Camera calibration mode does not exist: " << patternToUse << endl; 155 goodInput = false; 156 } 157 atImageList = 0; 158 159 } 160 Mat nextImage() 161 { 162 Mat result; 163 if( inputCapture.isOpened() ) 164 { 165 Mat view0; 166 inputCapture >> view0; 167 view0.copyTo(result); 168 } 169 else if( atImageList < imageList.size() ) 170 result = imread(imageList[atImageList++], IMREAD_COLOR); 171 172 return result; 173 } 174 175 static bool readStringList( const string& filename, vector<string>& l ) 176 { 177 l.clear(); 178 FileStorage fs(filename, FileStorage::READ); 179 if( !fs.isOpened() ) 180 return false; 181 FileNode n = fs.getFirstTopLevelNode(); 182 if( n.type() != FileNode::SEQ ) 183 return false; 184 FileNodeIterator it = n.begin(), it_end = n.end(); 185 for( ; it != it_end; ++it ) 186 l.push_back((string)*it); 187 return true; 188 } 189 190 static bool isListOfImages( const string& filename) 191 { 192 string s(filename); 193 // Look for file extension 194 if( s.find(".xml") == string::npos && s.find(".yaml") == string::npos && s.find(".yml") == string::npos ) 195 return false; 196 else 197 return true; 198 } 199 public: 200 Size boardSize; // The size of the board -> Number of items by width and height 201 Pattern calibrationPattern; // One of the Chessboard, circles, or asymmetric circle pattern 202 float squareSize; // The size of a square in your defined unit (point, millimeter,etc). 203 int nrFrames; // The number of frames to use from the input for calibration 204 float aspectRatio; // The aspect ratio 205 int delay; // In case of a video input 206 bool writePoints; // Write detected feature points 207 bool writeExtrinsics; // Write extrinsic parameters 208 bool writeGrid; // Write refined 3D target grid points 209 bool calibZeroTangentDist; // Assume zero tangential distortion 210 bool calibFixPrincipalPoint; // Fix the principal point at the center 211 bool flipVertical; // Flip the captured images around the horizontal axis 212 string outputFileName; // The name of the file where to write 213 bool showUndistorsed; // Show undistorted images after calibration 214 string input; // The input -> 215 bool useFisheye; // use fisheye camera model for calibration 216 bool fixK1; // fix K1 distortion coefficient 217 bool fixK2; // fix K2 distortion coefficient 218 bool fixK3; // fix K3 distortion coefficient 219 bool fixK4; // fix K4 distortion coefficient 220 bool fixK5; // fix K5 distortion coefficient 221 222 int cameraID; 223 vector<string> imageList; 224 size_t atImageList; 225 VideoCapture inputCapture; 226 InputType inputType; 227 bool goodInput; 228 int flag; 229 230 private: 231 string patternToUse; 232 233 234 }; 235 236 static inline void read(const FileNode& node, Settings& x, const Settings& default_value = Settings()) 237 { 238 if(node.empty()) 239 x = default_value; 240 else 241 x.read(node); 242 } 243 244 enum { DETECTION = 0, CAPTURING = 1, CALIBRATED = 2 }; 245 246 bool runCalibrationAndSave(Settings& s, Size imageSize, Mat& cameraMatrix, Mat& distCoeffs, 247 vector<vector<Point2f> > imagePoints, float grid_width, bool release_object); 248 249 int main(int argc, char* argv[]) 250 { 251 const String keys 252 = "{help h usage ? | | print this message }" 253 "{@settings |default.xml| input setting file }" 254 "{d | | actual distance between top-left and top-right corners of " 255 "the calibration grid }" 256 "{winSize | 11 | Half of search window for cornerSubPix }"; 257 CommandLineParser parser(argc, argv, keys); 258 parser.about("This is a camera calibration sample. " 259 "Usage: camera_calibration [configuration_file -- default ./default.xml] " 260 "Near the sample file you'll find the configuration file, which has detailed help of " 261 "how to edit it. It may be any OpenCV supported file format XML/YAML."); 262 if (!parser.check()) { 263 parser.printErrors(); 264 return 0; 265 } 266 267 if (parser.has("help")) { 268 parser.printMessage(); 269 return 0; 270 } 271 272 //! [file_read] 273 Settings s; 274 const string inputSettingsFile = parser.get<string>(0); 275 FileStorage fs(inputSettingsFile, FileStorage::READ); // Read the settings 276 if (!fs.isOpened()) 277 { 278 cout << "Could not open the configuration file: "" << inputSettingsFile << """ << endl; 279 parser.printMessage(); 280 return -1; 281 } 282 fs["Settings"] >> s; 283 fs.release(); // close Settings file 284 //! [file_read] 285 286 //FileStorage fout("settings.yml", FileStorage::WRITE); // write config as YAML 287 //fout << "Settings" << s; 288 289 if (!s.goodInput) 290 { 291 cout << "Invalid input detected. Application stopping. " << endl; 292 return -1; 293 } 294 295 int winSize = parser.get<int>("winSize"); 296 297 float grid_width = s.squareSize * (s.boardSize.width - 1); 298 bool release_object = false; 299 if (parser.has("d")) { 300 grid_width = parser.get<float>("d"); 301 release_object = true; 302 } 303 304 vector<vector<Point2f> > imagePoints; 305 Mat cameraMatrix, distCoeffs; 306 Size imageSize; 307 int mode = s.inputType == Settings::IMAGE_LIST ? CAPTURING : DETECTION; 308 clock_t prevTimestamp = 0; 309 const Scalar RED(0,0,255), GREEN(0,255,0); 310 const char ESC_KEY = 27; 311 312 //! [get_input] 313 for(;;) 314 { 315 Mat view; 316 bool blinkOutput = false; 317 318 view = s.nextImage(); 319 320 //----- If no more image, or got enough, then stop calibration and show result ------------- 321 if( mode == CAPTURING && imagePoints.size() >= (size_t)s.nrFrames ) 322 { 323 if(runCalibrationAndSave(s, imageSize, cameraMatrix, distCoeffs, imagePoints, grid_width, 324 release_object)) 325 mode = CALIBRATED; 326 else 327 mode = DETECTION; 328 } 329 if(view.empty()) // If there are no more images stop the loop 330 { 331 // if calibration threshold was not reached yet, calibrate now 332 if( mode != CALIBRATED && !imagePoints.empty() ) 333 runCalibrationAndSave(s, imageSize, cameraMatrix, distCoeffs, imagePoints, grid_width, 334 release_object); 335 break; 336 } 337 //! [get_input] 338 339 imageSize = view.size(); // Format input image. 340 if( s.flipVertical ) flip( view, view, 0 ); 341 342 //! [find_pattern] 343 vector<Point2f> pointBuf; 344 345 bool found; 346 347 int chessBoardFlags = CALIB_CB_ADAPTIVE_THRESH | CALIB_CB_NORMALIZE_IMAGE; 348 349 if(!s.useFisheye) { 350 // fast check erroneously fails with high distortions like fisheye 351 chessBoardFlags |= CALIB_CB_FAST_CHECK; 352 } 353 354 switch( s.calibrationPattern ) // Find feature points on the input format 355 { 356 case Settings::CHESSBOARD: 357 found = findChessboardCorners( view, s.boardSize, pointBuf, chessBoardFlags); 358 break; 359 case Settings::CIRCLES_GRID: 360 found = findCirclesGrid( view, s.boardSize, pointBuf ); 361 break; 362 case Settings::ASYMMETRIC_CIRCLES_GRID: 363 found = findCirclesGrid( view, s.boardSize, pointBuf, CALIB_CB_ASYMMETRIC_GRID ); 364 break; 365 default: 366 found = false; 367 break; 368 } 369 //! [find_pattern] 370 //! [pattern_found] 371 if ( found) // If done with success, 372 { 373 // improve the found corners' coordinate accuracy for chessboard 374 if( s.calibrationPattern == Settings::CHESSBOARD) 375 { 376 Mat viewGray; 377 cvtColor(view, viewGray, COLOR_BGR2GRAY); 378 cornerSubPix( viewGray, pointBuf, Size(winSize,winSize), 379 Size(-1,-1), TermCriteria( TermCriteria::EPS+TermCriteria::COUNT, 30, 0.0001 )); 380 } 381 382 if( mode == CAPTURING && // For camera only take new samples after delay time 383 (!s.inputCapture.isOpened() || clock() - prevTimestamp > s.delay*1e-3*CLOCKS_PER_SEC) ) 384 { 385 imagePoints.push_back(pointBuf); 386 prevTimestamp = clock(); 387 blinkOutput = s.inputCapture.isOpened(); 388 } 389 390 // Draw the corners. 391 drawChessboardCorners( view, s.boardSize, Mat(pointBuf), found ); 392 } 393 //! [pattern_found] 394 //----------------------------- Output Text ------------------------------------------------ 395 //! [output_text] 396 string msg = (mode == CAPTURING) ? "100/100" : 397 mode == CALIBRATED ? "Calibrated" : "Press 'g' to start"; 398 int baseLine = 0; 399 Size textSize = getTextSize(msg, 1, 1, 1, &baseLine); 400 Point textOrigin(view.cols - 2*textSize.width - 10, view.rows - 2*baseLine - 10); 401 402 if( mode == CAPTURING ) 403 { 404 if(s.showUndistorsed) 405 msg = format( "%d/%d Undist", (int)imagePoints.size(), s.nrFrames ); 406 else 407 msg = format( "%d/%d", (int)imagePoints.size(), s.nrFrames ); 408 } 409 410 putText( view, msg, textOrigin, 1, 1, mode == CALIBRATED ? GREEN : RED); 411 412 if( blinkOutput ) 413 bitwise_not(view, view); 414 //! [output_text] 415 //------------------------- Video capture output undistorted ------------------------------ 416 //! [output_undistorted] 417 if( mode == CALIBRATED && s.showUndistorsed ) 418 { 419 Mat temp = view.clone(); 420 if (s.useFisheye) 421 cv::fisheye::undistortImage(temp, view, cameraMatrix, distCoeffs); 422 else 423 undistort(temp, view, cameraMatrix, distCoeffs); 424 } 425 //! [output_undistorted] 426 //------------------------------ Show image and check for input commands ------------------- 427 //! [await_input] 428 imshow("Image View", view); 429 char key = (char)waitKey(s.inputCapture.isOpened() ? 50 : s.delay); 430 431 if( key == ESC_KEY ) 432 break; 433 434 if( key == 'u' && mode == CALIBRATED ) 435 s.showUndistorsed = !s.showUndistorsed; 436 437 if( s.inputCapture.isOpened() && key == 'g' ) 438 { 439 mode = CAPTURING; 440 imagePoints.clear(); 441 } 442 //! [await_input] 443 } 444 445 // -----------------------Show the undistorted image for the image list ------------------------ 446 //! [show_results] 447 if( s.inputType == Settings::IMAGE_LIST && s.showUndistorsed ) 448 { 449 Mat view, rview, map1, map2; 450 451 if (s.useFisheye) 452 { 453 Mat newCamMat; 454 fisheye::estimateNewCameraMatrixForUndistortRectify(cameraMatrix, distCoeffs, imageSize, 455 Matx33d::eye(), newCamMat, 1); 456 fisheye::initUndistortRectifyMap(cameraMatrix, distCoeffs, Matx33d::eye(), newCamMat, imageSize, 457 CV_16SC2, map1, map2); 458 } 459 else 460 { 461 initUndistortRectifyMap( 462 cameraMatrix, distCoeffs, Mat(), 463 getOptimalNewCameraMatrix(cameraMatrix, distCoeffs, imageSize, 1, imageSize, 0), imageSize, 464 CV_16SC2, map1, map2); 465 } 466 467 for(size_t i = 0; i < s.imageList.size(); i++ ) 468 { 469 view = imread(s.imageList[i], IMREAD_COLOR); 470 if(view.empty()) 471 continue; 472 remap(view, rview, map1, map2, INTER_LINEAR); 473 imshow("Image View", rview); 474 char c = (char)waitKey(); 475 if( c == ESC_KEY || c == 'q' || c == 'Q' ) 476 break; 477 } 478 } 479 //! [show_results] 480 481 return 0; 482 } 483 484 //! [compute_errors] 485 static double computeReprojectionErrors( const vector<vector<Point3f> >& objectPoints, 486 const vector<vector<Point2f> >& imagePoints, 487 const vector<Mat>& rvecs, const vector<Mat>& tvecs, 488 const Mat& cameraMatrix , const Mat& distCoeffs, 489 vector<float>& perViewErrors, bool fisheye) 490 { 491 vector<Point2f> imagePoints2; 492 size_t totalPoints = 0; 493 double totalErr = 0, err; 494 perViewErrors.resize(objectPoints.size()); 495 496 for(size_t i = 0; i < objectPoints.size(); ++i ) 497 { 498 if (fisheye) 499 { 500 fisheye::projectPoints(objectPoints[i], imagePoints2, rvecs[i], tvecs[i], cameraMatrix, 501 distCoeffs); 502 } 503 else 504 { 505 projectPoints(objectPoints[i], rvecs[i], tvecs[i], cameraMatrix, distCoeffs, imagePoints2); 506 } 507 err = norm(imagePoints[i], imagePoints2, NORM_L2); 508 509 size_t n = objectPoints[i].size(); 510 perViewErrors[i] = (float) std::sqrt(err*err/n); 511 totalErr += err*err; 512 totalPoints += n; 513 } 514 515 return std::sqrt(totalErr/totalPoints); 516 } 517 //! [compute_errors] 518 //! [board_corners] 519 static void calcBoardCornerPositions(Size boardSize, float squareSize, vector<Point3f>& corners, 520 Settings::Pattern patternType /*= Settings::CHESSBOARD*/) 521 { 522 corners.clear(); 523 524 switch(patternType) 525 { 526 case Settings::CHESSBOARD: 527 case Settings::CIRCLES_GRID: 528 for( int i = 0; i < boardSize.height; ++i ) 529 for( int j = 0; j < boardSize.width; ++j ) 530 corners.push_back(Point3f(j*squareSize, i*squareSize, 0)); 531 break; 532 533 case Settings::ASYMMETRIC_CIRCLES_GRID: 534 for( int i = 0; i < boardSize.height; i++ ) 535 for( int j = 0; j < boardSize.width; j++ ) 536 corners.push_back(Point3f((2*j + i % 2)*squareSize, i*squareSize, 0)); 537 break; 538 default: 539 break; 540 } 541 } 542 //! [board_corners] 543 static bool runCalibration( Settings& s, Size& imageSize, Mat& cameraMatrix, Mat& distCoeffs, 544 vector<vector<Point2f> > imagePoints, vector<Mat>& rvecs, vector<Mat>& tvecs, 545 vector<float>& reprojErrs, double& totalAvgErr, vector<Point3f>& newObjPoints, 546 float grid_width, bool release_object) 547 { 548 //! [fixed_aspect] 549 cameraMatrix = Mat::eye(3, 3, CV_64F); 550 if( s.flag & CALIB_FIX_ASPECT_RATIO ) 551 cameraMatrix.at<double>(0,0) = s.aspectRatio; 552 //! [fixed_aspect] 553 if (s.useFisheye) { 554 distCoeffs = Mat::zeros(4, 1, CV_64F); 555 } else { 556 distCoeffs = Mat::zeros(8, 1, CV_64F); 557 } 558 559 vector<vector<Point3f> > objectPoints(1); 560 calcBoardCornerPositions(s.boardSize, s.squareSize, objectPoints[0], s.calibrationPattern); 561 objectPoints[0][s.boardSize.width - 1].x = objectPoints[0][0].x + grid_width; 562 newObjPoints = objectPoints[0]; 563 564 objectPoints.resize(imagePoints.size(),objectPoints[0]); 565 566 //Find intrinsic and extrinsic camera parameters 567 double rms; 568 569 if (s.useFisheye) { 570 Mat _rvecs, _tvecs; 571 rms = fisheye::calibrate(objectPoints, imagePoints, imageSize, cameraMatrix, distCoeffs, _rvecs, 572 _tvecs, s.flag); 573 574 rvecs.reserve(_rvecs.rows); 575 tvecs.reserve(_tvecs.rows); 576 for(int i = 0; i < int(objectPoints.size()); i++){ 577 rvecs.push_back(_rvecs.row(i)); 578 tvecs.push_back(_tvecs.row(i)); 579 } 580 } else { 581 int iFixedPoint = -1; 582 if (release_object) 583 iFixedPoint = s.boardSize.width - 1; 584 rms = calibrateCameraRO(objectPoints, imagePoints, imageSize, iFixedPoint, 585 cameraMatrix, distCoeffs, rvecs, tvecs, newObjPoints, 586 s.flag | CALIB_USE_LU); 587 } 588 589 if (release_object) { 590 cout << "New board corners: " << endl; 591 cout << newObjPoints[0] << endl; 592 cout << newObjPoints[s.boardSize.width - 1] << endl; 593 cout << newObjPoints[s.boardSize.width * (s.boardSize.height - 1)] << endl; 594 cout << newObjPoints.back() << endl; 595 } 596 597 cout << "Re-projection error reported by calibrateCamera: "<< rms << endl; 598 599 bool ok = checkRange(cameraMatrix) && checkRange(distCoeffs); 600 601 objectPoints.clear(); 602 objectPoints.resize(imagePoints.size(), newObjPoints); 603 totalAvgErr = computeReprojectionErrors(objectPoints, imagePoints, rvecs, tvecs, cameraMatrix, 604 distCoeffs, reprojErrs, s.useFisheye); 605 606 return ok; 607 } 608 609 // Print camera parameters to the output file 610 static void saveCameraParams( Settings& s, Size& imageSize, Mat& cameraMatrix, Mat& distCoeffs, 611 const vector<Mat>& rvecs, const vector<Mat>& tvecs, 612 const vector<float>& reprojErrs, const vector<vector<Point2f> >& imagePoints, 613 double totalAvgErr, const vector<Point3f>& newObjPoints ) 614 { 615 FileStorage fs( s.outputFileName, FileStorage::WRITE ); 616 617 time_t tm; 618 time( &tm ); 619 struct tm *t2 = localtime( &tm ); 620 char buf[1024]; 621 strftime( buf, sizeof(buf), "%c", t2 ); 622 623 fs << "calibration_time" << buf; 624 625 if( !rvecs.empty() || !reprojErrs.empty() ) 626 fs << "nr_of_frames" << (int)std::max(rvecs.size(), reprojErrs.size()); 627 fs << "image_width" << imageSize.width; 628 fs << "image_height" << imageSize.height; 629 fs << "board_width" << s.boardSize.width; 630 fs << "board_height" << s.boardSize.height; 631 fs << "square_size" << s.squareSize; 632 633 if( s.flag & CALIB_FIX_ASPECT_RATIO ) 634 fs << "fix_aspect_ratio" << s.aspectRatio; 635 636 if (s.flag) 637 { 638 std::stringstream flagsStringStream; 639 if (s.useFisheye) 640 { 641 flagsStringStream << "flags:" 642 << (s.flag & fisheye::CALIB_FIX_SKEW ? " +fix_skew" : "") 643 << (s.flag & fisheye::CALIB_FIX_K1 ? " +fix_k1" : "") 644 << (s.flag & fisheye::CALIB_FIX_K2 ? " +fix_k2" : "") 645 << (s.flag & fisheye::CALIB_FIX_K3 ? " +fix_k3" : "") 646 << (s.flag & fisheye::CALIB_FIX_K4 ? " +fix_k4" : "") 647 << (s.flag & fisheye::CALIB_RECOMPUTE_EXTRINSIC ? " +recompute_extrinsic" : ""); 648 } 649 else 650 { 651 flagsStringStream << "flags:" 652 << (s.flag & CALIB_USE_INTRINSIC_GUESS ? " +use_intrinsic_guess" : "") 653 << (s.flag & CALIB_FIX_ASPECT_RATIO ? " +fix_aspectRatio" : "") 654 << (s.flag & CALIB_FIX_PRINCIPAL_POINT ? " +fix_principal_point" : "") 655 << (s.flag & CALIB_ZERO_TANGENT_DIST ? " +zero_tangent_dist" : "") 656 << (s.flag & CALIB_FIX_K1 ? " +fix_k1" : "") 657 << (s.flag & CALIB_FIX_K2 ? " +fix_k2" : "") 658 << (s.flag & CALIB_FIX_K3 ? " +fix_k3" : "") 659 << (s.flag & CALIB_FIX_K4 ? " +fix_k4" : "") 660 << (s.flag & CALIB_FIX_K5 ? " +fix_k5" : ""); 661 } 662 fs.writeComment(flagsStringStream.str()); 663 } 664 665 fs << "flags" << s.flag; 666 667 fs << "fisheye_model" << s.useFisheye; 668 669 fs << "camera_matrix" << cameraMatrix; 670 fs << "distortion_coefficients" << distCoeffs; 671 672 fs << "avg_reprojection_error" << totalAvgErr; 673 if (s.writeExtrinsics && !reprojErrs.empty()) 674 fs << "per_view_reprojection_errors" << Mat(reprojErrs); 675 676 if(s.writeExtrinsics && !rvecs.empty() && !tvecs.empty() ) 677 { 678 CV_Assert(rvecs[0].type() == tvecs[0].type()); 679 Mat bigmat((int)rvecs.size(), 6, CV_MAKETYPE(rvecs[0].type(), 1)); 680 bool needReshapeR = rvecs[0].depth() != 1 ? true : false; 681 bool needReshapeT = tvecs[0].depth() != 1 ? true : false; 682 683 for( size_t i = 0; i < rvecs.size(); i++ ) 684 { 685 Mat r = bigmat(Range(int(i), int(i+1)), Range(0,3)); 686 Mat t = bigmat(Range(int(i), int(i+1)), Range(3,6)); 687 688 if(needReshapeR) 689 rvecs[i].reshape(1, 1).copyTo(r); 690 else 691 { 692 //*.t() is MatExpr (not Mat) so we can use assignment operator 693 CV_Assert(rvecs[i].rows == 3 && rvecs[i].cols == 1); 694 r = rvecs[i].t(); 695 } 696 697 if(needReshapeT) 698 tvecs[i].reshape(1, 1).copyTo(t); 699 else 700 { 701 CV_Assert(tvecs[i].rows == 3 && tvecs[i].cols == 1); 702 t = tvecs[i].t(); 703 } 704 } 705 fs.writeComment("a set of 6-tuples (rotation vector + translation vector) for each view"); 706 fs << "extrinsic_parameters" << bigmat; 707 } 708 709 if(s.writePoints && !imagePoints.empty() ) 710 { 711 Mat imagePtMat((int)imagePoints.size(), (int)imagePoints[0].size(), CV_32FC2); 712 for( size_t i = 0; i < imagePoints.size(); i++ ) 713 { 714 Mat r = imagePtMat.row(int(i)).reshape(2, imagePtMat.cols); 715 Mat imgpti(imagePoints[i]); 716 imgpti.copyTo(r); 717 } 718 fs << "image_points" << imagePtMat; 719 } 720 721 if( s.writeGrid && !newObjPoints.empty() ) 722 { 723 fs << "grid_points" << newObjPoints; 724 } 725 } 726 727 //! [run_and_save] 728 bool runCalibrationAndSave(Settings& s, Size imageSize, Mat& cameraMatrix, Mat& distCoeffs, 729 vector<vector<Point2f> > imagePoints, float grid_width, bool release_object) 730 { 731 vector<Mat> rvecs, tvecs; 732 vector<float> reprojErrs; 733 double totalAvgErr = 0; 734 vector<Point3f> newObjPoints; 735 736 bool ok = runCalibration(s, imageSize, cameraMatrix, distCoeffs, imagePoints, rvecs, tvecs, reprojErrs, 737 totalAvgErr, newObjPoints, grid_width, release_object); 738 cout << (ok ? "Calibration succeeded" : "Calibration failed") 739 << ". avg re projection error = " << totalAvgErr << endl; 740 741 if (ok) 742 saveCameraParams(s, imageSize, cameraMatrix, distCoeffs, rvecs, tvecs, reprojErrs, imagePoints, 743 totalAvgErr, newObjPoints); 744 return ok; 745 } 746 //! [run_and_save]
VID5.xml (外部配置文件,包括了待标定相机采集图像的绝对路径或者相对路径)
<?xml version="1.0"?> <opencv_storage> <images> images/CameraCalibraation/VID5/xx1.jpg images/CameraCalibraation/VID5/xx2.jpg images/CameraCalibraation/VID5/xx3.jpg images/CameraCalibraation/VID5/xx4.jpg images/CameraCalibraation/VID5/xx5.jpg images/CameraCalibraation/VID5/xx6.jpg images/CameraCalibraation/VID5/xx7.jpg images/CameraCalibraation/VID5/xx8.jpg </images> </opencv_storage>
in_VID5.xml (标定基本参数配置,包括了标定的模式,图像样本的获取方式等等)
<?xml version="1.0"?> <opencv_storage> <Settings> <!-- Number of inner corners per a item row and column. (square, circle) --> <BoardSize_Width> 9</BoardSize_Width> <BoardSize_Height>6</BoardSize_Height> <!-- The size of a square in some user defined metric system (pixel, millimeter)--> <Square_Size>50</Square_Size> <!-- The type of input used for camera calibration. One of: CHESSBOARD CIRCLES_GRID ASYMMETRIC_CIRCLES_GRID --> <Calibrate_Pattern>"CHESSBOARD"</Calibrate_Pattern> <!-- The input to use for calibration. To use an input camera -> give the ID of the camera, like "1" To use an input video -> give the path of the input video, like "/tmp/x.avi" To use an image list -> give the path to the XML or YAML file containing the list of the images, like "/tmp/circles_list.xml" --> <Input>"images/CameraCalibration/VID5/VID5.xml"</Input> <!-- If true (non-zero) we flip the input images around the horizontal axis.--> <Input_FlipAroundHorizontalAxis>0</Input_FlipAroundHorizontalAxis> <!-- Time delay between frames in case of camera. --> <Input_Delay>100</Input_Delay> <!-- How many frames to use, for calibration. --> <Calibrate_NrOfFrameToUse>25</Calibrate_NrOfFrameToUse> <!-- Consider only fy as a free parameter, the ratio fx/fy stays the same as in the input cameraMatrix. Use or not setting. 0 - False Non-Zero - True--> <Calibrate_FixAspectRatio> 1 </Calibrate_FixAspectRatio> <!-- If true (non-zero) tangential distortion coefficients are set to zeros and stay zero.--> <Calibrate_AssumeZeroTangentialDistortion>1</Calibrate_AssumeZeroTangentialDistortion> <!-- If true (non-zero) the principal point is not changed during the global optimization.--> <Calibrate_FixPrincipalPointAtTheCenter> 1 </Calibrate_FixPrincipalPointAtTheCenter> <!-- The name of the output log file. --> <Write_outputFileName>"out_camera_data.xml"</Write_outputFileName> <!-- If true (non-zero) we write to the output file the feature points.--> <Write_DetectedFeaturePoints>1</Write_DetectedFeaturePoints> <!-- If true (non-zero) we write to the output file the extrinsic camera parameters.--> <Write_extrinsicParameters>1</Write_extrinsicParameters> <!-- If true (non-zero) we write to the output file the refined 3D target grid points.--> <Write_gridPoints>1</Write_gridPoints> <!-- If true (non-zero) we show after calibration the undistorted images.--> <Show_UndistortedImage>1</Show_UndistortedImage> <!-- If true (non-zero) will be used fisheye camera model.--> <Calibrate_UseFisheyeModel>0</Calibrate_UseFisheyeModel> <!-- If true (non-zero) distortion coefficient k1 will be equals to zero.--> <Fix_K1>0</Fix_K1> <!-- If true (non-zero) distortion coefficient k2 will be equals to zero.--> <Fix_K2>0</Fix_K2> <!-- If true (non-zero) distortion coefficient k3 will be equals to zero.--> <Fix_K3>0</Fix_K3> <!-- If true (non-zero) distortion coefficient k4 will be equals to zero.--> <Fix_K4>1</Fix_K4> <!-- If true (non-zero) distortion coefficient k5 will be equals to zero.--> <Fix_K5>1</Fix_K5> </Settings> </opencv_storage>
out_camera_data.yml (主程序输出保存的相机基本参数,以方便下次相机的使用)
%YAML:1.0
calibration_Time: "08/19/11 20:44:38"
nrOfFrames: 8
image_Width: 640
image_Height: 480
board_Width: 9
board_Height: 6
square_Size: 50.
FixAspectRatio: 1.
# flags: +fix_aspectRatio +fix_principal_point +zero_tangent_dist
flagValue: 14
Camera_Matrix: !!opencv-matrix
rows: 3
cols: 3
dt: d
data: [ 6.5746697810243404e+002, 0., 3.1950000000000000e+002, 0.,
6.5746697810243404e+002, 2.3950000000000000e+002, 0., 0., 1. ]
Distortion_Coefficients: !!opencv-matrix
rows: 5
cols: 1
dt: d
data: [ -4.1802327018241026e-001, 5.0715243805833121e-001, 0., 0.,
-5.7843596847939704e-001 ]
Avg_Reprojection_Error: 3.8441346462381665e-001
Per_View_Reprojection_Errors: !!opencv-matrix
rows: 8
cols: 1
dt: f
data: [ 5.04357755e-001, 4.85754758e-001, 3.99563968e-001,
4.13829178e-001, 3.53570908e-001, 3.21116358e-001,
2.74473161e-001, 2.39761785e-001 ]
# a set of 6-tuples (rotation vector + translation vector) for each view
Extrinsic_Parameters: !!opencv-matrix
rows: 8
cols: 6
dt: d
data: [ -7.8704123655486097e-002, -1.5922384772614945e-001,
3.1166227207451498e+000, 2.4224388101960471e+002,
1.1795590397660339e+002, 6.2576484126093249e+002,
-1.4117480285164308e-001, -1.7917415443804836e-002,
3.1333182268743949e+000, 2.5943034781849354e+002,
1.4039780562976958e+002, 6.3848706527260981e+002,
7.2230525186138789e-002, -7.5445981266787754e-002,
1.5712860749221762e+000, 1.7426560451795339e+002,
-1.9309240362258871e+002, 7.0891416556762647e+002,
2.0367310600105853e-002, 6.8565520026996951e-002,
-5.4313033031644169e-004, -2.0146314940404827e+002,
-1.3305643514116997e+002, 7.4933554744027231e+002,
-3.4468530027734055e-002, 2.1921265175331925e-002,
-1.5731053528054522e+000, -1.1155718744299284e+002,
2.0307615364261443e+002, 8.4915903914333899e+002,
3.7425562109513817e-002, 7.4883169379022230e-002,
-3.6031632305130512e-002, -2.0094505419395196e+002,
-1.1627359108310560e+002, 9.2021583518760133e+002,
6.8105689976949157e-002, 6.4426739692440949e-002,
-7.0967130057087435e-002, -1.9233852871740035e+002,
-1.0334652096641923e+002, 1.0755293563503658e+003,
-5.8017546499862287e-002, -1.6909812666033443e-003,
-1.5876137659782963e+000, -1.0242234847115104e+002,
2.2583088401423066e+002, 1.1125972190244058e+003 ]
Image_points: !!opencv-matrix
rows: 8
cols: 54
dt: "2f"
data: [ 5.58494690e+002, 3.55650085e+002, 5.13314697e+002,
3.59107666e+002, 4.65728333e+002, 3.62133911e+002,
4.15701111e+002, 3.65026459e+002, 3.64399353e+002,
3.67339203e+002, 3.12101196e+002, 3.69211914e+002,
2.59208405e+002, 3.70413513e+002, 2.07456192e+002,
3.71175995e+002, 1.56619507e+002, 3.72176544e+002,
5.60868713e+002, 3.08104828e+002, 5.15191772e+002,
3.10485626e+002, 4.67032959e+002, 3.12660004e+002,
4.16112152e+002, 3.14887177e+002, 3.64010712e+002,
3.16825775e+002, 3.10712372e+002, 3.18640808e+002,
2.56853943e+002, 3.20017365e+002, 2.04168182e+002,
3.20908417e+002, 1.52469528e+002, 3.22105377e+002,
5.62328369e+002, 2.58646881e+002, 5.16396301e+002,
2.59919281e+002, 4.67907654e+002, 2.61257874e+002,
4.16463440e+002, 2.62675537e+002, 3.63546570e+002,
2.64064117e+002, 3.09528137e+002, 2.65489990e+002,
2.54765533e+002, 2.66862030e+002, 2.01299225e+002,
2.67997345e+002, 1.48913437e+002, 2.69627167e+002,
5.63098022e+002, 2.08423523e+002, 5.16782654e+002,
2.08424667e+002, 4.68059296e+002, 2.08661697e+002,
4.16216431e+002, 2.09268982e+002, 3.62888763e+002,
2.10013397e+002, 3.08458557e+002, 2.11074738e+002,
2.53267990e+002, 2.12496582e+002, 1.99121384e+002,
2.14005814e+002, 1.46551376e+002, 2.15851318e+002,
5.62997437e+002, 1.57966492e+002, 5.16406494e+002,
1.56580688e+002, 4.67334900e+002, 1.55756500e+002,
4.15378235e+002, 1.55492874e+002, 3.62096710e+002,
1.55498734e+002, 3.07522827e+002, 1.56133240e+002,
2.52235214e+002, 1.57516571e+002, 1.97876328e+002,
1.59318787e+002, 1.45078247e+002, 1.61638428e+002,
5.62097168e+002, 1.07469536e+002, 5.15766846e+002,
1.04902527e+002, 4.66562866e+002, 1.03045807e+002,
4.14651459e+002, 1.01924713e+002, 3.61240662e+002,
1.01357826e+002, 3.06746613e+002, 1.01582802e+002,
2.51568024e+002, 1.02920105e+002, 1.97343307e+002,
1.04941299e+002, 1.44756821e+002, 1.07737488e+002,
5.68062500e+002, 3.73591125e+002, 5.25272644e+002,
3.77019318e+002, 4.79870941e+002, 3.80086578e+002,
4.31823730e+002, 3.83036652e+002, 3.81995758e+002,
3.85271759e+002, 3.30728729e+002, 3.86998779e+002,
2.78071167e+002, 3.88151031e+002, 2.26231567e+002,
3.88669586e+002, 1.74855331e+002, 3.89197998e+002,
5.69792542e+002, 3.27097382e+002, 5.26866028e+002,
3.29362366e+002, 4.81278229e+002, 3.31532928e+002,
4.32783203e+002, 3.33552185e+002, 3.82408234e+002,
3.35186554e+002, 3.30427399e+002, 3.36404053e+002,
2.77138519e+002, 3.37450958e+002, 2.24525131e+002,
3.37957092e+002, 1.72285507e+002, 3.38503540e+002,
5.70942749e+002, 2.79243713e+002, 5.27789307e+002,
2.80073486e+002, 4.82146576e+002, 2.81226410e+002,
4.33247375e+002, 2.82237427e+002, 3.82503662e+002,
2.83062286e+002, 3.30138885e+002, 2.83794434e+002,
2.76433228e+002, 2.84549286e+002, 2.23158783e+002,
2.84981049e+002, 1.70520218e+002, 2.85720886e+002,
5.71001953e+002, 2.30928329e+002, 5.27846863e+002,
2.30519928e+002, 4.82114563e+002, 2.30268906e+002,
4.33114563e+002, 2.30243515e+002, 3.82384857e+002,
2.30311340e+002, 3.29870392e+002, 2.30454620e+002,
2.76012634e+002, 2.30882156e+002, 2.22529434e+002,
2.31355362e+002, 1.69742065e+002, 2.32063004e+002,
5.70199036e+002, 1.82609772e+002, 5.27030884e+002,
1.80973267e+002, 4.81193573e+002, 1.79573792e+002,
4.32409821e+002, 1.78475616e+002, 3.81855530e+002,
1.77680283e+002, 3.29641937e+002, 1.77092087e+002,
2.75895782e+002, 1.77155502e+002, 2.22438889e+002,
1.77605667e+002, 1.69884583e+002, 1.78365585e+002,
5.69026245e+002, 1.34654831e+002, 5.26171570e+002,
1.31798691e+002, 4.80653503e+002, 1.29171509e+002,
4.31869904e+002, 1.27280067e+002, 3.81419739e+002,
1.25591202e+002, 3.29466644e+002, 1.24407089e+002,
2.76225342e+002, 1.24174736e+002, 2.23024109e+002,
1.24463333e+002, 1.70838898e+002, 1.25398903e+002,
4.73812897e+002, 6.94673386e+001, 4.74245453e+002,
1.12387466e+002, 4.74243347e+002, 1.56034164e+002,
4.73834778e+002, 2.00523651e+002, 4.72891602e+002,
2.44457306e+002, 4.71412811e+002, 2.87981171e+002,
4.69708252e+002, 3.30783173e+002, 4.67558228e+002,
3.71818420e+002, 4.65495667e+002, 4.11996979e+002,
4.31027649e+002, 6.75546722e+001, 4.31269440e+002,
1.10960022e+002, 4.31185486e+002, 1.55113556e+002,
4.30830139e+002, 2.00061066e+002, 4.30168427e+002,
2.44456863e+002, 4.29107544e+002, 2.88479645e+002,
4.27829071e+002, 3.31813507e+002, 4.26131653e+002,
3.73071228e+002, 4.24718811e+002, 4.13476563e+002,
3.86868805e+002, 6.61982269e+001, 3.86895416e+002,
1.09904411e+002, 3.86690216e+002, 1.54396423e+002,
3.86368591e+002, 1.99800369e+002, 3.85792206e+002,
2.44538574e+002, 3.85117279e+002, 2.88826447e+002,
3.84405273e+002, 3.32408020e+002, 3.83303772e+002,
3.74074097e+002, 3.82477448e+002, 4.14638977e+002,
3.41941437e+002, 6.54462357e+001, 3.41628204e+002,
1.09383698e+002, 3.41402344e+002, 1.54105545e+002,
3.41168854e+002, 1.99655045e+002, 3.40816681e+002,
2.44469910e+002, 3.40516937e+002, 2.88975800e+002,
3.40365662e+002, 3.32670990e+002, 3.39935211e+002,
3.74465759e+002, 3.39847626e+002, 4.14742279e+002,
2.96694000e+002, 6.56859589e+001, 2.96075226e+002,
1.09505333e+002, 2.95704895e+002, 1.54202652e+002,
2.95653107e+002, 1.99734131e+002, 2.95589661e+002,
2.44549530e+002, 2.95629547e+002, 2.88889496e+002,
2.96138733e+002, 3.32610931e+002, 2.96520905e+002,
3.74608551e+002, 2.96987091e+002, 4.14774902e+002,
2.51414978e+002, 6.65755463e+001, 2.50681854e+002,
1.10189331e+002, 2.50183380e+002, 1.54658005e+002,
2.50331161e+002, 2.00073761e+002, 2.50590790e+002,
2.44719513e+002, 2.51083817e+002, 2.88868286e+002,
2.52134262e+002, 3.32266937e+002, 2.53097809e+002,
3.74022491e+002, 2.54404007e+002, 4.14018066e+002,
1.49524078e+002, 1.27699501e+002, 1.89511658e+002,
1.25816605e+002, 2.31050888e+002, 1.24260918e+002,
2.74076721e+002, 1.23023209e+002, 3.17643005e+002,
1.22288109e+002, 3.61785889e+002, 1.22105164e+002,
4.06142670e+002, 1.22401566e+002, 4.49623962e+002,
1.23246025e+002, 4.92677216e+002, 1.24087708e+002,
1.48706085e+002, 1.69077423e+002, 1.88827805e+002,
1.67750443e+002, 2.30439865e+002, 1.66769333e+002,
2.73830933e+002, 1.65871170e+002, 3.17596741e+002,
1.65410919e+002, 3.61983459e+002, 1.65327866e+002,
4.06748322e+002, 1.65463974e+002, 4.50450226e+002,
1.66126526e+002, 4.93614655e+002, 1.66970413e+002,
1.48312607e+002, 2.11499451e+002, 1.88574097e+002,
2.10860214e+002, 2.30130676e+002, 2.10261612e+002,
2.73557709e+002, 2.09837143e+002, 3.17542572e+002,
2.09633057e+002, 3.62091248e+002, 2.09732620e+002,
4.06934570e+002, 2.09926758e+002, 4.50914612e+002,
2.10320221e+002, 4.94044495e+002, 2.10900925e+002,
1.48613831e+002, 2.53997177e+002, 1.88797791e+002,
2.53912842e+002, 2.30240204e+002, 2.53975067e+002,
2.73746704e+002, 2.54010208e+002, 3.17718262e+002,
2.54106003e+002, 3.62188965e+002, 2.54205475e+002,
4.06908783e+002, 2.54317505e+002, 4.50824951e+002,
2.54539490e+002, 4.93825714e+002, 2.54753876e+002,
1.49541687e+002, 2.96404175e+002, 1.89357727e+002,
2.97117523e+002, 2.30807007e+002, 2.97805603e+002,
2.74325470e+002, 2.97966522e+002, 3.18042206e+002,
2.98304535e+002, 3.62105774e+002, 2.98552643e+002,
4.06672272e+002, 2.98572418e+002, 4.50363068e+002,
2.98569550e+002, 4.93109894e+002, 2.98516205e+002,
1.50883698e+002, 3.38493195e+002, 1.90633621e+002,
3.39862610e+002, 2.31920990e+002, 3.40869415e+002,
2.74971252e+002, 3.41453766e+002, 3.18235229e+002,
3.41952637e+002, 3.62063477e+002, 3.42314026e+002,
4.06098938e+002, 3.42221802e+002, 4.49477386e+002,
3.42063812e+002, 4.91864716e+002, 3.41727600e+002,
2.36129852e+002, 3.92798004e+002, 2.34999939e+002,
3.56118683e+002, 2.34376099e+002, 3.18607025e+002,
2.33822159e+002, 2.80400696e+002, 2.33565445e+002,
2.42213104e+002, 2.33583069e+002, 2.03937286e+002,
2.34028824e+002, 1.65756607e+002, 2.34613373e+002,
1.28586639e+002, 2.35190308e+002, 9.18279037e+001,
2.73031616e+002, 3.93267242e+002, 2.72295166e+002,
3.56342743e+002, 2.71799347e+002, 3.18847412e+002,
2.71418854e+002, 2.80287872e+002, 2.71161469e+002,
2.41881134e+002, 2.71248962e+002, 2.03348145e+002,
2.71379303e+002, 1.64895874e+002, 2.71946045e+002,
1.27450935e+002, 2.72322418e+002, 9.06900787e+001,
3.10670715e+002, 3.93568848e+002, 3.10389160e+002,
3.56545959e+002, 3.10084625e+002, 3.18814514e+002,
3.09801544e+002, 2.80242737e+002, 3.09678711e+002,
2.41574814e+002, 3.09779663e+002, 2.02989838e+002,
3.09842712e+002, 1.64338043e+002, 3.10076782e+002,
1.26870911e+002, 3.10243286e+002, 8.98413315e+001,
3.48618134e+002, 3.93563202e+002, 3.48617065e+002,
3.56472382e+002, 3.48608795e+002, 3.18855621e+002,
3.48544556e+002, 2.80011017e+002, 3.48556396e+002,
2.41388168e+002, 3.48585388e+002, 2.02692429e+002,
3.48435089e+002, 1.64099731e+002, 3.48442902e+002,
1.26549957e+002, 3.48338043e+002, 8.98002014e+001,
3.86625610e+002, 3.93188599e+002, 3.87047729e+002,
3.56377594e+002, 3.87306274e+002, 3.18714752e+002,
3.87337799e+002, 2.79868896e+002, 3.87402740e+002,
2.41228760e+002, 3.87295166e+002, 2.02695313e+002,
3.87030273e+002, 1.64203415e+002, 3.86741211e+002,
1.26606262e+002, 3.86337311e+002, 8.99655075e+001,
4.24534088e+002, 3.92702545e+002, 4.25310822e+002,
3.55900452e+002, 4.25869019e+002, 3.18160614e+002,
4.25909790e+002, 2.79615753e+002, 4.25977295e+002,
2.41165100e+002, 4.25826477e+002, 2.02876389e+002,
4.25331665e+002, 1.64527618e+002, 4.24775787e+002,
1.27097328e+002, 4.23985138e+002, 9.08176651e+001,
1.79142670e+002, 1.58573654e+002, 2.12791580e+002,
1.56291031e+002, 2.47140106e+002, 1.54265656e+002,
2.82607300e+002, 1.52373688e+002, 3.18175507e+002,
1.50692184e+002, 3.54185852e+002, 1.49404175e+002,
3.90455200e+002, 1.48229370e+002, 4.26106689e+002,
1.47507843e+002, 4.61576141e+002, 1.46712479e+002,
1.80388336e+002, 1.93027603e+002, 2.14026459e+002,
1.91128204e+002, 2.48376541e+002, 1.89414978e+002,
2.83795807e+002, 1.87720856e+002, 3.19472473e+002,
1.86192383e+002, 3.55483826e+002, 1.84929199e+002,
3.91970764e+002, 1.83747040e+002, 4.27654572e+002,
1.82931534e+002, 4.63295227e+002, 1.81977234e+002,
1.81914261e+002, 2.27955460e+002, 2.15291260e+002,
2.26512482e+002, 2.49628265e+002, 2.25067520e+002,
2.85066406e+002, 2.23593185e+002, 3.20846680e+002,
2.22337708e+002, 3.56862885e+002, 2.21191040e+002,
3.93279907e+002, 2.19905640e+002, 4.29202271e+002,
2.18870361e+002, 4.64728424e+002, 2.17972977e+002,
1.83496948e+002, 2.62963226e+002, 2.16930527e+002,
2.61755219e+002, 2.51115829e+002, 2.60777222e+002,
2.86553406e+002, 2.59500336e+002, 3.22299896e+002,
2.58380737e+002, 3.58307648e+002, 2.57236694e+002,
3.94551819e+002, 2.56009125e+002, 4.30358948e+002,
2.54925797e+002, 4.65684998e+002, 2.54021484e+002,
1.85461685e+002, 2.97687378e+002, 2.18712234e+002,
2.96999207e+002, 2.52770218e+002, 2.96270752e+002,
2.88213776e+002, 2.95168213e+002, 3.23698334e+002,
2.94233032e+002, 3.59477722e+002, 2.93170715e+002,
3.95647766e+002, 2.91897400e+002, 4.31309845e+002,
2.90856995e+002, 4.66494110e+002, 2.89726410e+002,
1.87661331e+002, 3.32186188e+002, 2.20767746e+002,
3.31906250e+002, 2.54839096e+002, 3.31398651e+002,
2.89963745e+002, 3.30524139e+002, 3.25207642e+002,
3.29771820e+002, 3.60686035e+002, 3.28762695e+002,
3.96576447e+002, 3.27542206e+002, 4.31994415e+002,
3.26294189e+002, 4.66894653e+002, 3.24949921e+002,
2.03543015e+002, 1.77473557e+002, 2.32777847e+002,
1.74712509e+002, 2.62628723e+002, 1.72331970e+002,
2.93045898e+002, 1.69686768e+002, 3.23527618e+002,
1.67496246e+002, 3.54206787e+002, 1.65446075e+002,
3.85180176e+002, 1.63360580e+002, 4.15484253e+002,
1.61536423e+002, 4.45720947e+002, 1.59896164e+002,
2.05864395e+002, 2.07228104e+002, 2.35242096e+002,
2.04699326e+002, 2.64853973e+002, 2.02407455e+002,
2.95353882e+002, 1.99972321e+002, 3.25811890e+002,
1.97671921e+002, 3.56471252e+002, 1.95763168e+002,
3.87280548e+002, 1.93597977e+002, 4.17615814e+002,
1.91867371e+002, 4.48018677e+002, 1.90067413e+002,
2.08421249e+002, 2.37166977e+002, 2.37513824e+002,
2.34982773e+002, 2.67261261e+002, 2.32802841e+002,
2.97555817e+002, 2.30466080e+002, 3.28118103e+002,
2.28462463e+002, 3.58699707e+002, 2.26417038e+002,
3.89468842e+002, 2.24356827e+002, 4.19895996e+002,
2.22421921e+002, 4.50077850e+002, 2.20683517e+002,
2.11095444e+002, 2.66940186e+002, 2.40241348e+002,
2.64970093e+002, 2.69563019e+002, 2.63153290e+002,
2.99863464e+002, 2.60983551e+002, 3.30282440e+002,
2.58911560e+002, 3.60724792e+002, 2.56935730e+002,
3.91487915e+002, 2.54799423e+002, 4.21789093e+002,
2.52929688e+002, 4.51818481e+002, 2.51059357e+002,
2.13829117e+002, 2.96591217e+002, 2.42742859e+002,
2.94884583e+002, 2.72209076e+002, 2.93215668e+002,
3.02402985e+002, 2.91230591e+002, 3.32536072e+002,
2.89165192e+002, 3.62860901e+002, 2.87413605e+002,
3.93481842e+002, 2.85199615e+002, 4.23728851e+002,
2.83277496e+002, 4.53453094e+002, 2.81229309e+002,
2.16799316e+002, 3.25975220e+002, 2.45605515e+002,
3.24619904e+002, 2.74777344e+002, 3.22958679e+002,
3.04762817e+002, 3.21008057e+002, 3.34797150e+002,
3.19291443e+002, 3.65005798e+002, 3.17295044e+002,
3.95311981e+002, 3.15296021e+002, 4.25312592e+002,
3.13086945e+002, 4.54931152e+002, 3.11027130e+002,
2.60550232e+002, 3.70739563e+002, 2.59674011e+002,
3.42115936e+002, 2.58910492e+002, 3.13278015e+002,
2.58195618e+002, 2.84013580e+002, 2.57727173e+002,
2.55017166e+002, 2.57326263e+002, 2.25760986e+002,
2.57096619e+002, 1.96577972e+002, 2.57031860e+002,
1.68026199e+002, 2.56873383e+002, 1.39550308e+002,
2.89019318e+002, 3.70481354e+002, 2.88355560e+002,
3.41833252e+002, 2.87601471e+002, 3.12872925e+002,
2.87057190e+002, 2.83485535e+002, 2.86599762e+002,
2.54255096e+002, 2.86174438e+002, 2.25023285e+002,
2.85775940e+002, 1.95715347e+002, 2.85577087e+002,
1.66989502e+002, 2.85395477e+002, 1.38597382e+002,
3.18072754e+002, 3.70118317e+002, 3.17432709e+002,
3.41398743e+002, 3.16917267e+002, 3.12476044e+002,
3.16284363e+002, 2.83001587e+002, 3.15799072e+002,
2.53725845e+002, 3.15411957e+002, 2.24337708e+002,
3.15070374e+002, 1.95034119e+002, 3.14736847e+002,
1.66195313e+002, 3.14439789e+002, 1.37797058e+002,
3.47083588e+002, 3.69678101e+002, 3.46717987e+002,
3.40949524e+002, 3.46185303e+002, 3.12009857e+002,
3.45728088e+002, 2.82454071e+002, 3.45226624e+002,
2.53109863e+002, 3.44883606e+002, 2.23839539e+002,
3.44373535e+002, 1.94399933e+002, 3.43879852e+002,
1.65690643e+002, 3.43438629e+002, 1.37252930e+002,
3.76341522e+002, 3.68972321e+002, 3.76086884e+002,
3.40412842e+002, 3.75708893e+002, 3.11398376e+002,
3.75143494e+002, 2.81901520e+002, 3.74762970e+002,
2.52577988e+002, 3.74223969e+002, 2.23348221e+002,
3.73600891e+002, 1.93979538e+002, 3.72983917e+002,
1.65201294e+002, 3.72517273e+002, 1.36871033e+002,
4.05512115e+002, 3.68243225e+002, 4.05366333e+002,
3.39678650e+002, 4.05090027e+002, 3.10679108e+002,
4.04612366e+002, 2.81203522e+002, 4.04152649e+002,
2.52051605e+002, 4.03539703e+002, 2.22930420e+002,
4.02903351e+002, 1.93625381e+002, 4.02272827e+002,
1.65004440e+002, 4.01353333e+002, 1.36796814e+002 ]
(注:我这里使用的是最新版本Opencv4.x,其他版本的标定文件不一定兼容,例如我使用Opencv3中的camera_calibration.cpp在Opencv4.x环境下就无法编译通过)
编译方式采用Cmake,CMakeLists.txt脚本如下:
1 cmake_minimum_required(VERSION 2.8) 2 3 project(main_calibration) 4 5 find_package(OpenCV REQUIRED) 6 include_directories(${OpenCV_INCLUDE_DIRS}) 7 8 add_executable(main_calibration camera_calibration.cpp) 9 target_link_libraries(main_calibration ${OpenCV_LIBS})
编译流程如下:
Step1:本地主程序camera_calibration.cpp文件目录下新建编译临时文件夹build.(CMakeLists.txt和主程序文件在同一目录下).
Step2:切换目录到build文件夹下,使用cmake命令生成对应的Makefile以及预编译临时文件.
Step3:使用make命令进行编译,编译完成后自动在build文件夹下生成可执行文件main_calibration.
mkdir build cd build cmake .. make
接下来修改可执行文件main_calibration可执行文件的参数文件in_VID5.xml:
Line5: <BoardSize_Width>8</BoardSize_Width> # 设置Chess标定板宽度方向角点数.(注意是inner方块的角点数) Line6: <BoardSize_Height>6</BoardSize_Height> # 设置Chess标定板高度方向角点数.(注意inner角点表示出了最外侧一圈方格的部分) Line9: <Square_Size>40</Square_Size> # 实际物理棋盘上方块的边长(Units:mm) Line12: <Calibration_Pattern>"CHESSBARD"</Calibration_Pattern> # 一般这里都设置为棋盘,如果你的是其他标定板请使用其他参数. Line19:<Input>"absolute_path/VID5.xml"</Input> # 这里如果设置为1表示使用Camera来现场采集图片进行标定,一般来说可以设置VID5.xml文件路径,程序将会通过VID5.xml文件中标定图片路径读取图片,这里需要配置好VID5.xml文件. Line24:<Input_Delay>100</Input_Delay> # 如果使用Camera来实时采集标定图像,则这个参数用来设定两次采集动作之间的时间间隔,如果不采用Camera则不用设置. Line27:<Calibrate_NrOfFrameToUse><Calibrate_NrOfFrameToUse> # 设置用来标定的有效图像数量,25表示使用能够找到角点并满足角点要求的25张标定图进行相机标定. Line37: <Write_outputFileName>"out_camera_data.yml"</Write_outputFileName> # 设置标定完成后参数保存的路径以及文件,注意文件后缀名为.yml. Line47: <Calibrate_UseFisheyeModel>1</Calibrate_UseFishModel> # 如果需要标定的为鱼眼镜头,则需要设置为1.
根据in_VID5.xml中的设置,如果选用已经拍摄好的照片则需要修改VID5.xml文件内容,添加标定图像的绝对路径:
如图上所示,一般需要采集足够多的图像,以保证能够找到足够用来标定Camera的图像,如果不满足要求则会出现如下错误(请调整光照等条件重新采集图像):
完成基本参数文件设置之后开始运行:
./main_calibration ../in_VID5.xml
运行过程截图如下:
如上左图表示图像角点提取的情况,右图表示矫正之后的结果。
三、使用标定结果矫正畸变
参考程序如下,只采用了径向畸变矫正(注:完成了彩色图像畸变校正以及单通道灰度图像矫正):
在插值过程中使用了最近邻差值算法,如使用其他算法请自行封装调用。
1 #include <iostream> 2 #include <string.h> 3 #include <opencv2/opencv.hpp> 4 5 using namespace std; 6 using namespace cv; 7 8 #define SHOW 0 9 int main(void) 10 { 11 string img_path = "../CSICamera0-31.jpg"; 12 string res_path = "../undistort.jpg"; 13 cout << "Begin to Calibrate the image file:" << img_path << endl; 14 double k1=-0.3403,k2=0.1520125; 15 double fx=784.59,fy=784.59,cx=640,cy=480; 16 17 #if SHOW 18 namedWindow("OriImage", WINDOW_AUTOSIZE); 19 namedWindow("ResImage", WINDOW_AUTOSIZE); 20 #endif 21 22 Mat Ori = imread(img_path); 23 #if SHOW 24 imshow("OriImage",Ori); 25 #endif 26 27 int rows = Ori.rows, cols = Ori.cols,row,col; 28 double row_undistort,col_undistort,x1,x2,y1,y2,r; 29 Mat gry_undistort = Mat(rows,cols,CV_8UC1); 30 Mat Gry = Mat(rows,cols,CV_8UC1); 31 Mat img_undistort = Mat(rows,cols,CV_8UC3); 32 33 cvtColor(Ori,Gry,COLOR_BGR2GRAY); 34 35 for(row=0;row<rows;row++){ 36 for(col=0;col<cols;col++){ 37 x1 = (col-cx)/fx; // calc center x 38 y1 = (row-cy)/fy; // calc center y 39 r = pow(x1,2) + pow(y1,2); // calc center distance 40 x2 = x1*(1+k1*r+k2*pow(r,2)); // undistort img pos-col 41 y2 = y1*(1+k1*r+k2*pow(r,2)); // undistort img pos-row 42 row_undistort = fy*y2 + cy; 43 col_undistort = fx*x2 + cx; 44 45 if(row_undistort >=0 && col_undistort >= 0 && row_undistort < rows && col_undistort < cols){ 46 gry_undistort.at<uchar>(row,col) = Gry.at<uchar>((int)row_undistort,(int)col_undistort); 47 img_undistort.at<Vec3b>(row,col)[0] = Ori.at<Vec3b>((int)row_undistort,(int)col_undistort)[0]; 48 img_undistort.at<Vec3b>(row,col)[1] = Ori.at<Vec3b>((int)row_undistort,(int)col_undistort)[1]; 49 img_undistort.at<Vec3b>(row,col)[2] = Ori.at<Vec3b>((int)row_undistort,(int)col_undistort)[2]; 50 }else{ 51 gry_undistort.at<uchar>(row,col) = 0; 52 img_undistort.at<Vec3b>(row,col)[0] = 0; 53 img_undistort.at<Vec3b>(row,col)[1] = 0; 54 img_undistort.at<Vec3b>(row,col)[2] = 0; 55 } 56 } 57 } 58 59 #if SHOW 60 imshow("ResImage",img_undistort); 61 while(1){ 62 int keycode = waitKey(30) & 0xff ; 63 if (keycode == 27) break ; 64 } 65 destroyAllWindows(); 66 #else 67 imwrite(res_path,img_undistort); 68 #endif 69 70 return 0; 71 }
实际处理结果:
使用cuda加速的畸变矫正算法:传送门
四、参考资料
畸变矫正Opencv代码打包Baidu-Pan下载地址:https://pan.baidu.com/s/1icrwvNws2N1j7b7bjGxPNA 提取码:6koe
Matlab标定工具箱使用官方参考教程:http://www.vision.caltech.edu/bouguetj/calib_doc/htmls/example.html
图像矫正及畸变参数使用:https://blog.csdn.net/weixin_38009585/article/details/82356022