视觉SLAM实践入门——(20)视觉里程计之直接法

视觉SLAM十四讲习题答案 阅读《视觉SLAM十四讲》中遇到的问题及习题资源整理,或为原创,或来自网络,如有问题请与我联系,会在第一时间加以修正。 后来自己写完看到另一位写的博客,相形见绌,大家可以互相结合的看。 第一讲 1.线性方程的解:转载自宋洋鹏(youngpan1101),了解线性代数可关注B站3Blue1Brown,以及线性代数,知乎上有其精细笔记。 2.高斯分布参考这位博主Johnny-Cuii的文章,具体... 阅读详情

多层直接法的过程:

1、读图,随机取点并计算深度

2、创建图像金字塔(相机内参也需要缩放),并计算对应点的像素坐标

3、应用单层直接法(使用G-N、L-M等方法,或者使用g2o、ceres库)进行优化

源码中有一些地方会引起段错误,修改方法见下面代码中注释

总的来说,直接法与特征点法的实现过程大同小异。不同的地方在于:特征点法中需要提取角点,直接法则可以随机取点(也可以取角点)并且取点数不能太少;直接法与特征点法计算误差不同(因此雅可比矩阵也不同)

#include <opencv2/opencv.hpp>
#include <sophus/se3.hpp>
#include <boost/format.hpp>
#include <pangolin/pangolin.h>

using namespace std;
using namespace cv;

typedef vector<Eigen::Vector2d, Eigen::aligned_allocator<Eigen::Vector2d>> VecVector2d;
typedef Eigen::Matrix<double, 6, 6> Matrix6d;
typedef Eigen::Matrix<double, 2, 6> Matrix26d;
typedef Eigen::Matrix<double, 6, 1> Vector6d;

//相机内参
double fx = 718.856, fy = 718.856, cx = 607.1928, cy = 185.2157;
//基线
double baseline = 0.573;
//图片路径
string left_file = "../left.png";
string disparity_file = "../disparity.png";
boost::format fmt_others("../%06d.png");    

//用于计算增量方程各参数的类
class JacobianAccumulator {
public:
    JacobianAccumulator(
        const cv::Mat &img1_,
        const cv::Mat &img2_,
        const VecVector2d &px_ref_,
        const vector<double> depth_ref_,
        Sophus::SE3d &T21_) :
        img1(img1_), img2(img2_), px_ref(px_ref_), depth_ref(depth_ref_), T21(T21_) {
        projection = VecVector2d(px_ref.size(), Eigen::Vector2d(0, 0));
    }

    //计算各参数
    void accumulate_jacobian(const cv::Range &range);

    Matrix6d hessian() const { return H; }

    Vector6d bias() const { return b; }

    double cost_func() const { return cost; }

    VecVector2d projected_points() const { return projection; }

    //初始化各参数
    void reset() 
    {
        H = Matrix6d::Zero();
        b = Vector6d::Zero();
        cost = 0;
    }

private:
    const cv::Mat &img1;
    const cv::Mat &img2;
    const VecVector2d &px_ref;
    const vector<double> depth_ref;
    Sophus::SE3d &T21;
    VecVector2d projection;

    std::mutex hessian_mutex;
    Matrix6d H = Matrix6d::Zero();
    Vector6d b = Vector6d::Zero();
    double cost = 0;
};

//双线性插值
inline float GetPixelValue(const cv::Mat &img, float x, float y) 
{
    //边界检测,随机点不会取在边上,因此这里右边界和下边界是可以的
    if (x < 0) x = 0;
    if (y < 0) y = 0;
    if (x >= img.cols - 1) x = img.cols - 1;
    if (y >= img.rows - 1) y = img.rows - 1;
    uchar *data = &img.data[int(y) * img.step + int(x)];
    float xx = x - floor(x);
    float yy = y - floor(y);
    return float(
        (1 - xx) * (1 - yy) * data[0] +
        xx * (1 - yy) * data[1] +
        (1 - xx) * yy * data[img.step] +
        xx * yy * data[img.step + 1]
    );
}

