视觉SLAM十四讲-理论到实践课程作业笔记-第六讲-光流法和直接法

news2024/11/16 3:48:17

CH6_No1:

1-1. 光流法可分为哪几类?

答:正向和逆向,其中两种方法各又包含了直加法和组合法;

1-2 在 compositional 中,为什么有时候需要做原始图像的 wrap?该 wrap 有何物理意义?

答:我个人的理解此处的wrap就是高博视频课ppt中对图像像素点在dt时间内在x和y方向上做了dx,dy的操作,以此与模板T图像构建像素误差方程:

即:
在这里插入图片描述

1-3 forward 和 inverse 有何差别?

答:高博课程作业pdf后面的试题中其实已经给出了部分的答案,inverse的方法相比forward在于对图像梯度计算上取巧;特征点提取的过程中,我们一般只能保证原始图像所提取的特征点一定是角点,而对于后续的图像特征提取中,由于尺度不确定性导致的当相机发生运动后,前一帧图像提取的角点信息,在后续的提取工作中发现该点不是角点特征点,故无法保证后面的图像特征点有梯度保证。基于此,逆向方法用 I 1 I_1 I1的梯度去代替 I 2 I_2 I2的梯度,且根据梯度不变原则,此处计算的海森矩阵只需要计算一次即可,不需要如前向法需要在每次得到更新时重新计算一次海森矩阵,极大降低了计算量;这样就导致两种方法的目标函数也不一样;根据文献和查阅的资料可知,一般前向法适用于模板图像 T T T的噪声较大的情况;逆向法则适用于当输入图像 I I I的噪声更多的情况。

CH6_No2

2-1 从最小二乘角度来看,每个像素的误差怎么定义?

答:

在这里插入图片描述

2-2 误差相对于自变量的导数如何定义?

答:参考:链接1

2-3 forward && inverse coding:

void OpticalFlowSingleLevel( // 没有图像金字塔的光流法
        const Mat &img1,
        const Mat &img2,
        const vector<KeyPoint> &kp1,
        vector<KeyPoint> &kp2,
        vector<bool> &success,
        bool inverse
) {

    // parameters
    int half_patch_size = 4;
    int iterations = 10;
    bool have_initial = !kp2.empty();

    for (size_t i = 0; i < kp1.size(); i++) {
        auto kp = kp1[i];
        double dx = 0, dy = 0; // dx,dy need to be estimated
        if (have_initial) {
            dx = kp2[i].pt.x - kp.pt.x;
            dy = kp2[i].pt.y - kp.pt.y;
        }

        double cost = 0, lastCost = 0;
        bool succ = true; // indicate if this point succeeded

        // Gauss-Newton iterations
        for (int iter = 0; iter < iterations; iter++) {
            Eigen::Matrix2d H = Eigen::Matrix2d::Zero();
            Eigen::Vector2d b = Eigen::Vector2d::Zero();
            cost = 0;

            if (kp.pt.x + dx <= half_patch_size || kp.pt.x + dx >= img1.cols - half_patch_size ||
                kp.pt.y + dy <= half_patch_size || kp.pt.y + dy >= img1.rows - half_patch_size) {   // go outside 排除外点
                succ = false;
                break;
            }

            // compute cost and jacobian
            for (int x = -half_patch_size; x < half_patch_size; x++)
                for (int y = -half_patch_size; y < half_patch_size; y++) {

                    // TODO START YOUR CODE HERE (~8 lines)
                    double error = GetPixelValue(img1,kp.pt.x + x,kp.pt.y + y) - GetPixelValue(img2,kp.pt.x + x + dx,kp.pt.y + y + dy);
                    Eigen::Vector2d J;  // Jacobian
                    if (inverse == false) {
                        // Forward Jacobian 参考链接中的偏导方程
                        J = -1.0 * Eigen::Vector2d(
                            0.5 * (GetPixelValue(img2, kp.pt.x + dx + x + 1, kp.pt.y + dy + y) -
                                   GetPixelValue(img2, kp.pt.x + dx + x - 1, kp.pt.y + dy + y)),
                            0.5 * (GetPixelValue(img2, kp.pt.x + dx + x, kp.pt.y + dy + y + 1) -
                                   GetPixelValue(img2, kp.pt.x + dx + x, kp.pt.y + dy + y - 1))
                        );
                    } else {
                        // Inverse Jacobian
                        J = -1.0 * Eigen::Vector2d(
                            0.5 * (GetPixelValue(img1, kp.pt.x + dx + x + 1, kp.pt.y + dy + y) -
                                   GetPixelValue(img1, kp.pt.x + dx + x - 1, kp.pt.y + dy + y)),
                            0.5 * (GetPixelValue(img1, kp.pt.x + dx + x, kp.pt.y + dy + y + 1) -
                                   GetPixelValue(img1, kp.pt.x + dx + x, kp.pt.y + dy + y - 1))
                        );
                        // NOTE this J does not change when dx, dy is updated, so we can store it and only compute error
                    }

                    // compute H, b and set cost;
                    if(iter == 0 || inverse == false){
                        // H += J.transpose() * J;
                        H += J * J.transpose();
                    }
                    // b += -J.transpose() * error;
                    b += -J * error;
                    cost += error * error;
                    // TODO END YOUR CODE HERE
                }

            // compute update
            // TODO START YOUR CODE HERE (~1 lines)
            Eigen::Vector2d update;
            update = H.ldlt().solve(b);
            // TODO END YOUR CODE HERE

            if (isnan(update[0])) {
                // sometimes occurred when we have a black or white patch and H is irreversible
                cout << "update is nan" << endl;
                succ = false;
                break;
            }
            // cout << "iteration " << iter << " cost=" << cout.precision(12) << cost << endl;
            if (iter > 0 && cost > lastCost) {
                cout << "cost increased: " << cost << ", " << lastCost << endl;
                break;
            }

            
            // update dx, dy
            dx += update[0];
            dy += update[1];
            lastCost = cost;
            succ = true;
        }

        success.push_back(succ);

        // set kp2
        if (have_initial) {
            kp2[i].pt = kp.pt + Point2f(dx, dy);
        } else {
            KeyPoint tracked = kp;
            tracked.pt += cv::Point2f(dx, dy);
            kp2.push_back(tracked);
        }
    }
}

