使用单目相机前后帧特征点匹配进行3D深度估计的方法

news2024/11/15 12:28:39

在计算机视觉和机器人领域,三维空间感知是实现环境理解和交互的核心技术之一。特别是在资源受限的场合,使用针孔模型的单目相机进行深度估计成为了一种既经济又实用的解决方案。单目深度估计技术依赖于从连续视频帧中提取和匹配特征点,以估计场景中物体的三维位置。本文将探讨几种基于单目相机前后帧特征点匹配的3D深度估计方法。通过详细分析特征点匹配及其对深度恢复的贡献,我们旨在提供一种既精确又实用的深度估计技术,适用于从自动驾驶到增强现实等多种应用场景。

一、通过多视角立体几何 (Multi-view Stereo, MVS)的方法    

1. 原理

        特征点三角化是VSLAM中的一个基础问题,用于根据特征点在多个相机中的投影来恢复其在3D坐标中的位置。其的基本思想是:在某个相机中观测到的特征点可以通过相机位姿和观测向量来确定一条从相机中心出发的观测射线。当存在多个相机位姿观测时,会生成多条观测射线。理想情况下,这些观测射线会在空间中的一个点相交。通过求解所有观测射线的交点,可以确定特征点在3D空间中的位置。

        然而,在实际应用中,由于存在噪声,观测射线往往不会精确交于一点。为了确定特征点的坐标,有以下几种思路:

  1. 直观方法:寻找一个与所有观测射线距离都很近的三维点,将其作为特征点。

  2. 重投影误差最小化:认为误差源于二维图像观测,因此将特征点投影到每个相机平面,最小化所有二维投影点与对应观测点之间的距离(特征点的线性三角化(Triangulation)方法)。

  3. 在线滤波:三角化常常是在线进行的,即边获取观测边估计特征点。利用滤波器估计特征点的概率分布(通常为高斯分布),旧观测信息隐式存储在概率分布中。当新观测到来时,用其更新特征点的概率分布,节省计算量。这是滤波方法的基本思想。

        设3D特征点w_{\boldsymbol{p}_i}M个相机观测到,已知:相机姿态$\{_w^{c_j}R,{}^wt_{c_j}\}_{j=1,...,M}$,相机内参K,特征点在各相机的图像观测$\{z_{ij}\}_{j=1,...,M}$,求:特征点的3D坐标w_{\boldsymbol{p}_i}

$\left\{\{_{w}^{​{c_{j}}}R,{}^{w}t_{​{c_{j}}},z_{ij}\}_{j=1,...,M}\text{,}K\right\}\to{}^{w}p_{i}$

不考虑误差的情况有如下投影关系:

$s\begin{bmatrix}u_{ij}\\v_{ij}\\1\end{bmatrix}=K[_w^{c_j}R|-_w^{c_j}R^wt_{c_j}]\begin{bmatrix}x_i\\y_i\\z_i\\1\end{bmatrix}$

通过多视角立体几何 (Multi-view Stereo, MVS)的这类方法中,重点介绍思路2:重投影误差最小化。

设特征点齐次坐标$\mathbf{\bar{x}}=(x,y,z,1)$,在第j个相机的图像观测点齐次坐标为$\bar{\mathbf{z}}_{j}=(u_{j},v_{j},1)$投影矩阵为$P_j=K[R_j|t_j]$,投影模型如下:

$s\bar{\mathbf{z}}_j=s\begin{bmatrix}u_j\\v_j\\1\end{bmatrix}=K[R_j|t_j]\begin{bmatrix}x\\y\\z\\1\end{bmatrix}=P_j\bar{\mathbf{x}}=\begin{bmatrix}P_j^{(1)}\\P_j^{(2)}\\P_j^{(3)}\end{bmatrix}\bar{\mathbf{x}}$

\bar{\mathbf{z}}_j\times\bar{\mathbf{z}}_j=0可得\lfloor\bar{\mathbf{z}}_{j_\times}\rfloor P_j\bar{\mathbf{x}}=0,将其展开:

