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)

代价函数为:
在这里插入图片描述
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 normalizedif (sum < std::numeric_limits<Scalar>::epsilon()) {// 小于优化的值了 returnresidual.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 warpJw_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.mzph.cn/news/605242.shtml

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

相关文章

第九届中西部外语翻译大赛初赛阶段已经圆满结束

2023年第九届中西部外语翻译大赛&#xff08;以下简称竞赛&#xff09;&#xff0c;于2023年12月30日至31日举行。历届中西部外语翻译大赛均由中西部翻译协会共同体指导发起&#xff0c;各省市译协共建学术指导委员会&#xff0c;获奖证书盖章单位由四川省翻译协会、广西翻译协…

c# 学习笔记 - 集合(List)

文章目录 1.概论1.1 List 特性1.2 .NET API 2. 基本使用2.1 样例 3. 添加类操作3.1 Insert() 4. 删除类操作4.1 Remove()4.2 RemoveAt() 5. 查找类操作6. 排序类操作6.1 Sort(Comparsion<T>) 7. 其他类操作 1.概论 1.1 List 特性 可通过索引访问的强类型列表&#xff0c…

STM32学习笔记二十二:WS2812制作像素游戏屏-飞行射击游戏(12)总结

至此&#xff0c;飞行射击游戏已经基本实现该有的功能&#xff0c;已经比较接近早期的商业游戏了。 如果采用脚本&#xff0c;可以完成关卡游戏&#xff0c;如果不用&#xff0c;也可以做成无限挑战游戏。 我们汇总一下制作的过程&#xff1a; 1、建模UML 2、主循环处理过程…

设计模式篇章(1)——理论基础

设计模式&#xff1a;在软件开发中会面临许多不断重复发生的问题&#xff0c;这些问题可能是代码冗余、反复修改旧代码、重写以前的代码、在旧代码上不断堆新的代码&#xff08;俗称屎山&#xff09;等难以扩展、不好维护的问题。因此1990年有四位大佬&#xff08;GoF组合&…

3的幂00

题目链接 3的幂 题目描述 注意点 无 解答思路 不断除以3直到除数或余数为0为止&#xff0c;判断除完后的数字是否为1 代码 class Solution {public boolean isPowerOfThree(int n) {while (n / 3 ! 0) {if (n % 3 ! 0) {return false;}n n / 3;}return n 1;} }关键点 …

对称加密技术有哪些类型

对称加密是一种加密方式&#xff0c;其加密和解密所使用的密钥是相同的。这种方式的特点是速度快&#xff0c;效率高&#xff0c;适合用于大量数据的加密和解密。对称加密算法有很多种&#xff0c;常见的有AES、DES、3DES等。 对称加密的应用场景非常广泛&#xff0c;下面是一些…

Linux下配置静态ip地址

问题&#xff1a;虚拟机重启后ip地址动态更新&#xff0c;导致连shell十分麻烦 解决&#xff1a; 1. 进入配置文件 vi /etc/sysconfig/network-scripts/ifcfg-ens33 2.1 修改配置 BOOTPROTOstatic ONBOOTyes2.2 新增配置 #ip地址(自定义) IPADDR192.168.149.131 #子网掩码 …

2023年12月 C/C++(四级)真题解析#中国电子学会#全国青少年软件编程等级考试

C/C++编程(1~8级)全部真题・点这里 第1题:移动路线 桌子上有一个m行n列的方格矩阵,将每个方格用坐标表示,行坐标从下到上依次递增,列坐标从左至右依次递增,左下角方格的坐标为(1,1),则右上角方格的坐标为(m,n)。 小明是个调皮的孩子,一天他捉来一只蚂蚁,不小心把蚂蚁…

一款开源的MES系统

随着工业4.0的快速发展&#xff0c;制造执行系统&#xff08;MES&#xff09;成为了智能制造的核心。今天&#xff0c;将为大家推荐一款开源的MES系统——iMES工厂管家。 什么是iMES工厂管家 iMES工厂管家是一款专为中小型制造企业打造的开源MES系统。它具备高度的可定制性和灵…

[论文笔记] Qwen-7B tokenizer

https://github.com/QwenLM/Qwen/blob/main/tokenization_note_zh.md#%E6%99%AE%E9%80%9Atoken https://huggingface.co/Qwen/Qwen-7B 一、Qwen-7B 介绍 Qwen-7B采用UTF-8字节级别的BPE tokenization方式,并依赖tiktoken这一高效的软件包执行分词。 Qwen-7B中有两类token,即…

Selenium教程:级联选择+日期框+弹框,组件的示例练习

1.Cascader级联选择&#xff0c;通常指的是在多个层级或类别中进行选择&#xff0c;每个层级或类别的选择依赖于前一个层级或类别的选择结果。常用于省市区、公司级层、事务分类等。 网页元素结构 实现代码 # Author : 小红牛 # 微信公众号&#xff1a;WdPython from time i…

ROS-urdf集成gazebo

文章目录 一、URDF与Gazebo基本集成流程二、URDF集成Gazebo相关设置三、URDF集成Gazebo实操四、Gazebo仿真环境搭建 一、URDF与Gazebo基本集成流程 1.创建功能包 创建新功能包&#xff0c;导入依赖包: urdf、xacro、gazebo_ros、gazebo_ros_control、gazebo_plugins 2.编写URD…

HarmonyOS状态管理概述

状态管理概述 在前文的描述中&#xff0c;我们构建的页面多为静态界面。如果希望构建一个动态的、有交互的界面&#xff0c;就需要引入“状态”的概念。 图1 效果图 上面的示例中&#xff0c;用户与应用程序的交互触发了文本状态变更&#xff0c;状态变更引起了UI渲染&…

C#实现个人账本管理系统

git地址&#xff1a;https://gitee.com/myshort-term/personal-ledger-management-system 1.系统简介 LedgerManagementSystem是一个小型的个人账本管理系统&#xff0c;可对收支项目进行增加、删除、修改、查询以及导入和导出。可对每日的各类收支项目进行汇总并查看和修改收…

STM32 基础知识(探索者开发板)--146讲 IIC

IIC特点&#xff1a; 同步串行半双工通信总线 IIC有一个弱上拉电阻&#xff0c;在主机和从机都没有传输数据下拉时&#xff0c;总线会自动上拉 SCL在低电平期间&#xff0c;改变SDA的值来上传数据&#xff0c;方便SCL电平上升时进行数据读取 SCL在高电平期间&#xff0c;不能…

【蓝桥杯软件赛 零基础备赛20周】第7周——二叉树

文章目录 1 二叉树概念2 二叉树的存储和编码2.1 二叉树的存储方法2.2 二叉树存储的编码实现2.3 二叉树的极简存储方法 3 例题4 习题 前面介绍的数据结构数组、队列、栈&#xff0c;都是线性的&#xff0c;它们存储数据的方式是把相同类型的数据按顺序一个接一个串在一起。简单的…

【大数据进阶第三阶段之Datax学习笔记】阿里云开源离线同步工具Datax快速入门

【大数据进阶第三阶段之Datax学习笔记】阿里云开源离线同步工具Datax概述 【大数据进阶第三阶段之Datax学习笔记】阿里云开源离线同步工具Datax快速入门 【大数据进阶第三阶段之Datax学习笔记】阿里云开源离线同步工具Datax类图 【大数据进阶第三阶段之Datax学习笔记】使用…

看直播怎么录屏?精选工具助你轻松录制!

随着网络直播平台的兴起&#xff0c;观看直播已经成为人们日常生活的一部分。然而&#xff0c;有时我们可能想要保存直播内容以便日后回顾或分享。可是看直播怎么录屏呢&#xff1f;在本文中&#xff0c;我们将介绍两种录制直播的方法&#xff0c;通过这些步骤&#xff0c;你可…

Verilog学习记录

目录 一、Verilog简介 &#xff08;一&#xff09;Verilog 的主要特性 &#xff08;二&#xff09;Verilog的主要应用 &#xff08;三&#xff09;Verilog设计方法 二、Verilog基础语法 &#xff08;一&#xff09;标识符和关键字 &#xff08;二&#xff09;Verilog数据…