当前位置: 首页 > news >正文

LineSlam线特征投影融合(Fuse) 中pML->GetLineNormalVector()的理解代码理解

理解LineSlam中线段融合部分,里面有很多限制条件,但是比较明显,但是对于线段的Normal,有些不容易理解,因此研究了一下,以下是个人理解,并且记录

其中fuse的代码代码如下(部分自己已经修改,用于LineSlam的工程中):

int LSDmatcher::Fuse(KeyFrame *pKF, const std::vector<MapLine *> &vpMapLines, const float th){Eigen::Matrix3f Rcw_eigen = pKF->GetRotation();Eigen::Vector3f tcw_eigen = pKF->GetTranslation();cv::Mat Rcw = Converter::toCvMat(Rcw_eigen);;cv::Mat tcw = (Mat_<float>(3, 1) << tcw_eigen(0), tcw_eigen(1), tcw_eigen(2));const float &fx = pKF->fx;const float &fy = pKF->fy;const float &cx = pKF->cx;const float &cy = pKF->cy;const float &bf = pKF->mbf;Eigen::Vector3f Ow_eigen = pKF->GetCameraCenter();cv::Mat Ow = (Mat_<float>(3, 1) << Ow_eigen(0), Ow_eigen(1), Ow_eigen(2));int nFused=0;const int nLines = vpMapLines.size();// For each candidate MapPoint project and matchfor(int iML=0; iML<nLines; iML++){MapLine* pML = vpMapLines[iML];// Discard Bad MapLines and already foundif(!pML || pML->isBad())continue;//Vector6d P = pML->GetWorldPos();std::pair<Eigen::Vector3f, Eigen::Vector3f> lineWorldPosition = pML->GetLineWorldPos();cv::Mat SP = (Mat_<float>(3, 1) << lineWorldPosition.first(0), lineWorldPosition.first(1), lineWorldPosition.first(2));cv::Mat EP = (Mat_<float>(3, 1) << lineWorldPosition.second(0), lineWorldPosition.second(1), lineWorldPosition.second(2));const cv::Mat SPc = Rcw * SP + tcw;const auto &SPcX = SPc.at<float>(0);const auto &SPcY = SPc.at<float>(1);const auto &SPcZ = SPc.at<float>(2);const cv::Mat EPc = Rcw * EP + tcw;const auto &EPcX = EPc.at<float>(0);const auto &EPcY = EPc.at<float>(1);const auto &EPcZ = EPc.at<float>(2);if (SPcZ < 0.0f || EPcZ < 0.0f)continue;const float invz1 = 1.0f / SPcZ;const float u1 = fx * SPcX * invz1 + cx;const float v1 = fy * SPcY * invz1 + cy;if (u1 < pKF->mnMinX || u1 > pKF->mnMaxX)continue;if (v1 < pKF->mnMinY || v1 > pKF->mnMaxY)continue;const float invz2 = 1.0f / EPcZ;const float u2 = fx * EPcX * invz2 + cx;const float v2 = fy * EPcY * invz2 + cy;if (u2 < pKF->mnMinX || u2 > pKF->mnMaxX)continue;if (v2 < pKF->mnMinY || v2 > pKF->mnMaxY)continue;const float maxDistance = pML->GetMaxDistanceInvariance();const float minDistance = pML->GetMinDistanceInvariance();const cv::Mat OM = 0.5 * (SP + EP) - Ow;const float dist = cv::norm(OM);if (dist < minDistance || dist > maxDistance)continue;// Check viewing angle 这个LineNormal是啥意思?Eigen::Vector3f Pn = pML->GetLineNormalVector();cv::Mat pn = (Mat_<float>(3, 1) << Pn(0), Pn(1), Pn(2));if(OM.dot(pn)<0.5*dist)continue;const int nPredictedLevel = pML->PredictScale(dist, pKF);const float radius = th*pKF->mvScaleFactors[nPredictedLevel];const vector<size_t> vIndices = pKF->GetLinesInArea(u1,v1, u2, v2, radius);if(vIndices.empty())continue;const cv::Mat dML = pML->GetLineDescriptor();int bestDist=INT_MAX;int bestIdx =-1 ;for(unsigned long idx : vIndices){const int &klLevel = pKF->mvKeyLines[idx].octave;if(klLevel<nPredictedLevel-1 || klLevel>nPredictedLevel)continue;const cv::Mat &dKF = pKF->mLineDescriptors.row(idx);const int dist = DescriptorDistance(dML,dKF);if(dist<bestDist){bestDist = dist;bestIdx = idx;}}if(bestDist<=TH_LOW){MapLine* pMLinKF = pKF->GetMapLine(bestIdx);if(pMLinKF){if(!pMLinKF->isBad()) {if(pMLinKF->Observations()>pML->Observations())pML->Replace(pMLinKF);elsepMLinKF->Replace(pML);}}else{pML->AddLineObservation(pKF,bestIdx);pKF->AddMapLine(pML,bestIdx);}nFused++;}}return nFused;}

本来对于这段代码pML->GetLineNormal();,没有那么好理解,因此记录
先分析它附近的代码,如下:

// Check viewing angle 这个LineNormal是啥意思?
Eigen::Vector3f Pn = pML->GetLineNormalVector();
cv::Mat pn = (Mat_<float>(3, 1) << Pn(0), Pn(1), Pn(2));
if(OM.dot(pn)<0.5*dist)continue;

🧠 背景:MapLine 与其法向量 (Normal)

在 ORB-SLAM 或 LSD-SLAM 系列中,每个 MapLine(世界坐标系下的线段) 都维护了一条空间线的信息:

  1. 起点 P1

  2. 终点 P2

  3. 平均观测方向(normal vector)Pn

GetLineNormal() 返回的 Pn 是线段的平均观测方向法向量,用来判断当前相机是否处在这条线的“前方”或“后方”。

在这里插入图片描述

判断逻辑

if (OM.dot(pn) < 0.5 * dist)continue;

这行的作用是:
判断当前相机视角是否位于线段的可观测半空间内
解释:

  1. OM.dot(pn) 表示相机中心到线段中点方向 与 线段平均法向量之间的夹角。
  2. 若点积过小,说明相机与线段法向量方向几乎相反(即在线段背面),该线在当前视角下不可见或方向反转。
  3. 阈值 0.5 * dist 是一个启发式条件,过滤掉大角度背向的线条。

直观理解:

  1. 假设线段像一根小棍子,
  2. 法向量 Pn 表示线段“朝向相机的方向”;
  3. OM 是相机到这根棍子的位置方向;
  4. 点积较大 → 说明相机正看向线段;
  5. 点积较小 → 说明相机在反方向或斜后方,线段不可见

其中线段
这正是理解线特征几何一致性的关键之一。
在 ORB-SLAM / PL-SLAM / Line3D++ 等系统中,
MapLine::GetLineNormal() 所返回的 法向量 (normal, 它的变量为:mNormalVector)(这点非常重要)
并不是线段本身的方向向量
而是它的平均观测方向(Viewing Direction)
我们来分两步说明:
一、Normal 在 MapLine 中的含义

在 ORB-SLAM 的点特征中,MapPoint 存储一个 mNormalVector
用于表示从地图点指向所有观测该点的相机的平均方向

线特征(MapLine)也同理:
当某个关键帧观测到这条线段时,线段在世界坐标下的中点记作 Pc;相机在世界坐标下的位置记作 Ow
从线段到相机的方向为:
在这里插入图片描述
每次有新的关键帧观测该线段,就把这个方向向量累积:
在这里插入图片描述
然后归一化:
在这里插入图片描述
这就是 mNormalVector —— “线段的平均观测方向”

通常在 ORB-SLAM 或基于它**(典型 MapLine::UpdateNormalAndDepth)**的系统中,这样写:

void MapLine::UpdateNormalAndDepth()
{if (mbBad)return;std::map<KeyFrame*, std::tuple<int, int>> observations;{std::unique_lock<std::mutex> lock(mMutexFeatures);observations = mLineObservations;}if (observations.empty())return;cv::Mat normal = cv::Mat::zeros(3, 1, CV_32F);int n = 0;for (auto &obs : observations){KeyFrame* pKF = obs.first;if (pKF->isBad())continue;// 相机中心cv::Mat Ow = pKF->GetCameraCenter();// 世界坐标下线段起点与终点Eigen::Vector3f SP, EP;std::tie(SP, EP) = GetLineWorldPos();cv::Mat Pc = (cv::Mat_<float>(3,1) << 0.5f*(SP(0)+EP(0)), 0.5f*(SP(1)+EP(1)), 0.5f*(SP(2)+EP(2)));cv::Mat v = Ow - Pc;v = v / cv::norm(v);normal += v;n++;}mNormalVector = normal / static_cast<float>(n);mNormalVector = mNormalVector / cv::norm(mNormalVector);
}

拓展:其中GetLineNormal() ,它的返回值为mNormalVector在其它方面有用,见下图
在这里插入图片描述

希望对大家有用

http://www.dtcms.com/a/553344.html

相关文章:

  • 【图像处理基石】多波段图像融合算法入门:从概念到实践
  • Docker核心文件:DockerCompose文件
  • 企业网站 自助建站网站做竞价经常会被攻击吗
  • Android ble理解
  • 深入理解 Spring Boot Web 开发中的静态资源处理机制
  • 网站建设费记什么科目产品ui设计是什么
  • 衡水企业网站设计国内app开发公司
  • 【java EE】IDEA 中创建或迁移 Spring 或 Java EE 项目的核心步骤和注意事项
  • 如何保证缓存与数据库更新时候的一致性
  • 【Spring Boot Starter 设计思考:分离模式是否适用于所有场景】
  • HTTP 头部参数数据注入测试sqlilabs less 18
  • 网站速度慢的原因做网站建设优化的电话话术
  • 【数据结构】单链表 练习记录
  • mac 安装 jdk17
  • 【项目实战1-瑞吉外卖|day22】
  • 怎么用dw做响应式网站网站主持人制作网站代言人
  • Android开发自学笔记 --- Kotlin
  • 从VB到PyCharm:编程工具跨越时代的传承与革命
  • 网站建设创新成果四年级写一小段新闻
  • 生产环境用Go语言完成微服务搭建和业务融入
  • 第九课 四川料理は辛いです
  • DevEco Studio在模拟器中改变运行的 ets 文件
  • 第5讲:项目依赖管理与资源管理
  • 网站定制案例微安电力wordpress 分类合并
  • Orleans 的异步
  • comsol livelink with matlab
  • PDF文档中表格以及形状解析-后续处理(线段生成最小多边形)
  • 5G工业边缘计算网关,重构工业智能化
  • 网站中英文切换代码wordpress插件问题
  • 解析 Lua 虚拟机整数与浮解析 Lua 虚拟机整数与浮点数处理:类型转换与运算精度控制