\begin{bmatrix}0&-1&v_j\\1&0&-u_j\\-v_j&u_j&0\end{bmatrix}\begin{bmatrix}P_j^{(1)}\\P_j^{(2)}\\P_j^{(3)}\end{bmatrix}\bar{\mathbf{x}}=\begin{bmatrix}-P_j^{(2)}+v_jP_j^{(3)}\\P_j^{(1)}-u_jP_j^{(3)}\\-v_jP_j^{(1)}+u_jP_j^{(2)}\end{bmatrix}\bar{\mathbf{x}}=\begin{bmatrix}0\\0\\0\end{bmatrix}

        所以只有两个线性无关的方程,每个相机观测有两个线性方程,将M个相机观测的约束方程合并得到2M个线性方程:

\underbrace{\left[\begin{array}{c} -P_1^{(2)} + v_1 P_1^{(3)} \\ P_1^{(1)} - u_1 P_1^{(3)} \\ -P_2^{(2)} + v_2 P_2^{(3)} \\ P_2^{(1)} - u_2 P_2^{(3)} \\ \vdots \\ -P_M^{(2)} + v_M P_M^{(3)} \\ P_M^{(1)} - u_M P_M^{(3)} \end{array}\right]}_{\mathbf{H}} \bar{\mathbf{x}} = 0

这里可以使用SVD求解,齐次坐标\bar{\mathrm{x}}即为 H的最小奇异值的奇异向量。

2. 代码

 代码来自:

orbslam2_learn/linear_triangular at master · yepeichu123/orbslam2_learn · GitHubicon-default.png?t=N7T8https://github.com/yepeichu123/orbslam2_learn/tree/master/linear_triangular

这里给代码添加了中文注释。

main.cpp

#include "linear_triangular.h"
// c++
#include <iostream>
#include <vector>
// opencv
#include <opencv2/calib3d/calib3d.hpp>
// pcl
#include <pcl-1.8/pcl/io/pcd_io.h>
#include <pcl-1.8/pcl/point_types.h>
#include <pcl-1.8/pcl/point_cloud.h>
#include <pcl-1.8/pcl/common/impl/io.hpp>
#include <pcl-1.8/pcl/visualization/cloud_viewer.h>

using namespace cv;
using namespace std;
using namespace pcl;

// 从图像中提取 ORB 特征并计算描述子
void featureExtraction( const Mat& img, vector<KeyPoint>& kpt, Mat& desp );

// 通过暴力匹配方法进行特征匹配
// 根据匹配得分的阈值选择一些良好的匹配对
void featureMatching( const Mat& rDesp, const Mat& cDesp, vector<DMatch>& matches,
                        const vector<KeyPoint>& rKpt, const vector<KeyPoint>& cKpt,
                        vector<Point2d>& goodRKpt, vector<Point2d>& goodCKpt );

// 通过对极几何(Epipolar geometry)估计运动
bool motionEstimation( const vector<Point2d>& goodRKpt, const vector<Point2d>& goodCKpt,
                       const Mat& K, Mat& Rcr, Mat& tcr );

// 比较我们的方法和 OpenCV 的三角化误差
void triangularByOpencv( const vector<Point2d>& RKpt, const vector<Point2d>& CKpt, 
                         const Mat& Trw, const Mat& Tcw, const Mat& K,
                         vector<Point2d>& nRKpt, vector<Point2d>& nCKpt,
                         vector<Point3d>& Pw3dCV );



