slam14讲(第8讲、前端里程计)LK光流、直接法

直接法的引出

因为第7讲大部分都是讲特征点法,通过提取orb特征点和点的描述子,来构建两帧图像之间的特征点对应关系。这种方法会有缺点:

  1. 关键点和描述子提取计算耗时,如果相机的频率高,则slam算法大部分耗时被占。
  2. 特征点是稀疏的,往往只有几百个,而且在我看来容易被一些动态物体干扰,就会对后续匹配造成影响。
  3. 在一些特征不明显的地方,大白墙或者没有明显角点的地方,特征点难提取,对后续计算相机运动造成影响。

光流法

光流可以分成稀疏光流和稠密光流。Lucas-Kanade光流跟踪部分像素点的运动成为稀疏光流,Horn-Schunck光流跟踪所有像素点的运动成为稠密光流。而书中主要是介绍了LK稀疏光流法。

个人见解

光流法的作用就是用来跟踪上一帧图片的像素点在当前帧图片中的位置,本质上也是特征匹配的一种方法,只是特征点法的特征匹配是通过点的描述子来进行匹配,而LK光流从图像入手,在灰度不变假设上,根据两帧图像求出跟踪像素点的运动速度,从而在第二帧图片中找到跟踪点的像素。后续就是简单列一下书里的公式。

公式推导

将图片看成是关于时间(t)和像素(u,v)的函数,即三元函数。根据灰度不变假设,对于t时刻在第一帧图片(x,y)的像素,根据灰度不变:
I ( x + d x , y + d y , t + d t ) = I ( x , y , t ) \boldsymbol{I}(x+\mathrm{d} x, y+\mathrm{d} y, t+\mathrm{d} t)=\boldsymbol{I}(x, y, t) I(x+dx,y+dy,t+dt)=I(x,y,t)
然后就是经典的一阶泰勒展开:
I ( x + d x , y + d y , t + d t ) ≈ I ( x , y , t ) + ∂ I ∂ x   d x + ∂ I ∂ y   d y + ∂ I ∂ t   d t \boldsymbol{I}(x+\mathrm{d} x, y+\mathrm{d} y, t+\mathrm{d} t) \approx \boldsymbol{I}(x, y, t)+\frac{\partial \boldsymbol{I}}{\partial x} \mathrm{~d} x+\frac{\partial \boldsymbol{I}}{\partial y} \mathrm{~d} y+\frac{\partial \boldsymbol{I}}{\partial t} \mathrm{~d} t I(x+dx,y+dy,t+dt)I(x,y,t)+xI dx+yI dy+tI dt
因为灰度不变,所以只剩三项:
∂ I ∂ x   d x + ∂ I ∂ y   d y + ∂ I ∂ t   d t = 0 \frac{\partial \boldsymbol{I}}{\partial x} \mathrm{~d} x+\frac{\partial \boldsymbol{I}}{\partial y} \mathrm{~d} y+\frac{\partial \boldsymbol{I}}{\partial t} \mathrm{~d} t=0 xI dx+yI dy+tI dt=0
然后就是除dt移项
∂ I ∂ x d x   d t + ∂ I ∂ y d y   d t = − ∂ I ∂ t \frac{\partial \boldsymbol{I}}{\partial x} \frac{\mathrm{d} x}{\mathrm{~d} t}+\frac{\partial \boldsymbol{I}}{\partial y} \frac{\mathrm{d} y}{\mathrm{~d} t}=-\frac{\partial \boldsymbol{I}}{\partial t} xI dtdx+yI dtdy=tI
对于上面这个式子, d x / d t dx/dt dx/dt d y / d t dy/dt dy/dt就是我们要求的像素点的运动速度,记为u和v。其他项就是图像关于x,y,t的梯度,通过opencv或者自己去计算都可以求。最终就有
[ I x I y ] [ u v ] = − I t \left[\begin{array}{ll}\boldsymbol{I}_x & \boldsymbol{I}_y\end{array}\right]\left[\begin{array}{l}u \\ v\end{array}\right]=-\boldsymbol{I}_t [IxIy][uv]=It
但是只有一个像素点只有一个方程,所以一般是假设在点附近的一个窗口具有同样的运动,所以取一个w*w的窗口,就有 w 2 w^2 w2个方程
[ I x I y ] k [ u v ] = − I t k , k = 1 , … , w 2 \left[\begin{array}{ll}\boldsymbol{I}_x & \boldsymbol{I}_y\end{array}\right]_k\left[\begin{array}{l}u \\ v\end{array}\right]=-\boldsymbol{I}_{t k}, \quad k=1, \ldots, w^2 [IxIy]k[uv]=Itk,k=1,,w2
这个就是用经典最小二乘法即可求出u和v,因为两帧图像的时间差是已知的,所以就可以在当前帧得到上一帧的对应点位置。

