The open source OpenXR runtime

xrt: Refactor t_camera_calibration to use a tagged union instead of a bare array

Co-authored-by: Jakob Bornecrantz <jakob@collabora.com>

+530 -256
+28 -44
src/xrt/auxiliary/tracking/t_calibration.cpp
··· 575 575 cv::Size image_size(cols, rows); 576 576 cv::Size new_image_size(cols, rows); 577 577 578 - StereoCameraCalibrationWrapper wrapped = {5}; // We only use five distortion parameters. 578 + StereoCameraCalibrationWrapper wrapped(c.use_fisheye ? T_DISTORTION_FISHEYE_KB4 : T_DISTORTION_OPENCV_RADTAN_5); 579 579 wrapped.view[0].image_size_pixels.w = image_size.width; 580 580 wrapped.view[0].image_size_pixels.h = image_size.height; 581 581 wrapped.view[1].image_size_pixels = wrapped.view[0].image_size_pixels; 582 - 583 - wrapped.view[0].use_fisheye = c.use_fisheye; 584 - wrapped.view[1].use_fisheye = c.use_fisheye; 585 582 586 583 587 584 float rp_error = 0.0f; ··· 591 588 flags |= cv::fisheye::CALIB_RECOMPUTE_EXTRINSIC; 592 589 593 590 // fisheye version 594 - rp_error = cv::fisheye::stereoCalibrate(c.state.board_models_f64, // objectPoints 595 - c.state.view[0].measured_f64, // inagePoints1 596 - c.state.view[1].measured_f64, // imagePoints2 597 - wrapped.view[0].intrinsics_mat, // cameraMatrix1 598 - wrapped.view[0].distortion_fisheye_mat, // distCoeffs1 599 - wrapped.view[1].intrinsics_mat, // cameraMatrix2 600 - wrapped.view[1].distortion_fisheye_mat, // distCoeffs2 601 - image_size, // imageSize 602 - wrapped.camera_rotation_mat, // R 603 - wrapped.camera_translation_mat, // T 591 + rp_error = cv::fisheye::stereoCalibrate(c.state.board_models_f64, // objectPoints 592 + c.state.view[0].measured_f64, // inagePoints1 593 + c.state.view[1].measured_f64, // imagePoints2 594 + wrapped.view[0].intrinsics_mat, // cameraMatrix1 595 + wrapped.view[0].distortion_mat, // distCoeffs1 596 + wrapped.view[1].intrinsics_mat, // cameraMatrix2 597 + wrapped.view[1].distortion_mat, // distCoeffs2 598 + image_size, // imageSize 599 + wrapped.camera_rotation_mat, // R 600 + wrapped.camera_translation_mat, // T 604 601 flags); 605 602 } else { 606 603 // non-fisheye version ··· 645 642 } 646 643 to_stdout("disparity_to_depth", maps.disparity_to_depth_mat); 647 644 std::cout << "#####\n"; 648 - if (c.use_fisheye) { 649 - to_stdout("view[0].distortion_fisheye", wrapped.view[0].distortion_fisheye_mat); 650 - } else { 651 - to_stdout("view[0].distortion", wrapped.view[0].distortion_mat); 652 - } 645 + to_stdout("view[0].distortion", wrapped.view[0].distortion_mat); 653 646 to_stdout("view[0].intrinsics", wrapped.view[0].intrinsics_mat); 654 647 to_stdout("view[0].projection", maps.view[0].projection_mat); 655 648 to_stdout("view[0].rotation", maps.view[0].rotation_mat); 656 649 std::cout << "#####\n"; 657 - if (c.use_fisheye) { 658 - to_stdout("view[1].distortion_fisheye", wrapped.view[1].distortion_fisheye_mat); 659 - } else { 660 - to_stdout("view[1].distortion", wrapped.view[1].distortion_mat); 661 - } 650 + to_stdout("view[1].distortion", wrapped.view[1].distortion_mat); 662 651 to_stdout("view[1].intrinsics", wrapped.view[1].intrinsics_mat); 663 652 to_stdout("view[1].projection", maps.view[1].projection_mat); 664 653 to_stdout("view[1].rotation", maps.view[1].rotation_mat); ··· 681 670 cv::Mat intrinsics_mat = {}; 682 671 cv::Mat new_intrinsics_mat = {}; 683 672 cv::Mat distortion_mat = {}; 684 - cv::Mat distortion_fisheye_mat = {}; 685 673 686 674 if (c.dump_measurements) { 687 675 U_LOG_RAW("...measured = (ArrayOfMeasurements){"); ··· 712 700 view.measured_f64, // imagePoints 713 701 image_size, // image_size 714 702 intrinsics_mat, // K (cameraMatrix 3x3) 715 - distortion_fisheye_mat, // D (distCoeffs 4x1) 703 + distortion_mat, // D (distCoeffs 4x1) 716 704 cv::noArray(), // rvecs 717 705 cv::noArray(), // tvecs 718 706 flags, // flags ··· 720 708 721 709 double balance = 0.1f; 722 710 723 - cv::fisheye::estimateNewCameraMatrixForUndistortRectify(intrinsics_mat, // K 724 - distortion_fisheye_mat, // D 725 - image_size, // image_size 726 - cv::Matx33d::eye(), // R 727 - new_intrinsics_mat, // P 728 - balance); // balance 711 + cv::fisheye::estimateNewCameraMatrixForUndistortRectify(intrinsics_mat, // K 712 + distortion_mat, // D 713 + image_size, // image_size 714 + cv::Matx33d::eye(), // R 715 + new_intrinsics_mat, // P 716 + balance); // balance 729 717 730 718 // Probably a busted work-around for busted function. 731 719 new_intrinsics_mat.at<double>(0, 2) = (cols - 1) / 2.0; ··· 768 756 std::cout << "rp_error: " << rp_error << "\n"; 769 757 std::cout << "intrinsics_mat:\n" << intrinsics_mat << "\n"; 770 758 std::cout << "new_intrinsics_mat:\n" << new_intrinsics_mat << "\n"; 771 - if (c.use_fisheye) { 772 - std::cout << "distortion_fisheye_mat:\n" << distortion_fisheye_mat << "\n"; 773 - } else { 774 759 std::cout << "distortion_mat:\n" << distortion_mat << "\n"; 775 - } 776 760 // clang-format on 777 761 778 762 if (c.use_fisheye) { 779 - cv::fisheye::initUndistortRectifyMap(intrinsics_mat, // K 780 - distortion_fisheye_mat, // D 781 - cv::Matx33d::eye(), // R 782 - new_intrinsics_mat, // P 783 - image_size, // size 784 - CV_32FC1, // m1type 785 - view.map1, // map1 786 - view.map2); // map2 763 + cv::fisheye::initUndistortRectifyMap(intrinsics_mat, // K 764 + distortion_mat, // D 765 + cv::Matx33d::eye(), // R 766 + new_intrinsics_mat, // P 767 + image_size, // size 768 + CV_32FC1, // m1type 769 + view.map1, // map1 770 + view.map2); // map2 787 771 788 772 // Set the maps as valid. 789 773 view.maps_valid = true;
+32 -17
src/xrt/auxiliary/tracking/t_calibration_opencv.hpp
··· 22 22 23 23 namespace xrt::auxiliary::tracking { 24 24 25 + static inline size_t 26 + t_num_opencv_params_from_distortion_model(const enum t_camera_distortion_model model) 27 + { 28 + switch (model) { 29 + case T_DISTORTION_WMR: 30 + // OpenCV doesn't know what to do with codx, cody or rpmax. We reinterpret it as 31 + // T_DISTORTION_OPENCV_RADTAN_8. 32 + return t_num_params_from_distortion_model(T_DISTORTION_OPENCV_RADTAN_8); 33 + break; 34 + default: return t_num_params_from_distortion_model(model); 35 + } 36 + } 25 37 /*! 26 38 * @brief Essential calibration data wrapped for C++. 27 39 * ··· 35 47 const cv::Size image_size_pixels_cv; 36 48 cv::Mat_<double> intrinsics_mat; 37 49 cv::Mat_<double> distortion_mat; 38 - cv::Mat_<double> distortion_fisheye_mat; 39 - bool &use_fisheye; 50 + enum t_camera_distortion_model &distortion_model; 40 51 41 52 CameraCalibrationWrapper(t_camera_calibration &calib) 42 - : base(calib), image_size_pixels(calib.image_size_pixels), 43 - image_size_pixels_cv(calib.image_size_pixels.w, calib.image_size_pixels.h), 44 - intrinsics_mat(3, 3, &calib.intrinsics[0][0]), 45 - distortion_mat(base.distortion_num, 1, &calib.distortion[0]), 46 - distortion_fisheye_mat(4, 1, &calib.distortion_fisheye[0]), use_fisheye(calib.use_fisheye) 53 + : base(calib), // 54 + image_size_pixels(calib.image_size_pixels), // 55 + image_size_pixels_cv(calib.image_size_pixels.w, calib.image_size_pixels.h), // 56 + intrinsics_mat(3, 3, &calib.intrinsics[0][0]), // 57 + distortion_mat(t_num_opencv_params_from_distortion_model(base.distortion_model), 58 + 1, 59 + &calib.distortion_parameters_as_array[0]), // 60 + distortion_model(calib.distortion_model) 47 61 { 62 + if (base.distortion_model == T_DISTORTION_WMR) { 63 + U_LOG_W("Reinterpreting T_DISTORTION_WMR model as T_DISTORTION_OPENCV_RADTAN_8!"); 64 + } 48 65 assert(isDataStorageValid()); 49 66 } 50 67 ··· 54 71 { 55 72 return intrinsics_mat.size() == cv::Size(3, 3) && 56 73 (double *)intrinsics_mat.data == &(base.intrinsics[0][0]) && 57 - 58 - distortion_mat.size() == cv::Size(1, base.distortion_num) && 59 - (double *)distortion_mat.data == &(base.distortion[0]) && 60 - 61 - distortion_fisheye_mat.size() == cv::Size(1, 4) && 62 - (double *)distortion_fisheye_mat.data == &(base.distortion_fisheye[0]); 74 + (base.distortion_model != T_DISTORTION_FISHEYE_KB4 || distortion_mat.size() == cv::Size(1, 4)) && 75 + distortion_mat.size() == 76 + cv::Size(1, t_num_opencv_params_from_distortion_model(base.distortion_model)) && 77 + (double *)distortion_mat.data == &(base.distortion_parameters_as_array[0]); 63 78 } 64 79 }; 65 80 ··· 81 96 82 97 83 98 static t_stereo_camera_calibration * 84 - allocData(uint32_t distortion_num) 99 + allocData(enum t_camera_distortion_model distortion_model) 85 100 { 86 101 t_stereo_camera_calibration *data_ptr = NULL; 87 - t_stereo_camera_calibration_alloc(&data_ptr, distortion_num); 102 + t_stereo_camera_calibration_alloc(&data_ptr, distortion_model); 88 103 return data_ptr; 89 104 } 90 105 ··· 102 117 assert(isDataStorageValid()); 103 118 } 104 119 105 - StereoCameraCalibrationWrapper(uint32_t distortion_num) 106 - : StereoCameraCalibrationWrapper(allocData(distortion_num)) 120 + StereoCameraCalibrationWrapper(enum t_camera_distortion_model distortion_model) 121 + : StereoCameraCalibrationWrapper(allocData(distortion_model)) 107 122 { 108 123 109 124 // The function allocData returns with a ref count of one,
+13 -26
src/xrt/auxiliary/tracking/t_data_utils.c
··· 86 86 { 87 87 char buf[1024]; 88 88 ssize_t curr = 0; 89 - 90 - U_LOG_RAW("use_fisheye = %s", view->use_fisheye ? "true" : "false"); 89 + U_LOG_RAW("distortion_model = %s", t_stringify_camera_distortion_model(view->distortion_model)); 91 90 92 - if (view->use_fisheye) { 93 - P("distortion_fisheye = ["); 94 - for (uint32_t col = 0; col < 4; col++) { 95 - P("%f", view->distortion_fisheye[col]); 96 - if (col < 3) { 97 - P(", "); 98 - } 91 + P("distortion = ["); 92 + size_t num = t_num_params_from_distortion_model(view->distortion_model); 93 + for (uint32_t col = 0; col < num; col++) { 94 + P("%f", view->distortion_parameters_as_array[col]); 95 + if (col < num - 1) { 96 + P(", "); 99 97 } 100 - P("]"); 101 - } else { 102 - P("distortion = ["); 103 - for (uint32_t col = 0; col < view->distortion_num; col++) { 104 - P("%f", view->distortion[col]); 105 - if (col < view->distortion_num - 1) { 106 - P(", "); 107 - } 108 - } 109 - P("]"); 110 98 } 99 + P("]"); 100 + 111 101 U_LOG_RAW("%s", buf); 112 102 } 113 103 ··· 119 109 */ 120 110 121 111 void 122 - t_stereo_camera_calibration_alloc(struct t_stereo_camera_calibration **out_c, uint32_t distortion_num) 112 + t_stereo_camera_calibration_alloc(struct t_stereo_camera_calibration **out_c, 113 + const enum t_camera_distortion_model distortion_model) 123 114 { 124 - // Four parameters for kannala-brandt, 5, 8, 12, or 14 for the normal OpenCV pinhole distortion model 125 - assert(distortion_num == 4 || distortion_num == 5 || distortion_num == 8 || distortion_num == 12 || 126 - distortion_num == 14); 127 - 128 115 struct t_stereo_camera_calibration *c = U_TYPED_CALLOC(struct t_stereo_camera_calibration); 129 - c->view[0].distortion_num = distortion_num; 130 - c->view[1].distortion_num = distortion_num; 116 + c->view[0].distortion_model = distortion_model; 117 + c->view[1].distortion_model = distortion_model; 131 118 t_stereo_camera_calibration_reference(out_c, c); 132 119 } 133 120
+173 -72
src/xrt/auxiliary/tracking/t_file.cpp
··· 41 41 return false; \ 42 42 } 43 43 44 + #define COPY(TO, FROM) \ 45 + do { \ 46 + CALIB_ASSERT(FROM.size() == TO.size(), "Sizes doesn't match for " #FROM); \ 47 + FROM.copyTo(TO); \ 48 + } while (false) 49 + 50 + 44 51 /* 45 52 * 46 53 * Pre-declar functions. ··· 75 82 // calibration for does not match what was saved 76 83 cv::Size image_size(calib.image_size_pixels.w, calib.image_size_pixels.h); 77 84 78 - if (calib.use_fisheye) { 79 - cv::fisheye::initUndistortRectifyMap(wrap.intrinsics_mat, // cameraMatrix 80 - wrap.distortion_fisheye_mat, // distCoeffs 81 - rectify_transform_optional, // R 82 - new_camera_matrix_optional, // newCameraMatrix 83 - image_size, // size 84 - CV_32FC1, // m1type 85 - ret.remap_x, // map1 86 - ret.remap_y); // map2 87 - } else { 85 + switch (calib.distortion_model) { 86 + case (T_DISTORTION_FISHEYE_KB4): 87 + cv::fisheye::initUndistortRectifyMap(wrap.intrinsics_mat, // cameraMatrix 88 + wrap.distortion_mat, // distCoeffs 89 + rectify_transform_optional, // R 90 + new_camera_matrix_optional, // newCameraMatrix 91 + image_size, // size 92 + CV_32FC1, // m1type 93 + ret.remap_x, // map1 94 + ret.remap_y); // map2 95 + break; 96 + case T_DISTORTION_OPENCV_RADTAN_5: 88 97 cv::initUndistortRectifyMap(wrap.intrinsics_mat, // cameraMatrix 89 98 wrap.distortion_mat, // distCoeffs 90 99 rectify_transform_optional, // R ··· 93 102 CV_32FC1, // m1type 94 103 ret.remap_x, // map1 95 104 ret.remap_y); // map2 105 + break; 106 + default: assert(false); 96 107 } 97 108 98 109 return ret; ··· 104 115 CALIB_ASSERT_(data->view[0].image_size_pixels.w == data->view[1].image_size_pixels.w); 105 116 CALIB_ASSERT_(data->view[0].image_size_pixels.h == data->view[1].image_size_pixels.h); 106 117 107 - CALIB_ASSERT_(data->view[0].use_fisheye == data->view[1].use_fisheye); 118 + CALIB_ASSERT_(data->view[0].distortion_model == data->view[1].distortion_model); 108 119 109 120 cv::Size image_size(data->view[0].image_size_pixels.w, data->view[0].image_size_pixels.h); 110 121 StereoCameraCalibrationWrapper wrapped(data); ··· 114 125 * 115 126 * Here cv::noArray() means zero distortion. 116 127 */ 117 - if (data->view[0].use_fisheye) { 128 + switch (data->view[0].distortion_model) { 129 + case T_DISTORTION_FISHEYE_KB4: { 118 130 #if 0 119 131 //! @todo for some reason this looks weird? 120 132 // Alpha of 1.0 kinda works, not really. ··· 124 136 125 137 cv::fisheye::stereoRectify( 126 138 wrapped.view[0].intrinsics_mat, // K1 127 - wrapped.view[0].distortion_fisheye_mat, // D1 139 + wrapped.view[0].distortion_mat, // D1 128 140 wrapped.view[1].intrinsics_mat, // K2 129 - wrapped.view[1].distortion_fisheye_mat, // D2 141 + wrapped.view[1].distortion_mat, // D2 130 142 image_size, // imageSize 131 143 wrapped.camera_rotation_mat, // R 132 144 wrapped.camera_translation_mat, // tvec ··· 163 175 NULL, // validPixROI1 164 176 NULL); // validPixROI2 165 177 #endif 166 - } else { 178 + } break; 179 + case T_DISTORTION_OPENCV_RADTAN_5: { 167 180 // Have the same principal point on both. 168 181 int flags = cv::CALIB_ZERO_DISPARITY; 169 182 // Get all of the pixels from the camera. ··· 188 201 cv::Size(), // newImageSize 189 202 NULL, // validPixROI1 190 203 NULL); // validPixROI2 204 + } break; 205 + default: assert(false); 191 206 } 192 207 193 208 view[0].rectify = calibration_get_undistort_map(data->view[0], view[0].rotation_mat, view[0].projection_mat); ··· 212 227 extern "C" bool 213 228 t_stereo_camera_calibration_load_v1(FILE *calib_file, struct t_stereo_camera_calibration **out_data) 214 229 { 215 - 216 - t_stereo_camera_calibration *data_ptr = NULL; 217 - t_stereo_camera_calibration_alloc(&data_ptr, 5); // Hardcoded to 5 distortion parameters. 218 - StereoCameraCalibrationWrapper wrapped(data_ptr); 219 - 220 230 // Scratch-space temporary matrix 221 231 cv::Mat scratch; 222 232 233 + // Temp load matricies 234 + cv::Mat_<double> l_intrinsics(3, 3); 235 + cv::Mat_<double> r_intrinsics(3, 3); 236 + cv::Mat_<double> l_distortion(5, 1); 237 + cv::Mat_<double> r_distortion(5, 1); 238 + cv::Mat_<double> l_distortion_fisheye(4, 1); 239 + cv::Mat_<double> r_distortion_fisheye(4, 1); 240 + cv::Mat_<double> translation(3, 1); 241 + cv::Mat_<double> rotation(3, 3); 242 + cv::Mat_<double> essential(3, 3); 243 + cv::Mat_<double> fundamental(3, 3); 244 + cv::Mat_<float> mat_use_fisheye(1, 1, {0.0f}); // Ensure is initialised. 245 + cv::Mat_<float> mat_image_size(1, 2); 246 + cv::Mat_<float> mat_new_image_size(1, 2); 247 + 223 248 // Read our calibration from this file 224 - // clang-format off 225 - cv::Mat_<float> mat_image_size(2, 1); 226 - bool result = read_cv_mat(calib_file, &wrapped.view[0].intrinsics_mat, "l_intrinsics"); // 3 x 3 227 - result = result && read_cv_mat(calib_file, &wrapped.view[1].intrinsics_mat, "r_intrinsics"); // 3 x 3 228 - result = result && read_cv_mat(calib_file, &wrapped.view[0].distortion_mat, "l_distortion"); // 5 x 1 229 - result = result && read_cv_mat(calib_file, &wrapped.view[1].distortion_mat, "r_distortion"); // 5 x 1 230 - result = result && read_cv_mat(calib_file, &wrapped.view[0].distortion_fisheye_mat, "l_distortion_fisheye"); // 4 x 1 231 - result = result && read_cv_mat(calib_file, &wrapped.view[1].distortion_fisheye_mat, "r_distortion_fisheye"); // 4 x 1 232 - result = result && read_cv_mat(calib_file, &scratch, "l_rotation"); // 3 x 3 233 - result = result && read_cv_mat(calib_file, &scratch, "r_rotation"); // 3 x 3 234 - result = result && read_cv_mat(calib_file, &scratch, "l_translation"); // empty 235 - result = result && read_cv_mat(calib_file, &scratch, "r_translation"); // empty 236 - result = result && read_cv_mat(calib_file, &scratch, "l_projection"); // 3 x 4 237 - result = result && read_cv_mat(calib_file, &scratch, "r_projection"); // 3 x 4 238 - result = result && read_cv_mat(calib_file, &scratch, "disparity_to_depth"); // 4 x 4 249 + bool result = read_cv_mat(calib_file, &l_intrinsics, "l_intrinsics"); // 3 x 3 250 + result = result && read_cv_mat(calib_file, &r_intrinsics, "r_intrinsics"); // 3 x 3 251 + result = result && read_cv_mat(calib_file, &l_distortion, "l_distortion"); // 5 x 1 252 + result = result && read_cv_mat(calib_file, &r_distortion, "r_distortion"); // 5 x 1 253 + result = result && read_cv_mat(calib_file, &l_distortion_fisheye, "l_distortion_fisheye"); // 4 x 1 254 + result = result && read_cv_mat(calib_file, &r_distortion_fisheye, "r_distortion_fisheye"); // 4 x 1 255 + result = result && read_cv_mat(calib_file, &scratch, "l_rotation"); // 3 x 3 256 + result = result && read_cv_mat(calib_file, &scratch, "r_rotation"); // 3 x 3 257 + result = result && read_cv_mat(calib_file, &scratch, "l_translation"); // empty 258 + result = result && read_cv_mat(calib_file, &scratch, "r_translation"); // empty 259 + result = result && read_cv_mat(calib_file, &scratch, "l_projection"); // 3 x 4 260 + result = result && read_cv_mat(calib_file, &scratch, "r_projection"); // 3 x 4 261 + result = result && read_cv_mat(calib_file, &scratch, "disparity_to_depth"); // 4 x 4 239 262 result = result && read_cv_mat(calib_file, &mat_image_size, "mat_image_size"); 240 263 241 264 if (!result) { 242 265 CALIB_WARN("Re-run calibration!"); 243 266 return false; 244 267 } 245 - wrapped.view[0].image_size_pixels.w = uint32_t(mat_image_size(0, 0)); 246 - wrapped.view[0].image_size_pixels.h = uint32_t(mat_image_size(0, 1)); 247 - wrapped.view[1].image_size_pixels = wrapped.view[0].image_size_pixels; 248 268 249 - cv::Mat mat_new_image_size = mat_image_size.clone(); 250 269 if (read_cv_mat(calib_file, &mat_new_image_size, "mat_new_image_size")) { 251 270 // do nothing particular here. 252 271 } 253 - 254 - if (!read_cv_mat(calib_file, &wrapped.camera_translation_mat, "translation")) { 272 + if (!read_cv_mat(calib_file, &translation, "translation")) { // 3 x 1 255 273 CALIB_WARN("Re-run calibration!"); 256 274 } 257 - if (!read_cv_mat(calib_file, &wrapped.camera_rotation_mat, "rotation")) { 275 + if (!read_cv_mat(calib_file, &rotation, "rotation")) { // 3 x 3 258 276 CALIB_WARN("Re-run calibration!"); 259 277 } 260 - if (!read_cv_mat(calib_file, &wrapped.camera_essential_mat, "essential")) { 278 + if (!read_cv_mat(calib_file, &essential, "essential")) { // 3 x 3 261 279 CALIB_WARN("Re-run calibration!"); 262 280 } 263 - if (!read_cv_mat(calib_file, &wrapped.camera_fundamental_mat, "fundamental")) { 281 + if (!read_cv_mat(calib_file, &fundamental, "fundamental")) { // 3 x 3 264 282 CALIB_WARN("Re-run calibration!"); 265 283 } 266 - 267 - cv::Mat_<float> mat_use_fisheye(1, 1); 268 284 if (!read_cv_mat(calib_file, &mat_use_fisheye, "use_fisheye")) { 269 - wrapped.view[0].use_fisheye = false; 270 285 CALIB_WARN("Re-run calibration! (Assuming not fisheye)"); 286 + } 287 + 288 + 289 + /* 290 + * Extract some data. 291 + */ 292 + 293 + bool is_fisheye = mat_use_fisheye(0, 0) != 0.0f; 294 + uint32_t size_w = uint32_t(mat_image_size(0, 0)); 295 + uint32_t size_h = uint32_t(mat_image_size(0, 1)); 296 + t_camera_distortion_model model = is_fisheye ? T_DISTORTION_FISHEYE_KB4 : T_DISTORTION_OPENCV_RADTAN_5; 297 + 298 + 299 + /* 300 + * Copy to calibration struct. 301 + */ 302 + 303 + t_stereo_camera_calibration *data_ptr = NULL; 304 + t_stereo_camera_calibration_alloc(&data_ptr, model); 305 + StereoCameraCalibrationWrapper wrapped(data_ptr); 306 + 307 + COPY(wrapped.view[0].intrinsics_mat, l_intrinsics); 308 + COPY(wrapped.view[1].intrinsics_mat, r_intrinsics); 309 + if (is_fisheye) { 310 + COPY(wrapped.view[0].distortion_mat, l_distortion_fisheye); 311 + COPY(wrapped.view[1].distortion_mat, r_distortion_fisheye); 271 312 } else { 272 - wrapped.view[0].use_fisheye = mat_use_fisheye(0, 0) != 0.0f; 313 + COPY(wrapped.view[0].distortion_mat, l_distortion); 314 + COPY(wrapped.view[1].distortion_mat, r_distortion); 273 315 } 274 - wrapped.view[1].use_fisheye = wrapped.view[0].use_fisheye; 275 - // clang-format on 316 + COPY(wrapped.camera_translation_mat, translation); 317 + COPY(wrapped.camera_rotation_mat, rotation); 318 + COPY(wrapped.camera_essential_mat, essential); 319 + COPY(wrapped.camera_fundamental_mat, fundamental); 320 + wrapped.view[0].image_size_pixels.w = wrapped.view[1].image_size_pixels.w = size_w; 321 + wrapped.view[0].image_size_pixels.h = wrapped.view[1].image_size_pixels.h = size_h; 276 322 277 323 CALIB_ASSERT_(wrapped.isDataStorageValid()); 278 324 ··· 299 345 return success; 300 346 } 301 347 348 + //!@todo merge these with t_tracking.h 302 349 #define PINHOLE_RADTAN5 "pinhole_radtan5" 303 350 #define FISHEYE_EQUIDISTANT4 "fisheye_equidistant4" 304 351 ··· 359 406 360 407 size_t n = jc["distortion"].asObject().size(); 361 408 if (model == PINHOLE_RADTAN5) { 362 - cc->use_fisheye = false; 409 + cc->distortion_model = T_DISTORTION_OPENCV_RADTAN_5; 363 410 CALIB_ASSERTR(n == 5, "%zu != 5 distortion params", n); 364 411 365 - constexpr array names{"k1", "k2", "p1", "p2", "k3"}; 366 - for (size_t i = 0; i < n; i++) { 367 - cc->distortion[i] = jc["distortion"][names[i]].asDouble(); 368 - } 412 + cc->rt5.k1 = jc["distortion"]["k1"].asDouble(); 413 + cc->rt5.k2 = jc["distortion"]["k2"].asDouble(); 414 + cc->rt5.p1 = jc["distortion"]["p1"].asDouble(); 415 + cc->rt5.p2 = jc["distortion"]["p2"].asDouble(); 416 + cc->rt5.k3 = jc["distortion"]["k3"].asDouble(); 369 417 } else if (model == FISHEYE_EQUIDISTANT4) { 370 - cc->use_fisheye = true; 418 + cc->distortion_model = T_DISTORTION_FISHEYE_KB4; 371 419 CALIB_ASSERTR(n == 4, "%zu != 4 distortion params", n); 372 420 373 - constexpr array names{"k1", "k2", "k3", "k4"}; 374 - for (size_t i = 0; i < n; i++) { 375 - cc->distortion_fisheye[i] = jc["distortion"][names[i]].asDouble(); 376 - } 421 + cc->kb4.k1 = jc["distortion"]["k1"].asDouble(); 422 + cc->kb4.k2 = jc["distortion"]["k2"].asDouble(); 423 + cc->kb4.k3 = jc["distortion"]["k3"].asDouble(); 424 + cc->kb4.k4 = jc["distortion"]["k4"].asDouble(); 377 425 } else { 378 426 CALIB_ASSERTR(false, "Invalid camera model: '%s'", model.c_str()); 379 427 return false; ··· 388 436 t_stereo_camera_calibration_from_json_v2(cJSON *cjson, struct t_stereo_camera_calibration **out_stereo) 389 437 { 390 438 JSONNode json{cjson}; 391 - StereoCameraCalibrationWrapper stereo{5}; // Hardcoded to 5 distortion parameters. 392 439 393 440 // Load file metadata 394 441 const int supported_version = 2; ··· 398 445 } 399 446 CALIB_ASSERTR(version == supported_version, "Calibration json version (%d) != %d", version, supported_version); 400 447 448 + // Temporary camera calibration structs so we can infer the distortion model easily 449 + t_camera_calibration tmp_calibs[2]; 450 + 401 451 // Load cameras 402 452 vector<JSONNode> cameras = json["cameras"].asArray(); 403 453 bool okmats = true; 404 454 CALIB_ASSERTR(cameras.size() == 2, "Two cameras must be specified, %zu given", cameras.size()); 405 455 for (size_t i = 0; i < cameras.size(); i++) { 406 456 JSONNode jc = cameras[i]; 407 - CameraCalibrationWrapper &cc = stereo.view[i]; 408 - bool loaded = t_camera_calibration_load_v2(jc.getCJSON(), &cc.base); 457 + bool loaded = t_camera_calibration_load_v2(jc.getCJSON(), &tmp_calibs[i]); 409 458 CALIB_ASSERTR(loaded, "Unable to load camera calibration: %s", jc.toString(false).c_str()); 410 459 } 411 460 461 + t_camera_distortion_model model = tmp_calibs[0].distortion_model; 462 + 463 + //!@todo At some point it'll make sense to support different distortion models per-camera, but right now we 464 + //! don't have any cameras like that and the way t_stereo_camera_calib_alloc and 465 + //!(Stereo)CameraCalibrationWrapper work makes it pretty annoying. 466 + 467 + CALIB_ASSERT_(tmp_calibs[0].distortion_model == tmp_calibs[1].distortion_model); 468 + 469 + StereoCameraCalibrationWrapper stereo{model}; 470 + 471 + stereo.view[0].base = tmp_calibs[0]; 472 + stereo.view[1].base = tmp_calibs[1]; 473 + 474 + 412 475 JSONNode rel = json["opencv_stereo_calibrate"]; 413 476 okmats &= load_mat_field(rel["rotation"], 3, 3, stereo.camera_rotation_mat); 414 477 okmats &= load_mat_field(rel["translation"], 3, 1, stereo.camera_translation_mat); ··· 447 510 CALIB_WARN("Deprecated function: %s", __func__); 448 511 449 512 StereoCameraCalibrationWrapper wrapped(data); 513 + 514 + bool is_fisheye = false; 515 + 516 + switch (data->view[0].distortion_model) { 517 + case T_DISTORTION_OPENCV_RADTAN_5: is_fisheye = false; break; 518 + case T_DISTORTION_FISHEYE_KB4: is_fisheye = true; break; 519 + default: 520 + CALIB_ERROR("Can't save distortion model %s in a v1 calib file!", 521 + t_stringify_camera_distortion_model(data->view[0].distortion_model)); 522 + return false; 523 + } 524 + 525 + if (data->view[0].distortion_model != data->view[1].distortion_model) { 526 + CALIB_ERROR("v1 calibrations can't deal with differing distortion models!"); 527 + return false; 528 + } 529 + 450 530 // Scratch-space temporary matrix 451 531 cv::Mat scratch; 452 532 453 533 write_cv_mat(calib_file, &wrapped.view[0].intrinsics_mat); 454 534 write_cv_mat(calib_file, &wrapped.view[1].intrinsics_mat); 455 - write_cv_mat(calib_file, &wrapped.view[0].distortion_mat); 456 - write_cv_mat(calib_file, &wrapped.view[1].distortion_mat); 457 - write_cv_mat(calib_file, &wrapped.view[0].distortion_fisheye_mat); 458 - write_cv_mat(calib_file, &wrapped.view[1].distortion_fisheye_mat); 535 + if (is_fisheye) { 536 + cv::Mat_<double> distortion(5, 1, {0.0}); 537 + write_cv_mat(calib_file, &distortion); // l_distortion 538 + write_cv_mat(calib_file, &distortion); // r_distortion 539 + write_cv_mat(calib_file, &wrapped.view[0].distortion_mat); 540 + write_cv_mat(calib_file, &wrapped.view[1].distortion_mat); 541 + } else { 542 + cv::Mat_<double> distortion_fisheye(4, 1, {0.0}); 543 + write_cv_mat(calib_file, &wrapped.view[0].distortion_mat); 544 + write_cv_mat(calib_file, &wrapped.view[1].distortion_mat); 545 + write_cv_mat(calib_file, &distortion_fisheye); // l_distortion_fisheye 546 + write_cv_mat(calib_file, &distortion_fisheye); // r_distortion_fisheye 547 + } 548 + 459 549 write_cv_mat(calib_file, &scratch); // view[0].rotation_mat 460 550 write_cv_mat(calib_file, &scratch); // view[1].rotation_mat 461 551 write_cv_mat(calib_file, &scratch); // l_translation ··· 480 570 481 571 cv::Mat mat_use_fisheye; 482 572 mat_use_fisheye.create(1, 1, CV_32F); 483 - mat_use_fisheye.at<float>(0, 0) = wrapped.view[0].use_fisheye; 573 + mat_use_fisheye.at<float>(0, 0) = is_fisheye; 484 574 write_cv_mat(calib_file, &mat_use_fisheye); 485 575 486 576 return true; ··· 516 606 extern "C" bool 517 607 t_stereo_camera_calibration_to_json_v2(cJSON **out_cjson, struct t_stereo_camera_calibration *data) 518 608 { 609 + if (data->view[0].distortion_model != data->view[1].distortion_model) { 610 + CALIB_ASSERTR(false, 611 + "Can't deal with a stereo camera calibration with different distortion models per-view!"); 612 + } 613 + 614 + if (data->view[0].distortion_model != T_DISTORTION_FISHEYE_KB4 && 615 + data->view[0].distortion_model != T_DISTORTION_OPENCV_RADTAN_5) { 616 + CALIB_ASSERTR(false, "Can only deal with fisheye or radtan5 distortion models!"); 617 + } 618 + 519 619 StereoCameraCalibrationWrapper wrapped(data); 520 620 JSONBuilder jb{}; 521 621 ··· 533 633 // Cameras 534 634 for (size_t i = 0; i < 2; i++) { 535 635 const auto &view = wrapped.view[i]; 636 + bool fisheye = view.distortion_model == T_DISTORTION_FISHEYE_KB4; 536 637 jb << "{"; 537 - jb << "model" << (view.use_fisheye ? FISHEYE_EQUIDISTANT4 : PINHOLE_RADTAN5); 638 + jb << "model" << (fisheye ? FISHEYE_EQUIDISTANT4 : PINHOLE_RADTAN5); 538 639 539 640 jb << "intrinsics"; 540 641 jb << "{"; ··· 546 647 547 648 jb << "distortion"; 548 649 jb << "{"; 549 - if (view.use_fisheye) { 550 - int n = view.distortion_fisheye_mat.size().area(); // Number of distortion parameters 650 + if (fisheye) { 651 + int n = view.distortion_mat.size().area(); // Number of distortion parameters 551 652 CALIB_ASSERT_(n == 4); 552 653 553 654 constexpr array names{"k1", "k2", "k3", "k4"}; 554 655 for (int i = 0; i < n; i++) { 555 - jb << names[i] << view.distortion_fisheye_mat(i); 656 + jb << names[i] << view.distortion_mat(i); 556 657 } 557 658 } else { 558 659 int n = view.distortion_mat.size().area(); // Number of distortion parameters
+2 -4
src/xrt/auxiliary/tracking/t_tracker_psmv.cpp
··· 51 51 52 52 cv::Matx33d intrinsics; 53 53 cv::Mat distortion; // size may vary 54 - cv::Vec4d distortion_fisheye; 55 - bool use_fisheye; 54 + enum t_camera_distortion_model distortion_model; 56 55 57 56 std::vector<cv::KeyPoint> keypoints; 58 57 ··· 64 63 CameraCalibrationWrapper wrap(calib); 65 64 intrinsics = wrap.intrinsics_mat; 66 65 distortion = wrap.distortion_mat.clone(); 67 - distortion_fisheye = wrap.distortion_fisheye_mat; 68 - use_fisheye = wrap.use_fisheye; 66 + distortion_model = wrap.distortion_model; 69 67 70 68 undistort_rectify_map_x = rectification.remap_x; 71 69 undistort_rectify_map_y = rectification.remap_y;
+2 -4
src/xrt/auxiliary/tracking/t_tracker_psvr.cpp
··· 137 137 138 138 cv::Matx33d intrinsics; 139 139 cv::Mat distortion; // size may vary 140 - cv::Vec4d distortion_fisheye; 141 - bool use_fisheye; 140 + enum t_camera_distortion_model distortion_model; 142 141 143 142 std::vector<cv::KeyPoint> keypoints; 144 143 ··· 150 149 CameraCalibrationWrapper wrap(calib); 151 150 intrinsics = wrap.intrinsics_mat; 152 151 distortion = wrap.distortion_mat.clone(); 153 - distortion_fisheye = wrap.distortion_fisheye_mat; 154 - use_fisheye = wrap.use_fisheye; 152 + distortion_model = wrap.distortion_model; 155 153 156 154 undistort_rectify_map_x = rectification.remap_x; 157 155 undistort_rectify_map_y = rectification.remap_y;
+38 -9
src/xrt/auxiliary/tracking/t_tracker_slam.cpp
··· 1021 1021 params->cx = view.intrinsics[0][2]; 1022 1022 params->cy = view.intrinsics[1][2]; 1023 1023 1024 - params->distortion_model = view.use_fisheye ? "kb4" : string{"rt"} + to_string(view.distortion_num); 1025 - if (view.use_fisheye) { // Kannala-brandt pinhole (OpenCV's "fisheye") 1026 - params->distortion.assign(view.distortion_fisheye, std::end(view.distortion_fisheye)); 1024 + switch (view.distortion_model) { 1025 + case T_DISTORTION_OPENCV_RADTAN_8: 1026 + params->distortion_model = "rt8"; 1027 + params->distortion.push_back(view.rt8.k1); 1028 + params->distortion.push_back(view.rt8.k2); 1029 + params->distortion.push_back(view.rt8.p1); 1030 + params->distortion.push_back(view.rt8.p2); 1031 + params->distortion.push_back(view.rt8.k3); 1032 + params->distortion.push_back(view.rt8.k4); 1033 + params->distortion.push_back(view.rt8.k5); 1034 + params->distortion.push_back(view.rt8.k6); 1035 + // -1 metric radius tells Basalt to calculate the metric radius on its own. 1036 + params->distortion.push_back(-1.0); 1037 + SLAM_ASSERT_(params->distortion.size() == 9); 1038 + break; 1039 + case T_DISTORTION_WMR: 1040 + params->distortion_model = "rt8"; 1041 + params->distortion.push_back(view.wmr.k1); 1042 + params->distortion.push_back(view.wmr.k2); 1043 + params->distortion.push_back(view.wmr.p1); 1044 + params->distortion.push_back(view.wmr.p2); 1045 + params->distortion.push_back(view.wmr.k3); 1046 + params->distortion.push_back(view.wmr.k4); 1047 + params->distortion.push_back(view.wmr.k5); 1048 + params->distortion.push_back(view.wmr.k6); 1049 + params->distortion.push_back(view.wmr.rpmax); 1050 + SLAM_ASSERT_(params->distortion.size() == 9); 1051 + break; 1052 + case T_DISTORTION_FISHEYE_KB4: 1053 + params->distortion_model = "kb4"; 1054 + params->distortion.push_back(view.kb4.k1); 1055 + params->distortion.push_back(view.kb4.k2); 1056 + params->distortion.push_back(view.kb4.k3); 1057 + params->distortion.push_back(view.kb4.k4); 1027 1058 SLAM_ASSERT_(params->distortion.size() == 4); 1028 - } else { // Radial-tangential pinhole 1029 - params->distortion.assign(view.distortion, view.distortion + view.distortion_num); 1030 - 1031 - if (params->distortion_model == "rt8") { // rt8 has a ninth parameter rpmax ("metric_radius") 1032 - params->distortion.push_back(extra.rpmax); 1033 - } 1059 + break; 1060 + default: 1061 + SLAM_ASSERT(false, "SLAM doesn't support distortion type %s", 1062 + t_stringify_camera_distortion_model(view.distortion_model)); 1034 1063 } 1035 1064 1036 1065 xrt_matrix_4x4 T; // Row major T_imu_cam
+176 -15
src/xrt/auxiliary/tracking/t_tracking.h
··· 1 - // Copyright 2019-2021, Collabora, Ltd. 1 + // Copyright 2019-2023, Collabora, Ltd. 2 2 // SPDX-License-Identifier: BSL-1.0 3 3 /*! 4 4 * @file ··· 6 6 * @author Pete Black <pblack@collabora.com> 7 7 * @author Jakob Bornecrantz <jakob@collabora.com> 8 8 * @author Ryan Pavlik <ryan.pavlik@collabora.com> 9 + * @author Moses Turner <moses@collabora.com> 9 10 * @ingroup aux_tracking 10 11 */ 11 12 ··· 52 53 #define XRT_DISTORTION_MAX_DIM (14) 53 54 54 55 /*! 56 + * @brief The distortion model this camera calibration falls under. 57 + * @todo Add RiftS's Fisheye62 to this enumerator once we have native support for it in our hand tracking and SLAM. 58 + * @todo Feel free to add support for T_DISTORTION_OPENCV_RADTAN_4 or T_DISTORTION_OPENCV_RADTAN_12 whenever you have a 59 + * camera that uses those. 60 + */ 61 + enum t_camera_distortion_model 62 + { 63 + /*! 64 + * OpenCV's radial-tangential distortion model. Exactly equivalent to the distortion model from OpenCV's calib3d 65 + * module with just the first five parameters. This may be reinterpreted as RT8 with the last three parameters 66 + * zeroed out, which is 100% valid and results in exactly equivalent (un)projections. 67 + * 68 + * Parameters: 69 + * 70 + * \f[(k_1, k_2, p_1, p_2, k_3)\f] 71 + */ 72 + T_DISTORTION_OPENCV_RADTAN_5, 73 + 74 + /*! 75 + * OpenCV's radial-tangential distortion model. Exactly equivalent to the distortion model from OpenCV's calib3d 76 + * module, with just the first 8 parameters. 77 + * Parameters: 78 + * 79 + * \f[(k_1, k_2, p_1, p_2, k_3, k_4, k_5, k_6)\f] 80 + */ 81 + T_DISTORTION_OPENCV_RADTAN_8, 82 + 83 + /*! 84 + * OpenCV's radial-tangential distortion model. Exactly equivalent to the distortion model from OpenCV's calib3d 85 + * module, with all 14 parameters. 86 + * 87 + * In practice this is reinterpreted as RT8 because the last 6 parameters are almost always approximately 0. 88 + * 89 + * @todo Feel free to implement RT14 (un)projection functions if you have a camera that actually has a tilted 90 + * sensor. 91 + * 92 + * Parameters: 93 + * 94 + * \f[(k_1, k_2, p_1, p_2, k_3, k_4, k_5, k_6, s_1, s_2, s_3, s_4, \tau_x, \tau_y)\f] 95 + * 96 + * All known factory-calibrated Luxonis cameras use this distortion model, and in all known cases their last 6 97 + * parameters are approximately 0. 98 + * 99 + */ 100 + T_DISTORTION_OPENCV_RADTAN_14, 101 + 102 + /*! 103 + * Juho Kannalla and Sami Sebastian Brandt's fisheye distortion model. Exactly equivalent to the distortion 104 + * model from OpenCV's calib3d/fisheye module. 105 + * 106 + * Parameters: 107 + * 108 + * \f[(k_1, k_2, k_3, k_4)\f] 109 + * 110 + * Many cameras use this model. Here's a non-exhaustive list of cameras Monado knows about that fall under this 111 + * model: 112 + * * Intel T265 113 + * * Valve Index 114 + */ 115 + T_DISTORTION_FISHEYE_KB4, 116 + 117 + /*! 118 + * Windows Mixed Reality headsets' camera model. 119 + * 120 + * The model is listed as CALIBRATION_LensDistortionModelRational6KT in the WMR json files, which seems to be 121 + * equivalent to Azure-Kinect-Sensor-SDK's K4A_CALIBRATION_LENS_DISTORTION_MODEL_RATIONAL_6KT. 122 + * 123 + * The only difference between this model and RT8 are codx, cody, and the way p1 and p2 are interpreted. In 124 + * practice we reinterpret this as RT8 because those values are almost always approximately 0 for WMR headsets. 125 + * 126 + * Parameters: 127 + * 128 + * \f[(k_1, k_2, p_1, p_2, k_3, k_4, k_5, k_6, cod_x, cod_y, rpmax)\f] 129 + */ 130 + T_DISTORTION_WMR, 131 + }; 132 + 133 + 134 + /*! 135 + * Stringifies a @ref enum t_camera_distortion_model 136 + * @param model The distortion model to be stringified 137 + * @return The distortion model as a string 138 + */ 139 + static inline const char * 140 + t_stringify_camera_distortion_model(const enum t_camera_distortion_model model) 141 + { 142 + switch (model) { 143 + case T_DISTORTION_OPENCV_RADTAN_5: return "T_DISTORTION_OPENCV_RADTAN_5"; break; 144 + case T_DISTORTION_OPENCV_RADTAN_8: return "T_DISTORTION_OPENCV_RADTAN_8"; break; 145 + case T_DISTORTION_OPENCV_RADTAN_14: return "T_DISTORTION_OPENCV_RADTAN_14"; break; 146 + case T_DISTORTION_WMR: return "T_DISTORTION_WMR"; break; 147 + case T_DISTORTION_FISHEYE_KB4: return "T_DISTORTION_FISHEYE_KB4"; break; 148 + default: U_LOG_E("Invalid distortion_model! %d", model); return "INVALID"; 149 + } 150 + } 151 + 152 + /*! 153 + * Returns the number of parameters needed for this @ref enum t_camera_distortion_model to be held by an OpenCV Mat and 154 + * correctly interpreted by OpenCV's (un)projection functions. 155 + * 156 + * @param model The distortion model in question 157 + * @return The number of distortion coefficients, or 0 if this model cannot be represented inside OpenCV. 158 + */ 159 + static inline size_t 160 + t_num_params_from_distortion_model(const enum t_camera_distortion_model model) 161 + { 162 + switch (model) { 163 + case T_DISTORTION_OPENCV_RADTAN_5: return 5; break; 164 + case T_DISTORTION_OPENCV_RADTAN_8: return 8; break; 165 + case T_DISTORTION_OPENCV_RADTAN_14: return 14; break; 166 + case T_DISTORTION_WMR: return 11; break; 167 + case T_DISTORTION_FISHEYE_KB4: return 4; break; 168 + default: U_LOG_E("Invalid distortion_model! %d", model); return 0; 169 + } 170 + } 171 + 172 + /*! 173 + * Parameters for @ref T_DISTORTION_OPENCV_RADTAN_5 174 + * @ingroup aux_tracking 175 + */ 176 + struct t_camera_calibration_rt5_params 177 + { 178 + double k1, k2, p1, p2, k3; 179 + }; 180 + 181 + /*! 182 + * Parameters for @ref T_DISTORTION_OPENCV_RADTAN_8 183 + * @ingroup aux_tracking 184 + */ 185 + struct t_camera_calibration_rt8_params 186 + { 187 + double k1, k2, p1, p2, k3, k4, k5, k6; 188 + }; 189 + 190 + /*! 191 + * Parameters for @ref T_DISTORTION_OPENCV_RADTAN_14 192 + * @ingroup aux_tracking 193 + */ 194 + struct t_camera_calibration_rt14_params 195 + { 196 + double k1, k2, p1, p2, k3, k4, k5, k6, s1, s2, s3, s4, tx, ty; 197 + }; 198 + 199 + /*! 200 + * Parameters for @ref T_DISTORTION_FISHEYE_KB4 201 + * @ingroup aux_tracking 202 + */ 203 + struct t_camera_calibration_kb4_params 204 + { 205 + double k1, k2, k3, k4; 206 + }; 207 + 208 + /*! 209 + * Parameters for @ref T_DISTORTION_WMR 210 + * @ingroup aux_tracking 211 + */ 212 + struct t_camera_calibration_wmr_params 213 + { 214 + double k1, k2, p1, p2, k3, k4, k5, k6, codx, cody, rpmax; 215 + }; 216 + 217 + /*! 55 218 * @brief Essential calibration data for a single camera, or single lens/sensor 56 219 * of a stereo camera. 57 220 */ ··· 63 226 //! Camera intrinsics matrix 64 227 double intrinsics[3][3]; 65 228 66 - //! Number of distortion parameters (non-fisheye). 67 - size_t distortion_num; 229 + union { 230 + struct t_camera_calibration_rt5_params rt5; 231 + struct t_camera_calibration_rt8_params rt8; 232 + struct t_camera_calibration_rt14_params rt14; 233 + struct t_camera_calibration_kb4_params kb4; 234 + struct t_camera_calibration_wmr_params wmr; 235 + double distortion_parameters_as_array[XRT_DISTORTION_MAX_DIM]; 236 + }; 68 237 69 - //! Rectilinear distortion coefficients: k1, k2, p1, p2[, k3[, k4, k5, k6[, s1, s2, s3, s4[, Tx, Ty]]]] 70 - double distortion[XRT_DISTORTION_MAX_DIM]; 71 - 72 - //! Fisheye camera distortion coefficients 73 - double distortion_fisheye[4]; 74 238 75 - //! Is the camera fisheye? 76 - bool use_fisheye; 239 + //! Distortion model that this camera uses. 240 + enum t_camera_distortion_model distortion_model; 77 241 }; 78 242 79 243 /*! ··· 101 265 /*! 102 266 * Allocates a new stereo calibration data, unreferences the old data pointed to by @p out_c. 103 267 * 104 - * Also initializes t_camera_calibration::distortion_num in t_stereo_camera_calibration::view, only 5 and 14 is 105 - * accepted. 106 - * 107 268 * @public @memberof t_stereo_camera_calibration 108 269 */ 109 270 void 110 - t_stereo_camera_calibration_alloc(struct t_stereo_camera_calibration **out_c, uint32_t distortion_num); 271 + t_stereo_camera_calibration_alloc(struct t_stereo_camera_calibration **out_c, 272 + const enum t_camera_distortion_model distortion_model); 111 273 112 274 /*! 113 275 * Only to be called by @p t_stereo_camera_calibration_reference. ··· 452 614 { 453 615 double frequency; //!< Camera FPS 454 616 struct xrt_matrix_4x4 T_imu_cam; //!< Transform IMU to camera. Column major. 455 - float rpmax; //!< Used for rt8 calibrations. Rpmax or "metric_radius" property. 456 617 } cams[2]; 457 618 }; 458 619
+5 -6
src/xrt/auxiliary/vive/vive_config.c
··· 300 300 struct index_camera *cameras = d->cameras.view; 301 301 struct t_stereo_camera_calibration *calib = NULL; 302 302 303 - t_stereo_camera_calibration_alloc(&calib, 5); 303 + t_stereo_camera_calibration_alloc(&calib, T_DISTORTION_FISHEYE_KB4); 304 304 305 305 for (int i = 0; i < 2; i++) { 306 306 calib->view[i].image_size_pixels.w = cameras[i].intrinsics.image_size_pixels.w; ··· 319 319 calib->view[i].intrinsics[2][1] = 0.0f; 320 320 calib->view[i].intrinsics[2][2] = 1.0f; 321 321 322 - calib->view[i].use_fisheye = true; 323 - calib->view[i].distortion_fisheye[0] = cameras[i].intrinsics.distortion[0]; 324 - calib->view[i].distortion_fisheye[1] = cameras[i].intrinsics.distortion[1]; 325 - calib->view[i].distortion_fisheye[2] = cameras[i].intrinsics.distortion[2]; 326 - calib->view[i].distortion_fisheye[3] = cameras[i].intrinsics.distortion[3]; 322 + calib->view[i].kb4.k1 = cameras[i].intrinsics.distortion[0]; 323 + calib->view[i].kb4.k2 = cameras[i].intrinsics.distortion[1]; 324 + calib->view[i].kb4.k3 = cameras[i].intrinsics.distortion[2]; 325 + calib->view[i].kb4.k4 = cameras[i].intrinsics.distortion[3]; 327 326 } 328 327 329 328 struct xrt_vec3 pos = d->cameras.opencv.position;
+10 -15
src/xrt/drivers/depthai/depthai_driver.cpp
··· 215 215 * Copy to the Monado calibration struct. 216 216 */ 217 217 218 - uint32_t num_dist = 14; 219 218 220 219 // Good enough assumption that they're using the same distortion model 221 - bool use_fisheye = calibData.getDistortionModel(dai::CameraBoardSocket::LEFT) == dai::CameraModel::Fisheye; 222 - if (use_fisheye) { 223 - num_dist = 4; 220 + enum t_camera_distortion_model type = T_DISTORTION_OPENCV_RADTAN_14; 221 + if (calibData.getDistortionModel(dai::CameraBoardSocket::LEFT) == dai::CameraModel::Fisheye) { 222 + type = T_DISTORTION_FISHEYE_KB4; 224 223 } 224 + 225 + uint32_t num_dist = t_num_params_from_distortion_model(type); 225 226 226 227 struct t_stereo_camera_calibration *c = NULL; 227 - t_stereo_camera_calibration_alloc(&c, num_dist); 228 + t_stereo_camera_calibration_alloc(&c, type); 228 229 229 230 // Copy intrinsics 230 231 c->view[0].image_size_pixels.w = left.width; ··· 238 239 } 239 240 } 240 241 241 - // Copy distortion 242 - c->view[0].use_fisheye = use_fisheye; 243 - c->view[1].use_fisheye = use_fisheye; 242 + c->view[0].distortion_model = type; 243 + c->view[1].distortion_model = type; 244 244 for (uint32_t i = 0; i < num_dist; i++) { 245 - if (use_fisheye) { 246 - c->view[0].distortion_fisheye[i] = left.distortion[i]; 247 - c->view[1].distortion_fisheye[i] = right.distortion[i]; 248 - } else { 249 - c->view[0].distortion[i] = left.distortion[i]; 250 - c->view[1].distortion[i] = right.distortion[i]; 251 - } 245 + c->view[0].distortion_parameters_as_array[i] = left.distortion[i]; 246 + c->view[1].distortion_parameters_as_array[i] = right.distortion[i]; 252 247 } 253 248 254 249 // Copy translation
-2
src/xrt/drivers/rift_s/rift_s_tracker.c
··· 189 189 { 190 190 .frequency = CAMERA_FREQUENCY, 191 191 .T_imu_cam = T_imu_left_cam, 192 - .rpmax = 0.0, 193 192 }, 194 193 { 195 194 .frequency = CAMERA_FREQUENCY, 196 195 .T_imu_cam = T_imu_right_cam, 197 - .rpmax = 0.0, 198 196 }, 199 197 }, 200 198 };
+6 -4
src/xrt/drivers/rift_s/rift_s_util.cpp
··· 222 222 tcc->intrinsics[0][2] = rift_s_cam->projection.cx; 223 223 tcc->intrinsics[1][2] = rift_s_cam->projection.cy; 224 224 tcc->intrinsics[2][2] = 1.0; 225 - tcc->use_fisheye = true; 225 + tcc->distortion_model = T_DISTORTION_FISHEYE_KB4; 226 226 227 227 TargetPoint xy[STEPS * STEPS]; 228 228 ··· 271 271 TinySolver<AutoDiffDistortParamKB4Function> solver; 272 272 solver.Solve(f, &kb4_distort_params); 273 273 274 - for (int i = 0; i < 4; i++) 275 - tcc->distortion_fisheye[i] = kb4_distort_params[i]; 274 + tcc->kb4.k1 = kb4_distort_params[0]; 275 + tcc->kb4.k2 = kb4_distort_params[1]; 276 + tcc->kb4.k3 = kb4_distort_params[2]; 277 + tcc->kb4.k4 = kb4_distort_params[3]; 276 278 } 277 279 278 280 return true; ··· 290 292 rift_s_create_stereo_camera_calib_rotated(struct rift_s_camera_calibration_block *camera_calibration) 291 293 { 292 294 struct t_stereo_camera_calibration *calib = NULL; 293 - t_stereo_camera_calibration_alloc(&calib, 8); 295 + t_stereo_camera_calibration_alloc(&calib, T_DISTORTION_FISHEYE_KB4); 294 296 295 297 struct rift_s_camera_calibration *left = &camera_calibration->cameras[RIFT_S_CAMERA_FRONT_LEFT]; 296 298 struct rift_s_camera_calibration *right = &camera_calibration->cameras[RIFT_S_CAMERA_FRONT_RIGHT];
+20 -19
src/xrt/drivers/wmr/wmr_hmd.c
··· 1300 1300 * 2. The terms that use the tangential parameters, p1 and p2, aren't multiplied by 2 1301 1301 * 3. There is a "metric radius" that delimits a valid area of distortion/undistortion 1302 1302 * 1303 - * Thankfully, parameters of points 1 and 2 tend to be almost zero in practice and we 1304 - * only do unprojections (for hand tracking) in very safe camera regions so 3 1305 - * doesn't bother us that much either. 1303 + * Thankfully, parameters of points 1 and 2 tend to be almost zero in practice. For 3, we place metric_radius into 1304 + * the calibration struct so that downstream tracking algorithms can use it as needed. 1306 1305 */ 1307 1306 XRT_MAYBE_UNUSED static struct t_stereo_camera_calibration * 1308 1307 wmr_hmd_create_stereo_camera_calib(struct wmr_hmd *wh) 1309 1308 { 1310 1309 struct t_stereo_camera_calibration *calib = NULL; 1311 - t_stereo_camera_calibration_alloc(&calib, 8); 1310 + t_stereo_camera_calibration_alloc(&calib, T_DISTORTION_WMR); 1311 + 1312 1312 1313 1313 // Intrinsics 1314 1314 for (int view = 0; view < 2; view++) { // Assuming that cameras[0-1] are HT0 and HT1 ··· 1324 1324 tcc->intrinsics[1][2] = intr->params.cy * (double)cam->roi.extent.h; 1325 1325 tcc->intrinsics[2][2] = 1.0; 1326 1326 1327 - tcc->distortion[0] = intr->params.k[0]; 1328 - tcc->distortion[1] = intr->params.k[1]; 1329 - tcc->distortion[2] = intr->params.p1; 1330 - tcc->distortion[3] = intr->params.p2; 1331 - tcc->distortion[4] = intr->params.k[2]; 1332 - tcc->distortion[5] = intr->params.k[3]; 1333 - tcc->distortion[6] = intr->params.k[4]; 1334 - tcc->distortion[7] = intr->params.k[5]; 1335 - tcc->use_fisheye = false; 1327 + tcc->wmr.k1 = intr->params.k[0]; 1328 + tcc->wmr.k2 = intr->params.k[1]; 1329 + tcc->wmr.p1 = intr->params.p1; 1330 + tcc->wmr.p2 = intr->params.p2; 1331 + tcc->wmr.k3 = intr->params.k[2]; 1332 + tcc->wmr.k4 = intr->params.k[3]; 1333 + tcc->wmr.k5 = intr->params.k[4]; 1334 + tcc->wmr.k6 = intr->params.k[5]; 1335 + 1336 + tcc->wmr.codx = intr->params.dist_x; 1337 + tcc->wmr.cody = intr->params.dist_y; 1338 + 1339 + tcc->wmr.rpmax = intr->params.metric_radius; 1340 + 1341 + tcc->distortion_model = T_DISTORTION_WMR; 1336 1342 } 1337 1343 1338 1344 // Extrinsics ··· 1391 1397 XRT_MAYBE_UNUSED static struct t_slam_calib_extras 1392 1398 wmr_hmd_create_extra_calib(struct wmr_hmd *wh) 1393 1399 { 1394 - struct wmr_camera_config *ht0 = &wh->config.cameras[0]; 1395 - struct wmr_camera_config *ht1 = &wh->config.cameras[1]; 1396 - 1397 1400 struct xrt_pose P_imu_ht0 = wh->config.sensors.accel.pose; 1398 - struct xrt_pose P_ht1_ht0 = ht1->pose; 1401 + struct xrt_pose P_ht1_ht0 = wh->config.cameras[1].pose; 1399 1402 struct xrt_pose P_ht0_ht1; 1400 1403 math_pose_invert(&P_ht1_ht0, &P_ht0_ht1); 1401 1404 struct xrt_pose P_imu_ht1; ··· 1416 1419 { 1417 1420 .frequency = CAMERA_FREQUENCY, 1418 1421 .T_imu_cam = T_imu_ht0, 1419 - .rpmax = ht0->distortion6KT.params.metric_radius, 1420 1422 }, 1421 1423 { 1422 1424 .frequency = CAMERA_FREQUENCY, 1423 1425 .T_imu_cam = T_imu_ht1, 1424 - .rpmax = ht1->distortion6KT.params.metric_radius, 1425 1426 }, 1426 1427 }, 1427 1428 };
+25 -19
src/xrt/tracking/hand/mercury/hg_sync.cpp
··· 36 36 */ 37 37 38 38 static bool 39 - getCalibration(struct HandTracking *hgt, t_stereo_camera_calibration *calibration) 39 + getCalibration(struct HandTracking *hgt, t_stereo_camera_calibration &calibration) 40 40 { 41 - xrt::auxiliary::tracking::StereoCameraCalibrationWrapper wrap(calibration); 42 - xrt_vec3 trans = {(float)wrap.camera_translation_mat(0, 0), (float)wrap.camera_translation_mat(1, 0), 43 - (float)wrap.camera_translation_mat(2, 0)}; 41 + xrt::auxiliary::tracking::StereoCameraCalibrationWrapper wrap(&calibration); 42 + xrt_vec3 trans = { 43 + (float)calibration.camera_translation[0], 44 + (float)calibration.camera_translation[1], 45 + (float)calibration.camera_translation[2], 46 + }; 44 47 hgt->baseline = m_vec3_len(trans); 45 48 HG_DEBUG(hgt, "I think the baseline is %f meters!", hgt->baseline); 46 49 // Note, this assumes camera 0 is the left camera and camera 1 is the right camera. 47 50 // If you find one with the opposite arrangement, you'll need to invert hgt->baseline, and look at 48 51 // hgJointDisparityMath 49 52 50 - hgt->use_fisheye = wrap.view[0].use_fisheye; 53 + hgt->use_fisheye = wrap.view[0].distortion_model == T_DISTORTION_FISHEYE_KB4; 51 54 52 55 if (hgt->use_fisheye) { 53 56 HG_DEBUG(hgt, "I think the cameras are fisheye!"); ··· 66 69 s.v[1] = wrap.camera_rotation_mat(1, 0); 67 70 s.v[2] = wrap.camera_rotation_mat(2, 0); 68 71 69 - s.v[3] = wrap.camera_rotation_mat(0, 1); 70 - s.v[4] = wrap.camera_rotation_mat(1, 1); 71 - s.v[5] = wrap.camera_rotation_mat(2, 1); 72 + s.v[0] = (float)calibration.camera_rotation[0][0]; 73 + s.v[1] = (float)calibration.camera_rotation[1][0]; 74 + s.v[2] = (float)calibration.camera_rotation[2][0]; 72 75 73 - s.v[6] = wrap.camera_rotation_mat(0, 2); 74 - s.v[7] = wrap.camera_rotation_mat(1, 2); 75 - s.v[8] = wrap.camera_rotation_mat(2, 2); 76 + s.v[3] = (float)calibration.camera_rotation[0][1]; 77 + s.v[4] = (float)calibration.camera_rotation[1][1]; 78 + s.v[5] = (float)calibration.camera_rotation[2][1]; 79 + 80 + s.v[6] = (float)calibration.camera_rotation[0][2]; 81 + s.v[7] = (float)calibration.camera_rotation[1][2]; 82 + s.v[8] = (float)calibration.camera_rotation[2][2]; 76 83 77 84 xrt_pose left_in_right; 78 - left_in_right.position.x = wrap.camera_translation_mat(0); 85 + left_in_right.position = trans; 79 86 left_in_right.position.y = wrap.camera_translation_mat(1); 80 87 left_in_right.position.z = wrap.camera_translation_mat(2); 81 88 82 89 math_quat_from_matrix_3x3(&s, &left_in_right.orientation); 90 + 91 + //! @todo what are these magic values? 92 + //! they're probably turning the OpenCV formalism into OpenXR, but especially what gives with negating 93 + //! orientation.x? 83 94 left_in_right.orientation.x = -left_in_right.orientation.x; 84 95 left_in_right.position.y = -left_in_right.position.y; 85 96 left_in_right.position.z = -left_in_right.position.z; ··· 97 108 //* Good enough guess that view 0 and view 1 are the same size. 98 109 for (int i = 0; i < 2; i++) { 99 110 hgt->views[i].cameraMatrix = wrap.view[i].intrinsics_mat; 100 - 101 - if (hgt->use_fisheye) { 102 - hgt->views[i].distortion = wrap.view[i].distortion_fisheye_mat; 103 - } else { 104 - hgt->views[i].distortion = wrap.view[i].distortion_mat; 105 - } 111 + hgt->views[i].distortion = wrap.view[i].distortion_mat; 106 112 107 113 if (hgt->log_level <= U_LOGGING_DEBUG) { 108 114 HG_DEBUG(hgt, "K%d ->", i); ··· 1009 1015 hgt->calib = NULL; 1010 1016 // We have to reference it, getCalibration points at it. 1011 1017 t_stereo_camera_calibration_reference(&hgt->calib, calib); 1012 - getCalibration(hgt, calib); 1018 + getCalibration(hgt, *calib); 1013 1019 getModelsFolder(hgt); 1014 1020 1015 1021