int main( int argc, char** argv )
{
    // 确保我们有正确的输入数据
    if( argc < 3 )
    {
        cout << "Please enter ./linear_triangular currImg refImg" << endl;
        return -1;
    }

    // 从输入路径读取图像
    Mat currImg = imread( argv[1], 1 );
    Mat refImg = imread( argv[2], 1 );

    // 根据数据集设置相机参数
    Mat K = ( Mat_<double>(3,3) << 517.3, 0, 318.6, 0, 516.5, 255.3, 0, 0, 1 );

    // 检测 ORB 特征并计算 ORB 描述子
    vector<KeyPoint> ckpt, rkpt;
    Mat cdesp, rdesp;
    featureExtraction( currImg, ckpt, cdesp );
    featureExtraction( refImg, rkpt, rdesp );
    cout << "finish feature extration!" << endl;

    // 特征匹配
    vector<DMatch> matches;
    vector<Point2d> goodCKpt, goodRKpt;
    featureMatching( rdesp, cdesp, matches, rkpt, ckpt, goodRKpt, goodCKpt );
    cout << "finish feature matching!" << endl;

    // 运动估计
    Mat Rcr, tcr;
    if( !motionEstimation(goodRKpt, goodCKpt, K, Rcr, tcr) )
    {
        return -1;
    }
    cout << "finish motion estimation!" << endl;

    // 构造变换矩阵
    Mat Trw = Mat::eye(4,4,CV_64F);
    Mat Tcr = (Mat_<double>(3,4) << Rcr.at<double>(0,0), Rcr.at<double>(0,1), Rcr.at<double>(0,2), tcr.at<double>(0),
							     Rcr.at<double>(1,0), Rcr.at<double>(1,1), Rcr.at<double>(1,2), tcr.at<double>(1),
							     Rcr.at<double>(2,0), Rcr.at<double>(2,1), Rcr.at<double>(2,2), tcr.at<double>(2),
                                 0, 0, 0, 1);
    // 传播变换
    Mat Tcw = Tcr * Trw;
    Trw = Trw.rowRange(0,3).clone();
    Tcw = Tcw.rowRange(0,3).clone();                             
    cout << "Tcw = " << Tcw << endl;
    cout << "Trw = " << Trw << endl;
    cout << "现在我们通过线性三角化来三角化匹配对。" << endl;


    // 根据我们的博客开始三角化
    vector<Point3d> Pw3d;
    vector<Point2d> Pr2d;
    XIAOC::LinearTriangular myTriangular( K, Trw, Tcw );
    for( int i = 0; i < matches.size(); ++i )
    {
        Point3d pw;
        bool result = myTriangular.TriangularPoint( goodRKpt[i], goodCKpt[i], pw );
        // 检查三角化是否成功
        if( !result )
        {
            continue;
        }
        //cout << "pw in world coordinate is " << pw << endl;
        // 保存世界坐标系下的 3D 点
        Pw3d.push_back( pw );
        Pr2d.push_back( goodRKpt[i] );
    }

    // 根据 OpenCV 进行三角化
    vector<Point3d> Pw3dCV;
    vector<Point2d> nRKpt, nCKpt;
    triangularByOpencv( goodRKpt, goodCKpt, Trw, Tcw, K, nRKpt, nCKpt, Pw3dCV);


    // 重投影误差
    double errorOurs = 0, errorOpencv = 0;
    for( int i = 0; i < Pw3dCV.size(); ++i )
    {
        // 我们的三角化结果
        Point3d Pw = Pw3d[i];
        Point2d ppc = Point2d( Pw.x/Pw.z,Pw.y/Pw.z );

        // OpenCV 的三角化结果
        Point3d Pcv = Pw3dCV[i];
        Point2d ppcv = Point2d( Pcv.x/Pcv.z, Pcv.y/Pcv.z );

        // 参考归一化平面上的特征坐标
        Point2d ppr = nRKpt[i];

        errorOurs += norm( ppc - ppr );
        errorOpencv += norm( ppcv - ppr );
    }
    cout << "我们的总误差是 " << errorOurs << endl;
    cout << "OpenCV 的总误差是 " << errorOpencv << endl;
    cout << "我们的平均误差是 " << errorOurs / Pw3d.size() << endl;
    cout << "OpenCV 的平均误差是 " << errorOpencv / Pw3dCV.size() << endl;

    // 显示关键点
    Mat currOut;
    drawKeypoints( currImg, ckpt, currOut, Scalar::all(-1), DrawMatchesFlags::DRAW_RICH_KEYPOINTS );
    imshow( "CurrOut", currOut );
    waitKey(0);
    // 显示良好的匹配结果
    Mat goodMatchOut;
    drawMatches( refImg, rkpt, currImg, ckpt, matches, goodMatchOut );
    imshow( "goodMatchOut", goodMatchOut );
    waitKey(0);

    // 将 3D 点转换为点云进行显示
    PointCloud<PointXYZRGB>::Ptr cloud( new PointCloud<PointXYZRGB> );
    for( int i = 0; i < Pw3d.size(); ++i )
    {   
        PointXYZRGB point;
        Point3d p = Pw3d[i];
        Point2d pixel = Pr2d[i];
        point.x = p.x;
        point.y = p.y;
        point.z = p.z;
        point.r = refImg.at<Vec3b>(pixel.y,pixel.x)[0];
        point.g = refImg.at<Vec3b>(pixel.y,pixel.x)[1];
        point.b = refImg.at<Vec3b>(pixel.y,pixel.x)[2];
        cloud->push_back( point );
    }
    cout << "点云的数量是 " << cloud->size() << endl;
    visualization::CloudViewer viewer( "Viewer" );
    viewer.showCloud( cloud );
    while( !viewer.wasStopped() )
    {
        // loop loop loop~~~
    }
    
    cout << "成功!" << endl;
    return 0;
}