代码实现

书中除了直接用opencv去实现光流,同样通过高斯牛顿手写了一个光流法。本质上是求解一个优化问题:
min ⁡ Δ x , Δ y ∥ I 1 ( x , y ) − I 2 ( x + Δ x , y + Δ y ) ∥ 2 2 \min _{\Delta x, \Delta y}\left\|\boldsymbol{I}_1(x, y)-\boldsymbol{I}_2(x+\Delta x, y+\Delta y)\right\|_2^2 Δx,ΔyminI1(x,y)I2(x+Δx,y+Δy)22
这个写法有点像直接法的光度误差,只不过目标不一样,光流法求的是上一帧跟踪像素点在当前帧图片的像素位置。那么对应的雅可比就是 I 2 I_2 I2 x + Δ x , y + Δ y x+\Delta x, y+\Delta y x+Δx,y+Δy 处的梯度。但是这样子的定义感觉是有些歧义,最好是把 x + Δ x , y + Δ y x+\Delta x, y+\Delta y x+Δx,y+Δy当成一个整体 x 2 , y 2 x_2,y_2 x2,y2,优化目标就是 x 2 , y 2 x_2,y_2 x2,y2(即跟踪点在第二帧图片的位置)。
请添加图片描述
这样展开才能说得过去对应的雅可比就是 I 2 I_2 I2 x + Δ x , y + Δ y x+\Delta x, y+\Delta y x+Δx,y+Δy 处的梯度。也可以参考SLAM光流法、直接法代码踩坑记录

然后后续程序就是基于单层光流去构建构建图像金字塔从粗到细实现多层光流,多层光流实际上可以帮助整个优化问题更好逼近全局最优解,而不是受图像梯度影响陷入局部最优。
在这里插入图片描述

代码结果

很明显在目标函数优化后的误差,多层光流是优于单层光流的,也证明了多层光流的有效性。

直接法

直接法根据像素点的数量,可以分成稀疏、稠密和半稠密三种。与上面LK光流的区别在于,直接法通过构建两帧图片之间的光度误差,来直接优化两帧的R和t。而LK光流只是完成了像素点跟踪的问题。

公式推导

用下书的原图:
在这里插入图片描述
对于同一个三维点P,有下面的式子:其实就是相机模型的式子
p 1 = [ u v 1 ] 1 = 1 Z 1 K P , p 2 = [ u v 1 ] 2 = 1 Z 2 K ( R P + t ) = 1 Z 2 K ( T P ) 1 : 3 . \begin{aligned} & \boldsymbol{p}_1=\left[\begin{array}{l}u \\ v \\ 1\end{array}\right]_1=\frac{1}{Z_1} \boldsymbol{K} \boldsymbol{P}, \\ & \boldsymbol{p}_2=\left[\begin{array}{l}u \\ v \\ 1\end{array}\right]_2=\frac{1}{Z_2} \boldsymbol{K}(\boldsymbol{R} \boldsymbol{P}+\boldsymbol{t})=\frac{1}{Z_2} \boldsymbol{K}(\boldsymbol{T} \boldsymbol{P})_{1: 3} .\end{aligned} p1= uv1 1=Z11KP,p2= uv1 2=Z21K(RP+t)=Z21K(TP)1:3.

