Calico
A visual-inertial calibration library designed for rapid problem construction and debugging.
camera_models.h
1 #ifndef CALICO_SENSORS_CAMERA_MODELS_H_
2 #define CALICO_SENSORS_CAMERA_MODELS_H_
3 
4 #include "calico/typedefs.h"
5 
6 #include <memory>
7 
8 #include "absl/status/statusor.h"
9 #include "calico/statusor_macros.h"
10 #include "Eigen/Dense"
11 
12 
13 namespace calico::sensors {
14 
16 enum class CameraIntrinsicsModel : int {
18  kNone,
20  kOpenCv5,
22  kOpenCv8,
33 };
34 
36 class CameraModel {
37  public:
38  virtual ~CameraModel() = default;
39 
42  template <typename T>
43  absl::StatusOr<Eigen::Vector2<T>> ProjectPoint(
44  const Eigen::VectorX<T>& intrinsics,
45  const Eigen::Vector3<T>& point) const;
46 
52  template <typename T>
53  absl::StatusOr<Eigen::Vector3<T>> UnprojectPixel(
54  const Eigen::VectorX<T>& intrinsics,
55  const Eigen::Vector2<T>& pixel) const;
56 
58  virtual CameraIntrinsicsModel GetType() const = 0;
59 
61  virtual int NumberOfParameters() const = 0;
62 
63  // TODO(yangjames): This method should return an absl::StatusOr. Figure out
64  // how to support this using unique_ptr's, or find macros that already
65  // implement this feature (i.e. ASSIGN_OR_RETURN).
69  static std::unique_ptr<CameraModel> Create(
70  CameraIntrinsicsModel camera_model);
71 };
72 
79 class OpenCv5Model : public CameraModel {
80  public:
81  static constexpr int kNumberOfParameters = 8;
82  static constexpr CameraIntrinsicsModel kModelType = CameraIntrinsicsModel::kOpenCv5;
83 
84  OpenCv5Model() = default;
85  ~OpenCv5Model() override = default;
86  OpenCv5Model& operator=(const OpenCv5Model&) = default;
87 
104  template <typename T>
105  static absl::StatusOr<Eigen::Vector2<T>> ProjectPoint(
106  const Eigen::VectorX<T>& intrinsics,
107  const Eigen::Vector3<T>& point) {
108  if (point.z() <= T(0.0)) {
109  return absl::InvalidArgumentError(
110  "Camera point is behind the camera. Cannot project.");
111  }
112  if (intrinsics.size() != kNumberOfParameters) {
113  return absl::InvalidArgumentError(absl::StrCat(
114  "Invalid number of intrinsics parameters provided. Expected ",
115  kNumberOfParameters, ". Got ", intrinsics.size()));
116  }
117  // Prepare values.
118  const T& f = intrinsics(0);
119  const T& cx = intrinsics(1);
120  const T& cy = intrinsics(2);
121  const T& k1 = intrinsics(3);
122  const T& k2 = intrinsics(4);
123  const T& p1 = intrinsics(5);
124  const T& p2 = intrinsics(6);
125  const T& k3 = intrinsics(7);
126  Eigen::Vector2<T> projection(point.x() / point.z(), point.y() / point.z());
127  const T x = projection.x();
128  const T y = projection.y();
129  const T r2 = projection.squaredNorm();
130  const T s = T(1.0) + r2 * (k1 + r2 * (k2 + r2 * k3));
131  // Apply radial distortion.
132  projection *= s;
133  // Apply tangential distortion.
134  projection.x() += T(2.0) * p1 * x * y + p2 * (r2 + T(2.0) * x * x);
135  projection.y() += T(2.0) * p2 * x * y + p1 * (r2 + T(2.0) * y * y);
136  // Apply pinhole parameters.
137  projection *= f;
138  projection.x() += cx;
139  projection.y() += cy;
140  return projection;
141  }
142 
156  template <typename T>
157  static absl::StatusOr<Eigen::Vector3<T>> UnprojectPixel(
158  const Eigen::VectorX<T>& intrinsics,
159  const Eigen::Vector2<T>& pixel,
160  int max_iterations = 30) {
161  constexpr T kSmallValue = T(1e-14);
162  if (intrinsics.size() != kNumberOfParameters) {
163  return absl::InvalidArgumentError(absl::StrCat(
164  "Expected ", kNumberOfParameters, " parameters for intrinsics. Got ",
165  intrinsics.size()));
166  }
167  const T& f = intrinsics(0);
168  const T& cx = intrinsics(1);
169  const T& cy = intrinsics(2);
170  const T& k1 = intrinsics(3);
171  const T& k2 = intrinsics(4);
172  const T& p1 = intrinsics(5);
173  const T& p2 = intrinsics(6);
174  const T& k3 = intrinsics(7);
175  const T inv_f = T(1.0) / f;
176  // Invert pinhole model from distorted point.
177  const T xd0 = inv_f * (pixel.x() - cx);
178  const T yd0 = inv_f * (pixel.y() - cy);
179  // Set distorted pixel as initial guess.
180  T x = xd0;
181  T y = yd0;
182  // Run Newton's method on the residual of distorted pixel.
183  for (int i = 0; i < max_iterations; ++i) {
184  const T x2 = x * x;
185  const T y2 = y * y;
186  const T r2 = x2 + y2;
187  const T s = T(1.0) + r2 * (k1 + r2 * (k2 + r2 * k3));
188  // Compute residual.
189  const T err_x = xd0 - (s * x + 2 * p1 * x * y + p2 * (r2 + 2 * x2));
190  const T err_y = yd0 - (s * y + 2 * p2 * x * y + p1 * (r2 + 2 * y2));
191  // If residual is small enough, exit early.
192  if (std::abs(err_x) + std::abs(err_y) < kSmallValue) {
193  break;
194  }
195  // Compute Jacobian:
196  // J = [a, b]
197  // [b, c]
198  const T ds = 2 * (k1 + r2 * (2 * k2 + 3 * k3 * r2));
199  const T a = ds * x2 + s + 2 * (p1 * y + 3 * p2 * x);
200  const T b = ds * x * y + 2 * (p1 * x + p2 * y);
201  const T c = ds * y2 + s + 2 * (p2 * x + 3 * p1 * y);
202  // Invert Jacobian;
203  const T det = T(1.0) / (a * c - b * b);
204  const T alpha = det * c;
205  const T beta = -det * b;
206  const T gamma = det * a;
207  // Apply update.
208  x = x + (alpha * err_x + beta * err_y);
209  y = y + (beta * err_x + gamma * err_y);
210  }
211  Eigen::Vector3<T> bearing_vector(x, y, 1);
212  bearing_vector.normalize();
213  return bearing_vector;
214  }
215 
217  return kModelType;
218  }
219 
220  int NumberOfParameters() const final {
221  return kNumberOfParameters;
222  }
223 };
224 
231 class OpenCv8Model : public CameraModel {
232  public:
233  static constexpr int kNumberOfParameters = 11;
234  static constexpr CameraIntrinsicsModel kModelType = CameraIntrinsicsModel::kOpenCv8;
235 
236  OpenCv8Model() = default;
237  ~OpenCv8Model() override = default;
238  OpenCv8Model& operator=(const OpenCv8Model&) = default;
239 
256  template <typename T>
257  static absl::StatusOr<Eigen::Vector2<T>> ProjectPoint(
258  const Eigen::VectorX<T>& intrinsics,
259  const Eigen::Vector3<T>& point) {
260  if (point.z() <= T(0.0)) {
261  return absl::InvalidArgumentError(
262  "Camera point is behind the camera. Cannot project.");
263  }
264  if (intrinsics.size() != kNumberOfParameters) {
265  return absl::InvalidArgumentError(absl::StrCat(
266  "Invalid number of intrinsics parameters provided. Expected ",
267  kNumberOfParameters, ". Got ", intrinsics.size()));
268  }
269  // Prepare values.
270  const T& f = intrinsics(0);
271  const T& cx = intrinsics(1);
272  const T& cy = intrinsics(2);
273  const T& k1 = intrinsics(3);
274  const T& k2 = intrinsics(4);
275  const T& p1 = intrinsics(5);
276  const T& p2 = intrinsics(6);
277  const T& k3 = intrinsics(7);
278  const T& k4 = intrinsics(8);
279  const T& k5 = intrinsics(9);
280  const T& k6 = intrinsics(10);
281  Eigen::Vector2<T> projection(point.x() / point.z(), point.y() / point.z());
282  const T x = projection.x();
283  const T y = projection.y();
284  const T r2 = projection.squaredNorm();
285  const T s_num = T(1.0) + r2 * (k1 + r2 * (k2 + r2 * k3));
286  const T s_den = T(1.0) + r2 * (k4 + r2 * (k5 + r2 * k6));
287  const T s = s_num / s_den;
288  // Apply radial distortion.
289  projection *= s;
290  // Apply tangential distortion.
291  projection.x() += T(2.0) * p1 * x * y + p2 * (r2 + T(2.0) * x * x);
292  projection.y() += T(2.0) * p2 * x * y + p1 * (r2 + T(2.0) * y * y);
293  // Apply pinhole parameters.
294  projection *= f;
295  projection.x() += cx;
296  projection.y() += cy;
297  return projection;
298  }
299 
313  template <typename T>
314  static absl::StatusOr<Eigen::Vector3<T>> UnprojectPixel(
315  const Eigen::VectorX<T>& intrinsics,
316  const Eigen::Vector2<T>& pixel,
317  int max_iterations = 30) {
318  constexpr T kSmallValue = T(1e-14);
319  if (intrinsics.size() != kNumberOfParameters) {
320  return absl::InvalidArgumentError(absl::StrCat(
321  "Expected ", kNumberOfParameters, " parameters for intrinsics. Got ",
322  intrinsics.size()));
323  }
324  const T& f = intrinsics(0);
325  const T& cx = intrinsics(1);
326  const T& cy = intrinsics(2);
327  const T& k1 = intrinsics(3);
328  const T& k2 = intrinsics(4);
329  const T& p1 = intrinsics(5);
330  const T& p2 = intrinsics(6);
331  const T& k3 = intrinsics(7);
332  const T& k4 = intrinsics(8);
333  const T& k5 = intrinsics(9);
334  const T& k6 = intrinsics(10);
335  const T inv_f = T(1.0) / f;
336  // Invert pinhole model from distorted point.
337  const T xd0 = inv_f * (pixel.x() - cx);
338  const T yd0 = inv_f * (pixel.y() - cy);
339  // Set distorted pixel as initial guess.
340  T x = xd0;
341  T y = yd0;
342  // Run Newton's method on the residual of distorted pixel.
343  for (int i = 0; i < max_iterations; ++i) {
344  const T x2 = x * x;
345  const T y2 = y * y;
346  const T r2 = x2 + y2;
347  const T s_num = T(1.0) + r2 * (k1 + r2 * (k2 + r2 * k3));
348  const T s_den = T(1.0) + r2 * (k4 + r2 * (k5 + r2 * k6));
349  const T s = s_num / s_den;
350  // Compute residual.
351  const T err_x = xd0 - (s * x + 2 * p1 * x * y + p2 * (r2 + 2 * x2));
352  const T err_y = yd0 - (s * y + 2 * p2 * x * y + p1 * (r2 + 2 * y2));
353  // If residual is small enough, exit early.
354  if (std::abs(err_x) + std::abs(err_y) < kSmallValue) {
355  break;
356  }
357  // Compute Jacobian:
358  // J = [a, b]
359  // [b, c]
360  const T dnum = k1 + r2 * (T(2.0) * k2 + T(3.0) * r2 * k3);
361  const T dden = k4 + r2 * (T(2.0) * k5 + T(3.0) * r2 * k6);
362  const T ds = (dnum - s * dden) / s_den;
363  const T a = ds * x2 + s + 2 * (p1 * y + 3 * p2 * x);
364  const T b = ds * x * y + 2 * (p1 * x + p2 * y);
365  const T c = ds * y2 + s + 2 * (p2 * x + 3 * p1 * y);
366  // Invert Jacobian;
367  const T det = T(1.0) / (a * c - b * b);
368  const T alpha = det * c;
369  const T beta = -det * b;
370  const T gamma = det * a;
371  // Apply update.
372  x = x + (alpha * err_x + beta * err_y);
373  y = y + (beta * err_x + gamma * err_y);
374  }
375  Eigen::Vector3<T> bearing_vector(x, y, 1);
376  bearing_vector.normalize();
377  return bearing_vector;
378  }
379 
381  return kModelType;
382  }
383 
384  int NumberOfParameters() const final {
385  return kNumberOfParameters;
386  }
387 };
388 
396  public:
397  static constexpr int kNumberOfParameters = 7;
398  static constexpr CameraIntrinsicsModel kModelType = CameraIntrinsicsModel::kKannalaBrandt;
399 
400  KannalaBrandtModel() = default;
401  ~KannalaBrandtModel() override = default;
402  KannalaBrandtModel& operator=(const KannalaBrandtModel&) = default;
403 
419  template <typename T>
420  static absl::StatusOr<Eigen::Vector2<T>> ProjectPoint(
421  const Eigen::VectorX<T>& intrinsics,
422  const Eigen::Vector3<T>& point) {
423  if (point.z() <= T(0.0)) {
424  return absl::InvalidArgumentError(
425  "Camera point is behind the camera. Cannot project.");
426  }
427  if (intrinsics.size() != kNumberOfParameters) {
428  return absl::InvalidArgumentError(absl::StrCat(
429  "Invalid number of intrinsics parameters provided. Expected ",
430  kNumberOfParameters, ". Got ", intrinsics.size()));
431  }
432  // Prepare values.
433  const T& f = intrinsics(0);
434  const T& cx = intrinsics(1);
435  const T& cy = intrinsics(2);
436  const T& k1 = intrinsics(3);
437  const T& k2 = intrinsics(4);
438  const T& k3 = intrinsics(5);
439  const T& k4 = intrinsics(6);
440 
441  Eigen::Vector2<T> projection(point.x() / point.z(), point.y() / point.z());
442  const T r = projection.norm();
443  T s;
444  if (r < T(1e-9)) {
445  const T r2 = r * r;
446  s = T(1.0) + r2 * (k1 - T(1.0/3.0) + r2 * (-k1 + k2 + 0.2));
447  } else {
448  const T theta = atan(r);
449  const T theta2 = theta * theta;
450  const T theta_d = theta * (T(1.0) + theta2 *
451  (k1 + theta2 *
452  (k2 + theta2 * (k3 + theta2 * k4))));
453  s = theta_d / r;
454  }
455  // Apply radial distortion.
456  projection *= s;
457  // Apply pinhole parameters.
458  projection *= f;
459  projection.x() += cx;
460  projection.y() += cy;
461  return projection;
462  }
463 
475  // TODO(yangjames): Unproject is not that great for KB model :( it takes 100
476  // Newton iterations to get down to 1e-9, converges way too slowly. Figure out
477  // why this is.
478  template <typename T>
479  static absl::StatusOr<Eigen::Vector3<T>> UnprojectPixel(
480  const Eigen::VectorX<T>& intrinsics,
481  const Eigen::Vector2<T>& pixel,
482  int max_iterations = 100) {
483  constexpr T kSmallValue = T(1e-14);
484  constexpr T kSmallR = T(1e-9);
485  if (intrinsics.size() != kNumberOfParameters) {
486  return absl::InvalidArgumentError(absl::StrCat(
487  "Expected ", kNumberOfParameters, " parameters for intrinsics. Got ",
488  intrinsics.size()));
489  }
490  const T& f = intrinsics(0);
491  const T& cx = intrinsics(1);
492  const T& cy = intrinsics(2);
493  const T& k1 = intrinsics(3);
494  const T& k2 = intrinsics(4);
495  const T& k3 = intrinsics(5);
496  const T& k4 = intrinsics(6);
497  const T inv_f = T(1.0) / f;
498  // Invert pinhole model from distorted point.
499  const T xd0 = inv_f * (pixel.x() - cx);
500  const T yd0 = inv_f * (pixel.y() - cy);
501  // If pixel is already close to the origin, no need to continue.
502  if (xd0 * xd0 + yd0 * yd0 < kSmallValue) {
503  return Eigen::Vector3<T>(xd0, yd0, 1.0);
504  }
505  // Set distorted pixel as initial guess.
506  T x = xd0;
507  T y = yd0;
508  // Run Newton's method on the residual of distorted pixel.
509  for (int i = 0; i < max_iterations; ++i) {
510  const T r2 = x * x + y * y;
511  const T r = sqrt(r2);
512  T s;
513  if (r < kSmallR) {
514  const T r2 = r * r;
515  s = T(1.0) + r2 * (k1 - T(1.0/3.0) + r2 * (-k1 + k2 + 0.2));
516  } else {
517  const T theta = atan(r);
518  const T theta2 = theta * theta;
519  const T theta_d = theta * (T(1.0) + theta2 *
520  (k1 + theta2 *
521  (k2 + theta2 * (k3 + theta2 * k4))));
522  s = theta_d / r;
523  }
524  const T err_x = xd0 - s * x;
525  const T err_y = yd0 - s * y;
526  // If residual is small enough, exit early.
527  if (std::abs(err_x) + std::abs(err_y) < kSmallValue) {
528  break;
529  }
530  // Compute Jacobian:
531  // J = [a, b]
532  // [b, c]
533  T a, b, c;
534  // If small r, use 3rd order Taylor expansion instead.
535  if (r < kSmallR) {
536  const T q = T(2.0) * (k1 - T(1.0 / 3.0)) + T(4.0) * r2 * (-k1 + k2 + T(0.2));
537  const T dsdx = q * x;
538  const T dsdy = q * y;
539  a = dsdx * x + s;
540  b = q * x * y;
541  c = dsdy * y + s;
542  } else {
543  const T theta = atan(r);
544  const T theta2 = theta * theta;
545  const T theta_d = theta * (1.0 + theta2 *
546  (k1 + theta2 *
547  (k2 + theta2 * (k3 + theta2 * k4))));
548  const T s = theta_d / r;
549 
550  const T drdx = x / r;
551  const T drdy = y / r;
552  const T inv_r = T(1.0) / r;
553  const T dthetadr = T(1.0) / (T(1.0) + r2);
554  const T dthetaddtheta =
555  T(1.0) + theta2 * (T(3.0) * k1 + theta2 *
556  (T(5.0) * k2 + theta2 *
557  (T(7.0) * k3 + theta2 * T(9.0) * k4)));
558  const T dthetaddr = dthetaddtheta * dthetadr;
559  const T dsdr = dthetaddr*inv_r + s * inv_r;
560  a = dsdr * drdx * x + s;
561  b = dsdr * x * y / r;
562  c = dsdr * drdy * y + s;
563  }
564  // Invert Jacobian;
565  const T det = T(1.0) / (a * c - b * b);
566  if (std::isnan(det)) {
567  std::cout << "r: " << r << std::endl;
568  std::cout << "a, b, c: " << a << ", " << b << ", " << c << std::endl;
569  std::cout << "det: " << det << std::endl;
570  }
571  const T alpha = det * c;
572  const T beta = -det * b;
573  const T gamma = det * a;
574  // Apply update.
575  x = x + (alpha * err_x + beta * err_y);
576  y = y + (beta * err_x + gamma * err_y);
577  }
578  Eigen::Vector3<T> bearing_vector(x, y, 1);
579  bearing_vector.normalize();
580  return bearing_vector;
581  }
582 
584  return kModelType;
585  }
586 
587  int NumberOfParameters() const final {
588  return kNumberOfParameters;
589  }
590 };
591 
597  public:
598  static constexpr int kNumberOfParameters = 5;
599  static constexpr CameraIntrinsicsModel kModelType = CameraIntrinsicsModel::kDoubleSphere;
600 
601  DoubleSphereModel() = default;
602  ~DoubleSphereModel() override = default;
603  DoubleSphereModel& operator=(const DoubleSphereModel&) = default;
604 
622  template <typename T>
623  static absl::StatusOr<Eigen::Vector2<T>> ProjectPoint(
624  const Eigen::VectorX<T>& intrinsics,
625  const Eigen::Vector3<T>& point) {
626  if (intrinsics.size() != kNumberOfParameters) {
627  return absl::InvalidArgumentError(absl::StrCat(
628  "Invalid number of intrinsics parameters provided. Expected ",
629  kNumberOfParameters, ". Got ", intrinsics.size()));
630  }
631  const T& xi = intrinsics(3);
632  const T& alpha = intrinsics(4);
633  const T w1 = alpha > T(0.5) ?
634  (T(1.0) - alpha) / alpha : alpha / (T(1.0) - alpha);
635  const T num = w1 + xi;
636  const T w2_sq = num * num / (T(2.0) * w1 * xi + xi * xi + T(1.0));
637  const T r2 = point.squaredNorm();
638  if (point.z() * point.z() <= -w2_sq * r2) {
639  return absl::InvalidArgumentError(
640  "Invalid point. Cannot project.");
641  }
642  // Prepare values.
643  const T& f = intrinsics(0);
644  const T& cx = intrinsics(1);
645  const T& cy = intrinsics(2);
646  const T r = sqrt(r2);
647  const T d = sqrt(r2 * (T(1.0) + xi * xi) + T(2.0) * xi * r * point.z());
648  const T s = T(1.0) / (alpha * d + (T(1.0) - alpha) * (xi * r + point.z()));
649  // Apply distortion.
650  Eigen::Vector2<T> projection(point.x(), point.y());
651  projection *= s;
652  // Apply pinhole parameters.
653  projection *= f;
654  projection.x() += cx;
655  projection.y() += cy;
656  return projection;
657  }
658 
673  template <typename T>
674  static absl::StatusOr<Eigen::Vector3<T>> UnprojectPixel(
675  const Eigen::VectorX<T>& intrinsics,
676  const Eigen::Vector2<T>& pixel) {
677  if (intrinsics.size() != kNumberOfParameters) {
678  return absl::InvalidArgumentError(absl::StrCat(
679  "Expected ", kNumberOfParameters, " parameters for intrinsics. Got ",
680  intrinsics.size()));
681  }
682  const T& f = intrinsics(0);
683  const T& cx = intrinsics(1);
684  const T& cy = intrinsics(2);
685  const T& xi = intrinsics(3);
686  const T& alpha = intrinsics(4);
687  const T inv_f = T(1.0) / f;
688  // Invert pinhole model from distorted point.
689  T mx = inv_f * (pixel.x() - cx);
690  T my = inv_f * (pixel.y() - cy);
691  // Invert distortion.
692  const T r2 = mx * mx + my * my;
693  T mz =
694  (1.0 - alpha * alpha * r2) /
695  (alpha * sqrt(1.0 - (2.0 * alpha - 1.0) * r2) + 1.0 - alpha);
696  const T mz2 = mz * mz;
697  const T inv_s = (mz * xi + sqrt(mz2 + (1 - xi * xi) * r2)) / (mz2 + r2);
698  Eigen::Vector3<T> bearing_vector(inv_s * mx, inv_s * my, inv_s * mz - xi);
699  bearing_vector.normalize();
700  return bearing_vector;
701  }
702 
704  return kModelType;
705  }
706 
707  int NumberOfParameters() const final {
708  return kNumberOfParameters;
709  }
710 };
711 
717  public:
718  static constexpr int kNumberOfParameters = 4;
719  static constexpr CameraIntrinsicsModel kModelType = CameraIntrinsicsModel::kFieldOfView;
720 
721  FieldOfViewModel() = default;
722  ~FieldOfViewModel() override = default;
723  FieldOfViewModel& operator=(const FieldOfViewModel&) = default;
724 
739  template <typename T>
740  static absl::StatusOr<Eigen::Vector2<T>> ProjectPoint(
741  const Eigen::VectorX<T>& intrinsics,
742  const Eigen::Vector3<T>& point) {
743  if (intrinsics.size() != kNumberOfParameters) {
744  return absl::InvalidArgumentError(absl::StrCat(
745  "Invalid number of intrinsics parameters provided. Expected ",
746  kNumberOfParameters, ". Got ", intrinsics.size()));
747  }
748  const T& f = intrinsics(0);
749  const T& cx = intrinsics(1);
750  const T& cy = intrinsics(2);
751  const T& w = intrinsics(3);
752  if (point.z() <= T(0.0)) {
753  return absl::InvalidArgumentError(
754  "Invalid point. Cannot project.");
755  }
756  // Prepare values.
757  Eigen::Vector2<T> projection(point.x(), point.y());
758  projection /= point.z();
759  const T r = projection.norm();
760 
761  T s;
762  if (w * w < 1e-5) {
763  // When w approaches 0.
764  s = T(1.0);
765  } else {
766  const T tan_term = T(2.0) * tan(w * T(0.5));
767  if (r * r < 1e-5) {
768  // For when r approaches 0.
769  s = tan_term / w;
770  } else {
771  s = atan(r * tan_term) / (r * w);
772  }
773  }
774  // Apply distortion.
775  projection *= s;
776  // Apply pinhole parameters.
777  projection *= f;
778  projection.x() += cx;
779  projection.y() += cy;
780  return projection;
781  }
782 
797  template <typename T>
798  static absl::StatusOr<Eigen::Vector3<T>> UnprojectPixel(
799  const Eigen::VectorX<T>& intrinsics,
800  const Eigen::Vector2<T>& pixel) {
801  if (intrinsics.size() != kNumberOfParameters) {
802  return absl::InvalidArgumentError(absl::StrCat(
803  "Expected ", kNumberOfParameters, " parameters for intrinsics. Got ",
804  intrinsics.size()));
805  }
806  const T& f = intrinsics(0);
807  const T& cx = intrinsics(1);
808  const T& cy = intrinsics(2);
809  const T& w = intrinsics(3);
810  const T inv_f = T(1.0) / f;
811  // Invert pinhole model from distorted point.
812  const T mx = inv_f * (pixel.x() - cx);
813  const T my = inv_f * (pixel.y() - cy);
814  // Undistort.
815  const T r = sqrt(mx * mx + my * my);
816  T eta;
817  if (w * w < 1e-5) {
818  // Limit as w approaches 0.
819  eta = T(1.0);
820  } else {
821  const T tan_term = T(2.0) * tan(w * T(0.5));
822  if (r * r < 1e-5) {
823  // Limit as r approaches 0.
824  eta = w / tan_term;
825  } else {
826  eta = sin(r * w) / (r * tan_term);
827  }
828  }
829  Eigen::Vector3<T> pm(eta * mx, eta * my, cos(r * w));
830  pm.normalize();
831  return pm;
832  }
833 
835  return kModelType;
836  }
837 
838  int NumberOfParameters() const final {
839  return kNumberOfParameters;
840  }
841 };
842 
843 
849  public:
850  static constexpr int kNumberOfParameters = 4;
851  static constexpr CameraIntrinsicsModel kModelType = CameraIntrinsicsModel::kUnifiedCamera;
852 
853  UnifiedCameraModel() = default;
854  ~UnifiedCameraModel() override = default;
855  UnifiedCameraModel& operator=(const UnifiedCameraModel&) = default;
856 
871  template <typename T>
872  static absl::StatusOr<Eigen::Vector2<T>> ProjectPoint(
873  const Eigen::VectorX<T>& intrinsics,
874  const Eigen::Vector3<T>& point) {
875  if (intrinsics.size() != kNumberOfParameters) {
876  return absl::InvalidArgumentError(absl::StrCat(
877  "Invalid number of intrinsics parameters provided. Expected ",
878  kNumberOfParameters, ". Got ", intrinsics.size()));
879  }
880  const T& alpha = intrinsics(3);
881  const T w = alpha > T(0.5) ?
882  (T(1.0) - alpha) / alpha : alpha / (T(1.0) - alpha);
883  const T d = point.norm();
884  if (point.z() <= -w * d) {
885  return absl::InvalidArgumentError(
886  "Invalid point. Cannot project.");
887  }
888  const T& f = intrinsics(0);
889  const T& cx = intrinsics(1);
890  const T& cy = intrinsics(2);
891  // Prepare values.
892  Eigen::Vector2<T> projection(point.x(), point.y());
893  T s = T(1.0) / (alpha * d + (T(1.0) - alpha) * point.z());
894  // Apply distortion.
895  projection *= s;
896  // Apply pinhole parameters.
897  projection *= f;
898  projection.x() += cx;
899  projection.y() += cy;
900  return projection;
901  }
902 
919  template <typename T>
920  static absl::StatusOr<Eigen::Vector3<T>> UnprojectPixel(
921  const Eigen::VectorX<T>& intrinsics,
922  const Eigen::Vector2<T>& pixel) {
923  if (intrinsics.size() != kNumberOfParameters) {
924  return absl::InvalidArgumentError(absl::StrCat(
925  "Expected ", kNumberOfParameters, " parameters for intrinsics. Got ",
926  intrinsics.size()));
927  }
928  const T& f = intrinsics(0);
929  const T& cx = intrinsics(1);
930  const T& cy = intrinsics(2);
931  const T& alpha = intrinsics(3);
932  const T inv_f = T(1.0) / f;
933  // Invert pinhole model from distorted point.
934  const T one_m_alpha = T(1.0) - alpha;
935  const T mx = one_m_alpha * inv_f * (pixel.x() - cx);
936  const T my = one_m_alpha * inv_f * (pixel.y() - cy);
937  const T r2 = mx * mx + my * my;
938  const T xi = alpha / one_m_alpha;
939  // Undistort.
940  const T s = (xi + sqrt(T(1.0) + (T(1.0) - xi * xi) * r2)) / (T(1.0) + r2);
941  Eigen::Vector3d bearing_vector(mx, my, 1.0);
942  bearing_vector *= s;
943  bearing_vector.z() -= xi;
944  bearing_vector.normalize();
945  return bearing_vector;
946  }
947 
949  return kModelType;
950  }
951 
952  int NumberOfParameters() const final {
953  return kNumberOfParameters;
954  }
955 };
956 
962  public:
963  static constexpr int kNumberOfParameters = 5;
965 
966  ExtendedUnifiedCameraModel() = default;
967  ~ExtendedUnifiedCameraModel() override = default;
968  ExtendedUnifiedCameraModel& operator=(const ExtendedUnifiedCameraModel&) = default;
969 
984  template <typename T>
985  static absl::StatusOr<Eigen::Vector2<T>> ProjectPoint(
986  const Eigen::VectorX<T>& intrinsics,
987  const Eigen::Vector3<T>& point) {
988  if (intrinsics.size() != kNumberOfParameters) {
989  return absl::InvalidArgumentError(absl::StrCat(
990  "Invalid number of intrinsics parameters provided. Expected ",
991  kNumberOfParameters, ". Got ", intrinsics.size()));
992  }
993  const T& alpha = intrinsics(3);
994  const T& beta = intrinsics(4);
995  const T d = sqrt(beta * point.head(2).norm() + point.z() * point.z());
996  const T w = alpha > T(0.5) ?
997  (T(1.0) - alpha) / alpha : alpha / (T(1.0) - alpha);
998  if (point.z() <= -w * d) {
999  return absl::InvalidArgumentError(
1000  "Invalid point. Cannot project.");
1001  }
1002  const T& f = intrinsics(0);
1003  const T& cx = intrinsics(1);
1004  const T& cy = intrinsics(2);
1005  // Prepare values.
1006  Eigen::Vector2<T> projection(point.x(), point.y());
1007  T s = T(1.0) / (alpha * d + (T(1.0) - alpha) * point.z());
1008  // Apply distortion.
1009  projection *= s;
1010  // Apply pinhole parameters.
1011  projection *= f;
1012  projection.x() += cx;
1013  projection.y() += cy;
1014  return projection;
1015  }
1016 
1025 
1035  template <typename T>
1036  static absl::StatusOr<Eigen::Vector3<T>> UnprojectPixel(
1037  const Eigen::VectorX<T>& intrinsics,
1038  const Eigen::Vector2<T>& pixel) {
1039  if (intrinsics.size() != kNumberOfParameters) {
1040  return absl::InvalidArgumentError(absl::StrCat(
1041  "Expected ", kNumberOfParameters, " parameters for intrinsics. Got ",
1042  intrinsics.size()));
1043  }
1044  const T& f = intrinsics(0);
1045  const T& cx = intrinsics(1);
1046  const T& cy = intrinsics(2);
1047  const T& alpha = intrinsics(3);
1048  const T& beta = intrinsics(4);
1049  const T inv_f = T(1.0) / f;
1050  // Invert pinhole model from distorted point.
1051  const T mx = inv_f * (pixel.x() - cx);
1052  const T my = inv_f * (pixel.y() - cy);
1053  const T r2 = mx * mx + my * my;
1054  const T mz = (T(1.0) - beta * alpha * alpha * r2) /
1055  (alpha * sqrt(T(1.0) - (T(2.0) * alpha - T(1.0)) * beta * r2)
1056  + (T(1.0) - alpha));
1057  // Undistort.
1058  const T s = T(1.0) / sqrt(r2 + mz * mz);
1059  Eigen::Vector3d bearing_vector(mx, my, mz);
1060  bearing_vector *= s;
1061  bearing_vector.normalize();
1062  return bearing_vector;
1063  }
1064 
1066  return kModelType;
1067  }
1068 
1069  int NumberOfParameters() const final {
1070  return kNumberOfParameters;
1071  }
1072 };
1073 
1074 
1075 template <typename T>
1076 absl::StatusOr<Eigen::Vector2<T>> CameraModel::ProjectPoint(
1077  const Eigen::VectorX<T>& intrinsics,
1078  const Eigen::Vector3<T>& point) const {
1079  if (const auto derived = dynamic_cast<const OpenCv5Model*>(this)) {
1080  return derived->ProjectPoint(intrinsics, point);
1081  }
1082  if (const auto derived = dynamic_cast<const OpenCv8Model*>(this)) {
1083  return derived->ProjectPoint(intrinsics, point);
1084  }
1085  if (const auto derived = dynamic_cast<const KannalaBrandtModel*>(this)) {
1086  return derived->ProjectPoint(intrinsics, point);
1087  }
1088  if (const auto derived = dynamic_cast<const DoubleSphereModel*>(this)) {
1089  return derived->ProjectPoint(intrinsics, point);
1090  }
1091  if (const auto derived = dynamic_cast<const FieldOfViewModel*>(this)) {
1092  return derived->ProjectPoint(intrinsics, point);
1093  }
1094  if (const auto derived = dynamic_cast<const UnifiedCameraModel*>(this)) {
1095  return derived->ProjectPoint(intrinsics, point);
1096  }
1097  if (const auto derived =
1098  dynamic_cast<const ExtendedUnifiedCameraModel*>(this)) {
1099  return derived->ProjectPoint(intrinsics, point);
1100  }
1101  return absl::InvalidArgumentError(absl::StrCat(
1102  "ProjectPoint for camera model ", this->GetType(), " not supported."));
1103 }
1104 
1105 template <typename T>
1106 absl::StatusOr<Eigen::Vector3<T>> CameraModel::UnprojectPixel(
1107  const Eigen::VectorX<T>& intrinsics,
1108  const Eigen::Vector2<T>& pixel) const {
1109  if (const auto derived = dynamic_cast<const OpenCv5Model*>(this)) {
1110  return derived->UnprojectPixel(intrinsics, pixel);
1111  }
1112  if (const auto derived = dynamic_cast<const OpenCv8Model*>(this)) {
1113  return derived->UnprojectPixel(intrinsics, pixel);
1114  }
1115  if (const auto derived = dynamic_cast<const KannalaBrandtModel*>(this)) {
1116  return derived->UnprojectPixel(intrinsics, pixel);
1117  }
1118  if (const auto derived = dynamic_cast<const DoubleSphereModel*>(this)) {
1119  return derived->UnprojectPixel(intrinsics, pixel);
1120  }
1121  if (const auto derived = dynamic_cast<const FieldOfViewModel*>(this)) {
1122  return derived->UnprojectPixel(intrinsics, pixel);
1123  }
1124  if (const auto derived = dynamic_cast<const UnifiedCameraModel*>(this)) {
1125  return derived->UnprojectPixel(intrinsics, pixel);
1126  }
1127  if (const auto derived =
1128  dynamic_cast<const ExtendedUnifiedCameraModel*>(this)) {
1129  return derived->UnprojectPixel(intrinsics, pixel);
1130  }
1131  return absl::InvalidArgumentError(absl::StrCat(
1132  "UnprojectPixel for camera model ", this->GetType(), " not supported."));
1133 }
1134 
1135 } // namespace calico::sensors
1136 
1137 #endif // CALICO_SENSORS_CAMERA_MODELS_H_
Base class for camera models.
Definition: camera_models.h:36
virtual CameraIntrinsicsModel GetType() const =0
Getter for camera model type.
absl::StatusOr< Eigen::Vector3< T > > UnprojectPixel(const Eigen::VectorX< T > &intrinsics, const Eigen::Vector2< T > &pixel) const
Definition: camera_models.h:1106
virtual int NumberOfParameters() const =0
Getter for the number of parameters for this camera model.
static std::unique_ptr< CameraModel > Create(CameraIntrinsicsModel camera_model)
Definition: camera_models.cpp:5
absl::StatusOr< Eigen::Vector2< T > > ProjectPoint(const Eigen::VectorX< T > &intrinsics, const Eigen::Vector3< T > &point) const
Definition: camera_models.h:1076
Definition: camera_models.h:596
static absl::StatusOr< Eigen::Vector3< T > > UnprojectPixel(const Eigen::VectorX< T > &intrinsics, const Eigen::Vector2< T > &pixel)
Definition: camera_models.h:674
static absl::StatusOr< Eigen::Vector2< T > > ProjectPoint(const Eigen::VectorX< T > &intrinsics, const Eigen::Vector3< T > &point)
Definition: camera_models.h:623
CameraIntrinsicsModel GetType() const final
Getter for camera model type.
Definition: camera_models.h:703
int NumberOfParameters() const final
Getter for the number of parameters for this camera model.
Definition: camera_models.h:707
Definition: camera_models.h:961
int NumberOfParameters() const final
Getter for the number of parameters for this camera model.
Definition: camera_models.h:1069
static absl::StatusOr< Eigen::Vector3< T > > UnprojectPixel(const Eigen::VectorX< T > &intrinsics, const Eigen::Vector2< T > &pixel)
Definition: camera_models.h:1036
CameraIntrinsicsModel GetType() const final
Getter for camera model type.
Definition: camera_models.h:1065
static absl::StatusOr< Eigen::Vector2< T > > ProjectPoint(const Eigen::VectorX< T > &intrinsics, const Eigen::Vector3< T > &point)
Definition: camera_models.h:985
Definition: camera_models.h:716
static absl::StatusOr< Eigen::Vector2< T > > ProjectPoint(const Eigen::VectorX< T > &intrinsics, const Eigen::Vector3< T > &point)
Definition: camera_models.h:740
CameraIntrinsicsModel GetType() const final
Getter for camera model type.
Definition: camera_models.h:834
int NumberOfParameters() const final
Getter for the number of parameters for this camera model.
Definition: camera_models.h:838
static absl::StatusOr< Eigen::Vector3< T > > UnprojectPixel(const Eigen::VectorX< T > &intrinsics, const Eigen::Vector2< T > &pixel)
Definition: camera_models.h:798
Definition: camera_models.h:395
CameraIntrinsicsModel GetType() const final
Getter for camera model type.
Definition: camera_models.h:583
int NumberOfParameters() const final
Getter for the number of parameters for this camera model.
Definition: camera_models.h:587
static absl::StatusOr< Eigen::Vector3< T > > UnprojectPixel(const Eigen::VectorX< T > &intrinsics, const Eigen::Vector2< T > &pixel, int max_iterations=100)
Definition: camera_models.h:479
static absl::StatusOr< Eigen::Vector2< T > > ProjectPoint(const Eigen::VectorX< T > &intrinsics, const Eigen::Vector3< T > &point)
Definition: camera_models.h:420
Definition: camera_models.h:79
static absl::StatusOr< Eigen::Vector3< T > > UnprojectPixel(const Eigen::VectorX< T > &intrinsics, const Eigen::Vector2< T > &pixel, int max_iterations=30)
Definition: camera_models.h:157
static absl::StatusOr< Eigen::Vector2< T > > ProjectPoint(const Eigen::VectorX< T > &intrinsics, const Eigen::Vector3< T > &point)
Definition: camera_models.h:105
CameraIntrinsicsModel GetType() const final
Getter for camera model type.
Definition: camera_models.h:216
int NumberOfParameters() const final
Getter for the number of parameters for this camera model.
Definition: camera_models.h:220
Definition: camera_models.h:231
static absl::StatusOr< Eigen::Vector2< T > > ProjectPoint(const Eigen::VectorX< T > &intrinsics, const Eigen::Vector3< T > &point)
Definition: camera_models.h:257
CameraIntrinsicsModel GetType() const final
Getter for camera model type.
Definition: camera_models.h:380
static absl::StatusOr< Eigen::Vector3< T > > UnprojectPixel(const Eigen::VectorX< T > &intrinsics, const Eigen::Vector2< T > &pixel, int max_iterations=30)
Definition: camera_models.h:314
int NumberOfParameters() const final
Getter for the number of parameters for this camera model.
Definition: camera_models.h:384
Definition: camera_models.h:848
CameraIntrinsicsModel GetType() const final
Getter for camera model type.
Definition: camera_models.h:948
int NumberOfParameters() const final
Getter for the number of parameters for this camera model.
Definition: camera_models.h:952
static absl::StatusOr< Eigen::Vector2< T > > ProjectPoint(const Eigen::VectorX< T > &intrinsics, const Eigen::Vector3< T > &point)
Definition: camera_models.h:872
static absl::StatusOr< Eigen::Vector3< T > > UnprojectPixel(const Eigen::VectorX< T > &intrinsics, const Eigen::Vector2< T > &pixel)
Definition: camera_models.h:920
Sensors namespace.
Definition: accelerometer.cpp:8
CameraIntrinsicsModel
Camera model types.
Definition: camera_models.h:16
@ kExtendedUnifiedCamera
Extended unified camera model.
@ kOpenCv5
5-parameter OpenCV model.
@ kKannalaBrandt
Kannala-Brandt model.
@ kUnifiedCamera
Unified camera model.
@ kOpenCv8
8-parameter OpenCV model.