//计算增量方程的参数
void JacobianAccumulator::accumulate_jacobian(const cv::Range &range) 
{
    const int half_patch_size = 1;
    int cnt_good = 0;
    Matrix6d hessian = Matrix6d::Zero();
    Vector6d bias = Vector6d::Zero();
    double cost_tmp = 0;

    for (size_t i = range.start; i < range.end; i++) 
	{
		//估计运动后点的空间坐标
		//1、将像素坐标转换为归一化坐标
		//2、归一化坐标乘以深度,得到空间坐标
		//3、空间坐标左乘运动
        Eigen::Vector3d point_ref =
            depth_ref[i] * Eigen::Vector3d((px_ref[i][0] - cx) / fx, (px_ref[i][1] - cy) / fy, 1);
        Eigen::Vector3d point_cur = T21 * point_ref;
        if (point_cur[2] < 0)
            continue;

		//计算运动后点的像素坐标
		//根据下面for循环以及GetPixelValue,这里需要修改if的条件,否则会出现段错误
        float u = fx * point_cur[0] / point_cur[2] + cx, v = fy * point_cur[1] / point_cur[2] + cy;
        if (u < half_patch_size || 
			u >= img2.cols - 1 - half_patch_size - 1 || 
			v <= half_patch_size ||
            v >= img2.rows - 1 - half_patch_size - 1)
            continue;

        projection[i] = Eigen::Vector2d(u, v);
		
        cnt_good++;
		
        double X = point_cur[0], Y = point_cur[1], Z = point_cur[2],
            Z2 = Z * Z, Z_inv = 1.0 / Z, Z2_inv = Z_inv * Z_inv;
        //计算误差、目标函数、雅可比、H、b
		//3x3窗口
        for (int x = -half_patch_size; x <= half_patch_size; x++)
            for (int y = -half_patch_size; y <= half_patch_size; y++) 
			{

                double error = GetPixelValue(img1, px_ref[i][0] + x, px_ref[i][1] + y) -
                               GetPixelValue(img2, u + x, v + y);
                Matrix26d J_pixel_xi;
                Eigen::Vector2d J_img_pixel;

                J_pixel_xi(0, 0) = fx * Z_inv;
                J_pixel_xi(0, 1) = 0;
                J_pixel_xi(0, 2) = -fx * X * Z2_inv;
                J_pixel_xi(0, 3) = -fx * X * Y * Z2_inv;
                J_pixel_xi(0, 4) = fx + fx * X * X * Z2_inv;
                J_pixel_xi(0, 5) = -fx * Y * Z_inv;

                J_pixel_xi(1, 0) = 0;
                J_pixel_xi(1, 1) = fy * Z_inv;
                J_pixel_xi(1, 2) = -fy * Y * Z2_inv;
                J_pixel_xi(1, 3) = -fy - fy * Y * Y * Z2_inv;
                J_pixel_xi(1, 4) = fy * X * Y * Z2_inv;
                J_pixel_xi(1, 5) = fy * X * Z_inv;
				J_img_pixel = Eigen::Vector2d(
                    0.5 * (GetPixelValue(img2, u + 1 + x, v + y) - GetPixelValue(img2, u - 1 + x, v + y)),
                    0.5 * (GetPixelValue(img2, u + x, v + 1 + y) - GetPixelValue(img2, u + x, v - 1 + y))
                );
                
                Vector6d J = -1.0 * (J_img_pixel.transpose() * J_pixel_xi).transpose();
                hessian += J * J.transpose();
                bias += -error * J;
                cost_tmp += error * error;
            }
    }

	//更新H、b、cost
    if (cnt_good) 
	{
		//因为使用并行计算,为了防止抢占变量,需要先获得锁
        unique_lock<mutex> lck(hessian_mutex);	
        H += hessian;
        b += bias;
		//cost只是一个判断收敛的依据,可以进行缩放
        cost += cost_tmp / cnt_good;
    }
}