// 特征提取并计算描述子
void featureExtraction( const Mat& img, vector<KeyPoint>& kpt, Mat& desp )
{
    // 设置要提取的特征数量
    Ptr<FeatureDetector> detector = ORB::create( 10000 );
    Ptr<DescriptorExtractor> descriptor = ORB::create();
    detector->detect( img, kpt );
    descriptor->compute( img, kpt, desp );
}

// 通过暴力匹配方法进行特征匹配并找到良好的匹配
void featureMatching( const Mat& rDesp, const Mat& cDesp, vector<DMatch>& matches, 
                        const vector<KeyPoint>& rKpt, const vector<KeyPoint>& cKpt,
                        vector<Point2d>& goodRKpt, vector<Point2d>& goodCKpt )
{
    // 通过暴力匹配方法进行粗匹配,以汉明距离为度量
    Ptr<DescriptorMatcher> matcher = DescriptorMatcher::create( "BruteForce-Hamming" );
    vector<DMatch> initMatches;
    matcher->match( rDesp, cDesp, initMatches );
    cout << "初始匹配完成!" << endl;

    // 计算最大距离和最小距离
    double min_dist = min_element( initMatches.begin(), initMatches.end(), 
				       [](const DMatch& m1, const DMatch& m2) {return m1.distance<m2.distance;} )->distance;
    double max_dist = max_element( initMatches.begin(), initMatches.end(), 
				       [](const DMatch& m1, const DMatch& m2) {return m1.distance<m2.distance;} )->distance;

    // 找到良好的匹配并记录相应的像素坐标
    for( int i = 0; i < initMatches.size(); ++i )
    {
        if( initMatches[i].distance <= max(min_dist*2, 30.0) )
        {
            matches.push_back( initMatches[i] );
            goodRKpt.push_back( rKpt[initMatches[i].queryIdx].pt );
            goodCKpt.push_back( cKpt[initMatches[i].trainIdx].pt );
        }
    }
}

// 估计当前帧和参考帧之间的运动
bool motionEstimation( const vector<Point2d>& goodRKpt, const vector<Point2d>& goodCKpt,
                       const Mat& K, Mat& Rcr, Mat& tcr )
{
    // 根据对极几何计算本质矩阵
    Mat E = findEssentialMat( goodRKpt, goodCKpt, K, RANSAC );
    // 从本质矩阵恢复姿态
    int inliers = recoverPose( E, goodRKpt, goodCKpt, K, Rcr, tcr );
    // 确保我们有足够的内点进行三角化
    return inliers > 100;
}



