Basal前端梳理

news2024/11/15 11:12:06

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)

代价函数为:
在这里插入图片描述
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);
    }
  }

得到三层拼接得到的图像金字塔
在这里插入图片描述

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

双线性插值:
在这里插入图片描述
当有一点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点的图像梯度的时候对利用上下插值出的点求取出其图像梯度,再利用其上下插值出的计算竖直方向的梯度。
在这里插入图片描述

另一部分 ∂ 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

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

降阈值的思路可以参考
在这里插入图片描述

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

点的描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1365186.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

new和delete表达式的工作步骤

new表达式工作步骤 调用一个operator new库函数开辟未类型化的空间 void *operator new(size_t); 在为类型化的空间上调用构造函数&#xff0c;初始化对象的成员 返回相应类型的指针 delete表达式工作步骤 调用相应类型的析构函数,但析构函数并不能删除对象所在的空间&…

【linux学习笔记】网络

目录 【linux学习笔记】网络检查、监测网络ping-向网络主机发送特殊数据包traceroute-跟踪网络数据包的传输路径netstat-检查网络设置及相关统计数据 【linux学习笔记】网络 检查、监测网络 ping-向网络主机发送特殊数据包 最基本的网络连接命令就是ping命令。ping命令会向指…

062:vue中将一维数组变换为三维数组(图文示例)

第062个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…

Leetcode2487. 从链表中移除节点

Every day a Leetcode 题目来源&#xff1a;2487. 从链表中移除节点 解法1&#xff1a;单调栈 遍历链表&#xff0c;建立一个单调栈&#xff08;单调递减&#xff09;。 再用头插法将单调栈的元素建立一个新的链表&#xff0c;返回该链表的头结点。 代码&#xff1a; /*…

计算机基础面试题 |16.精选计算机基础面试题

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

how2heap-2.23-11-poison_null_byte

什么是poison_null_byte 当然不止这一种&#xff0c;下面最简单的形式 #include <malloc.h> int main() {char * a malloc(0x200);char * b malloc(0x200);size_t real_size malloc_usable_size(a);a[real_size] 0;return 0; }影响&#xff1a; chunk a&#xff0…

【Hadoop】说下HDFS读文件和写文件的底层原理?

文件读取文件的写入 文件读取 客户端调用 FileSystem 对象的 open&#xff08;&#xff09;函数&#xff0c;打开想要读取的文件。其中FileSystem 是 DistributeFileSystem 的一个实例&#xff1b;DistributedFileSystem 通过使用 RPC&#xff08;远程过程调用&#xff09; 访N…

Large Language Models Paper 分享

论文1&#xff1a; ChatGPTs One-year Anniversary: Are Open-Source Large Language Models Catching up? 简介 2022年11月&#xff0c;OpenAI发布了ChatGPT&#xff0c;这一事件在AI社区甚至全世界引起了轰动。首次&#xff0c;一个基于应用的AI聊天机器人能够提供有帮助、…

vue3组件传参

1、props: 2、自定义事件子传父 3、mitt任意组件通讯 4、v-model通讯(v-model绑定在组件上) (1)V2中父子组件的v-model通信&#xff0c;限制了popos接收的属性名必须为value和emit触发的事件名必须为input,所以有时会有冲突; 父组件: 子组件: (2)V3中:限制了popos接收的属性名…

读元宇宙改变一切笔记02_元素(上)

1. 很多组织和机构都想在元宇宙的定义上掌握话语权&#xff0c;使得它的定义中存在矛盾之处&#xff0c;也有大量含义混淆之处 1.1. 微软 1.1.1. 在谈论“多个元宇宙” 1.1.2. 微软首席执行官萨提亚纳德拉将元宇宙描述为一种可以将“整个…

Dockerfile的ENV

文章目录 环境总结测试测试1测试2测试3测试4测试5测试6 参考 环境 RHEL 9.3Docker Community 24.0.7 总结 如果懒得看测试的详细信息&#xff0c;可以直接看结果&#xff1a; 一条 ENV 指令可以定义多个环境变量。Dockerfile里可以包含多条 ENV 指令。环境变量的值不需要用…