//单层直接法
void DirectPoseEstimationSingleLayer(
    const cv::Mat &img1,
    const cv::Mat &img2,
    const VecVector2d &px_ref,
    const vector<double> depth_ref,
    Sophus::SE3d &T21) 
{

    const int iterations = 10;
    double cost = 0, lastCost = 0;
    auto t1 = chrono::steady_clock::now();
    JacobianAccumulator jaco_accu(img1, img2, px_ref, depth_ref, T21);

    for (int iter = 0; iter < iterations; iter++) 
	{
		//初始化H、b、cost
        jaco_accu.reset();
		
		//并行计算H、b等参数
        cv::parallel_for_(cv::Range(0, px_ref.size()),
                          std::bind(&JacobianAccumulator::accumulate_jacobian, &jaco_accu, std::placeholders::_1));
//		jaco_accu.accumulate_jacobian(cv::Range(0, px_ref.size()));
        Matrix6d H = jaco_accu.hessian();
        Vector6d b = jaco_accu.bias();
        cost = jaco_accu.cost_func();

        //更新
        Vector6d update = H.ldlt().solve(b);;
        T21 = Sophus::SE3d::exp(update) * T21;

        if (std::isnan(update[0])) 
		{
            cout << "update is nan" << endl;
            break;
        }
        if (iter > 0 && cost > lastCost) 
		{
            cout << "cost increased: " << cost << ", " << lastCost << endl;
            break;
        }
        if (update.norm() < 1e-3) 
            break;

        lastCost = cost;
        cout << "iteration: " << iter << ", cost: " << cost << endl;
    }

    cout << "T21 = \n" << T21.matrix() << endl;
    auto t2 = chrono::steady_clock::now();
    auto time_used = chrono::duration_cast<chrono::duration<double>>(t2 - t1);
    cout << "direct method for single layer: " << time_used.count() << endl;

	
    //画出跟踪结果
    cv::Mat img2_show;
    cv::cvtColor(img2, img2_show, COLOR_GRAY2BGR);
    VecVector2d projection = jaco_accu.projected_points();
    for (size_t i = 0; i < px_ref.size(); ++i) 
	{
        auto p_ref = px_ref[i];
        auto p_cur = projection[i];
        if (p_cur[0] > 0 && p_cur[1] > 0) 
		{
            cv::circle(img2_show, cv::Point2f(p_cur[0], p_cur[1]), 2, cv::Scalar(0, 250, 0), 2);
            cv::line(img2_show, cv::Point2f(p_ref[0], p_ref[1]), cv::Point2f(p_cur[0], p_cur[1]),
                     cv::Scalar(0, 250, 0));
        }
    }
    cv::imshow("current", img2_show);
    cv::waitKey();
}

//多层直接法
void DirectPoseEstimationMultiLayer(
    const cv::Mat &img1,
    const cv::Mat &img2,
    const VecVector2d &px_ref,
    const vector<double> depth_ref,
    Sophus::SE3d &T21) 
{
	//金字塔参数
    int pyramids = 4;
    double pyramid_scale = 0.5;
    double scales[] = {1.0, 0.5, 0.25, 0.125};

    //建立金字塔
    vector<cv::Mat> pyr1, pyr2; 
    for (int i = 0; i < pyramids; i++) 
	{
        if (i == 0) 
		{
            pyr1.push_back(img1);
            pyr2.push_back(img2);
        } else 
		{
            cv::Mat img1_pyr, img2_pyr;
            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);
        }
    }

	//备份相机内参
    double fxG = fx, fyG = fy, cxG = cx, cyG = cy; 
	
	//多层直接法
    for (int level = pyramids - 1; level >= 0; level--) 
	{
		//计算提取点在当前图层的位置
        VecVector2d px_ref_pyr; 
        for (auto &px: px_ref)
            px_ref_pyr.push_back(scales[level] * px);

        //缩放相机内参
        fx = fxG * scales[level];
        fy = fyG * scales[level];
        cx = cxG * scales[level];
        cy = cyG * scales[level];														
        DirectPoseEstimationSingleLayer(pyr1[level], pyr2[level], px_ref_pyr, depth_ref, T21);
    }

}

int main(int argc, char **argv) 
{
	//读取灰度图
    cv::Mat left_img = cv::imread(left_file, 0);
    cv::Mat disparity_img = cv::imread(disparity_file, 0);

    cv::RNG rng;
    int nPoints = 2000;
    int boarder = 20;
    VecVector2d pixels_ref;
    vector<double> depth_ref;

    //随机取点(为了增加可用点,不取边界点)
    for (int i = 0; i < nPoints; i++) 
	{
        int x = rng.uniform(boarder, left_img.cols - boarder);  
        int y = rng.uniform(boarder, left_img.rows - boarder);  
        int disparity = disparity_img.at<uchar>(y, x);
        double depth = fx * baseline / disparity;	//根据视差求深度
        depth_ref.push_back(depth);
        pixels_ref.push_back(Eigen::Vector2d(x, y));
    }

    //待估计运动
    Sophus::SE3d T_cur_ref;

    for (int i = 1; i < 6; i++) 
	{  
		//以读取灰度图
        cv::Mat img = cv::imread((fmt_others % i).str(), 0);
        DirectPoseEstimationMultiLayer(left_img, img, pixels_ref, depth_ref, T_cur_ref);
    }
    return 0;
}

