Basal前端梳理

这篇具有很好参考价值的文章主要介绍了Basal前端梳理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Basalt前端逻辑梳理

TBB安装参考
https://zhuanlan.zhihu.com/p/480823197
代码注释参考
https://blog.csdn.net/qq_39266065/article/details/106175701#t7
光流追踪参考
https://blog.csdn.net/weixin_41738773/article/details/130282527

VI Odometry

KLT tracking

原理

为了实现快速、鲁棒和准确的光流追踪,为了强度尺度不变性,将逆合成方法和强度缩放不变的patch dissimilarity 范数相结合。一些作者建议对光照不变光流使用零均值归一化互相关(ZNCC),但我们使用[21]中定义的局部缩放平方差和(LSSD),其计算成本少于备选方案

vio的视觉前端我们采用基于像素块的inverse compositional光流方法, 并且采用**locally-scaled sum of squared differences (LSSD)**作为衡量像素块光度一致性的误差计算方法。

这里把像素块跟踪问题建模为求解图像 I t I_t It I t + 1 I_{t+1} It+1上对应像素块的2维仿射矩阵 T ∈ S E ( 2 ) T\in SE(2) TSE(2)

代价函数为:
Basal前端梳理,前端
I t ( x ) I_t(x) It(x)表示像素x处的强度值,$\Omega 表示图像区域 , 表示图像区域, 表示图像区域,\overline{I_{t}} 表示区域 表示区域 表示区域\Omega $内的平均像素强度值。

为了剔除错误的像素匹配,这里采用了交叉跟踪 I t ⟺ I t + 1 I_t\Longleftrightarrow I_{t+1} ItIt+1的方法。原文就说不想用阈值,而是用这种双向检验的方式来剔除外点。

processFrame()→addPoint()

每个50size的cell提取一个特征点

输入图像金字塔构建

金字塔构建+图像下采样

  inline void setFromImage(const ManagedImage<T>& other, size_t num_levels) {
    orig_w = other.w;
    image.Reinitialise(other.w + other.w / 2, other.h);
    image.Fill(0);
    lvl_internal(0).CopyFrom(other);

    for (size_t i = 0; i < num_levels; i++) {
      const Image<const T> l = lvl(i);
      Image<T> lp1 = lvl_internal(i + 1);
      subsample(l, lp1);
    }
  }

得到三层拼接得到的图像金字塔
Basal前端梳理,前端

插值取图像里的像素灰度值

双线性插值:
Basal前端梳理,前端
当有一点M位于内部时利用线性插值

E = d y 1 ( b − a ) + a = d y ∗ b + d d y ∗ a F = d y 1 ( d − c ) + c = d y ∗ c + d d y ∗ d M = d x 1 ( F − E ) + E = d x ∗ d y ∗ c + d x ∗ d d y ∗ d + d d x ∗ d y ∗ b + d d x ∗ d d y ∗ a E=\frac{dy}{1}(b-a)+a=dy*b+ddy*a\\ F=\frac{dy}{1}(d-c)+c=dy*c+ddy*d \\ M=\frac{dx}{1}(F-E)+E=dx*dy*c+dx*ddy*d+ddx*dy*b+ddx*ddy*a E=1dy(ba)+a=dyb+ddyaF=1dy(dc)+c=dyc+ddydM=1dx(FE)+E=dxdyc+dxddyd+ddxdyb+ddxddya

template <typename S>
  inline S interp(S x, S y) const {
    static_assert(std::is_floating_point_v<S>,
                  "interpolation / gradient only makes sense "
                  "for floating point result type");

    BASALT_BOUNDS_ASSERT(InBounds(x, y, 0));
    // 下采样后的(int)
    int ix = x;
    int iy = y;
    S dx = x - ix;// 小数的部分
    S dy = y - iy;
    S ddx = S(1.0) - dx;// 负的小数字部分
    S ddy = S(1.0) - dy;
    // 双线性插值
    return ddx * ddy * (*this)(ix, iy) + ddx * dy * (*this)(ix, iy + 1) +
           dx * ddy * (*this)(ix + 1, iy) + dx * dy * (*this)(ix + 1, iy + 1);
  }

光度残差的计算
inline bool residual(const Image<const uint16_t> &img,
                       const Matrix2P &transformed_pattern,
                       VectorP &residual) const {
    Scalar sum = 0;
    int num_valid_points = 0;
    // 对pattern的每一个数据进行计算 这里还没有做差,只是求取了每个pattern在像素处的值
    for (int i = 0; i < PATTERN_SIZE; i++) {
      if (img.InBounds(transformed_pattern.col(i), 2)) {// 在图像边界里面
        residual[i] = img.interp<Scalar>(transformed_pattern.col(i));
        sum += residual[i];// 求总和值
        num_valid_points++;
      } else {
        residual[i] = -1;// 不存在图像的就是-1
      }
    }
    // all-black patch cannot be normalized
    if (sum < std::numeric_limits<Scalar>::epsilon()) {// 小于优化的值了 return
      residual.setZero();
      return false;
    }
    int num_residuals = 0;
    // 对于pattern的每个点进行计算
    for (int i = 0; i < PATTERN_SIZE; i++) {
      if (residual[i] >= 0 && data[i] >= 0) {// 有数的
        const Scalar val = residual[i];// 这地方相当于做类型转换
        residual[i] = num_valid_points * val / sum - data[i];// 归一化后再相减
        num_residuals++;
      } else {
        residual[i] = 0;
      }
    }
    return num_residuals > PATTERN_SIZE / 2;// 超过一半的值才是符合的
  }

