orbslam2代码解读(2):tracking跟踪线程

news2024/12/28 19:52:47

书接上回,mpTracker->GrabImageMonocular(im,timestamp)函数处理过程:

  1. 如果图像是彩色图,就转成灰度图
  2. 如果当前帧是初始化的帧,那么在构建Frame的时候,提取orb特征点数量为正常的两倍(目的就是能够在初始化的时候有更多匹配点对),如果是普通帧,就正常构建Frame。
  3. 接着就是调用tracking线程中的Track()函数。
  4. 返回当前图像帧的位姿估计结果。

上一篇文章已经讲完了构造Frame并且进行图像数据处理的过程,这篇主要解读tracking跟踪线程。

MonocularInitialization()单目初始化

step1

记录第一帧图像的关键点数量,只有关键点数量大于100才认为是有效的初始帧。

step2

记录第二帧图像的关键点数量,也是第二帧特征点数量大于100,就进行初始化。简而言之,要有连续两帧图像的特征点数量都大于100,才进行后续初始化操作

step3

构建一个ORBmatcher对象,很关键的一个对象,主要是用来搜索两帧图像之间的特征点匹配关系。

  ORBmatcher matcher(
      0.9,        //最佳的和次佳特征点评分的比值阈值,这里是比较宽松的,跟踪时一般是0.7
      true);      //检查特征点的方向

然后就是通过matcher.SearchForInitialization函数找到两帧图像之间的特征点匹配关系
这个函数的步骤如下:

  1. 构建旋转直方图。就是通过大多数特征点的角度一致性来过滤外点。
  2. 遍历第一帧的所有特征点,然后根据半径窗口搜索在第二帧的所有候选匹配特征点,过程如下图所示:(图中红色的圆圈就是搜索半径)
    在这里插入图片描述
  3. 接着遍历步骤2中第二帧的所有候选匹配特征点,计算它们的描述子距离(因为是二进制存储,所以用的是汉明距离,即不同位数的个数),找到最优的和次优的。
  4. 对最优和次优的结果进行检查,当最优的距离小于阈值且最优距离小于次优距离的百分之90(比较宽松的阈值),这个是初始化ORBmatcher对象对象的参数。都通过检查后记录最佳的特征点匹配关系。
  5. 然后对这个特征点匹配的结果计算两个特征之间的角度差(这个角度是通过前面灰度质心法算出来的),加入到旋转直方图中。
  6. 结束遍历第一帧的所有特征点的大循环。筛选旋转直方图中不符合大多数旋转方向的特征点匹配关系(旋转直方图过滤)。

step4

如果第一帧和第二帧的匹配点对太少(代码中阈值是匹配点对小于100),那么就需要重新选初始化的图像帧

step5

通过本质矩阵F或者通过单应矩阵H来得到两帧图像的相对运动,得到相对运动后将平移t进行归一化(即将t的模取为1),通过这个R和t的关系,进行三角化恢复出三维地图点(这个时候单目slam系统的尺度就固定了,后续的slam尺度都是以初始化时侯的尺度为基准,尺度漂移也是在这个初始化尺度上去漂移)。

