| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #ifndef COLMAP_SRC_BASE_CAMERA_MODELS_H_ |
| | #define COLMAP_SRC_BASE_CAMERA_MODELS_H_ |
| |
|
| | #include <cfloat> |
| | #include <string> |
| | #include <vector> |
| |
|
| | #include <Eigen/Core> |
| | #include <Eigen/Dense> |
| |
|
| | #include <ceres/ceres.h> |
| |
|
| | namespace colmap { |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | static const int kInvalidCameraModelId = -1; |
| |
|
| | #ifndef CAMERA_MODEL_DEFINITIONS |
| | #define CAMERA_MODEL_DEFINITIONS(model_id_value, model_name_value, \ |
| | num_params_value) \ |
| | static const int kModelId = model_id_value; \ |
| | static const size_t kNumParams = num_params_value; \ |
| | static const int model_id; \ |
| | static const std::string model_name; \ |
| | static const size_t num_params; \ |
| | static const std::string params_info; \ |
| | static const std::vector<size_t> focal_length_idxs; \ |
| | static const std::vector<size_t> principal_point_idxs; \ |
| | static const std::vector<size_t> extra_params_idxs; \ |
| | \ |
| | static inline int InitializeModelId() { return model_id_value; }; \ |
| | static inline std::string InitializeModelName() { \ |
| | return model_name_value; \ |
| | }; \ |
| | static inline size_t InitializeNumParams() { return num_params_value; }; \ |
| | static inline std::string InitializeParamsInfo(); \ |
| | static inline std::vector<size_t> InitializeFocalLengthIdxs(); \ |
| | static inline std::vector<size_t> InitializePrincipalPointIdxs(); \ |
| | static inline std::vector<size_t> InitializeExtraParamsIdxs(); \ |
| | static inline std::vector<double> InitializeParams( \ |
| | const double focal_length, const size_t width, const size_t height); \ |
| | \ |
| | template <typename T> \ |
| | static void WorldToImage(const T* params, const T u, const T v, T* x, T* y); \ |
| | template <typename T> \ |
| | static void ImageToWorld(const T* params, const T x, const T y, T* u, T* v); \ |
| | template <typename T> \ |
| | static void Distortion(const T* extra_params, const T u, const T v, T* du, \ |
| | T* dv); |
| | #endif |
| |
|
| | #ifndef CAMERA_MODEL_CASES |
| | #define CAMERA_MODEL_CASES \ |
| | CAMERA_MODEL_CASE(SimplePinholeCameraModel) \ |
| | CAMERA_MODEL_CASE(PinholeCameraModel) \ |
| | CAMERA_MODEL_CASE(SimpleRadialCameraModel) \ |
| | CAMERA_MODEL_CASE(SimpleRadialFisheyeCameraModel) \ |
| | CAMERA_MODEL_CASE(RadialCameraModel) \ |
| | CAMERA_MODEL_CASE(RadialFisheyeCameraModel) \ |
| | CAMERA_MODEL_CASE(OpenCVCameraModel) \ |
| | CAMERA_MODEL_CASE(OpenCVFisheyeCameraModel) \ |
| | CAMERA_MODEL_CASE(FullOpenCVCameraModel) \ |
| | CAMERA_MODEL_CASE(FOVCameraModel) \ |
| | CAMERA_MODEL_CASE(ThinPrismFisheyeCameraModel) |
| | #endif |
| |
|
| | #ifndef CAMERA_MODEL_SWITCH_CASES |
| | #define CAMERA_MODEL_SWITCH_CASES \ |
| | CAMERA_MODEL_CASES \ |
| | default: \ |
| | CAMERA_MODEL_DOES_NOT_EXIST_EXCEPTION \ |
| | break; |
| | #endif |
| |
|
| | #define CAMERA_MODEL_DOES_NOT_EXIST_EXCEPTION \ |
| | throw std::domain_error("Camera model does not exist"); |
| |
|
| | |
| | |
| | |
| | template <typename CameraModel> |
| | struct BaseCameraModel { |
| | template <typename T> |
| | static inline bool HasBogusParams(const std::vector<T>& params, |
| | const size_t width, const size_t height, |
| | const T min_focal_length_ratio, |
| | const T max_focal_length_ratio, |
| | const T max_extra_param); |
| |
|
| | template <typename T> |
| | static inline bool HasBogusFocalLength(const std::vector<T>& params, |
| | const size_t width, |
| | const size_t height, |
| | const T min_focal_length_ratio, |
| | const T max_focal_length_ratio); |
| |
|
| | template <typename T> |
| | static inline bool HasBogusPrincipalPoint(const std::vector<T>& params, |
| | const size_t width, |
| | const size_t height); |
| |
|
| | template <typename T> |
| | static inline bool HasBogusExtraParams(const std::vector<T>& params, |
| | const T max_extra_param); |
| |
|
| | template <typename T> |
| | static inline T ImageToWorldThreshold(const T* params, const T threshold); |
| |
|
| | template <typename T> |
| | static inline void IterativeUndistortion(const T* params, T* u, T* v); |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | struct SimplePinholeCameraModel |
| | : public BaseCameraModel<SimplePinholeCameraModel> { |
| | CAMERA_MODEL_DEFINITIONS(0, "SIMPLE_PINHOLE", 3) |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | struct PinholeCameraModel : public BaseCameraModel<PinholeCameraModel> { |
| | CAMERA_MODEL_DEFINITIONS(1, "PINHOLE", 4) |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | struct SimpleRadialCameraModel |
| | : public BaseCameraModel<SimpleRadialCameraModel> { |
| | CAMERA_MODEL_DEFINITIONS(2, "SIMPLE_RADIAL", 4) |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | struct RadialCameraModel : public BaseCameraModel<RadialCameraModel> { |
| | CAMERA_MODEL_DEFINITIONS(3, "RADIAL", 5) |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | struct OpenCVCameraModel : public BaseCameraModel<OpenCVCameraModel> { |
| | CAMERA_MODEL_DEFINITIONS(4, "OPENCV", 8) |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | struct OpenCVFisheyeCameraModel |
| | : public BaseCameraModel<OpenCVFisheyeCameraModel> { |
| | CAMERA_MODEL_DEFINITIONS(5, "OPENCV_FISHEYE", 8) |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | struct FullOpenCVCameraModel : public BaseCameraModel<FullOpenCVCameraModel> { |
| | CAMERA_MODEL_DEFINITIONS(6, "FULL_OPENCV", 12) |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | struct FOVCameraModel : public BaseCameraModel<FOVCameraModel> { |
| | CAMERA_MODEL_DEFINITIONS(7, "FOV", 5) |
| |
|
| | template <typename T> |
| | static void Undistortion(const T* extra_params, const T u, const T v, T* du, |
| | T* dv); |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | struct SimpleRadialFisheyeCameraModel |
| | : public BaseCameraModel<SimpleRadialFisheyeCameraModel> { |
| | CAMERA_MODEL_DEFINITIONS(8, "SIMPLE_RADIAL_FISHEYE", 4) |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | struct RadialFisheyeCameraModel |
| | : public BaseCameraModel<RadialFisheyeCameraModel> { |
| | CAMERA_MODEL_DEFINITIONS(9, "RADIAL_FISHEYE", 5) |
| | }; |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | struct ThinPrismFisheyeCameraModel |
| | : public BaseCameraModel<ThinPrismFisheyeCameraModel> { |
| | CAMERA_MODEL_DEFINITIONS(10, "THIN_PRISM_FISHEYE", 12) |
| | }; |
| |
|
| | |
| | bool ExistsCameraModelWithName(const std::string& model_name); |
| | bool ExistsCameraModelWithId(const int model_id); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | int CameraModelNameToId(const std::string& model_name); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | std::string CameraModelIdToName(const int model_id); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | std::vector<double> CameraModelInitializeParams(const int model_id, |
| | const double focal_length, |
| | const size_t width, |
| | const size_t height); |
| |
|
| | |
| | |
| | |
| | std::string CameraModelParamsInfo(const int model_id); |
| |
|
| | |
| | |
| | |
| | const std::vector<size_t>& CameraModelFocalLengthIdxs(const int model_id); |
| | const std::vector<size_t>& CameraModelPrincipalPointIdxs(const int model_id); |
| | const std::vector<size_t>& CameraModelExtraParamsIdxs(const int model_id); |
| |
|
| | |
| | size_t CameraModelNumParams(const int model_id); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | bool CameraModelVerifyParams(const int model_id, |
| | const std::vector<double>& params); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | bool CameraModelHasBogusParams(const int model_id, |
| | const std::vector<double>& params, |
| | const size_t width, const size_t height, |
| | const double min_focal_length_ratio, |
| | const double max_focal_length_ratio, |
| | const double max_extra_param); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | inline void CameraModelWorldToImage(const int model_id, |
| | const std::vector<double>& params, |
| | const double u, const double v, double* x, |
| | double* y); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | inline void CameraModelImageToWorld(const int model_id, |
| | const std::vector<double>& params, |
| | const double x, const double y, double* u, |
| | double* v); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | inline double CameraModelImageToWorldThreshold( |
| | const int model_id, const std::vector<double>& params, |
| | const double threshold); |
| |
|
| | |
| | |
| | |
| |
|
| | |
| | |
| |
|
| | template <typename CameraModel> |
| | template <typename T> |
| | bool BaseCameraModel<CameraModel>::HasBogusParams( |
| | const std::vector<T>& params, const size_t width, const size_t height, |
| | const T min_focal_length_ratio, const T max_focal_length_ratio, |
| | const T max_extra_param) { |
| | if (HasBogusPrincipalPoint(params, width, height)) { |
| | return true; |
| | } |
| |
|
| | if (HasBogusFocalLength(params, width, height, min_focal_length_ratio, |
| | max_focal_length_ratio)) { |
| | return true; |
| | } |
| |
|
| | if (HasBogusExtraParams(params, max_extra_param)) { |
| | return true; |
| | } |
| |
|
| | return false; |
| | } |
| |
|
| | template <typename CameraModel> |
| | template <typename T> |
| | bool BaseCameraModel<CameraModel>::HasBogusFocalLength( |
| | const std::vector<T>& params, const size_t width, const size_t height, |
| | const T min_focal_length_ratio, const T max_focal_length_ratio) { |
| | const size_t max_size = std::max(width, height); |
| |
|
| | for (const auto& idx : CameraModel::focal_length_idxs) { |
| | const T focal_length_ratio = params[idx] / max_size; |
| | if (focal_length_ratio < min_focal_length_ratio || |
| | focal_length_ratio > max_focal_length_ratio) { |
| | return true; |
| | } |
| | } |
| |
|
| | return false; |
| | } |
| |
|
| | template <typename CameraModel> |
| | template <typename T> |
| | bool BaseCameraModel<CameraModel>::HasBogusPrincipalPoint( |
| | const std::vector<T>& params, const size_t width, const size_t height) { |
| | const T cx = params[CameraModel::principal_point_idxs[0]]; |
| | const T cy = params[CameraModel::principal_point_idxs[1]]; |
| | return cx < 0 || cx > width || cy < 0 || cy > height; |
| | } |
| |
|
| | template <typename CameraModel> |
| | template <typename T> |
| | bool BaseCameraModel<CameraModel>::HasBogusExtraParams( |
| | const std::vector<T>& params, const T max_extra_param) { |
| | for (const auto& idx : CameraModel::extra_params_idxs) { |
| | if (std::abs(params[idx]) > max_extra_param) { |
| | return true; |
| | } |
| | } |
| |
|
| | return false; |
| | } |
| |
|
| | template <typename CameraModel> |
| | template <typename T> |
| | T BaseCameraModel<CameraModel>::ImageToWorldThreshold(const T* params, |
| | const T threshold) { |
| | T mean_focal_length = 0; |
| | for (const auto& idx : CameraModel::focal_length_idxs) { |
| | mean_focal_length += params[idx]; |
| | } |
| | mean_focal_length /= CameraModel::focal_length_idxs.size(); |
| | return threshold / mean_focal_length; |
| | } |
| |
|
| | template <typename CameraModel> |
| | template <typename T> |
| | void BaseCameraModel<CameraModel>::IterativeUndistortion(const T* params, T* u, |
| | T* v) { |
| | |
| | |
| | |
| | const size_t kNumIterations = 100; |
| | const double kMaxStepNorm = 1e-10; |
| | const double kRelStepSize = 1e-6; |
| |
|
| | Eigen::Matrix2d J; |
| | const Eigen::Vector2d x0(*u, *v); |
| | Eigen::Vector2d x(*u, *v); |
| | Eigen::Vector2d dx; |
| | Eigen::Vector2d dx_0b; |
| | Eigen::Vector2d dx_0f; |
| | Eigen::Vector2d dx_1b; |
| | Eigen::Vector2d dx_1f; |
| |
|
| | for (size_t i = 0; i < kNumIterations; ++i) { |
| | const double step0 = std::max(std::numeric_limits<double>::epsilon(), |
| | std::abs(kRelStepSize * x(0))); |
| | const double step1 = std::max(std::numeric_limits<double>::epsilon(), |
| | std::abs(kRelStepSize * x(1))); |
| | CameraModel::Distortion(params, x(0), x(1), &dx(0), &dx(1)); |
| | CameraModel::Distortion(params, x(0) - step0, x(1), &dx_0b(0), &dx_0b(1)); |
| | CameraModel::Distortion(params, x(0) + step0, x(1), &dx_0f(0), &dx_0f(1)); |
| | CameraModel::Distortion(params, x(0), x(1) - step1, &dx_1b(0), &dx_1b(1)); |
| | CameraModel::Distortion(params, x(0), x(1) + step1, &dx_1f(0), &dx_1f(1)); |
| | J(0, 0) = 1 + (dx_0f(0) - dx_0b(0)) / (2 * step0); |
| | J(0, 1) = (dx_1f(0) - dx_1b(0)) / (2 * step1); |
| | J(1, 0) = (dx_0f(1) - dx_0b(1)) / (2 * step0); |
| | J(1, 1) = 1 + (dx_1f(1) - dx_1b(1)) / (2 * step1); |
| | const Eigen::Vector2d step_x = J.inverse() * (x + dx - x0); |
| | x -= step_x; |
| | if (step_x.squaredNorm() < kMaxStepNorm) { |
| | break; |
| | } |
| | } |
| |
|
| | *u = x(0); |
| | *v = x(1); |
| | } |
| |
|
| | |
| | |
| |
|
| | std::string SimplePinholeCameraModel::InitializeParamsInfo() { |
| | return "f, cx, cy"; |
| | } |
| |
|
| | std::vector<size_t> SimplePinholeCameraModel::InitializeFocalLengthIdxs() { |
| | return {0}; |
| | } |
| |
|
| | std::vector<size_t> SimplePinholeCameraModel::InitializePrincipalPointIdxs() { |
| | return {1, 2}; |
| | } |
| |
|
| | std::vector<size_t> SimplePinholeCameraModel::InitializeExtraParamsIdxs() { |
| | return {}; |
| | } |
| |
|
| | std::vector<double> SimplePinholeCameraModel::InitializeParams( |
| | const double focal_length, const size_t width, const size_t height) { |
| | return {focal_length, width / 2.0, height / 2.0}; |
| | } |
| |
|
| | template <typename T> |
| | void SimplePinholeCameraModel::WorldToImage(const T* params, const T u, |
| | const T v, T* x, T* y) { |
| | const T f = params[0]; |
| | const T c1 = params[1]; |
| | const T c2 = params[2]; |
| |
|
| | |
| |
|
| | |
| | *x = f * u + c1; |
| | *y = f * v + c2; |
| | } |
| |
|
| | template <typename T> |
| | void SimplePinholeCameraModel::ImageToWorld(const T* params, const T x, |
| | const T y, T* u, T* v) { |
| | const T f = params[0]; |
| | const T c1 = params[1]; |
| | const T c2 = params[2]; |
| |
|
| | *u = (x - c1) / f; |
| | *v = (y - c2) / f; |
| | } |
| |
|
| | |
| | |
| |
|
| | std::string PinholeCameraModel::InitializeParamsInfo() { |
| | return "fx, fy, cx, cy"; |
| | } |
| |
|
| | std::vector<size_t> PinholeCameraModel::InitializeFocalLengthIdxs() { |
| | return {0, 1}; |
| | } |
| |
|
| | std::vector<size_t> PinholeCameraModel::InitializePrincipalPointIdxs() { |
| | return {2, 3}; |
| | } |
| |
|
| | std::vector<size_t> PinholeCameraModel::InitializeExtraParamsIdxs() { |
| | return {}; |
| | } |
| |
|
| | std::vector<double> PinholeCameraModel::InitializeParams( |
| | const double focal_length, const size_t width, const size_t height) { |
| | return {focal_length, focal_length, width / 2.0, height / 2.0}; |
| | } |
| |
|
| | template <typename T> |
| | void PinholeCameraModel::WorldToImage(const T* params, const T u, const T v, |
| | T* x, T* y) { |
| | const T f1 = params[0]; |
| | const T f2 = params[1]; |
| | const T c1 = params[2]; |
| | const T c2 = params[3]; |
| |
|
| | |
| |
|
| | |
| | *x = f1 * u + c1; |
| | *y = f2 * v + c2; |
| | } |
| |
|
| | template <typename T> |
| | void PinholeCameraModel::ImageToWorld(const T* params, const T x, const T y, |
| | T* u, T* v) { |
| | const T f1 = params[0]; |
| | const T f2 = params[1]; |
| | const T c1 = params[2]; |
| | const T c2 = params[3]; |
| |
|
| | *u = (x - c1) / f1; |
| | *v = (y - c2) / f2; |
| | } |
| |
|
| | |
| | |
| |
|
| | std::string SimpleRadialCameraModel::InitializeParamsInfo() { |
| | return "f, cx, cy, k"; |
| | } |
| |
|
| | std::vector<size_t> SimpleRadialCameraModel::InitializeFocalLengthIdxs() { |
| | return {0}; |
| | } |
| |
|
| | std::vector<size_t> SimpleRadialCameraModel::InitializePrincipalPointIdxs() { |
| | return {1, 2}; |
| | } |
| |
|
| | std::vector<size_t> SimpleRadialCameraModel::InitializeExtraParamsIdxs() { |
| | return {3}; |
| | } |
| |
|
| | std::vector<double> SimpleRadialCameraModel::InitializeParams( |
| | const double focal_length, const size_t width, const size_t height) { |
| | return {focal_length, width / 2.0, height / 2.0, 0}; |
| | } |
| |
|
| | template <typename T> |
| | void SimpleRadialCameraModel::WorldToImage(const T* params, const T u, |
| | const T v, T* x, T* y) { |
| | const T f = params[0]; |
| | const T c1 = params[1]; |
| | const T c2 = params[2]; |
| |
|
| | |
| | T du, dv; |
| | Distortion(¶ms[3], u, v, &du, &dv); |
| | *x = u + du; |
| | *y = v + dv; |
| |
|
| | |
| | *x = f * *x + c1; |
| | *y = f * *y + c2; |
| | } |
| |
|
| | template <typename T> |
| | void SimpleRadialCameraModel::ImageToWorld(const T* params, const T x, |
| | const T y, T* u, T* v) { |
| | const T f = params[0]; |
| | const T c1 = params[1]; |
| | const T c2 = params[2]; |
| |
|
| | |
| | *u = (x - c1) / f; |
| | *v = (y - c2) / f; |
| |
|
| | IterativeUndistortion(¶ms[3], u, v); |
| | } |
| |
|
| | template <typename T> |
| | void SimpleRadialCameraModel::Distortion(const T* extra_params, const T u, |
| | const T v, T* du, T* dv) { |
| | const T k = extra_params[0]; |
| |
|
| | const T u2 = u * u; |
| | const T v2 = v * v; |
| | const T r2 = u2 + v2; |
| | const T radial = k * r2; |
| | *du = u * radial; |
| | *dv = v * radial; |
| | } |
| |
|
| | |
| | |
| |
|
| | std::string RadialCameraModel::InitializeParamsInfo() { |
| | return "f, cx, cy, k1, k2"; |
| | } |
| |
|
| | std::vector<size_t> RadialCameraModel::InitializeFocalLengthIdxs() { |
| | return {0}; |
| | } |
| |
|
| | std::vector<size_t> RadialCameraModel::InitializePrincipalPointIdxs() { |
| | return {1, 2}; |
| | } |
| |
|
| | std::vector<size_t> RadialCameraModel::InitializeExtraParamsIdxs() { |
| | return {3, 4}; |
| | } |
| |
|
| | std::vector<double> RadialCameraModel::InitializeParams( |
| | const double focal_length, const size_t width, const size_t height) { |
| | return {focal_length, width / 2.0, height / 2.0, 0, 0}; |
| | } |
| |
|
| | template <typename T> |
| | void RadialCameraModel::WorldToImage(const T* params, const T u, const T v, |
| | T* x, T* y) { |
| | const T f = params[0]; |
| | const T c1 = params[1]; |
| | const T c2 = params[2]; |
| |
|
| | |
| | T du, dv; |
| | Distortion(¶ms[3], u, v, &du, &dv); |
| | *x = u + du; |
| | *y = v + dv; |
| |
|
| | |
| | *x = f * *x + c1; |
| | *y = f * *y + c2; |
| | } |
| |
|
| | template <typename T> |
| | void RadialCameraModel::ImageToWorld(const T* params, const T x, const T y, |
| | T* u, T* v) { |
| | const T f = params[0]; |
| | const T c1 = params[1]; |
| | const T c2 = params[2]; |
| |
|
| | |
| | *u = (x - c1) / f; |
| | *v = (y - c2) / f; |
| |
|
| | IterativeUndistortion(¶ms[3], u, v); |
| | } |
| |
|
| | template <typename T> |
| | void RadialCameraModel::Distortion(const T* extra_params, const T u, const T v, |
| | T* du, T* dv) { |
| | const T k1 = extra_params[0]; |
| | const T k2 = extra_params[1]; |
| |
|
| | const T u2 = u * u; |
| | const T v2 = v * v; |
| | const T r2 = u2 + v2; |
| | const T radial = k1 * r2 + k2 * r2 * r2; |
| | *du = u * radial; |
| | *dv = v * radial; |
| | } |
| |
|
| | |
| | |
| |
|
| | std::string OpenCVCameraModel::InitializeParamsInfo() { |
| | return "fx, fy, cx, cy, k1, k2, p1, p2"; |
| | } |
| |
|
| | std::vector<size_t> OpenCVCameraModel::InitializeFocalLengthIdxs() { |
| | return {0, 1}; |
| | } |
| |
|
| | std::vector<size_t> OpenCVCameraModel::InitializePrincipalPointIdxs() { |
| | return {2, 3}; |
| | } |
| |
|
| | std::vector<size_t> OpenCVCameraModel::InitializeExtraParamsIdxs() { |
| | return {4, 5, 6, 7}; |
| | } |
| |
|
| | std::vector<double> OpenCVCameraModel::InitializeParams( |
| | const double focal_length, const size_t width, const size_t height) { |
| | return {focal_length, focal_length, width / 2.0, height / 2.0, 0, 0, 0, 0}; |
| | } |
| |
|
| | template <typename T> |
| | void OpenCVCameraModel::WorldToImage(const T* params, const T u, const T v, |
| | T* x, T* y) { |
| | const T f1 = params[0]; |
| | const T f2 = params[1]; |
| | const T c1 = params[2]; |
| | const T c2 = params[3]; |
| |
|
| | |
| | T du, dv; |
| | Distortion(¶ms[4], u, v, &du, &dv); |
| | *x = u + du; |
| | *y = v + dv; |
| |
|
| | |
| | *x = f1 * *x + c1; |
| | *y = f2 * *y + c2; |
| | } |
| |
|
| | template <typename T> |
| | void OpenCVCameraModel::ImageToWorld(const T* params, const T x, const T y, |
| | T* u, T* v) { |
| | const T f1 = params[0]; |
| | const T f2 = params[1]; |
| | const T c1 = params[2]; |
| | const T c2 = params[3]; |
| |
|
| | |
| | *u = (x - c1) / f1; |
| | *v = (y - c2) / f2; |
| |
|
| | IterativeUndistortion(¶ms[4], u, v); |
| | } |
| |
|
| | template <typename T> |
| | void OpenCVCameraModel::Distortion(const T* extra_params, const T u, const T v, |
| | T* du, T* dv) { |
| | const T k1 = extra_params[0]; |
| | const T k2 = extra_params[1]; |
| | const T p1 = extra_params[2]; |
| | const T p2 = extra_params[3]; |
| |
|
| | const T u2 = u * u; |
| | const T uv = u * v; |
| | const T v2 = v * v; |
| | const T r2 = u2 + v2; |
| | const T radial = k1 * r2 + k2 * r2 * r2; |
| | *du = u * radial + T(2) * p1 * uv + p2 * (r2 + T(2) * u2); |
| | *dv = v * radial + T(2) * p2 * uv + p1 * (r2 + T(2) * v2); |
| | } |
| |
|
| | |
| | |
| |
|
| | std::string OpenCVFisheyeCameraModel::InitializeParamsInfo() { |
| | return "fx, fy, cx, cy, k1, k2, k3, k4"; |
| | } |
| |
|
| | std::vector<size_t> OpenCVFisheyeCameraModel::InitializeFocalLengthIdxs() { |
| | return {0, 1}; |
| | } |
| |
|
| | std::vector<size_t> OpenCVFisheyeCameraModel::InitializePrincipalPointIdxs() { |
| | return {2, 3}; |
| | } |
| |
|
| | std::vector<size_t> OpenCVFisheyeCameraModel::InitializeExtraParamsIdxs() { |
| | return {4, 5, 6, 7}; |
| | } |
| |
|
| | std::vector<double> OpenCVFisheyeCameraModel::InitializeParams( |
| | const double focal_length, const size_t width, const size_t height) { |
| | return {focal_length, focal_length, width / 2.0, height / 2.0, 0, 0, 0, 0}; |
| | } |
| |
|
| | template <typename T> |
| | void OpenCVFisheyeCameraModel::WorldToImage(const T* params, const T u, |
| | const T v, T* x, T* y) { |
| | const T f1 = params[0]; |
| | const T f2 = params[1]; |
| | const T c1 = params[2]; |
| | const T c2 = params[3]; |
| |
|
| | |
| | T du, dv; |
| | Distortion(¶ms[4], u, v, &du, &dv); |
| | *x = u + du; |
| | *y = v + dv; |
| |
|
| | |
| | *x = f1 * *x + c1; |
| | *y = f2 * *y + c2; |
| | } |
| |
|
| | template <typename T> |
| | void OpenCVFisheyeCameraModel::ImageToWorld(const T* params, const T x, |
| | const T y, T* u, T* v) { |
| | const T f1 = params[0]; |
| | const T f2 = params[1]; |
| | const T c1 = params[2]; |
| | const T c2 = params[3]; |
| |
|
| | |
| | *u = (x - c1) / f1; |
| | *v = (y - c2) / f2; |
| |
|
| | IterativeUndistortion(¶ms[4], u, v); |
| | } |
| |
|
| | template <typename T> |
| | void OpenCVFisheyeCameraModel::Distortion(const T* extra_params, const T u, |
| | const T v, T* du, T* dv) { |
| | const T k1 = extra_params[0]; |
| | const T k2 = extra_params[1]; |
| | const T k3 = extra_params[2]; |
| | const T k4 = extra_params[3]; |
| |
|
| | const T r = ceres::sqrt(u * u + v * v); |
| |
|
| | if (r > T(std::numeric_limits<double>::epsilon())) { |
| | const T theta = ceres::atan(r); |
| | const T theta2 = theta * theta; |
| | const T theta4 = theta2 * theta2; |
| | const T theta6 = theta4 * theta2; |
| | const T theta8 = theta4 * theta4; |
| | const T thetad = |
| | theta * (T(1) + k1 * theta2 + k2 * theta4 + k3 * theta6 + k4 * theta8); |
| | *du = u * thetad / r - u; |
| | *dv = v * thetad / r - v; |
| | } else { |
| | *du = T(0); |
| | *dv = T(0); |
| | } |
| | } |
| |
|
| | |
| | |
| |
|
| | std::string FullOpenCVCameraModel::InitializeParamsInfo() { |
| | return "fx, fy, cx, cy, k1, k2, p1, p2, k3, k4, k5, k6"; |
| | } |
| |
|
| | std::vector<size_t> FullOpenCVCameraModel::InitializeFocalLengthIdxs() { |
| | return {0, 1}; |
| | } |
| |
|
| | std::vector<size_t> FullOpenCVCameraModel::InitializePrincipalPointIdxs() { |
| | return {2, 3}; |
| | } |
| |
|
| | std::vector<size_t> FullOpenCVCameraModel::InitializeExtraParamsIdxs() { |
| | return {4, 5, 6, 7, 8, 9, 10, 11}; |
| | } |
| |
|
| | std::vector<double> FullOpenCVCameraModel::InitializeParams( |
| | const double focal_length, const size_t width, const size_t height) { |
| | return {focal_length, |
| | focal_length, |
| | width / 2.0, |
| | height / 2.0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0}; |
| | } |
| |
|
| | template <typename T> |
| | void FullOpenCVCameraModel::WorldToImage(const T* params, const T u, const T v, |
| | T* x, T* y) { |
| | const T f1 = params[0]; |
| | const T f2 = params[1]; |
| | const T c1 = params[2]; |
| | const T c2 = params[3]; |
| |
|
| | |
| | T du, dv; |
| | Distortion(¶ms[4], u, v, &du, &dv); |
| | *x = u + du; |
| | *y = v + dv; |
| |
|
| | |
| | *x = f1 * *x + c1; |
| | *y = f2 * *y + c2; |
| | } |
| |
|
| | template <typename T> |
| | void FullOpenCVCameraModel::ImageToWorld(const T* params, const T x, const T y, |
| | T* u, T* v) { |
| | const T f1 = params[0]; |
| | const T f2 = params[1]; |
| | const T c1 = params[2]; |
| | const T c2 = params[3]; |
| |
|
| | |
| | *u = (x - c1) / f1; |
| | *v = (y - c2) / f2; |
| |
|
| | IterativeUndistortion(¶ms[4], u, v); |
| | } |
| |
|
| | template <typename T> |
| | void FullOpenCVCameraModel::Distortion(const T* extra_params, const T u, |
| | const T v, T* du, T* dv) { |
| | const T k1 = extra_params[0]; |
| | const T k2 = extra_params[1]; |
| | const T p1 = extra_params[2]; |
| | const T p2 = extra_params[3]; |
| | const T k3 = extra_params[4]; |
| | const T k4 = extra_params[5]; |
| | const T k5 = extra_params[6]; |
| | const T k6 = extra_params[7]; |
| |
|
| | const T u2 = u * u; |
| | const T uv = u * v; |
| | const T v2 = v * v; |
| | const T r2 = u2 + v2; |
| | const T r4 = r2 * r2; |
| | const T r6 = r4 * r2; |
| | const T radial = (T(1) + k1 * r2 + k2 * r4 + k3 * r6) / |
| | (T(1) + k4 * r2 + k5 * r4 + k6 * r6); |
| | *du = u * radial + T(2) * p1 * uv + p2 * (r2 + T(2) * u2) - u; |
| | *dv = v * radial + T(2) * p2 * uv + p1 * (r2 + T(2) * v2) - v; |
| | } |
| |
|
| | |
| | |
| |
|
| | std::string FOVCameraModel::InitializeParamsInfo() { |
| | return "fx, fy, cx, cy, omega"; |
| | } |
| |
|
| | std::vector<size_t> FOVCameraModel::InitializeFocalLengthIdxs() { |
| | return {0, 1}; |
| | } |
| |
|
| | std::vector<size_t> FOVCameraModel::InitializePrincipalPointIdxs() { |
| | return {2, 3}; |
| | } |
| |
|
| | std::vector<size_t> FOVCameraModel::InitializeExtraParamsIdxs() { return {4}; } |
| |
|
| | std::vector<double> FOVCameraModel::InitializeParams(const double focal_length, |
| | const size_t width, |
| | const size_t height) { |
| | return {focal_length, focal_length, width / 2.0, height / 2.0, 1e-2}; |
| | } |
| |
|
| | template <typename T> |
| | void FOVCameraModel::WorldToImage(const T* params, const T u, const T v, T* x, |
| | T* y) { |
| | const T f1 = params[0]; |
| | const T f2 = params[1]; |
| | const T c1 = params[2]; |
| | const T c2 = params[3]; |
| |
|
| | |
| | Distortion(¶ms[4], u, v, x, y); |
| |
|
| | |
| | *x = f1 * *x + c1; |
| | *y = f2 * *y + c2; |
| | } |
| |
|
| | template <typename T> |
| | void FOVCameraModel::ImageToWorld(const T* params, const T x, const T y, T* u, |
| | T* v) { |
| | const T f1 = params[0]; |
| | const T f2 = params[1]; |
| | const T c1 = params[2]; |
| | const T c2 = params[3]; |
| |
|
| | |
| | const T uu = (x - c1) / f1; |
| | const T vv = (y - c2) / f2; |
| |
|
| | |
| | Undistortion(¶ms[4], uu, vv, u, v); |
| | } |
| |
|
| | template <typename T> |
| | void FOVCameraModel::Distortion(const T* extra_params, const T u, const T v, |
| | T* du, T* dv) { |
| | const T omega = extra_params[0]; |
| |
|
| | |
| | const T kEpsilon = T(1e-4); |
| |
|
| | const T radius2 = u * u + v * v; |
| | const T omega2 = omega * omega; |
| |
|
| | T factor; |
| | if (omega2 < kEpsilon) { |
| | |
| | |
| | |
| | |
| | |
| | factor = (omega2 * radius2) / T(3) - omega2 / T(12) + T(1); |
| | } else if (radius2 < kEpsilon) { |
| | |
| | |
| | |
| | |
| | |
| | const T tan_half_omega = ceres::tan(omega / T(2)); |
| | factor = (T(-2) * tan_half_omega * |
| | (T(4) * radius2 * tan_half_omega * tan_half_omega - T(3))) / |
| | (T(3) * omega); |
| | } else { |
| | const T radius = ceres::sqrt(radius2); |
| | const T numerator = ceres::atan(radius * T(2) * ceres::tan(omega / T(2))); |
| | factor = numerator / (radius * omega); |
| | } |
| |
|
| | *du = u * factor; |
| | *dv = v * factor; |
| | } |
| |
|
| | template <typename T> |
| | void FOVCameraModel::Undistortion(const T* extra_params, const T u, const T v, |
| | T* du, T* dv) { |
| | T omega = extra_params[0]; |
| |
|
| | |
| | const T kEpsilon = T(1e-4); |
| |
|
| | const T radius2 = u * u + v * v; |
| | const T omega2 = omega * omega; |
| |
|
| | T factor; |
| | if (omega2 < kEpsilon) { |
| | |
| | |
| | |
| | |
| | |
| | factor = (omega2 * radius2) / T(3) - omega2 / T(12) + T(1); |
| | } else if (radius2 < kEpsilon) { |
| | |
| | |
| | |
| | |
| | |
| | factor = (omega * (omega * omega * radius2 + T(3))) / |
| | (T(6) * ceres::tan(omega / T(2))); |
| | } else { |
| | const T radius = ceres::sqrt(radius2); |
| | const T numerator = ceres::tan(radius * omega); |
| | factor = numerator / (radius * T(2) * ceres::tan(omega / T(2))); |
| | } |
| |
|
| | *du = u * factor; |
| | *dv = v * factor; |
| | } |
| |
|
| | |
| | |
| |
|
| | std::string SimpleRadialFisheyeCameraModel::InitializeParamsInfo() { |
| | return "f, cx, cy, k"; |
| | } |
| |
|
| | std::vector<size_t> |
| | SimpleRadialFisheyeCameraModel::InitializeFocalLengthIdxs() { |
| | return {0}; |
| | } |
| |
|
| | std::vector<size_t> |
| | SimpleRadialFisheyeCameraModel::InitializePrincipalPointIdxs() { |
| | return {1, 2}; |
| | } |
| |
|
| | std::vector<size_t> |
| | SimpleRadialFisheyeCameraModel::InitializeExtraParamsIdxs() { |
| | return {3}; |
| | } |
| |
|
| | std::vector<double> SimpleRadialFisheyeCameraModel::InitializeParams( |
| | const double focal_length, const size_t width, const size_t height) { |
| | return {focal_length, width / 2.0, height / 2.0, 0}; |
| | } |
| |
|
| | template <typename T> |
| | void SimpleRadialFisheyeCameraModel::WorldToImage(const T* params, const T u, |
| | const T v, T* x, T* y) { |
| | const T f = params[0]; |
| | const T c1 = params[1]; |
| | const T c2 = params[2]; |
| |
|
| | |
| | T du, dv; |
| | Distortion(¶ms[3], u, v, &du, &dv); |
| | *x = u + du; |
| | *y = v + dv; |
| |
|
| | |
| | *x = f * *x + c1; |
| | *y = f * *y + c2; |
| | } |
| |
|
| | template <typename T> |
| | void SimpleRadialFisheyeCameraModel::ImageToWorld(const T* params, const T x, |
| | const T y, T* u, T* v) { |
| | const T f = params[0]; |
| | const T c1 = params[1]; |
| | const T c2 = params[2]; |
| |
|
| | |
| | *u = (x - c1) / f; |
| | *v = (y - c2) / f; |
| |
|
| | IterativeUndistortion(¶ms[3], u, v); |
| | } |
| |
|
| | template <typename T> |
| | void SimpleRadialFisheyeCameraModel::Distortion(const T* extra_params, |
| | const T u, const T v, T* du, |
| | T* dv) { |
| | const T k = extra_params[0]; |
| |
|
| | const T r = ceres::sqrt(u * u + v * v); |
| |
|
| | if (r > T(std::numeric_limits<double>::epsilon())) { |
| | const T theta = ceres::atan(r); |
| | const T theta2 = theta * theta; |
| | const T thetad = theta * (T(1) + k * theta2); |
| | *du = u * thetad / r - u; |
| | *dv = v * thetad / r - v; |
| | } else { |
| | *du = T(0); |
| | *dv = T(0); |
| | } |
| | } |
| |
|
| | |
| | |
| |
|
| | std::string RadialFisheyeCameraModel::InitializeParamsInfo() { |
| | return "f, cx, cy, k1, k2"; |
| | } |
| |
|
| | std::vector<size_t> RadialFisheyeCameraModel::InitializeFocalLengthIdxs() { |
| | return {0}; |
| | } |
| |
|
| | std::vector<size_t> RadialFisheyeCameraModel::InitializePrincipalPointIdxs() { |
| | return {1, 2}; |
| | } |
| |
|
| | std::vector<size_t> RadialFisheyeCameraModel::InitializeExtraParamsIdxs() { |
| | return {3, 4}; |
| | } |
| |
|
| | std::vector<double> RadialFisheyeCameraModel::InitializeParams( |
| | const double focal_length, const size_t width, const size_t height) { |
| | return {focal_length, width / 2.0, height / 2.0, 0, 0}; |
| | } |
| |
|
| | template <typename T> |
| | void RadialFisheyeCameraModel::WorldToImage(const T* params, const T u, |
| | const T v, T* x, T* y) { |
| | const T f = params[0]; |
| | const T c1 = params[1]; |
| | const T c2 = params[2]; |
| |
|
| | |
| | T du, dv; |
| | Distortion(¶ms[3], u, v, &du, &dv); |
| | *x = u + du; |
| | *y = v + dv; |
| |
|
| | |
| | *x = f * *x + c1; |
| | *y = f * *y + c2; |
| | } |
| |
|
| | template <typename T> |
| | void RadialFisheyeCameraModel::ImageToWorld(const T* params, const T x, |
| | const T y, T* u, T* v) { |
| | const T f = params[0]; |
| | const T c1 = params[1]; |
| | const T c2 = params[2]; |
| |
|
| | |
| | *u = (x - c1) / f; |
| | *v = (y - c2) / f; |
| |
|
| | IterativeUndistortion(¶ms[3], u, v); |
| | } |
| |
|
| | template <typename T> |
| | void RadialFisheyeCameraModel::Distortion(const T* extra_params, const T u, |
| | const T v, T* du, T* dv) { |
| | const T k1 = extra_params[0]; |
| | const T k2 = extra_params[1]; |
| |
|
| | const T r = ceres::sqrt(u * u + v * v); |
| |
|
| | if (r > T(std::numeric_limits<double>::epsilon())) { |
| | const T theta = ceres::atan(r); |
| | const T theta2 = theta * theta; |
| | const T theta4 = theta2 * theta2; |
| | const T thetad = theta * (T(1) + k1 * theta2 + k2 * theta4); |
| | *du = u * thetad / r - u; |
| | *dv = v * thetad / r - v; |
| | } else { |
| | *du = T(0); |
| | *dv = T(0); |
| | } |
| | } |
| |
|
| | |
| | |
| |
|
| | std::string ThinPrismFisheyeCameraModel::InitializeParamsInfo() { |
| | return "fx, fy, cx, cy, k1, k2, p1, p2, k3, k4, sx1, sy1"; |
| | } |
| |
|
| | std::vector<size_t> ThinPrismFisheyeCameraModel::InitializeFocalLengthIdxs() { |
| | return {0, 1}; |
| | } |
| |
|
| | std::vector<size_t> |
| | ThinPrismFisheyeCameraModel::InitializePrincipalPointIdxs() { |
| | return {2, 3}; |
| | } |
| |
|
| | std::vector<size_t> ThinPrismFisheyeCameraModel::InitializeExtraParamsIdxs() { |
| | return {4, 5, 6, 7, 8, 9, 10, 11}; |
| | } |
| |
|
| | std::vector<double> ThinPrismFisheyeCameraModel::InitializeParams( |
| | const double focal_length, const size_t width, const size_t height) { |
| | return {focal_length, |
| | focal_length, |
| | width / 2.0, |
| | height / 2.0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0, |
| | 0}; |
| | } |
| |
|
| | template <typename T> |
| | void ThinPrismFisheyeCameraModel::WorldToImage(const T* params, const T u, |
| | const T v, T* x, T* y) { |
| | const T f1 = params[0]; |
| | const T f2 = params[1]; |
| | const T c1 = params[2]; |
| | const T c2 = params[3]; |
| |
|
| | const T r = ceres::sqrt(u * u + v * v); |
| |
|
| | T uu, vv; |
| | if (r > T(std::numeric_limits<double>::epsilon())) { |
| | const T theta = ceres::atan(r); |
| | uu = theta * u / r; |
| | vv = theta * v / r; |
| | } else { |
| | uu = u; |
| | vv = v; |
| | } |
| |
|
| | |
| | T du, dv; |
| | Distortion(¶ms[4], uu, vv, &du, &dv); |
| | *x = uu + du; |
| | *y = vv + dv; |
| |
|
| | |
| | *x = f1 * *x + c1; |
| | *y = f2 * *y + c2; |
| | } |
| |
|
| | template <typename T> |
| | void ThinPrismFisheyeCameraModel::ImageToWorld(const T* params, const T x, |
| | const T y, T* u, T* v) { |
| | const T f1 = params[0]; |
| | const T f2 = params[1]; |
| | const T c1 = params[2]; |
| | const T c2 = params[3]; |
| |
|
| | |
| | *u = (x - c1) / f1; |
| | *v = (y - c2) / f2; |
| |
|
| | IterativeUndistortion(¶ms[4], u, v); |
| |
|
| | const T theta = ceres::sqrt(*u * *u + *v * *v); |
| | const T theta_cos_theta = theta * ceres::cos(theta); |
| | if (theta_cos_theta > T(std::numeric_limits<double>::epsilon())) { |
| | const T scale = ceres::sin(theta) / theta_cos_theta; |
| | *u *= scale; |
| | *v *= scale; |
| | } |
| | } |
| |
|
| | template <typename T> |
| | void ThinPrismFisheyeCameraModel::Distortion(const T* extra_params, const T u, |
| | const T v, T* du, T* dv) { |
| | const T k1 = extra_params[0]; |
| | const T k2 = extra_params[1]; |
| | const T p1 = extra_params[2]; |
| | const T p2 = extra_params[3]; |
| | const T k3 = extra_params[4]; |
| | const T k4 = extra_params[5]; |
| | const T sx1 = extra_params[6]; |
| | const T sy1 = extra_params[7]; |
| |
|
| | const T u2 = u * u; |
| | const T uv = u * v; |
| | const T v2 = v * v; |
| | const T r2 = u2 + v2; |
| | const T r4 = r2 * r2; |
| | const T r6 = r4 * r2; |
| | const T r8 = r6 * r2; |
| | const T radial = k1 * r2 + k2 * r4 + k3 * r6 + k4 * r8; |
| | *du = u * radial + T(2) * p1 * uv + p2 * (r2 + T(2) * u2) + sx1 * r2; |
| | *dv = v * radial + T(2) * p2 * uv + p1 * (r2 + T(2) * v2) + sy1 * r2; |
| | } |
| |
|
| | |
| |
|
| | void CameraModelWorldToImage(const int model_id, |
| | const std::vector<double>& params, const double u, |
| | const double v, double* x, double* y) { |
| | switch (model_id) { |
| | #define CAMERA_MODEL_CASE(CameraModel) \ |
| | case CameraModel::kModelId: \ |
| | CameraModel::WorldToImage(params.data(), u, v, x, y); \ |
| | break; |
| |
|
| | CAMERA_MODEL_SWITCH_CASES |
| |
|
| | #undef CAMERA_MODEL_CASE |
| | } |
| | } |
| |
|
| | void CameraModelImageToWorld(const int model_id, |
| | const std::vector<double>& params, const double x, |
| | const double y, double* u, double* v) { |
| | switch (model_id) { |
| | #define CAMERA_MODEL_CASE(CameraModel) \ |
| | case CameraModel::kModelId: \ |
| | CameraModel::ImageToWorld(params.data(), x, y, u, v); \ |
| | break; |
| |
|
| | CAMERA_MODEL_SWITCH_CASES |
| |
|
| | #undef CAMERA_MODEL_CASE |
| | } |
| | } |
| |
|
| | double CameraModelImageToWorldThreshold(const int model_id, |
| | const std::vector<double>& params, |
| | const double threshold) { |
| | switch (model_id) { |
| | #define CAMERA_MODEL_CASE(CameraModel) \ |
| | case CameraModel::kModelId: \ |
| | return CameraModel::ImageToWorldThreshold(params.data(), threshold); \ |
| | break; |
| |
|
| | CAMERA_MODEL_SWITCH_CASES |
| |
|
| | #undef CAMERA_MODEL_CASE |
| | } |
| |
|
| | return -1; |
| | } |
| |
|
| | } |
| |
|
| | #endif |
| |
|