雅可比计算(对应pattern原图位置中的雅可比)

目的:减小计算量,反向光流是在上一帧上计算,因此只需要计算一遍

理论:

对应单个像素的光流$\frac{\partial I}{\partial se2}=\frac{\partial I}{\partial p}*\frac{\partial p}{\partial se2}$

其中 ∂ I ∂ p \frac{\partial I}{\partial p} pI这部分是在特征点处的图像梯度,图像是离散表达,因此实际上是采用定义计算的, f ′ ( x ) = f ( x + Δ x − f ( x ) ) Δ x f'(x)=\frac{f(x+\Delta x - f(x))}{\Delta x} f(x)=Δxf(x+Δxf(x)),简单来说,取相邻像素差作为图像梯度。但是为了保证精度,Basalt做了线性插值。如图所示,求T点的图像梯度的时候对利用上下插值出的点求取出其图像梯度,再利用其上下插值出的计算竖直方向的梯度。
Basal前端梳理,前端

另一部分 ∂ p ∂ s e 2 \frac{\partial p}{\partial se2} se2p是像素位移对特征点在图像坐标系中位姿se2的导数,对于第i个pattern在图像坐标系下的位置 p = p o s + s e 2 ∗ p a t t e r n p=pos+se2*pattern p=pos+se2pattern其中se(2)由so(2)和一个二维位置组成 [ c o s θ − s i n θ s i n θ c o s θ ] \begin{bmatrix} cos\theta&-sin\theta\\ sin\theta&cos\theta \end{bmatrix} [cosθsinθsinθcosθ]

p对pos的导数是单位阵,对s e 2 se2se2中的旋转求导数需要稍微推倒一下对矩阵中的θ求导为 [ − s i n θ − c o s θ c o s θ − s i n θ ] \begin{bmatrix} -sin\theta&-cos\theta\\ cos\theta&-sin\theta \end{bmatrix} [sinθcosθcosθsinθ],可以看到其正好与原矩阵是反过来的。(哪反过来了?)

图像梯度 ∂ I ∂ p \frac{\partial I}{\partial p} pI计算

template <typename S>
  inline Eigen::Matrix<S, 3, 1> interpGrad(S x, S y) const {
  ......

    Eigen::Matrix<S, 3, 1> res;
    const T& px0y0 = (*this)(ix, iy);
    const T& px1y0 = (*this)(ix + 1, iy);
    const T& px0y1 = (*this)(ix, iy + 1);
    const T& px1y1 = (*this)(ix + 1, iy + 1);
    // 插值的像素  
    res[0] = ddx * ddy * px0y0 + ddx * dy * px0y1 + dx * ddy * px1y0 +
             dx * dy * px1y1;

    const T& pxm1y0 = (*this)(ix - 1, iy);
    const T& pxm1y1 = (*this)(ix - 1, iy + 1);

    S res_mx = ddx * ddy * pxm1y0 + ddx * dy * pxm1y1 + dx * ddy * px0y0 +
               dx * dy * px0y1;

    const T& px2y0 = (*this)(ix + 2, iy);
    const T& px2y1 = (*this)(ix + 2, iy + 1);

    S res_px = ddx * ddy * px1y0 + ddx * dy * px1y1 + dx * ddy * px2y0 +
               dx * dy * px2y1;
    // x 方向梯度
    res[1] = S(0.5) * (res_px - res_mx);

    const T& px0ym1 = (*this)(ix, iy - 1);
    const T& px1ym1 = (*this)(ix + 1, iy - 1);

    S res_my = ddx * ddy * px0ym1 + ddx * dy * px0y0 + dx * ddy * px1ym1 +
               dx * dy * px1y0;

    const T& px0y2 = (*this)(ix, iy + 2);
    const T& px1y2 = (*this)(ix + 1, iy + 2);

    S res_py = ddx * ddy * px0y1 + ddx * dy * px0y2 + dx * ddy * px1y1 +
               dx * dy * px1y2;
    // y 方向梯度
    res[2] = S(0.5) * (res_py - res_my);

    return res;
  }

se2及整体梯度