然后就是通过光度误差构建优化问题。
e = I 1 ( p 1 ) − I 2 ( p 2 ) e=\boldsymbol{I}_1\left(\boldsymbol{p}_1\right)-\boldsymbol{I}_2\left(\boldsymbol{p}_2\right) e=I1(p1)I2(p2)
min ⁡ T J ( T ) = ∥ e ∥ 2 \min _{\boldsymbol{T}} J(\boldsymbol{T})=\|e\|^2 minTJ(T)=e2
对于多个空间点就能构成多个误差项
min ⁡ T J ( T ) = ∑ i = 1 N e i T e i , e i = I 1 ( p 1 , i ) − I 2 ( p 2 , i ) \min _{\boldsymbol{T}} J(\boldsymbol{T})=\sum_{i=1}^N e_i^{\mathrm{T}} e_i, \quad e_i=\boldsymbol{I}_1\left(\boldsymbol{p}_{1, i}\right)-\boldsymbol{I}_2\left(\boldsymbol{p}_{2, i}\right) TminJ(T)=i=1NeiTei,ei=I1(p1,i)I2(p2,i)

接下来就是去求误差对位姿T的雅可比。定义两个中间变量:
q = T P , u = 1 Z 2 K q \begin{aligned} \boldsymbol{q} & =\boldsymbol{T} \boldsymbol{P}, \\ \boldsymbol{u} & =\frac{1}{Z_2} \boldsymbol{K} \boldsymbol{q} \end{aligned} qu=TP,=Z21Kq
q是图中三维点P经过T的变换后在第二帧图像的三维坐标,u就是其在第二帧图像对应的像素。接着也是一阶的泰勒展开:
∂ e ∂ T = ∂ I 2 ∂ u ∂ u ∂ q ∂ q ∂ δ ξ δ ξ \frac{\partial e}{\partial \boldsymbol{T}}=\frac{\partial \boldsymbol{I}_2}{\partial \boldsymbol{u}} \frac{\partial \boldsymbol{u}}{\partial \boldsymbol{q}} \frac{\partial \boldsymbol{q}}{\partial \delta \boldsymbol{\xi}} \delta \boldsymbol{\xi} Te=uI2quδξqδξ
最终可以得到:
J = − ∂ I 2 ∂ u ∂ u ∂ δ ξ \boldsymbol{J}=-\frac{\partial \boldsymbol{I}_2}{\partial \boldsymbol{u}} \frac{\partial \boldsymbol{u}}{\partial \delta \boldsymbol{\xi}} J=uI2δξu
其中
∂ u ∂ δ ξ = [ f x Z 0 − f x X Z 2 − f x X Y Z 2 f x + f x X 2 Z 2 − f x Y Z 0 f y Z − f y Y Z 2 − f y − f y Y 2 Z 2 f y X Y Z 2 f y X Z ] \frac{\partial \boldsymbol{u}}{\partial \delta \boldsymbol{\xi}}=\left[\begin{array}{cccccc}\frac{f_x}{Z} & 0 & -\frac{f_x X}{Z^2} & -\frac{f_x X Y}{Z^2} & f_x+\frac{f_x X^2}{Z^2} & -\frac{f_x Y}{Z} \\ 0 & \frac{f_y}{Z} & -\frac{f_y Y}{Z^2} & -f_y-\frac{f_y Y^2}{Z^2} & \frac{f_y X Y}{Z^2} & \frac{f_y X}{Z}\end{array}\right] δξu=[Zfx00ZfyZ2fxXZ2fyYZ2fxXYfyZ2fyY2fx+Z2fxX2Z2fyXYZfxYZfyX]
是前面BA也涉及到的雅可比,经典像素对左扰动的雅可比。

代码实现

书中的代码也是给出了手动高斯牛顿的方法,并且结合图像金字塔。结果也是显然,多层直接发的误差最终是更小,效果也更好。

总结

光流法总结

本质上做了特征匹配的工作,光流法通过估计像素的运动来得到上一帧跟踪点在当前帧图像的像素位置。
优点:

  1. 运算速度快,因为省去了计算特征点描述子和特征匹配的过程

缺点:

  1. 灰度不变是很强的假设,实际并不一定满足,对图像的连续性和光照稳定性要求高
  2. 如果在一些角点很难提取的地方,像素点的跟踪效果会变差,导致后续位姿估计的结果

直接发总结

直接基于两帧图片,构建光度误差优化两帧图像之间的R和t。避免了跟踪点的过程。
优点:

  1. 直接法根据点的来源分成稀疏直接法,半稠密直接法,稠密直接法。一般稀疏直接法用于实时性要求高的视觉定位系统中。稠密直接法一般用于结构重建上。
  2. 稀疏直接法计算速度快,省去计算特征点和描述子的时间,实时性较好。
  3. 有像素梯度即可,不要求特征点,所以在一些特征缺失的场景可以使用。