// 比较我们的方法和 OpenCV 的三角化误差
void triangularByOpencv( const vector<Point2d>& RKpt, const vector<Point2d>& CKpt, 
                         const Mat& Trw, const Mat& Tcw, const Mat& K, 
                         vector<Point2d>& nRKpt, vector<Point2d>& nCKpt,
                         vector<Point3d>& Pw3dCV )
{
    Mat pts_4d;

    // 将特征点反投影到归一化平面
    for( int i = 0; i < RKpt.size(); ++i )
    {
        Point2d pr( (RKpt[i].x-K.at<double>(0,2))/K.at<double>(0,0),
                    (RKpt[i].y-K.at<double>(1,2))/K.at<double>(1,1) );
        Point2d pc( (CKpt[i].x-K.at<double>(0,2))/K.at<double>(0,0),
                    (CKpt[i].y-K.at<double>(1,2))/K.at<double>(1,1) );
        nRKpt.push_back( pr );
        nCKpt.push_back( pc );                
    }

    // 三角化点
    triangulatePoints( Trw, Tcw, nRKpt, nCKpt, pts_4d );

    // 从齐次坐标中恢复位置
    for( int i = 0; i < pts_4d.cols; ++i )
    {
        Mat x = pts_4d.col(i);
        x /= x.at<double>(3,0);
        Point3d pcv( x.at<double>(0,0), x.at<double>(1,0), x.at<double>(2,0));
        Pw3dCV.push_back( pcv );
    }  

    cout << "pw3d 的大小 =  " << Pw3dCV.size() << endl;
}

liner_triangular.cpp


#include "linear_triangular.h"
#include <iostream>

// 构造函数:设置相机参数和变换矩阵
XIAOC::LinearTriangular::LinearTriangular(const cv::Mat& K, const cv::Mat& Trw, const cv::Mat& Tcw):
    mK_(K), mTrw_(Trw), mTcw_(Tcw)
{
    
}

// 从两个视图的特征点三角化出 3D 点
// 输入:2D pr 和 2D pc,分别是参考帧和当前帧中的像素坐标
// 输出:3D Pw,即世界坐标系下的 3D 坐标
bool XIAOC::LinearTriangular::TriangularPoint(const cv::Point2d& pr, const cv::Point2d& pc, cv::Point3d& Pw )
{
    // 反投影到归一化平面
    UnprojectPixel( pr, pc );
    // 构造矩阵 A
    ConstructMatrixA( mPrn_, mPcn_, mTrw_, mTcw_ );
    // 获取 3D 位置
    if( CompBySVD( mA_, mPw_ ) )
    {
        Pw = mPw_;
        return true;
    }
    return false;
}

// 将像素点反投影到归一化平面
void XIAOC::LinearTriangular::UnprojectPixel(const cv::Point2d& pr, const cv::Point2d& pc)
{
    // X = (u-cx)/fx;
    // Y = (v-cy)/fy;
    mPrn_.x = (pr.x - mK_.at<double>(0,2))/mK_.at<double>(0,0);
    mPrn_.y = (pr.y - mK_.at<double>(1,2))/mK_.at<double>(1,1);
    mPrn_.z  = 1;
    
    mPcn_.x = (pc.x - mK_.at<double>(0,2))/mK_.at<double>(0,0);
    mPcn_.y = (pc.y - mK_.at<double>(1,2))/mK_.at<double>(1,1);
    mPcn_.z = 1;
}

// 根据我们的博客构造矩阵 A
void XIAOC::LinearTriangular::ConstructMatrixA(const cv::Point3d& Prn, const cv::Point3d& Pcn, const cv::Mat& Trw, const cv::Mat& Tcw )
{

    // ORBSLAM 方法
    /* cv::Mat A(4,4,CV_64F);
     A.row(0) = Prn.x*Trw.row(2)-Trw.row(0);
     A.row(1) = Prn.y*Trw.row(2)-Trw.row(1);
     A.row(2) = Pcn.x*Tcw.row(2)-Tcw.row(0);
     A.row(3) = Pcn.y*Tcw.row(2)-Tcw.row(1);
     A.copyTo( mA_ );
     */

    // 我博客中的原始方法
     cv::Mat PrnX = (cv::Mat_<double>(3,3) << 0, -Prn.z, Prn.y, 
									 Prn.z, 0, -Prn.x,
									 -Prn.y, Prn.x, 0);
     cv::Mat PcnX = (cv::Mat_<double>(3,3) << 0, -Pcn.z, Pcn.y, 
									 Pcn.z, 0, -Pcn.x,
									 -Pcn.y, Pcn.x, 0);
     // A = [prX*Trw; pcX*Tcw ]
     // APw = 0
     cv::Mat B = PrnX * Trw;
     cv::Mat C = PcnX * Tcw;
     cv::vconcat( B, C, mA_ );
     
}