前向和后向两种方式计算得到的时间:

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

2-2 coarse-to-fine_LK

void OpticalFlowMultiLevel(
        const Mat &img1,
        const Mat &img2,
        const vector<KeyPoint> &kp1,
        vector<KeyPoint> &kp2,
        vector<bool> &success,
        bool inverse) {

    // parameters
    int pyramids = 4;   //金字塔层数
    double pyramid_scale = 0.3;     //每一层按照0.3倍进行缩放
    // 想象金字塔的样子,img1是金字塔的底层,从低向上以此0.3倍缩放四次到塔顶
    double scales[] = {1.0, 0.3, 0.09, 0.027};

    // create pyramids
    vector<Mat> pyr1, pyr2; // image pyramids 注意这里是个vector
    // TODO START YOUR CODE HERE (~8 lines)
    for (int i = 0; i < pyramids; i++)
    {
        if (i == 0)
        {   
            pyr1.push_back(img1);
            pyr2.push_back(img2);
        }
        else
        {
            Mat img1_pyr, img2_pyr;
            //resize函数改变图像的大小
            cv::resize(pyr1[i -1], img1_pyr, cv::Size(pyr1[i - 1].cols * pyramid_scale, pyr1[i - 1].rows * pyramid_scale));
            cv::resize(pyr2[i -1], img2_pyr, cv::Size(pyr2[i - 1].cols * pyramid_scale, pyr2[i - 1].rows * pyramid_scale));

            pyr1.push_back(img1_pyr);
            pyr2.push_back(img2_pyr);
        }
    }
    // TODO END YOUR CODE HERE
    // coarse-to-fine LK tracking in pyramids
    // 搭建好金字塔后,从顶向下依次扩大0.3
    // TODO START YOUR CODE HERE
    vector<KeyPoint> kp1_pyr, kp2_pyr;
    for (auto &kp:kp1)
    {
        auto kp_top = kp;
        kp_top.pt *= scales[pyramids -1];// 顶层相对于第一张底层(img1)的缩放倍率为0.027
        kp1_pyr.push_back(kp_top);
        kp2_pyr.push_back(kp_top);
    }
    for (int level = pyramids -1; level >= 0; level--)
    {
        success.clear();
        OpticalFlowSingleLevel(pyr1[level], pyr2[level], kp1_pyr, kp2_pyr, success, inverse);
        if (level > 0)
        {// 每次从上倒下,直到底层的之前,都是按照0.3倍率进行扩大
            for (auto &kp:kp1_pyr){
                kp.pt /= pyramid_scale;
            }
            for (auto &kp:kp2_pyr){
                kp.pt /= pyramid_scale;
            }
        }
    }

    for (auto &kp:kp2_pyr) // 底层 即最后一层 kp2 
    {
        kp2.push_back(kp);
    }
    // TODO END YOUR CODE HERE
    // don't forget to set the results into kp2
}