缺点:

  1. 本质上图像是非凸函数,所以优化的过程中会容易陷入局部最优。可以通过图像金字塔改善效果。
  2. 直接法在选点少的情况下,效果不好,一般建议500个点以上。
  3. 灰度不变假设,实际不一定满足。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2种方法将集合数据List构建成树形结构

文章目录 递归循环构建树结构hutool.TreeUtil.build构建树结构 递归循环构建树结构 先查最外层树节点数据&#xff0c;再递归遍历每一层子节点数据 public ApiResultDto<List<LocationDto>> getTreeByParams(LocationSearchDto searchDto, SecurityUser user) {// …

代码随想录算法训练营第三十六天|860.柠檬水找零、406.根据身高重建队列、452. 用最少数量的箭引爆气球

860.柠檬水找零 文档讲解&#xff1a;代码随想录 题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 注意看提示&#xff1a; bills[i] 不是 5 就是 10 或是 20 场景较为固定 遇到了20&#xff0c;优先消耗10 class Solution:def lemonadeChange(self, bills: …

秋招突击——算法——模板题——区间DP——合并石子

文章目录 题目内容思路分析实现代码分析与总结 题目内容 思路分析 基本思路&#xff0c;先是遍历区间长度&#xff0c;然后再是遍历左端点&#xff0c;最后是遍历中间的划分点&#xff0c;将阶乘问题变成n三次方的问题 实现代码 // 组合数问题 #include <iostream> #in…

宝塔Linux下安装EMQX服务并设置匿名访问

简述 之前有在Windows和Linux下搭建过EMQX服务并且使用方面都没问题,但那都是使用的用户和密码方式访问,且前提都是通过浏览器进入EMQX的配置页面设置的属性; 但这次使用的是腾讯云租用的宝塔Liniux,由于没有浏览器只能通过命令行方式修改EMQX配置以达到目的;由于事先没看…

使用 CapSolver API 服务解决 Arkose Labs FunCaptcha 验证码

使用 CapSolver API 服务解决 Arkose Labs FunCaptcha 验证码 FunCaptcha 以其复杂的图像验证而闻名&#xff0c;对自动化系统构成了巨大的挑战。CapSolver 的 API 服务利用先进的 AI 技术轻松应对和解决 FunCaptcha 挑战。本指南探讨了 CapSolver 如何实现无缝自动化&#xff…

汇聚荣科技有限公司优点有哪些?

在当今快速发展的科技时代&#xff0c;企业之间的竞争愈发激烈。作为一家专注于科技创新与研发的公司&#xff0c;汇聚荣科技有限公司凭借其卓越的技术实力和创新能力&#xff0c;在业界树立了良好的口碑。那么&#xff0c;汇聚荣科技有限公司究竟有哪些优点呢?接下来&#xf…

XX数字中台技术栈及能力

XX数字中台技术栈及能力 1 概述 XX数字中台面向数据开发者、数据管理者和数据应用者&#xff0c;提供数据汇聚、融合、治理、开发、挖掘、共享、可视化、智能化等能力&#xff0c;实现数据端到端的全生命周期管理&#xff0c;以共筑数字基础底座&#xff0c;共享数据服务能力…

The Missing Semester of Your CS Education(计算机教育中缺失的一课)

Shell 工具和脚本(Shell Tools and Scripting) 一、shell脚本 1.1、变量赋值 在bash中为变量赋值的语法是foobar&#xff0c;访问变量中存储的数值&#xff0c;其语法为 $foo。 需要注意的是&#xff0c;foo bar &#xff08;使用空格隔开&#xff09;是不能正确工作的&…

llama-factory学习个人记录

框架、模型、数据集准备 1.llama-factory部署 # 克隆仓库 git clone https://github.com/hiyouga/LLaMA-Factory.git # 创建虚拟环境 conda create --name llama_factory python3.10 # 激活虚拟环境 conda activate llama_factory # 安装依赖 cd LLaMA-Factory pip install -…

Java 使用继承和重写父类方法写一个商品入库案例