//  通过计算 SVD 求解问题
bool XIAOC::LinearTriangular::CompBySVD(const cv::Mat& A, cv::Point3d& Pw )
{
    if( A.empty() )
    {
	    return false;
    }
    // 计算矩阵 A 的 SVD
    cv::Mat w, u, vt;
    cv::SVD::compute( A, w, u, vt, cv::SVD::MODIFY_A|cv::SVD::FULL_UV );

    // 归一化
    cv::Mat Pw3d;
    Pw3d = vt.row(3).t();
    if( Pw3d.at<double>(3) == 0 )
    {
	    return false;
    }
    Pw3d = Pw3d.rowRange(0,3)/Pw3d.at<double>(3);

    // 保存位置的值
    Pw.x = Pw3d.at<double>(0);
    Pw.y = Pw3d.at<double>(1);
    Pw.z = Pw3d.at<double>(2);

    return true;
}

// 检查两个视图之间特征点的角度
bool XIAOC::LinearTriangular::CheckCrossAngle( const cv::Point3d& Prn, const cv::Point3d& Pcn, 
								const cv::Mat& Trw, const cv::Mat& Tcw )
{
    // 待办:通过角度检查是否适合三角化
}

// 检查我们三角化得到的点的深度是否正确
bool XIAOC::LinearTriangular::CheckDepth( const cv::Point3d& Pw3d )
{
    // 待办:通过深度检查是否适合接受
}

CMakeList.txt 

cmake_minimum_required( VERSION 2.8 )
project( linear_triangular )

set( CMAKE_BUILD_TYPE "Release" )
set( CMAKE_CXX_FLAGS "-std=c++11" )
set( EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin )
set( LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib )

find_package( OpenCV 3.0 REQUIRED )
include_directories( ${OpenCV_INCLUDE_DIRS} )

find_package( PCL REQUIRED )
include_directories( ${PCL_INCLUDE_DIRS} )
link_directories( ${PCL_LIBRARY_DIRS} )
add_definitions( ${PCL_DEFINITIONS} )

include_directories( ${PROJECT_SOURCE_DIR}/include )
#add_subdirectory( ${PROJECT_SOURCE_DIR} )

add_executable( linear_triangular src/linear_triangular.cpp src/main.cpp ) 
target_link_libraries( linear_triangular ${OpenCV_LIBS} ${PCL_LIBRARIES} ) 

3. 结果 

二、通过端到端深度学习方法估计单目深度---DPT

      Ranftl et al. (2020) - 提出了一种基于Transformer的深度估计方法,称为Vision Transformer for Dense Prediction (DPT)。该方法利用了Transformer强大的全局建模能力,通过self-attention机制学习像素之间的长距离依赖关系。同时,他们还设计了一种多尺度融合策略,以结合不同层次的特征信息。DPT在多个数据集上都取得了最先进的性能。

1. 原理

        根据论文 "Vision Transformers for Dense Prediction" 的内容,Dense Prediction Transformers (DPT) 的主要原理可以总结如下:DPT在编码器-解码器架构中利用视觉transformer (ViT)作为骨干网络,用于深度估计和语义分割等密集预测任务。与逐步下采样特征图的卷积骨干网络不同,ViT骨干网络通过对分块/令牌进行操作,在整个过程中保持恒定的空间分辨率。在每个阶段,ViT通过自注意力机制具有全局感受野。这允许在高分辨率下捕获长距离依赖关系。来自不同ViT阶段的令牌被重组为多尺度的类图像特征。然后,卷积解码器融合这些特征并对其进行上采样,以获得最终的密集预测。通过在每个阶段保持高分辨率并具有全局感受野,与全卷积网络相比,DPT能够提供细粒度和全局连贯的预测。当有大量训练数据可用时,DPT在先前技术的基础上大幅改进,在具有挑战性的数据集上设置了新的最先进水平。
        总之,DPT的创新之处在于使用了ViT骨干,该骨干在每个阶段以全局上下文处理高分辨率特征,然后由卷积解码器将其合并为细粒度预测,从而在密集预测任务上显著提高了性能。
不是搞深度学习的我也看不懂啊,读者自行分别吧。这个坑后面再填