template <typename ImgT>
  static void setDataJacSe2(const ImgT &img, const Vector2 &pos, Scalar &mean,
                            VectorP &data, MatrixP3 &J_se2) {
  ......
    Jw_se2.template topLeftCorner<2, 2>().setIdentity();
    // 对于每个pattern内部的点进行计算
    for (int i = 0; i < PATTERN_SIZE; i++) {
      Vector2 p = pos + pattern2.col(i);// 位于图像的位置

      // Fill jacobians with respect to SE2 warp
      Jw_se2(0, 2) = -pattern2(1, i);
      Jw_se2(1, 2) = pattern2(0, i);

      if (img.InBounds(p, 2)) {
        Vector3 valGrad = img.template interpGrad<Scalar>(p);
        J_se2.row(i) = valGrad.template tail<2>().transpose() * Jw_se2;// 链式法则
        grad_sum_se2 += J_se2.row(i);
        num_valid_points++;
      } else {
        data[i] = -1;
      }
    }

detectKeyPoints

只对左目提点,右目的特征点靠前后帧追踪和左右目光流。

降阈值的思路可以参考
Basal前端梳理,前端

trackPoint

inline bool trackPoint(const basalt::ManagedImagePyr<uint16_t> &old_pyr,

const basalt::ManagedImagePyr<uint16_t> &pyr,

const Eigen::AffineCompact2f &old_transform,

Eigen::AffineCompact2f &transform) const

点的描述
Basal前端梳理,前端文章来源地址https://www.toymoban.com/news/detail-781932.html

到了这里,关于Basal前端梳理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【前端必备技能java之若依框架认证(登录注册)模块梳理】

    必备技能java系列梳理的文章并不涉及造轮子,以若依框架为基础,分析微服务Spring Cloud的能力,并理清微服务在业务处理上搭建的应用层架构,不会追问技术实践的底层细节,目标是可以让有后端经验的非java相关的程序员可以使用Spring Cloud搭建属于自己的后端服务 上面的结

    2024年03月12日
    浏览(46)
  • 【WEB前端进阶之路】 HTML 全路线学习知识点梳理(中)

    本文是HTML零基础学习系列的第二篇文章,点此阅读 上一篇文章。 标题是通过 h1 - h6 标签进行定义的。 h1 定义最大的标题。 h6 定义最小的标题。浏览器会自动地在标题的前后添加空行,例如: 标题用来正确的显示文章结构 ,通过不同的标题可以为文章建立索引,所以,标题

    2024年02月02日
    浏览(47)
  • web服务和前端交互相关的上中游业务技术知识点梳理

    可能之前在学校里面做的很多东西是纯后端的,不会涉及到太多和前端交互的细节,很多新手对前后端交互以及上中游业务链路的整体流程不够清晰,做一些javaWeb项目可以让我们有机会对其进行更深入的研究,最近总结了一下相关技术知识点并结合自己的实践经验来和大家分

    2024年02月21日
    浏览(44)
  • mysql知识点梳理

    1.检查是否走了索引,如果没有则优化SQL利用索引 2.检查所利用的索引,是否是最优索引 3.检查所查字段是否都是必须的,是否查询了过多字段,查出了多余数据;查询太多的字段会回表 4.检查表中数据是否过多,是否应该进行分库分表了 5.检查数据库实例所在机器的性能配

    2024年04月12日
    浏览(34)
  • JAVA知识点梳理

    boo=false; //每一轮排序都会将最大的一个排到最后 所以-i 2.第一次不同如果是数值,返回长度差 第一次不同如果是字符,返回两个字符的Ascrll码的差值

    2024年02月12日
    浏览(46)
  • TC8测试梳理

    目录 一、概述 1.1 测试内容 二、TCP/IP协议测试 2.1 准备 2.2 ARP 2.2.1 接线拓扑 2.2.2 测试内容 2.3 ICMPv4 2.3.1 接线拓扑 2.3.2 测试内容 2.4 IPv4 2.4.1 接线拓扑 2.4.2 测试内容 2.5 IPv4链路本地地址动态配置 2.5.1 接线拓扑 2.5.2 测试内容 2.6 DHCP 2.6.1 接线拓扑 2.6.2 测试范围 2.7 UDP 2.7.1 接线拓

    2024年02月12日
    浏览(35)
  • 运维流程梳理

    在这里只是结合个人的经验,提供另外一个看待运维的角度。 1. 梳理流程目的(为什么) 解决当前运维过程中遇到问题 将平常在做的事,抽象成部分可识别的框架。 通过实践验证更新流程,规范工作,提高工作效率 流程可追溯有记录,能够形成有用的文档。 2. 运维涉及流

    2023年04月22日
    浏览(40)
  • webpack 相关知识梳理

    在 package.json 里面有一个scripts属性,这个数据是配置执行脚本的

    2024年02月08日
    浏览(39)
  • NVIDIA GPU 架构梳理

    文中图片大部分来自NVIDIA 产品白皮书 TODO:英伟达显卡型号梳理 目录 : 一、NVIDIA GPU的架构演变历史 二、Tesla 架构 三、Fermi架构 四、Kepler架构 五、Maxwell架构 六、Pascal架构 七、Volta架构 八、Turing架构 九、Ampere架构 十、Hopper架构 截止2021年,发布时间离我们最近的8种NVIDIA

    2024年02月02日
    浏览(64)
  • java基础知识梳理

    虽然已经在实际工作中与java打交道5年之多,但是一直没系统地对java这门语言进行梳理和总结,掌握的知识也比较零散。恰好利用这段时间重新认识下java,并对一些常见的语法和知识点做个总结与回顾,一方面为了加深印象,方便后面查阅,一方面为了学好java打下基础。拉

    2024年02月04日
    浏览(65)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包