主要是通过mpInitializer->Initialize函数实现,步骤如下:

  1. 初始化一个用于RANSAC的二维数组,里面存的是有匹配关系的特征点索引。为了方便理解画了个简易表格去说明,最后就是在这个二维数组中用随机数去填充,类似随机选取8个点的过程。
    在这里插入图片描述

  2. 为了并行计算F矩阵和H矩阵,代码中是分别开了两个线程去计算F矩阵和H矩阵。(F矩阵和H矩阵的秩和性质后续开独立的文章去说明)
    计算H矩阵的过程如下步骤:
    2.1.1 将当前帧和参考帧中的特征点坐标进行归一化,归一化的目的可以参考SLAM入门之视觉里程计(4):基础矩阵的估计,总的来说就是可以减少噪声的干扰,大大提升8点法的精度。(PS其实对这里的归一化没有深刻的认识,所以后续还需要学习这部分
    2.1.2 根据设定的RANSAC迭代次数(200),用对应的8个特征点计算H矩阵,其实计算H矩阵也是如视觉slam14讲上的类似,根据8对点构造出一个线性方程,然后就是用SVD分解来求解这个超定的齐次线性方程,求解的推导可以参考这篇文章:SVD在求解超定齐次线性方程组Ax=0中的应用。最优解就是 V T V^T VT的第9个奇异向量。
    在这里插入图片描述
    2.1.3 计算完H矩阵就利用重投影误差为当次RANSAC的结果评分,评分的规则其实就是卡方分布,因为重投影的形式与卡方分布检验的形式很像。得分的代码如下:

        // Step 2.3 用阈值标记离群点,内点的话累加得分
        if(chiSquare1>th)
            bIn = false;    
        else
            // 误差越大,得分越低
            score += th - chiSquare1;
    

    th就是卡方分布的阈值。重投影误差有双向投影(第一帧图像往第二帧图像投影得到一个分数,第二帧图像往第一帧图像投影得到又一个分数),分数都加到score中,最终就得到这8个点构成H矩阵的得分(得分越高,误差越小)。

    2.1.4 完成RANSAC迭代次数(200)后,选择得分最高的一组点计算出的H矩阵作为结果输出。

    计算F矩阵的过程如下步骤:
    2.2.1 一样需要将特征点归一化,跟上面计算H矩阵的一样。
    2.2.2 根据设定的RANSAC迭代次数(200),用对应的8个特征点计算F矩阵。只是构建的线性方程有区别。但是求解方式都是一样的都是基于SVD分解来求解一个最优解。
    在这里插入图片描述
    2.2.3 计算完F矩阵就利用点到直线距离(与重投影误差差不多)为当次RANSAC的结果评分,评分的规则其实就是卡方分布。
    2.2.4 完成RANSAC迭代次数(200)后,选择得分最高的一组点计算出的F矩阵作为结果输出。

  3. 计算完H矩阵和F矩阵,根据得分情况,来选择H矩阵来恢复R,t和三角化的点。程序中更偏向用H矩阵来恢复。

通过H矩阵恢复R,t和三角化的点

程序参考的是Motion and structure from motion in a piecewise plannar environment这篇文章。

  1. 首先将H矩阵进行奇异值分解,得到H矩阵分解出来的8组解
  2. 对8组解进行验证,选择在相机前方最多3D点的解作为最优解。
  3. 最终最优解需要满足:
    (1)最优解的3D点个数明显大于次优解的3D点
    (2)视差角大于规定的阈值
    (3)最优解的3D点个数大于规定的最小的被三角化的点数量
    (4)最优解的3D点个数要足够多,达到总数的90%以上

通过F矩阵恢复R,t和三角化的点

过程序上面差不多,只是F矩阵恢复出来的只有四组解,所以只需要对4组解进行验证。

step6

初始化成功了,就把初始化的参考帧设置为第一个关键帧,第二帧设置为第二个关键帧。这两个关键帧都分别计算Bow(词袋后续讲解)。最后将三角化得到的三维点加入到全局地图中,为了后续pnp求解两帧图像运动。

StereoInitialization()双目或者RGBD的初始化

这个过程很简单,因为在前面预处理的时候都得到了当前图像的3D点了,只要特征点的数量大于500个,直接就以当前帧作为原点,直接把特征点对应的3D点加入到全局地图中即可。

TrackReferenceKeyFrame()。跟踪参考关键帧

这个函数的使用时机:

  1. 当运动模型为空,即刚刚完成初始化开始时的几帧,并没办法得到运动模型,所以只能选最近的参考关键帧来跟踪。
  2. 当刚刚完成重定位,也是需要依靠参考关键帧来跟踪。
    在这里插入图片描述

step1

将当前帧的描述子转化为BoW向量。这里详细展开说一下词袋模型。

视觉词袋模型

本质上词袋模型(bag of words),指的就是用“图像上有哪几种特征”来描述一幅图像。在图像上words代表着已经提取了的特征点,然后把这些words组成一个bag就可以通过这个词袋去搜索相似的图像从而构成回环。
在这里插入图片描述
词袋模型的使用分为两个过程:

  1. 我们通过某种方式得到了一本字典(orbslam2通过离线训练的方式,用了很多图像,都提取了ORB特征点,将描述子通过k-means进行聚类,根据设定的树分支数和深度,从叶子节点开始聚类,一直到根节点,最后得到一个非常大的vocabulary tree)。
  2. 在线图像生成BoW向量。当我们在线跑SLAM的时候,就是根据当前帧的图像中提取的ORB特征点,遍历每个ORB特征点,从离线创建好的vocabulary tree中开始找自己的位置,从根节点开始,用该描述子和每个节点的描述子计算汉明距离,选择汉明距离最小的作为自己所在的节点,一直遍历到叶子节点。
    在这里插入图片描述
理解词袋向量BowVector

它内部实际存储的是这个std::map<WordId, WordValue>,其中 WordId 和 WordValue 表示Word在所有叶子中距离最近的叶子的id 和权重(后面解释)。同一个Word id 的权重是累加更新的。

理解特征向量FeatureVector

内部实际是个std::map<NodeId, std::vector<unsigned int>>

  1. 其中NodeId 并不是该叶子节点直接的父节点id,而是距离叶子节点深度为level up对应的node 的id,对应上面 vocabulary tree 图示里的 Word’s node id。为什么不直接设置为父节点?因为后面搜索该Word 的匹配点的时候是在和它具有同样node id下面所有子节点中的Word 进行匹配,搜索区域见图示中的 Word’s search region。所以搜索范围大小是根据level up来确定的,level up 值越大,搜索范围越广,速度越慢;level up 值越小,搜索范围越小,速度越快,但能够匹配的特征就越少。
  2. 另外 std::vector 中实际存的是NodeId 下所有特征点在图像中的索引。FeatureVector主要用于不同图像特征点快速匹配,加速几何关系验证,比如ORBmatcher::SearchByBoW 中就是通过NodeId 来进行快速匹配。
词袋模型相似度计算

这个部分我主要参考视觉slam14将来描述。正如前面介绍的词袋向量和特征向量,我们可以清楚特征向量主要是用于两个图像之间快速找到特征点匹配关系的,而相似度计算是通过词袋向量。
因为不一定每个单词重要性都是一样的(很多常出现的单词往往不是很重要),为了对单词的重要性或区分性加以评估,一般给单词不同的权值来得到更好的效果。
TF的意思是某单词在一幅图像上经常出现,它的区分度就高,而IDF的思想,是某个单词在字典中出现的频率越低,分类图像时,区分度越高。
IDF在建立字典的时候就计算:统计某个叶子节点 w i w_i wi中特征数量相对于所有特征数量的比例,作为IDF的值。假设所有特征数量为n, w i w_i wi数量为 n i n_i ni,那么这个单词的IDF就是:
I D F i = l o g ( n / n i ) {IDF}_i=log(n/n_i) IDFi=log(n/ni)
TF就是指某个特征出现在一张图像中出现频率。假设图像A中单词 w i w_i wi出现了 n i n_i ni次,而一共出现的单词次数是n,那么TF的值为:
T F i = n i / n TF_i = n_i/n TFi=ni/n
所以最终叶子节点 w i w_i wi的权重就是 I D F i {IDF}_i IDFi T F i TF_i TFi的积。
η i = T F i × I D F i \eta_i=TF_i × {IDF}_i ηi=TFi×IDFi
所以这时候每个叶子节点都会有一个权重,如果这时候给出两个图像A、B,它们的词袋向量可以表示为:
A = { ( w 1 , η 1 ) , ( w 2 , η 2 ) , … , ( w N , η N ) } =  def  v A A=\left\{\left(w_1, \eta_1\right),\left(w_2, \eta_2\right), \ldots,\left(w_N, \eta_N\right)\right\} \stackrel{\text { def }}{=} \boldsymbol{v}_A A={(w1,η1),(w2,η2),,(wN,ηN)}= def vA
B = { ( w 1 , η 1 ) , ( w 2 , η 2 ) , … , ( w N , η N ) } =  def  v B B=\left\{\left(w_1, \eta_1\right),\left(w_2, \eta_2\right), \ldots,\left(w_N, \eta_N\right)\right\} \stackrel{\text { def }}{=} \boldsymbol{v}_B B={(w1,η1),(w2,η2),,(wN,ηN)}= def vB
slam14讲中,给出的一种计算得分的公式:
s ( v A − v B ) = 2 ∑ i = 1 N ∣ v A i ∣ + ∣ v B i ∣ − ∣ v A i − v B i ∣ s\left(\boldsymbol{v}_A-\boldsymbol{v}_B\right)=2 \sum_{i=1}^N\left|\boldsymbol{v}_{A i}\right|+\left|\boldsymbol{v}_{B i}\right|-\left|\boldsymbol{v}_{A i}-\boldsymbol{v}_{B i}\right| s(vAvB)=2i=1NvAi+vBivAivBi
在orbslam的代码中,当两个图像之间某个 WordId 相同时,那么就将它们的权重进行运算,在orbslam中实现了很多的得分形式,有L1,L2和卡方得分等等。反正都是得分越高,那么这两帧图像的相似度越高,越容易构建回环。

orbslam中的直接索引表和倒排索引表

直接索引表是以图像为对象,存储图像中的特征点与字典中某个节点(node)的关联关系。直接索引表的作用就是给定了两个Frame对象或者KeyFrame对象,直接根据这个特征向量FeatureVector,找到属于同一个节点(node)下的特征点,然后遍历这些特征点计算描述子的相似得分即可快速找到匹配点对,而不是直接去遍历所有的特征点。

倒排索引表是以单词(word)为对象,存的是word的权重和这个word在哪个图像出现。倒排索引表的作用就是检测回环的时候使用,能够方便对比哪些图像有共同的word。
在这里插入图片描述

step2

通过词袋BoW加速当前帧与参考帧之间的特征点匹配。所以上面讲词袋模型的时候,特征向量FeatureVector就是用来快速得到匹配点对的。记录特征匹配成功后当前帧每个特征点对饮的MapPoint(来自参考帧),最后通过旋转直方图取出误匹配的点对。

step3

用上一帧位姿当作当前帧位姿的初始值。这样做的目的:因为根据参考关键帧跟踪的情况只在刚刚完成初始化或者重定位,没有匀速运动模型,所以就用上一帧位姿来当作这一帧预测位姿的初始值。

step4

通过3D-2D的重投影误差(pnp)来求解当前帧到参考帧之间的位姿。代码中就是通过g2o图优化的方式来实现(只优化位姿):

  1. 定义顶点g2o::VertexSE3Expmap,只有一个顶点就是当前帧的 T c w T_{cw} Tcw
  2. 定义边,如果是单目就是g2o::EdgeSE3ProjectXYZOnlyPose,如果是双目或者rgbd就是g2o::EdgeStereoSE3ProjectXYZOnlyPose,都是一元边。后续就是把边的各种属性:空间点位置,相机内参,观测,鲁棒核等信息
  3. 最后就是迭代优化,分四次迭代,每次迭代10次。期间需要根据卡方分布来去除外点,最终优化出当前帧的位姿。

step5

根据优化期间得到的外点标记,去除当前帧的地图点mCurrentFrame.mvpMapPoints中的外点。

TrackWithMotionModel()。匀速模型跟踪

这个函数是跟踪线程使用最频繁的函数,因为这个函数是用于两个普通帧之间跟踪,输出当前帧的位姿。

step1

更新上一帧的位姿,就是将上一帧在世界坐标系下的位姿计算出来:
l a s t T w o r l d = l a s t T r e f ∗ r e f T w o r l d ^{last}T_{world} = ^{last}T_{ref} * ^{ref}T_{world} lastTworld=lastTrefrefTworld
如果是双目或者rgbd的情况,会根据上一帧有深度的特征点创建临时的地图点,这些地图点只是用来跟踪,不会加入到全局地图中,所以跟踪成功后会删除。

step2

根据上一帧估计的速度,用恒速运动模型,预测当前帧的位姿。本质上就是假设相邻的位姿变化率相同
在这里插入图片描述

step3

用上一帧的地图点进行投影匹配。将上一帧的地图点根据预测的当前帧位姿,投影到当前帧的图像上,根据设定的搜索半径去搜索匹配点对。
通过matcher.SearchByProjection函数来找匹配关系
这个函数的步骤如下:

  1. 构建旋转直方图。就是通过大多数特征点的角度一致性来过滤外点。
  2. 遍历第一帧的所有特征点,然后根据半径窗口搜索在第二帧的所有候选匹配特征点,过程如下图所示:(图中红色的圆圈就是搜索半径)
    在这里插入图片描述
  3. 接着遍历步骤2中第二帧的所有候选匹配特征点,计算它们的描述子距离(因为是二进制存储,所以用的是汉明距离,即不同位数的个数),找到最优的和次优的。
  4. 对最优和次优的结果进行检查,当最优的距离小于阈值且最优距离小于次优距离的百分之90(比较宽松的阈值),这个是初始化ORBmatcher对象对象的参数。都通过检查后记录最佳的特征点匹配关系。
  5. 然后对这个特征点匹配的结果计算两个特征之间的角度差(这个角度是通过前面灰度质心法算出来的),加入到旋转直方图中。
  6. 结束遍历第一帧的所有特征点的大循环。筛选旋转直方图中不符合大多数旋转方向的特征点匹配关系(旋转直方图过滤)。

大部分的过程都跟单目初始化的时候很相似。都是通过半径搜索的方式去找匹配点对。如果匹配点太少,orbslam中的程序还会加大半径范围去搜索匹配点对。

step4

通过3D-2D的重投影误差(pnp)来求解当前帧到参考帧之间的位姿。代码中就是通过g2o图优化的方式来实现(只优化位姿)。过程是跟TrackReferenceKeyFrame()跟踪参考关键帧的step4是一样的,因为都是调用同一个函数且只优化当前帧位姿,这里不再赘述。

step5

根据g20优化过程中知道的外点关系,直接剔除mCurrentFrame.mvpMapPoints中的外点。

Relocalization()。重定位

这个函数主要是当跟踪跟丢了的时候,能够让当前帧重新与之前的地图重新连接上的方法。也可以在基于先建地图定位时,初始化定位信息的时候用。

step1

计算当前帧特征点的词袋向量(BoW)

step2

用词袋模型找到与当前帧相似的候选关键帧。通过mpKeyFrameDB->DetectRelocalizationCandidates函数来找到与当前帧相似的候选关键帧,这个过程跟找回环关键帧是类似的。
这个函数的步骤如下:

  1. 根据前面介绍的orbslam2中的倒排索引表,就是找出和当前帧有公共word的所有关键帧。
  2. 统计上述关键帧中与当前帧具有单词最多的单词数,用来设定阈值1筛选(最小公共单词数为最大公共单词数的0.8倍)
  3. 再次遍历上述关键帧,并且挑出符合阈值的关键帧并且计算它们与当前帧的相似得分(通过词袋向量来计算)
  4. 单单计算当前帧和某一关键帧的相似性是不够的,这里将与关键帧共视程度最高的前十个关键帧归为一组,计算组的累计得分,并且用于设定阈值2来筛选关键帧。(最高得分的0.75倍)
  5. 再次遍历候选关键帧,找到最终符合要求的候选关键帧。
    请添加图片描述

step3

遍历从上面过程得到的候选关键帧,通过词袋模型进行快速搜索匹配点对,如果匹配点对数量足够就初始化EPnP求解器。如果EPnP没有超过迭代次数上限,那么说明能够得到一个比较好的初值,再继续用g2o优化当前帧的位姿,内点数如果也满足要求那么就证明重定位成功,把优化后位姿设置为当前帧位姿,并且将当前帧的地图点进行外点去除。
除了这个重定位成功的过程,程序还有很长的代码是面对一些不理想的情景,比如内点数不符合要求,那么就用投影的方式找新的匹配点对再去做位姿优化,尽最大努力完成初始化的过程,这个过程大致都说过,所以这里就不赘述了。

TrackLocalMap()。局部地图跟踪

局部地图跟踪是跟踪过程的最后一步了,本质上这个过程就是根据关键帧的共视关系,进一步提高当前帧的定位精度,其实跟激光SLAM的局部地图匹配是一样的,激光SLAM中帧与帧之间的匹配精度有限(因为一帧激光点云的不完整性),而帧与局部地图的匹配的精度是更高的(局部地图更加完整)。而orbslam2是通过关键帧的共视关系来构建局部地图

step1

更新局部关键帧mvpLocalKeyFrames和局部地图点mvpLocalMapPoints
更新局部关键帧的过程:

  1. 遍历当前帧的地图点,记录所有能观测到当前帧地图点的关键帧。
  2. 更新局部关键帧,添加的局部关键帧有三种类型:
    2.1能观测到当前帧地图点的关键帧,称为一级共视关键帧
    2.2一级共视关键帧的共视关键帧,成为二级共视关键帧
    2.3一级共视关键帧的子关键帧、父关键帧
  3. 更新当前帧的参考关键帧,选择与自己共视程度最高的关键帧作为参考关键帧
  4. 先清空局部地图点,然后根据局部关键帧中的地图点添加到局部地图点中。
    在这里插入图片描述

step2

筛选局部地图中新增的在视野范围内的地图点,投影到当前帧搜索匹配,得到更多的匹配关系。其实就是把局部地图点根据前面跟踪出来的位姿,转换到当前帧的相机坐标系上(并且会筛选这些局部地图点确保是在当前帧的视野内,比如z值为负的地图点肯定需要去除)。最后就是经典的matcher.SearchByProjection通过投影再用半径搜索去找匹配点对。

step3

通过3D-2D的重投影误差(pnp)来求解当前帧到参考帧之间的位姿。又是经典的g2o优化(仅优化当前帧位姿)。不赘述

step4

更新当前帧的地图点被观测程度。注意:地图点的被观测状态有两种:

  1. 一个IncreaseVisible,这个其实是指这个地图点在构建局部地图的时候,在当前帧视野范围内(通过Frame::isInFrustum()函数判断是否在视野内)。
  2. 另一个IncreaseFound,这个就是指这个地图点与当前帧特征点匹配上的意思。意义比IncreaseVisible更强,因为在视野范围内也有可能因为一些遮挡而导致没有特征点与之匹配。

并统计跟踪局部地图后匹配数目,如果匹配数目大于等于30就说明这个局部地图跟踪是成功的。

CreateNewKeyFrame()。产生新的关键帧

跟踪线程中的跟踪函数基本上都讲完了,实际SLAM的过程就是根据当前的跟踪的质量来选择不同的跟踪函数。最后再通过局部地图的跟踪,我们已经得到一个比较好的当前帧位姿了,所以我们就需要判断需不需要创建新的关键帧。
常见的关键帧生成方式:

  1. 根据运动距离或者转动角度的阈值来产生关键帧。
  2. 根据时间来产生关键帧。比如每10个雷达帧为一个关键帧
  3. orbslam2则是通过前面跟踪的状态来决定是否应该产生关键帧

orbslam2产生关键帧的判断条件

  1. 在参考关键帧中,统计观测次数大于3的地图点数量nRefMatches。单目的情况下,如果当前帧的内点数小于nRefMatches0.9倍就要加新的关键帧(因为单目的跟踪依赖关键帧之间三角化的地图点来通过pnp求解相机运动,所以插入关键帧的频率要高),如果是双目或rgbd的话,就是当前帧的内点数小于nRefMatches0.75倍就要加新的关键帧(因为在跟踪的过程中,双目或rgbd会加入一些距离可信的点来提高跟踪效果)。
  2. 除了根据跟踪状态来,orbslam2还会设置mMaxFramesmMinFrames参数来根据时间且结合跟踪效果决定是否产生关键帧。

创建新的关键帧

  1. 将当前帧构造成关键帧。
  2. 将当前关键帧设置为当前帧的参考关键帧
  3. 如果相机是双目或rgbd相机,则为当前帧生成新的地图点,单目不需要。(就是直接把当前帧深度可信的3d点分别加入到当前关键帧中和全局地图中)。
  4. 插入关键帧,就是把新创建的关键帧插入到mpLocalMapper这个线程的mlNewKeyFrames容器中。
  5. tracking线程中新的关键帧mpLastKeyFrame设置为新创建的关键帧。

总结

这篇文章主要就是整理了tracking跟踪线程中的主要函数:

  1. 单目初始化
  2. 双目或rgbd初始化
  3. 参考关键帧的跟踪
  4. 匀速模型跟踪
  5. 重定位(与回环有点像,主要是跟丢后重新找到在地图中的定位)
  6. 局部地图跟踪(提高定位信息在局部的准确性)
  7. 判断产生关键帧的时机

其实整个Tracking::Track()函数很大,逻辑也很多,但是本质上通过一系列逻辑将上述函数组合,而实现跟踪的效果。我们学习的目的应该是聚焦在函数实现上,过度关注这些逻辑有点浪费时间,因为我认为这些逻辑可能每个人的思考都不同,在需要的时候参考参考即可。

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

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

相关文章

使用 C# 学习面向对象编程:第 2 部分

C# 类属性简介 属性在面向对象编程中起着至关重要的作用。它们允许我们从类外部访问类的私有变量。在类中使用私有变量是很好的。属性看起来像变量和方法的组合。属性有部分&#xff1a;“get 和 set”方法。get 方法应该返回变量&#xff0c;而 set 方法应该为其赋值。 步骤…

用48个字,总结8条创业心法,悟透将受用一生!

我用48个字&#xff0c;总结8条创业心法&#xff0c;悟透将受用一生。我把这8条创业心法&#xff0c;送给副业微商圈公众号里面的所有朋友们。 希望大家能从中有所启发&#xff0c;脚踏实地&#xff0c;在学习中成长&#xff0c;在前行中坚定。人的烦恼不是源于没有钱&#xf…

【iOS】界面推出的方法

【iOS】界面推出的方法 在学习过程中我们发现在iOS中有两种界面推出的方法&#xff1a;push 和 present这两种方法都可以用来推出一个新的界面 但是这两者是存在区别的 push 方法是通过 UINavigationController 进行导航,新的视图控制器会被压入导航栈中&#xff0c;可以跨级…

BoardLight - hackthebox

简介 靶机名称&#xff1a;BoardLight 难度&#xff1a;简单 靶场地址&#xff1a;https://app.hackthebox.com/machines/603 本地环境 靶机IP &#xff1a;10.10.11.11 ubuntu渗透机IP(ubuntu 22.04)&#xff1a;10.10.16.17 windows渗透机IP&#xff08;windows11&…

springCloudAlibaba之服务熔断组件---sentinel

sentinel组件学习 sentinel学习sentinel容错机制使用代码方式进行QPS流控-流控规则初体验使用SentinelResource注解进行流控 通过代码方式设置降级规则-降级规则初体验sentinel控制台部署客户端整合服务端 springcloud整合sentinelQPS流控规则并发线程数-流控规则BlockExceptio…

wooyun_2015_110216-Elasticsearch-vulfocus

1.原理 ElasticSearch具有备份数据的功能&#xff0c;用户可以传入一个路径&#xff0c;让其将数据备份到该路径下&#xff0c;且文件名和后缀都可控。 所以&#xff0c;如果同文件系统下还跑着其他服务&#xff0c;如Tomcat、PHP等&#xff0c;我们可以利用ElasticSearch的备…

群体优化算法----火山爆发算法介绍以及离散优化Pareto最优解示例

介绍 火山爆发算法&#xff08;Volcano Eruption Algorithm&#xff0c;VEA&#xff09;是一种新兴的群智能优化算法&#xff0c;其灵感来源于火山爆发的自然现象。火山爆发算法模拟了火山爆发过程中熔岩流动和喷发的行为&#xff0c;以寻找全局最优解。这种算法利用了火山爆发…

全网短剧资源

热门短剧资源库&#xff0c;已更新了 9000&#xff0c;记得收藏&#xff1a;https://www.kdocs.cn/l/ciptAICGdWYz

ABB机械人模型下载

可以下载不同格式的 https://new.abb.com/products/robotics/zh/robots/articulated-robots/irb-6700 step的打开各部件是分开的&#xff0c;没有装配在一起&#xff0c;打开看单个零件时&#xff0c;我们会发现其各零件是有装配的定位关系的。 新建一个装配环境&#xff0c;点…

武忠祥17堂课没必要全听,这几个才是精华!

作者&#xff1a;Captain 链接&#xff1a;https://www.zhihu.com/question/381665751/answer/3197724055 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 17堂课类似于习题课&#xff0c;是专题训练 17堂课省略了…

基于AI大文本模型的智慧对话开发设计及C#源码实现,实现智能文本改写与智慧对话

文章目录 1.AI 大模型发展现状2.基于AI服务的智慧对话开发2.1 大模型API选择2.2 基于C#的聊天界面开发2.3 星火大模型API接入2.4 优化开发界面与显示逻辑 3.源码工程Demo及相关软件下载参考文献 1.AI 大模型发展现状 端午假期几天&#xff0c;关注到国内的AI大模型厂商近乎疯狂…

6.7.29 基于卷积神经网络的乳腺良恶性图像分类

计算机化乳腺癌诊断系统在早期癌症诊断中发挥着重要作用。为此&#xff0c;应用深度学习&#xff0c;利用卷积神经网络 (CNN) 对基于小型乳房 X 线图像分析协会 (mini-MIAS) 数据库的乳房 X 线图像中的异常&#xff08;良性或恶性&#xff09;进行分类。观察准确度、灵敏度和特…

java之基础2笔记

1 类型转换 1.1 自动类型转换&#xff08;隐式类型转换&#xff09; 从小的数据类型到大的数据类型的转换&#xff08;如 int 到 long&#xff09;。 从低精度的数据类型到高精度的数据类型的转换&#xff08;如 float 到 double&#xff09;。 1.2 强制类型转换&#xff0…

逆序队专题

逆序对的定义是&#xff0c;在一个数组中&#xff0c;对于下标 ( i ) 和 ( j )&#xff08;其中 ( i < j )&#xff09;&#xff0c;如果 ( a[i] > a[j] )&#xff0c;则称 ((a[i], a[j])) 为数组的一个逆序对。 换句话说&#xff0c;逆序对就是在数组中前面的元素大于后…

每日十题---三

1. Vue中$nextTick原理 1. 简单的理解就是它就是一个setTimeout函数&#xff0c;将函数放到异步后去处理。 2. Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化&#xff0c;Vue 将开启一个队列&#xff0c;并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被…

【计算机毕业设计】273基于微信小程序的刷题系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

AI日报0610 -- Prompt这样改,AI成本瞬降20%!

全球首届人工智能选美大赛 世界 AI 创作者大赛和创作者平台 FanVue 正在举办首届“Miss AI”大赛 超过 1,500 名 AI 生成的模特竞逐。这些模型不仅形象逼真 还展示了不同的个性和原因。 评委将评估技术和吸引观众的能力。 奖金池高达 20,000 美元&#xff0c;并有机会参加公关…

讨论C++类与对象

讨论C类与对象 C语言结构体和C类的对比类的实例化类对象的大小猜想一猜想二针对上述猜想的实践 this指针不同对象调用成员函数 类的6个默认成员函数构造函数析构函数拷贝构造函数浅拷贝和深拷贝 赋值运算符重载 初始化列表初始化顺序 C语言结构体和C类的对比 在C语言中&#x…

require.context()函数介绍

业务需求&#xff1a; 前端Vue项目怎样读取src/assets目录下所有jpg文件 require.context()方法来读取src/assets目录下的所有.jpg文件 <template><div><img v-for"image in images" :src"image" :key"image" /></div> …

Vision-LSTM: xLSTM 作为通用视觉主干

摘要 尽管Transformer最初是为自然语言处理引入的&#xff0c;但它现在已经被广泛用作计算机视觉中的通用主干结构。最近&#xff0c;长短期记忆&#xff08;LSTM&#xff09;已被扩展为一种可扩展且性能优越的架构——xLSTM&#xff0c;它通过指数门控和可并行化的矩阵内存结…