(低级错误)IDEA/Goland报错连接数据库失败:URL错误和权限问题。

前言 做毕设ing&#xff0c;使用Goland自带的数据库工具连接服务器的数据库。报错 错误: Malformed database URL, failed to parse the main URL sections. (view)服务器是华为云&#xff0c;使用宝塔面板。数据库版本5.6.50。 排查过程 鉴于Goland报错报的狗屁不是&#…

H266/VVC率失真优化与速率控制概述

率失真优化技术 率失真优化&#xff1a; 视频编码的主要目的是在保证一定视频质量的条件下尽量降低视频的编码比特率&#xff0c;或者在一定编码比特率限制条件下尽量地减小编码失真。在固定的编码框架下&#xff0c;为了应对不同的视频内容&#xff0c;往往有多种候选的编码方…

YOLOv5改进 | 损失函数篇 | EIoU、SIoU、WIoU、DIoU、FocusIoU等二十余种损失函数

一、本文介绍 这篇文章介绍了YOLOv5的重大改进,特别是在损失函数方面的创新。它不仅包括了多种IoU损失函数的改进和变体,如SIoU、WIoU、GIoU、DIoU、EIOU、CIoU,还融合了“Focus”思想,创造了一系列新的损失函数。这些组合形式的损失函数超过了二十余种,每种都针对特定的…

万界星空科技商业开源MES主要功能介绍

一、系统概述&#xff1a; MES制造执行系统&#xff0c;其定位于制造执行系统的Java开源版本。系统包括系统管理&#xff0c;车间基础数据建模&#xff0c;计划管理&#xff0c;物料控制&#xff0c;生产执行&#xff0c;质量管理&#xff0c;库存管理&#xff0c;看板管理&am…

System学习笔记 - MacOs编译环境配置(一)

前言 好几年没有记录过东西&#xff0c;一是确实很忙&#xff0c;二是人也变懒了。新年开个新的学习计划&#xff0c;希望能坚持下去。 SystemC 简介 SystemC是一个建模语言&#xff0c;其本质是一个C的库&#xff0c;一般用于SoC建模&#xff0c;具体介绍不赘述&#xff0…

GPDB - 高可用 - 流复制状态

GPDB - 高可用 - 流复制状态 GPDB的高可用基于流复制&#xff0c;通过FTS进行自动故障切换。自动故障切换需要根据primary-mirror流复制的各种状态进行判断。本节就聊聊primary-mirror流复制的各种状态。同样适用于PgSQL 1、WalSndState typedef enum WalSndState {WALSNDSTATE…

为什么 Kafka 这么快?它是如何工作的?

随着数据以指数级的速度流入企业&#xff0c;强大且高性能的消息传递系统至关重要。Apache Kafka 因其速度和可扩展性而成为热门选择&#xff0c;但究竟是什么让它如此之快&#xff1f; 在本期中&#xff0c;我们将探讨&#xff1a; Kafka 的架构及其核心组件&#xff0c;如生…

如何编写高效的正则表达式?

正则表达式&#xff08;Regular Expression&#xff0c;简称regex&#xff09;是一种强大的文本处理技术&#xff0c;广泛应用于各种编程语言和工具中。本文将从多个方面介绍正则表达式的原理、应用和实践&#xff0c;帮助你掌握这一关键技术。 正则可视化 | 一个覆盖广泛主题…

嵌入式(六)模数转换ADC | ADC 工作模式 寄存器 轮询和中断方式

文章目录 1 CC2530的ADC模块2 ADC工作模式3 ADC相关寄存器3.1数据寄存器3.2 控制寄存器 4 ADC初始化配置5 ADC使用方式5.1 轮询方式5.2 中断方式 模拟/数字转换 (Analog to Digital Converter&#xff0c;简称ADC) 是将输入的模拟信号转换为数字信号。 各种被测控的物理量&…