视觉SLAM⑧----视觉里程计Ⅱ(光流法与直接法) 1.理解光流法跟踪特征点的原理 2.理解直接法是如何估计相机位姿的 3.实现多层直接法的计算 阅读详情

相关推荐

SLAM机器人开发(三)SLAM中常见的里程计

自主导航技术可以使运动载体实时确定自身的位姿和速度,从而引导自身运动的技术,整个导航过程不依赖外部支持。比如,在 GPS 不可靠的情况下,自主导航技术也能够具有可靠的导 航能力。由此可见,自主导航系统的开发是构建运动机器人的主要挑战之一。机器人领域中,自主导航技术主要包括建图定位与路径规划。而近几年成为关注焦点的 SLAM 技术是自主导航技术 中很重要的一项技术。SLAM 技术包括传感器信息读取、里程测量、后端优化、回环检测、实时 建图等部分[2]。自主导航技术中需要实时确定自身的位姿,因此计算里程计是举足

qq_42450767的博客 1万+

SLAM14讲--单层直接法--GetPixelValue

近期学习直接法时,单层直接法中有个计算每个像素点的误差和雅可比矩阵,其中得到GetPixelValue让我困惑了好久,研究许久后发表下自己的见解,如果有错误还希望各位大佬指出,我会虚心接受批评和指导。 1.计算像素点位置 这是部分源代码 J_img_pixel = Eigen::Vector2d( 0.5 * (GetPixelValue(img2, u + 1 + x, v + y) - GetPixelValue(img2, u - 1 + x, v ...

wuxin110a的博客 1025

SLAM 06.视觉里程计-3-直接法

直接法跳过了提取特征点的步骤。它构建一个优化问题,直接根据像素信息(通常是亮度),来估计相机的运动。这种方法省去了提特征的时间,然而代价则是,利用了所有信息之后,使得优化问题规模远远大于使用特征点的规模。因此,基于直接法的VO,多数需要GPU加速,才能做到实时化。...

jiankangyq的博客 2504

SLAM~CH8

本章主要介绍了直接法和光流法

闲某欧 390

视觉SLAM十四讲 Ubuntu20.04 Pangolin 环境配置

视觉SLAM十四讲 Ubuntu20.04 Pangolin 环境配置 一. github下载源代码 选择0.5版本的,要不然版本装高了编译ORB-SLAM2会遇到问题 二. 报错及处理 error: ‘AV_PIX_FMT_XVMC_MPEG2_MC’ was not declared in this scope 详细报错信息 /home/andy/Downloads/Pangolin/src/video/drivers/ffmpeg.cpp: In function ‘std::__cxx11::

Robert_Q的博客 1万+

视觉SLAM理论入门——(6)视觉里程计之特征点法—特征点

一个SLAM系统可以分为前端和后端,其中前端称为视觉里程计,其作用是根据相邻图像的信息估计出粗略的相机运动,给后端提供较好的初始值 视觉里程计的算法主要有两大类:特征点法(主流方法)、直接法 1、特征点 视觉里程计的核心问题是根据图像估计相机运动。特征点法是从各个图像中选取相同的有代表性的点(相机视角发生少量变化时,这些点保持不变),在这些点的基础上讨论相机位姿估计问题以及这些点的定位问题。在经典SLAM模型中,这些点是路标;在视觉SLAM中,这些点是图像特征 数字图像在计算机中以灰度值...

floatinglong的博客 2773

视觉十四讲:第八讲_直接法

1.直接法的推导: 考虑某个空间点P和两个时刻的相机,P的世界坐标为[X,Y,Z],它在两个相机上成像,记非齐次像素坐标为\(p_{1},p_{2}\),目标是求第一个相机到第二个相机的相对位姿变换。 直接法的思路是根据当前相机的位姿估计值,来寻找\(p_{2}\)的位置,误差项为光度误差 \(e = I_{1}(p_{1}) - I_{2}(p_{2})\) 考虑多个空间点P,那个...

penuel 1198

Sophus线性代数库学习大全

一个较好的李群和李代数的库是Sophus库,它很好的支持了SO(3),so(3),SE(3)和se(3)

几度春风里的博客 1431

AE IRasterCursor 获取栅格图层像素值

在编写使用栅格图层的代码时,常常要获取栅格图层的像素值(PixelValue)。如果想获取某一点的像素值,可以使用IRaster2中的getPixelValue方法。但如果想要获得的是图层中的某一块甚至整个图层,那么用getPixelValue方法就太过缓慢了。 如果利用IRasterCursor、IPixelBlock3接口,从内存入手,速度就会加快很多。说一下我对他们的理解。首先应用IRas...

weixin_34377919的博客 285

视觉里程计-直接法

直接法的引入  SLAM 主要分为视觉前端和优化后端。前端也称为 视觉里程计(VO)。它根据相邻图像的信息,估计出粗略的相机运动,给后端提供较好的初始值。   VO 的实现方法,按是否需要提取特征,分为特征点法的前端以及不提特征的直接法前端。尽管特征点法在视觉里程计中占据主流地位,研究者们认识它至少有以下几个缺点: 关键点的提取与描述子的计算非常耗时。实践当中,SIFT目前在CPU上是无法实...

zfjBIT的专栏 3675

【第23话:定位建图】SLAM视觉里程计——直接法详解

SLAM视觉里程计特征点法通过检测图像特征点(如FAST、ORB)并生成描述子进行匹配,利用几何约束(PnP、本质矩阵)估计相机运动。其核心步骤包括特征检测、描述、匹配和运动估计,依赖相机模型和重投影误差优化。优点是鲁棒高效,但受限于纹理缺失和动态场景。该方法在机器人导航中广泛应用,是SLAM系统的重要基础。

一碗白开水一 995

视觉SLAM理论到实践系列(七)——直接法视觉里程计

直接法视觉里程计

YangtseJin的博客 1361

视觉SLAM里程计项目详解与实践

SLAM技术在机器人和自动驾驶车辆领域占据着举足轻重的地位。它使得机器人能够在未知环境中实现自我定位,并且构建起周围环境的地图。这一过程无需外部帮助,完全依靠机器人上的传感器进行。

weixin_42602241的博客 937

SLAM之视觉里程计和回环检测

1.特征点法 特征点法有几个问题: a) 关键点的提取和描述子的计算非常耗时,如果保证SLAM 实时运行,需要 30 Frame/s,也就是每帧图像的处理时间约 30 ms,而实时性最好的 ORB也需要近 20 ms/Frame; b) 特征点法仅仅使用了图像中几百个特征点,占整个图像几十万个像素的很小部分, 丢弃了大量可以利用的图像信息; c) 特征点的寻找是根据人类自己设计的检测算法,并不完善,有些图像没有明显的纹理,有些图像的纹理比较相似,这种情况下特征点法的 VSLAM 就很难运行; d) 特征点法