2-3 讨论

1.图像块大小是否有明显差异?取 16x16 和 8x8 的图像块会让结果发生变化吗?

答:有差异,尝试了8、16、32三种图像块大小,其中,8效果最差,16和32效果与opencv效果相当(对于单层LK而言)

2.金字塔层数对结果有怎样的影响?缩放倍率呢?

答:

对比实验:

2.1 扩大、缩小倍率效果:

原始为0.5倍,缩小到0.3,扩大到0.8效果图:

原始0.5倍:

在这里插入图片描述

缩小在0.3倍:

在这里插入图片描述

扩大在0.8倍:

在这里插入图片描述

可以看到效果最佳在0.5倍,即当层数不变,适当调整缩放倍率可以达到最佳效果,缩放缩放倍率不宜过大或过小,当然可能也与相机本身的图像效果有关吧。

2.2 改变层数,缩放倍率不变:

2/4/8层三种情况:

2层,0.5倍:

在这里插入图片描述

4层,0.5倍:

在这里插入图片描述

8层,0.5倍:

在这里插入图片描述

可以看到,当层数逐级增加,貌似精度没有得到明显的提升,但当层数从低层数到一定高的层数时,效果提升还是较为明显的;

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

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

相关文章

大数据系统测试——大数据系统解析(下)

各位好&#xff0c;我是 道普云 欢迎关注我的主页 希望这篇文章对想提高软件测试水平的你有所帮助。 学习和理解大数据系统每一个层次需要解决的技术问题和对应的一些技术需求是对大数据系统进行软件测试的一个基础&#xff0c;上篇文章里我们已经跟大家分享了数据收集层、…

33.python socket

python socket编程 概念说明心跳包的作用基于以上知识的基础上我们来实现一个代码socket serversocket client执行结果 概念说明 socket传输数据是基于字节流的&#xff0c;默认情况下是无边界的字节流。 一般情况下数据里中包含心跳包和数据包。数据包就是我们实际需要发送给…

中国建筑轮廓数据

建筑轮廓数据是建筑的边界矢量数据&#xff0c;一般该数据属性中会记录对应建筑的高度或者楼层数&#xff0c;通过建筑轮廓数据置顶的高程字段拉伸&#xff0c;就可以得到建筑白盒模型&#xff0c;所以&#xff0c;我们在各类导航地图中看到的白盒模型&#xff0c;实际上是建筑…

深度剖析:医疗行业财务报表的核心要素与解析策略

在当今医疗行业中&#xff0c;财务报表分析扮演着至关重要的角色。医疗机构需要通过对财务数据的准确分析来做出关键决策&#xff0c;管理资源&#xff0c;改善效率&#xff0c;并确保最终提供高质量的医疗服务。本文将深入探讨医疗行业中财务报表分析的重要性、关键指标和分析…

《一种个性化逻辑定制与类置换方案》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

通过违法app发现大量网站被挂黑页

免责声明&#xff1a;文章来源于真实事件&#xff0c;关键信息已经打码处理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一…

AI搜索的商业模式困境:Perplexity从拒绝广告到高价广告的转变

随着人工智能技术的发展&#xff0c;AI搜索逐渐成为人们日常生活中不可或缺的一部分。在这个领域&#xff0c;Perplexity曾被认为是AI搜索领域的领军企业&#xff0c;以其强大的智能搜索能力脱颖而出。然而&#xff0c;随着OpenAI的SearchGPT发布在即&#xff0c;Perplexity似乎…

掌握结构型模式——全景回顾

在前几篇文章中&#xff0c;我们详细探讨了多种结构型设计模式&#xff0c;今天来进行一个大总结——结构型设计模式主要关注类与对象的组合和组织&#xff0c;确保我们能够构建出稳固、灵活且易于维护的软件系统。无论你是初学者还是有经验的开发者&#xff0c;这篇文章都会帮…

计算循环冗余码(CRC)--软考笔记

1、什么是CRC循环冗余码&#xff08;CRC&#xff09; CRC&#xff08;Cyclic Redundancy Check&#xff09;是一种用于检测数据传输错误的校验码。它通过一个预定义的生成多项式来计算一个固定长度的校验值&#xff0c;这个值被附加到原始数据上一起发送。接收端使用相同的生成…

二叉树 - 完全二叉树的节点个数