package 练习.商品入库;import java.util.Scanner; // 抽象手机类 public abstract class Phone {//测试方法public static void main(String[] args){// 华为手机huawei h new huawei();h.setName("华为P40");h.setPrice(1999.99);h.setConfiguration("8128GB…

Go知识点复习

Go知识点复习 1.关于包的使用和GOPATH的配置 src:用于以代码包的形式组织并保存Go源码文件, 需要手动创建pkg目录&#xff1a;用于存放经由go install命令构建安装后的代码包&#xff08;包含Go库源码文件&#xff09;的“.a”归档文件bin目录&#xff1a;与pkg目录类似&…

kind: Telemetry

访问日志 访问日志提供了一种从单个工作负载实例的角度监控和理解行为的方法。 Istio 能够以一组可配置的格式为服务流量生成访问日志&#xff0c; 使操作员可以完全控制日志记录的方式、内容、时间和地点。 有关更多信息&#xff0c;请参阅获取 Envoy 的访问日志。 https:/…

ThingsBoard如何拆分前后端分离启动

后端启动 前端启动 注意事项 ThingsBoard是一个开源的物联网平台&#xff0c;它原本的设计就考虑到了现代Web应用的前后端分离架构。尽管其核心是一个后端服务&#xff0c;负责设备连接、数据处理和存储等&#xff0c;但其用户界面是作为单独的前端应用程序实现的&#xff0c…

Windows下部署Seata1.5.2,解决Seata无法启动问题

目录 1. 版本说明 2. Windows下部署Seata1.5.2 2.1 创建回滚日志表undo_log 2.2 创建Seata服务端需要的四张表 2.3 在nacos创建seata命名空间&#xff0c;添加seataServer.yml配置 2.4 修改本地D:/tool/seata-server-1.5.2/seata/conf/applicaltion.yml文件 2.5 启动Seat…

抖音运营_抖音电商介绍

截止20年8月&#xff0c;抖音的日活跃数高达6亿。 20年6月&#xff0c;上线抖店 &#xff08;抖音官方电商&#xff09; 一 抖店的定位和特色 1 一站式经营 帮助商家进行 商品交易、店铺管理、客户服务 等全链路的生意经营 2 多渠道拓展 抖音、今日头条、西瓜、抖音火山版…

markdown 文件渲染工具推荐 obsidian publish

背景 Markdown 是一种轻量级的标记语言&#xff0c;最开始使用它是觉得码字非常方便&#xff0c;从一开始的 word 排版到 markdown &#xff0c;还不太不习惯&#xff0c;用了 obsidian把一些文字发在网上后&#xff0c;才逐渐发现他的厉害之处。 让人更加专注于内容本身&…

信号:MSK调制和GMSK调制

目录 一、MSK信号 1. MSK信号的第k个码元 2.MSK信号的频率间隔 3.MSK信号的相位连续性 3.1 相位路径 3.2初始相位ψk 4.MSK信号的产生 原理框图 5.MSK信号的频谱图 二、高斯最小频移键控(GMSK) 1.频率响应 2.GMSK调制产生方式 2.1 高斯滤波器法 2.2 正交调制器法…

激光雷达SLAM算法综述

大家好呀&#xff0c;我是一个SLAM方向的在读博士&#xff0c;深知SLAM学习过程一路走来的坎坷&#xff0c;也十分感谢各位大佬的优质文章和源码。随着知识的越来越多&#xff0c;越来越细&#xff0c;我准备整理一个自己的激光SLAM学习笔记专栏&#xff0c;从0带大家快速上手激…

Android Ktor 网络请求框架

Ktor 是一个由 JetBrains 开发的用于 Kotlin 编程语言的应用框架&#xff0c;旨在创建高性能的异步服务器和客户端应用程序。由于完全基于 Kotlin 语言&#xff0c;Ktor 能够让开发者编写出简洁、可读性强且功能强大的代码&#xff0c;特别适合那些已经熟悉 Kotlin 的开发人员。…

《SpringBoot》系列文章目录

SpringBoot是由Pivotal团队提供的全新框架&#xff0c;旨在简化新Spring应用的初始搭建以及开发过程。以下是一些关于SpringBoot的详细介绍&#xff1a; 设计目的&#xff1a;SpringBoot通过特定的方式来进行配置&#xff0c;使得开发人员不再需要定义样板化的配置&#xff0c…