格物致知 5358

深度学习、图像分类入门,从VGG16卷积神经网络开始

刚开始接触深度学习、卷积神经网络的时候非常懵逼,不知道从何入手,我觉得应该有一个进阶的过程,也就是说,理应有一些基本概念作为奠基石,让你有底气去完全理解一个庞大的卷积神经网络: 本文思路: 一、我认为学习卷积神经网络必须知道的几个概念: 1、卷积过程:   我们经常说卷积神经网络卷积神经网络,到底什么才是卷积?网络层卷积过程到底怎么实现?我们在这里借鉴了另一位博客大牛的动态图来给大

Errors_In_Life的博客 16万+

基于 WebGoat 平台的 SQL 注入攻击

基于 WebGoat 平台的 SQL 注入攻击

Labulongdong的博客 3522

视觉slam十四讲学习笔记(七)视觉里程计 2

1.理解光流法跟踪特征点的原理。2.理解直接法是如何估计相机位姿的。3.使用g2o进行直接法的计算。视觉SLAM十四讲ch7_2_哔哩哔哩_bilibili一、直接法的引出视觉slam十四讲学习笔记(六)视觉里程计 1-优快云博客介绍了使用特征点估计相机运动的方法。尽管特征点法在视觉里程计中占据主流地位,但有以下几个缺点:关键点的提取与描述子的计算非常耗时。实践当中,SIFT目前在CPU 上是无法实时计算的,而ORB也需要近20毫秒的计算。如果整个SLAM以30。

qq_53589322的博客 1347

BUUCTF--[OGeek2019]babyrop

才学习了基本的ROP流程,到处找题练,不过也没做出来几道题,以这道32位的题作为例题吧。 这道题是BUUCTF上pwn练习题里的[OGeek2019]babyrop。 代码审计 老规矩先checksec一下: 没有canary保护,nx保护开启排除shellcode可能性,FULL RELEO为地址随机化。 观察主函数,先设定了一个闹铃,到时间就会强制退出程序,解除闹铃的方法在上一篇博客中提到过了, 这道题中闹铃函数依然对我们解题起不到什么干扰作用。 主函数的思路大概是:生成一个随机数,把这个随机数作为