222. 完全二叉树的节点个数 方法一&#xff1a;递归 /*** Definition for a binary tree node.* function TreeNode(val, left, right) {* this.val (valundefined ? 0 : val)* this.left (leftundefined ? null : left)* this.right (rightundefined ? nul…

多商户2.3.0后台顶部添加修改密码修复方法

问题&#xff1a;后台登录不能修改自己的密码&#xff1b; 解决方法&#xff1a; 修改前端代码&#xff0c;文件路径&#xff08;平台后台&#xff0c;商户后台一样的修改方法&#xff09;&#xff1a;src/layout/navBars/breadcrumb/user.vue 修改第一处 代码&#xff1a; 修…

IM即时通讯给娱乐社交、游戏等行业带来了什么影响?看这篇就知道

企业IM即时通讯技术的发展和应用&#xff0c;对我们的生活、工作、沟通和交流带来了显著的影响和改变。 IM即时通讯技术的发展不仅改变了我们的沟通方式&#xff0c;还提高了工作效率&#xff0c;促进了团队协作&#xff0c;保障了信息安全&#xff0c;并推动了业务创新。随着技…

机器人学导论之连杆参数

目录 一、连杆参数[1] 二、将下肢看作二连杆结构 三、参考文献 一、连杆参数[1] 1.1关节角 绕轴&#xff0c;从旋转到的角度。 备注&#xff1a;从z轴正方向看&#xff0c;顺时针为正&#xff0c;逆时针为负。 图 1 平面三连杆操作臂 以图1为例子&#xff0c; 表示…

文献解读-农业-第二十九期|《β-淀粉酶和磷脂酸参与板栗种子萌发》

关键词&#xff1a;农业&#xff1b;基因测序&#xff1b;变异检测&#xff1b; 文献简介 标题&#xff08;英文&#xff09;&#xff1a;Beta-amylase and phosphatidic acid involved in recalcitrant seed germination of Chinese chestnut标题&#xff08;中文&#xff09…

岩土工程中的渗流问题:有限单元法的理论与实践

有限单元法在岩土工程问题中应用非常广泛&#xff0c;很多商业软件如Plaxis/Abaqus/Comsol等都采用有限单元解法。尽管各类商业软件使用方便&#xff0c;但其使用对用户来说往往是一个“黑箱子”。相比而言&#xff0c;开源的有限元程序计算方法透明、计算过程可控&#xff0c;…

宝藏!盟主自控独家讲义:《掌中宝》(精卫篇)1-9章:甄选部分

本文内容&#xff0c;全部选自自动化考研联盟的&#xff1a;盟主自控独家讲义&#xff1a;《掌中宝》(精卫篇)。 Part1&#xff1a;资料封面&目录 Part2&#xff1a;资料各个章节具体内容 自控专属数学基础储备 第1章 自动控制的基本概念 第2章 控制系统的数学模型 第3章…

8.26算法训练

1.八皇后 Checker Challenge 输入&#xff1a; 6输出&#xff1a; 2 4 6 1 3 5 3 6 2 5 1 4 4 1 5 2 6 3 4是以前寒假写过的题目&#xff0c;所以有的影响&#xff0c;大致思路就是用深度遍历然后判断是否在对角线上就ok了&#xff0c;有大概思路的话&#xff0c; 还是不难的…

比特币的签名和验证(基于ECDSA)

比特币&#xff08;Bitcoin&#xff09;和以太坊&#xff08;Ethereum&#xff09;等区块链技术使用了加密算法来确保交易的安全性。私钥签名和公钥验证是这些算法的核心部分&#xff0c;主要用于证明交易的发起者拥有交易中使用的资金的控制权&#xff0c;而不需要暴露私钥本身…

【开源分享】java+swing+mysql简单学生信息管理系统设计与实现

个人主页&#xff1a;程序员杨工 个人简介&#xff1a;从事软件开发多年&#xff0c;前后端均有涉猎&#xff0c;具有丰富的开发经验 博客内容&#xff1a;全栈开发&#xff0c;分享Java、Python、Php、小程序、前后端、数据库经验和实战 文末有本人名片&#xff0c;希望和大家…

大模型学习全面教程:零基础入门至精通,一篇文章全掌握

人人都看得懂的大模型简介 大模型就像一座庞大的图书馆&#xff0c;里面有非常多的书籍。但与普通图书馆不同的是&#xff0c;这座图书馆中的每本书都是关于不同事物的描述和知识。而这些书籍中的每一页都代表了这个事物的一些特征或细节。现在&#xff0c;想象一下&#xff0…