当前位置:   article > 正文

牛顿迭代法c++代码_事件相机的光流计算-代码

事件相机光流 distance surface

之前在文章中提到,会用c++实现里面的光流估计的核心算法Algorithm 1

huhupy:事件相机的光流计算​zhuanlan.zhihu.com
4028690efa15bbe4a26197200423bc9d.png

这里面的主要的问题是从时空的局部估计一个平面的表达式,算法1使用的是类似于Ransac的求解方式,不同的地方在于Ransac方法是从数据集合中随机抽取3点(三点确定平面)来估计平面参数,然后在所有数据上对参数进行评估,统计inlier的数目,重复进行多次迭代选择最好的参数。理论上只要是不超过50%的outlier都能使用这样的方法估计出参数。而算法1采用的方法是先用全部点集用最小二乘去求解平面参数,然后再利用参数进行outlier的筛选,然后用剩余的inlier集合估计平面参数,重复进行以上过程直至参数收敛。可以明显的发现算法1的性能受outlier数目的影响较大,试想第一次如果把一些真正的inlier过滤而outlier保留的话,算法很有可能最后并不能收敛。好处是这样的算法在outlier数目不多的情况能够快速收敛,但我认为Ransac方法也能达到同样的效率甚至更快(因为只用3个点估计平面参数,ps. 大家知道怎么由三个点计算平面参数吗[狗头])。

C++的代码实现如下

  1. #pragma once
  2. #include <array>
  3. #include <vector>
  4. #include <ceres/ceres.h>
  5. struct Event{int x, y; double timestamp;};
  6. struct PlaneFit {
  7. PlaneFit(double xx, double yy, double zz) : x(xx), y(yy), z(zz) {}
  8. template <typename T> bool operator()(const T *const abc, T *residual) const {
  9. residual[0] = T(z) - abc[0] * T(x) - abc[1] * T(y) - abc[2];
  10. return true;
  11. }
  12. double x, y, z;
  13. };
  14. namespace event {
  15. /**
  16. *
  17. * Algorithm is based on the method presented in “Event-Based Visual Flow,”
  18. * ref{Algorithm 1}
  19. *
  20. * It uses the local properties of events' spatiotemporal neighborhood to estimate
  21. * a plane parameter.
  22. *
  23. * */
  24. class LocalPlanesFlow {
  25. private:
  26. // half of the search window
  27. const int L = 1;
  28. // threshold of updated plane parameter magnitude difference to the last estimate
  29. const float th1 = 1e-5f;
  30. // when an event in the spatiotemporal is far away from the estimated plane,
  31. // it will be discarded
  32. const float th2 = 5e-2f;
  33. // Events must occur in this time interval before curernt event to be
  34. // considered. */
  35. const double max_dt_threshold = 1e-3;
  36. // {a, b, c} && z = a * x + b * y + c
  37. double old_estimate[3], new_estimate[3];
  38. // Map of input event times, surface of events(SAE)
  39. double *sae0, *sae1, *sae;
  40. // image resolution
  41. int w, h;
  42. // current event location on the image plane
  43. int cur_x, cur_y;
  44. // current event timestamp
  45. double cur_t;
  46. double difference() {
  47. double sum = 0.;
  48. for (int i = 0; i < 3; ++i)
  49. sum += (old_estimate[i] - new_estimate[i]) * (old_estimate[i] - new_estimate[i]);
  50. return std::sqrt(sum);
  51. }
  52. double distance(const std::array<double, 3> &point, double *plane){
  53. double e = point[2] - plane[0] * point[0] - plane[1] * point[1] - plane[2];
  54. return std::abs(e) / std::sqrt(plane[0] * plane[0] + plane[1] * plane[1] + 1);
  55. }
  56. void neighborhood(std::vector<std::array<double, 3>> &neighbors) {
  57. neighbors.clear();
  58. for (int i = -L; i <= L; ++i) {
  59. for (int j = -L; j <= L; ++j) {
  60. double t = sae[cur_x + i + (cur_y + j) * w];
  61. if (std::abs(t - cur_t) < max_dt_threshold) {
  62. neighbors.push_back({(double)cur_x + i, (double)cur_y + j, t});
  63. }
  64. }
  65. }
  66. }
  67. void estimateParameter(const std::vector<std::array<double, 3>> &neighbors, double *parameter) {
  68. ceres::Problem problem;
  69. for (const auto &data : neighbors)
  70. problem.AddResidualBlock(new ceres::AutoDiffCostFunction<PlaneFit, 1, 3>(
  71. new PlaneFit(data[0], data[1], data[2])),
  72. nullptr, parameter);
  73. ceres::Solver::Options options;
  74. options.linear_solver_type = ceres::DENSE_QR;
  75. options.minimizer_progress_to_stdout = true;
  76. ceres::Solver::Summary summary;
  77. ceres::Solve(options, &problem, &summary);
  78. }
  79. public:
  80. LocalPlanesFlow(int ww, int hh) : w(ww), h(hh) {
  81. sae0 = new double[w * h];
  82. sae1 = new double[w * h];
  83. memset(sae0, 0, w * h * sizeof(double));
  84. memset(sae1, 0, w * h * sizeof(double));
  85. }
  86. ~LocalPlanesFlow() {
  87. delete sae0;
  88. delete sae1;
  89. }
  90. std::array<double, 2> opticalVelocity(const Event &curr_event) {
  91. sae = curr_event.polarity ? sae1 : sae0;
  92. cur_x = curr_event.x;
  93. cur_y = curr_event.y;
  94. cur_t = curr_event.timestamp;
  95. sae[cur_x + cur_y * w] = cur_t;
  96. // skip boundary pixels
  97. if (cur_x < L || cur_y < L || cur_x > w - L - 1 || cur_y > h - L - 1)
  98. return {0., 0.};
  99. std::vector<std::array<double, 3>> neighbors;
  100. neighborhood(neighbors);
  101. // too less pixel to estimate the plane
  102. if (neighbors.size() < 3) {
  103. return {0., 0.};
  104. }
  105. estimateParameter(neighbors, new_estimate);
  106. do {
  107. memcpy(old_estimate, new_estimate, 3 * sizeof(double));
  108. decltype(neighbors) new_neighbors;
  109. for (const auto &data : neighbors) {
  110. if(distance(data, old_estimate) < th2){
  111. new_neighbors.push_back(data);
  112. }
  113. }
  114. // if(new_neighbors.size() == new_neighbors.size())
  115. // break;
  116. estimateParameter(new_neighbors, new_estimate);
  117. neighbors = new_neighbors;
  118. } while (difference() > th1);
  119. // since we get the optimal plane parameter
  120. // we have that
  121. // $frac{partial z}{partial x} = a, frac{partial z}{partial y} = b$
  122. return {1. / new_estimate[0], 1. / new_estimate[0]};
  123. }
  124. };
  125. } // namespace event

使用需要安装好ceres求解器,大家也可以直接使用Eigen求解这个线性最小二乘问题(

),没有必要用迭代法去解。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/368329
推荐阅读
相关标签
  

闽ICP备14008679号