N1rvana的博客 6005

视觉SLAM8讲:视觉里程计3(光流法、直接法)

本文介绍了直接法在视觉里程计中的应用,重点讨论了光流法和直接法的原理、实现及优缺点。光流法基于灰度不变假设,通过最小化像素灰度误差估计特征点运动,而直接法则通过最小化光度误差直接优化相机位姿。文章详细推导了直接法的雅可比矩阵计算,并实现了单层和多层直接法。实验表明,直接法能避免特征提取耗时,适用于特征缺失场景,但受图像非凸性和光照变化影响较大。未来发展方向包括与特征点法结合(如LD-SLAM)和深度学习增强(如DeepVO)。本文提供了完整的代码实现和实验分析,为理解直接法在SLAM中的应用提供了实践基础。

weixin_45728280的博客 972

【Python计量】linearmodels面板数据回归(一)

我们以伍德里奇《计量经济学导论现代方法》的”第14章高级面板数据方法“的案例14.1为例,使用jtrain中的数据来进行固定效应估计。

mfsdmlove的博客 9277

YOLO26科研升级:BiFPN双向特征金字塔,让多尺度特征融合精度再突破

在目录下,新建AddModules文件夹,将bifpn.py放入其中。

LXY_LOVE36的博客 559

视觉SLAM学习——视觉里程计VO

视觉SLAM学习——视觉里程计VO 本文为个人学习视觉slam之后的一些片面的理解,希望能够对大家有所帮助 特征点估计 ORB特征检测 这里仅仅介绍ORB特征点的提取的算法过程: 1、FAST特征点提取(设提取n个角点): 1.1、选取像素p,获得其亮度为Ip 1.2、设置阈值T(如:Ip的 20%) 1.3、以p为中心选取半径为3的圆上的16个像素点 1.4、预测试像素:检测索引为1、5、9、1...

qq_37702859的博客 1126

YOLOv8与UNetV2融合:一种面向目标检测与图像分割的混合方法【附核心代码】

我们提出了一种基于YOLOv8和UNetV2相结合的混合架构。该架构利用YOLOv8作为主干进行目标检测,同时在YOLOv8的特征提取过程中,引入UNetV2来进行图像分割任务的增强。在该架构中,YOLOv8负责大范围目标检测和位置预测,而UNetV2则在检测网络的特征图基础上,通过其高效的上下文信息提取和细节恢复能力,进一步优化分割任务。在本文中,我们提出了一种改进YOLOv8的方法,通过引入图像分割网络UNetV2来提高图像分割检测性能。

一键难忘的博客 3525

视觉SLAM和激光SLAM的实现

激光 SLAM: 早在 2005 年的时候,激光 SLAM 就已经被研究的比较透彻,框架也已初步确定。激光 SLAM,是目前最稳定、最主流的定位导航方法。 激光 SLAM 地图构建 VSLAM(基于视觉的定位与建图): 随着计算机视觉的迅速发展,视觉 SLAM 因为信息量大,适用范围广等优点受到广泛关注。 视觉 SLAM 地图构建,图片来源:百度 AI (1)基于深度摄像机的 Vslam,跟...

戈扬的博客 8234

AI测试:手把手教你用AI创建流程图「喂饭教程」

本文介绍了如何利用next-ai-draw-io开源项目,通过AI快速生成专业流程图,提升测试工作效率。该工具支持自然语言交互,兼容多种AI模型(如OpenAI、Claude等),并集成draw.io图表引擎,实现实时编辑。

blues_C的博客 757

【即插即用模块】Transformer篇 | CVPR 2025 | SADA:多尺度+可变形采样,完美替换自注意力实现涨点!

文章摘要 本文提出了一种尺度自适应可变形注意力模块(SADA),用于处理图像修复中的结构化伪影问题。SADA通过多尺度多头机制捕获远程依赖关系,并引入可变形采样偏移来有效处理非均匀空间分布的模式。该模块适用于底层视觉任务,可替代ViT自注意力,实验证明能显著提升性能。代码已开源,支持即插即用集成。 核心特点: 多尺度头适应不同大小的重复模式 可变形偏移优化非均匀分布采样 轻量级设计,易于集成到现有架构 适用场景:图像去噪、超分辨率等底层视觉任务。

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 1431
上一篇: 视觉SLAM实践入门——(19)视觉里程计之多层光流法(光流金字塔)
floatinglong
博客等级 码龄9年 90粉丝 143原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值