2. 代码

代码连接: https://github.com/intel-isl/DPTicon-default.png?t=N7T8https://github.com/intel-isl/DPT论文连接:

https://arxiv.org/pdf/2103.13413icon-default.png?t=N7T8https://arxiv.org/pdf/2103.13413

3. 结果


 

参考文章:

 VSLAM中的特征点三角化 - 知乎 (zhihu.com)

SLAM--三角化求解3D空间点_jacobisvd(eigen::computefullv).matrixv()-CSDN博客

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

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

相关文章

企业大模型如何成为自己数据的“百科全书”?

作者 | 郭炜 编辑 | Debra Chen 在当今的商业环境中&#xff0c;大数据的管理和应用已经成为企业决策和运营的核心组成部分。然而&#xff0c;随着数据量的爆炸性增长&#xff0c;如何有效利用这些数据成为了一个普遍的挑战。 本文将探讨大数据架构、大模型的集成&#xff0…

线上网页点击菜单没有反应 报错ChunkLoadError:Loading chunk chunk-***** failed

现象 点击菜单无反应并且控制台报错Loading chunk chunk-***** failed 具体错误现象截图如下 分析 在线上页面已经打开的情况下&#xff0c;重新打包部署了前端项目。每次打包&#xff0c;js文件的hash值都会发生改变&#xff0c;因为我们的路由采用了懒加载&#xff0c;未…

吴恩达深度学习笔记:优化算法 (Optimization algorithms)2.7

目录 第二门课: 改善深层神经网络&#xff1a;超参数调试、正 则 化 以 及 优 化 (Improving Deep Neural Networks:Hyperparameter tuning, Regularization and Optimization)第二周&#xff1a;优化算法 (Optimization algorithms)2.7 RMSprop 第二门课: 改善深层神经网络&am…

代码复现|Demucs Music Source Separation

一、背景介绍 Demucs是一个开源的音源分离项目。 Demucs在算法层面前后经历了三次大版本的进化&#xff0c;最原始的V1版本是&#xff1a;编解码LSTM。具体算法原理图如下所示。该版本在时域进行音源分离。关于阅读笔记请点击这篇文章。 V1版本原理图 V2版本是同时使用时域和频…

小白必看:新手学编程必会的100个代码

前言 我记得刚开始接触编程的时候&#xff0c;觉得太难了。 也很好奇&#xff0c;写代码的那些人也太厉害了吧&#xff1f;全是英文的&#xff0c;他们的英文水平一定很好吧&#xff1f; 他们是怎么记住这么多代码格式的&#xff1f;而且错了一个标点符号&#xff0c;整个程…

Flash attention入门

一、目录 flash attentionGPU运算流程flash attention 原理flash attention 与 standard attention 时间/内存 对比。flash attention 算法实现比较flash attention 计算、memory-efficient attention 等不同内核下用时 二、实现 flash attention 目的&#xff1a; 提高运行速…

NGM-SLAM:首创融合神经辐射场子图的3DGS-SLAM,问鼎SOTA!

论文标题&#xff1a; NGM-SLAM: Gaussian Splatting SLAM with Radiance Field Submap 论文作者&#xff1a; Mingrui Li, Jingwei Huang, Lei Sun Aaron, Xuxiang Tian, Tianchen Deng, Hongyu Wang 导读&#xff1a; 3DGS技术因其性能卓越而备受关注&#xff0c;3DGS-SLA…

GPT-4o 炸裂发布!你竟然还没用上?(附详细教程)

今天AI界的爆炸新闻非chatgpt-4o莫属&#xff0c;从早上到现在随处可见的文章推送&#xff0c;视频推送。 大家或多或少都有耳闻了&#xff0c;今天主要讲一讲我们普通人到底怎么用&#xff1f;如果不氪金行不行&#xff1f;我就想体验一下可不可以&#xff1f;带着问题往下看 …

Python 海龟画图(Turtle)命令大全

移动和绘制 forward() | fd() 使用语法: ​​turtle.forward(距离)​​ ​​turtle.fd(距离)​​ 参数说明: 距离 一个数字 (整数 或者 浮点) (注:单位是像素) 代码示例: import turtle turtle.forward(200) 效果: backward () | bk() | back() 使用语法: ​…

