├── test_fisheye.jpg ├── test_catadioptric.jpg ├── undistorted_panoramic.jpg ├── undistorted_perspective.jpg ├── README.md ├── calib_results_fisheye.txt ├── calib_results_catadioptric.txt ├── LICENSE ├── ocam_functions.h ├── ocam_functions.cpp └── main.cpp /test_fisheye.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonear/OCamCalib/HEAD/test_fisheye.jpg -------------------------------------------------------------------------------- /test_catadioptric.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonear/OCamCalib/HEAD/test_catadioptric.jpg -------------------------------------------------------------------------------- /undistorted_panoramic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonear/OCamCalib/HEAD/undistorted_panoramic.jpg -------------------------------------------------------------------------------- /undistorted_perspective.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stonear/OCamCalib/HEAD/undistorted_perspective.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Omnidirectional-Camera-Calibration 2 | opencv2 c++ implementation of OCamCalib (Davide Scaramuzza ~ University of Zurich) 3 | https://sites.google.com/site/scarabotix/ocamcalib-toolbox 4 | -------------------------------------------------------------------------------- /calib_results_fisheye.txt: -------------------------------------------------------------------------------- 1 | #polynomial coefficients for the DIRECT mapping function (ocam_model.ss in MATLAB). These are used by cam2world 2 | 3 | 5 -5.517655e+002 0.000000e+000 8.372454e-004 -6.474789e-007 1.235631e-009 4 | 5 | #polynomial coefficients for the inverse mapping function (ocam_model.invpol in MATLAB). These are used by world2cam 6 | 7 | 8 730.949123 315.876984 -177.960849 -352.468231 -678.144608 -615.917273 -262.086205 -42.961956 8 | 9 | #center: "row" and "column", starting from 0 (C convention) 10 | 11 | 381.777786 505.480427 12 | 13 | #affine parameters "c", "d", "e" 14 | 15 | 1.000000 0.000000 0.000000 16 | 17 | #image size: "height" and "width" 18 | 19 | 768 1024 20 | 21 | -------------------------------------------------------------------------------- /calib_results_catadioptric.txt: -------------------------------------------------------------------------------- 1 | #polynomial coefficients for the DIRECT mapping function (ocam_model.ss in MATLAB). These are used by cam2world 2 | 3 | 5 -1.314797e+002 0.000000e+000 1.848991e-003 2.251206e-007 -7.343233e-010 4 | 5 | #polynomial coefficients for the inverse mapping function (ocam_model.invpol in MATLAB). These are used by world2cam 6 | 7 | 16 266.092981 272.489459 148.629937 112.702231 82.875684 59.965334 62.304757 91.439809 57.271700 -59.213406 -74.159984 78.399482 187.931187 138.893318 47.348063 6.335636 8 | 9 | #center: "row" and "column", starting from 0 (C convention) 10 | 11 | 382.294245 515.323794 12 | 13 | #affine parameters "c", "d", "e" 14 | 15 | 0.999845 0.000044 0.000008 16 | 17 | #image size: "height" and "width" 18 | 19 | 768 1024 20 | 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ocam_functions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define MAX_POL_LENGTH 64 6 | 7 | using namespace cv; 8 | using namespace std; 9 | 10 | class ocam_model 11 | { 12 | public: 13 | ocam_model(); 14 | ~ocam_model(); 15 | double pol[MAX_POL_LENGTH]; // the polynomial coefficients: pol[0] + x"pol[1] + x^2*pol[2] + ... + x^(N-1)*pol[N-1] 16 | int length_pol; // length of polynomial 17 | double invpol[MAX_POL_LENGTH]; // the coefficients of the inverse polynomial 18 | int length_invpol; // length of inverse polynomial 19 | double xc; // row coordinate of the center 20 | double yc; // column coordinate of the center 21 | double c; // affine parameter 22 | double d; // affine parameter 23 | double e; // affine parameter 24 | int width; // image width 25 | int height; // image height 26 | 27 | int get_ocam_model(string filename); 28 | void world2cam(double point2D[2], double point3D[3]); 29 | void cam2world(double point3D[3], double point2D[2]); 30 | void create_perspecive_undistortion_LUT(Mat &mapx, Mat &mapy, float sf); 31 | void create_panoramic_undistortion_LUT(Mat &mapx, Mat &mapy, float Rmin, float Rmax); 32 | }; -------------------------------------------------------------------------------- /ocam_functions.cpp: -------------------------------------------------------------------------------- 1 | #include "ocam_functions.h" 2 | 3 | ocam_model::ocam_model() 4 | { 5 | } 6 | 7 | ocam_model::~ocam_model() 8 | { 9 | } 10 | 11 | int ocam_model::get_ocam_model(string filename) 12 | { 13 | ifstream file(filename); 14 | string temp; 15 | 16 | //Read polynomial coefficients 17 | for (int i = 0; i < 2; i++) getline(file, temp); 18 | file >> length_pol; 19 | for (int i = 0; i < length_pol; i++) file >> pol[i]; 20 | 21 | //Read inverse polynomial coefficients 22 | for (int i = 0; i < 3; i++) getline(file, temp); 23 | file >> length_invpol; 24 | for (int i = 0; i < length_invpol; i++) file >> invpol[i]; 25 | 26 | //Read center coordinates 27 | for (int i = 0; i < 3; i++) getline(file, temp); 28 | file >> xc; 29 | file >> yc; 30 | 31 | //Read affine coefficients 32 | for (int i = 0; i < 3; i++) getline(file, temp); 33 | file >> c; 34 | file >> d; 35 | file >> e; 36 | 37 | //Read image size 38 | for (int i = 0; i < 3; i++) getline(file, temp); 39 | file >> height; 40 | file >> width; 41 | 42 | file.close(); 43 | 44 | return 0; 45 | } 46 | 47 | void ocam_model::world2cam(double point2D[2], double point3D[3]) 48 | { 49 | double norm = sqrt(point3D[0] * point3D[0] + point3D[1] * point3D[1]); 50 | double theta = atan(point3D[2] / norm); 51 | double t, t_i; 52 | double rho, x, y; 53 | double invnorm; 54 | int i; 55 | 56 | if (norm != 0) 57 | { 58 | invnorm = 1 / norm; 59 | t = theta; 60 | rho = invpol[0]; 61 | t_i = 1; 62 | 63 | for (i = 1; i < length_invpol; i++) 64 | { 65 | t_i *= t; 66 | rho += t_i*invpol[i]; 67 | } 68 | 69 | x = point3D[0] * invnorm * rho; 70 | y = point3D[1] * invnorm * rho; 71 | 72 | point2D[0] = x * c + y * d + xc; 73 | point2D[1] = x * e + y + yc; 74 | } 75 | else 76 | { 77 | point2D[0] = xc; 78 | point2D[1] = yc; 79 | } 80 | } 81 | 82 | void ocam_model::cam2world(double point3D[3], double point2D[2]) 83 | { 84 | double invdet = 1/(c-d*e); // 1/det(A), where A = [c,d;e,1] as in the Matlab file 85 | double xp = invdet * ((point2D[0] - xc) - d * (point2D[1] - yc)); 86 | double yp = invdet * (-e * (point2D[0] - xc) + c * (point2D[1] - yc)); 87 | 88 | double r = sqrt( xp*xp + yp*yp ); //distance [pixels] of the point from the image center 89 | double zp = pol[0]; 90 | double r_i = 1; 91 | 92 | for (int i = 1; i < length_pol; i++) 93 | { 94 | r_i *= r; 95 | zp += r_i*pol[i]; 96 | } 97 | 98 | //normalize to unit norm 99 | double invnorm = 1 / sqrt(xp * xp + yp * yp + zp * zp); 100 | 101 | point3D[0] = invnorm * xp; 102 | point3D[1] = invnorm * yp; 103 | point3D[2] = invnorm * zp; 104 | } 105 | 106 | void ocam_model::create_perspecive_undistortion_LUT(Mat &mapx, Mat &mapy, float sf) 107 | { 108 | Size s = mapx.size(); 109 | int width = s.width; 110 | int height = s.height; 111 | float Nxc = height / 2.0; 112 | float Nyc = width / 2.0; 113 | float Nz = -width / sf; 114 | double M[3]; 115 | double m[2]; 116 | for (int i = 0; i < height; i++) 117 | { 118 | for (int j = 0; j < width; j++) 119 | { 120 | M[0] = (i - Nxc); 121 | M[1] = (j - Nyc); 122 | M[2] = Nz; 123 | world2cam(m, M); 124 | mapx.at(i, j) = (float)m[1]; 125 | mapy.at(i, j) = (float)m[0]; 126 | } 127 | } 128 | } 129 | 130 | void ocam_model::create_panoramic_undistortion_LUT(Mat &mapx, Mat &mapy, float Rmin, float Rmax) 131 | { 132 | float theta; 133 | Size s = mapx.size(); 134 | int width = s.width; 135 | int height = s.height; 136 | float rho; 137 | 138 | for (int i = 0; i(i, j) = yc + rho * sin(theta); 144 | mapy.at(i, j) = xc + rho * cos(theta); 145 | } 146 | } -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "ocam_functions.h" 2 | 3 | int main() 4 | { 5 | ocam_model o, o_cata; // our ocam_models for the fisheye and catadioptric cameras 6 | 7 | /* --------------------------------------------------------------------*/ 8 | /* Read the parameters of the omnidirectional camera from the TXT file */ 9 | /* --------------------------------------------------------------------*/ 10 | o.get_ocam_model("./calib_results_fisheye.txt"); 11 | o_cata.get_ocam_model("./calib_results_catadioptric.txt"); 12 | 13 | /* --------------------------------------------------------------------*/ 14 | /* Print ocam_model parameters */ 15 | /* --------------------------------------------------------------------*/ 16 | cout << "pol =" << endl; 17 | for (int i=0; i < o.length_pol; i++) cout << o.pol[i] << endl; 18 | cout << endl; 19 | cout << "invpol =" << endl; 20 | for (int i = 0; i < o.length_invpol; i++) cout << o.invpol[i] << endl; 21 | cout << endl; 22 | cout << "nxc = " << o.xc << endl; 23 | cout << "nyc = " << o.yc << endl; 24 | cout << "width = " << o.width << endl; 25 | cout << "height = " << o.height << endl; 26 | 27 | /* --------------------------------------------------------------------*/ 28 | /* WORLD2CAM projects 3D point into the image */ 29 | /* NOTE!!! The coordinates are expressed according the C convention, */ 30 | /* that is, from the origin (0,0) instead than from 1 (MATLAB). */ 31 | /* --------------------------------------------------------------------*/ 32 | double point3D[3] = { 100 , 200 , -300 }; // a sample 3D point 33 | double point2D[2]; // the image point in pixel coordinates 34 | o.world2cam(point2D, point3D); // The behaviour of this function is the same as in MATLAB 35 | 36 | /* --------------------------------------------------------------------*/ 37 | /* Display re-projected coordinates */ 38 | /* --------------------------------------------------------------------*/ 39 | cout << endl; 40 | cout << "world2cam: pixel coordinates reprojected onto the image" << endl; 41 | cout << "m_row= " << point2D[0] << ", m_col= " << point2D[1] << endl; 42 | 43 | /* --------------------------------------------------------------------*/ 44 | /* CAM2WORLD back-projects pixel points on to the unit sphere */ 45 | /* The behaviour of this function is the same as in MATLAB */ 46 | /* --------------------------------------------------------------------*/ 47 | o.cam2world(point3D, point2D); 48 | 49 | /* --------------------------------------------------------------------*/ 50 | /* Display back-projected normalized coordinates (on the unit sphere) */ 51 | /* --------------------------------------------------------------------*/ 52 | cout << endl; 53 | cout << "cam2world: coordinates back-projected onto the unit sphere (x^2+y^2+z^2=1)" << endl; 54 | cout << "x= " << point3D[0] << ", y=" << point3D[1] << ", z=" << point3D[2] << endl; 55 | 56 | Mat src1, src2; 57 | src1 = imread("./test_fisheye.jpg"); // source image 1 58 | src2 = imread("./test_catadioptric.jpg"); // source image 2 59 | Mat dst_persp(src1.size(), src1.type()); // undistorted perspective and panoramic image 60 | Size size_pan_image(1200, 400); // size of the undistorted panoramic image 61 | Mat dst_pan(size_pan_image, src2.type()); // undistorted panoramic image 62 | 63 | Mat mapx_persp(src1.size(), CV_32FC1); 64 | Mat mapy_persp(src1.size(), CV_32FC1); 65 | Mat mapx_pan(size_pan_image, CV_32FC1); 66 | Mat mapy_pan(size_pan_image, CV_32FC1); 67 | 68 | /* -------------------------------------------------------------------- */ 69 | /* Create Look-Up-Table for perspective undistortion */ 70 | /* SF is kind of distance from the undistorted image to the camera */ 71 | /* (it is not meters, it is justa zoom fator) */ 72 | /* Try to change SF to see how it affects the result */ 73 | /* The undistortion is done on a plane perpendicular to the camera axis */ 74 | /* -------------------------------------------------------------------- */ 75 | float sf = 5; 76 | o.create_perspecive_undistortion_LUT(mapx_persp, mapy_persp, sf); 77 | 78 | /* -------------------------------------------------------------------- */ 79 | /* Create Look-Up-Table for panoramic undistortion */ 80 | /* The undistortoin is just a simple cartesia-to-polar transformation */ 81 | /* Note, only the knowledge of image center (xc,yc) is used to undisort the image */ 82 | /* xc, yc are the row and column coordinates of the image center */ 83 | /* Note, if you would like to flip the image, just inverte the sign of theta in this function */ 84 | /* -------------------------------------------------------------------- */ 85 | float Rmax = 470; // the maximum radius of the region you would like to undistort into a panorama 86 | float Rmin = 20; // the minimum radius of the region you would like to undistort into a panorama 87 | o_cata.create_panoramic_undistortion_LUT(mapx_pan, mapy_pan, Rmin, Rmax); 88 | 89 | /* --------------------------------------------------------------------*/ 90 | /* Undistort using specified interpolation method */ 91 | /* For other possible values, see OpenCV doc */ 92 | /* --------------------------------------------------------------------*/ 93 | //remap(src1, dst_persp, mapx_persp, mapy_persp, CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0)); 94 | remap(src1, dst_persp, mapx_persp, mapy_persp, CV_INTER_LINEAR, BORDER_REPLICATE); 95 | remap(src2, dst_pan, mapx_pan, mapy_pan, CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0)); 96 | 97 | /* --------------------------------------------------------------------*/ 98 | /* Display image */ 99 | /* --------------------------------------------------------------------*/ 100 | imshow("Original fisheye camera image", src1); 101 | imshow("Undistorted Perspective Image", dst_persp); 102 | imshow("Original Catadioptric camera image", src2); 103 | imshow("Undistorted Panoramic Image", dst_pan); 104 | 105 | /* --------------------------------------------------------------------*/ 106 | /* Wait until key presses */ 107 | /* --------------------------------------------------------------------*/ 108 | waitKey(); 109 | 110 | return 0; 111 | } --------------------------------------------------------------------------------