掏心经验分享,软考中项0基础入门篇!

想备考下半年中项&#xff08;系统集成项目管理工程师&#xff09;的朋友&#xff0c;不知道如何了解软考中项&#xff0c;今天给大家整理一篇关于我自己在备考软考时的一些考量和踩过的一些坑。&#xff08;无广&#xff0c;放心看&#xff09; 很多小伙伴总是听大家说软考中…

你是学会了还是学废了:Elasticsearch 7 集群拷贝到其它环境如何重置密码

欢迎您关注我的公众号【尚雷的驿站】 公众号&#xff1a;尚雷的驿站 CSDN &#xff1a;https://blog.csdn.net/shlei5580 墨天轮&#xff1a;https://www.modb.pro/u/2436 PGFans&#xff1a;https://www.pgfans.cn/user/home?userId4159 前言 本文描述了将生产ES集群打包拷贝…

线性模型之岭回归的用法

实战&#xff1a;使用岭回归模型 完整代码&#xff1a; import numpy as np import matplotlib.pyplot as plt from sklearn.linear_model import LinearRegression from sklearn.datasets import make_regression from sklearn.model_selection import train_test_split fro…

平芯微PW4056HH中文规格书

概述 PW4056HH 是一款完整的采用恒定电流/恒定电压的高压、大电流、单节锂离子电池线性充电 IC。充电电流可达 1A。输入 MAX 低工作电压 3.75V&#xff0c;降低充电功耗&#xff0c;提高效率。 PW4056HH 采用了内部 PMOS 架构&#xff0c;加上防反充电路&#xff0c;不需要外部…

Java开发大厂面试第04讲:深入理解ThreadPoolExecutor,参数含义与源码执行流程全解

线程池是为了避免线程频繁的创建和销毁带来的性能消耗&#xff0c;而建立的一种池化技术&#xff0c;它是把已创建的线程放入“池”中&#xff0c;当有任务来临时就可以重用已有的线程&#xff0c;无需等待创建的过程&#xff0c;这样就可以有效提高程序的响应速度。但如果要说…

Linux服务器lvm磁盘管理fdisk和df磁盘大小不同修改

服务器端由于硬盘是通过VCenter原来100G磁盘复制的虚拟机,复制完成后,原来100G的磁盘通过选择 磁盘重新复制出150G的磁盘,开机后发现还是原来的100G的磁盘,通过fdisk -l 查看有个sdb是150G, 但是已经划转的lvm盘只有100G, 通过df查看也是原来的100G: pvs查看pv里也是10…

【链路层和局域网】

文章目录 链路层和局域网网络节点的连接方式数据链路层和局域网链路层导论链路层&#xff1a;上下文链路层服务链路层在哪里实现&#xff1f;适配器通信错误检测奇偶校验校验和&#xff1a;CRC&#xff08;循环冗余校验&#xff09;多点访问链路和协议多路访问协议MAC&#xff…

立创EDA绘制PCB电路板

1、绘制好原理图后&#xff0c;点击设计---原理图转PCB&#xff0c;生成PCB文件 2、将元器件拖入电路板方框内&#xff0c;摆放布局并使用工具栏布线、放置过孔及丝印 3、然后顶层和底层铺铜 4、后面就可以生成制板文件发送嘉立创制板了。

基于国产LoRa的智慧农业解决方案--ASR6601、SX1278

我国《数字乡村发展战略纲要》明确指出“要推进农业数字化转型”&#xff0c;加快推广云计算、大数据、物联网、人工智能在农业生产经营管理中的运用。 然而&#xff0c;目前我国的农业数字化转型还面临着诸多挑战。我国整体农业机械化程度和自动化控制水平仍然较低。由于农田面…

ubuntu quota配置磁盘配额

安装quota工具:sudo apt-get install quota这条命令会安装quota工具&#xff0c;它用于在Linux系统中管理和强制执行磁盘配额。编辑用户quota:sudo edquota -u <username> /data这条命令会打开默认的文本编辑器&#xff0c;允许你为用户liushenshen在/data文件系统上设置…