DS-SLAM代码(动态环境下基于图像分割的SLAM)阅读笔记 (三)

上一篇文章讲到了Moving Consistency Check 得到了放置不满足对极约束的特征点矩阵T_M,这一篇文章我们来一起学习一下DS-SLAM的 Segment.cc 语义分割线程和动态点的删除.

1 语义分割线程的开启在System.cc 文件里面:

mpSegment =new Segment(
            pascal_prototxt,     //模型文件,网络模型
            pascal_caffemodel,   //训练文件,训练好的权重参数
            pascal_png);         // 标签文件,上色的图片
    mptSegment =new thread(&ORB_SLAM2::Segment::Run,mpSegment);

程序会不停的执行Segment::Run 这个函数,首先当我们创建 Segment对象的时候,程序会执行类构造函数:

Segment::Segment(const string &pascal_prototxt, const string &pascal_caffemodel, const string &pascal_png):mbFinishRequested(false),mSkipIndex(SKIP_NUMBER),mSegmentTime(0),imgIndex(0)
{

    model_file = pascal_prototxt;           //模型文件,网络模型
    trained_file = pascal_caffemodel;       //训练文件,训练好的参数
    LUT_file = pascal_png;                  // 调色版
    label_colours = cv::imread(LUT_file,1);
    cv::cvtColor(label_colours, label_colours, CV_RGB2BGR); // 格式转换
    mImgSegmentLatest=cv::Mat(Camera::height,Camera::width,CV_8UC1);
    mbNewImgFlag=false;
}

在构造函数里面我们会加载网络模型文件 pascal_prototxt ,权重文件(提前训练好的)pascal_caffemodel,和调色版pascal_png.
关于调色版文件的解释,有如下的回答:

https://blog.csdn.net/yang332233/article/details/107995917

2 Segment线程不停执行Segment::Run() 线程主函数
主要代码如下:

if(mSkipIndex==SKIP_NUMBER)
        {
            std::chrono::steady_clock::time_point t3 = std::chrono::steady_clock::now();

            // Recognise by Semantin segmentation  分割的代码就只有这一行
            mImgSegment=classifier->Predict(mImg, label_colours);

            mImgSegment_color = mImgSegment.clone();
            // 将单通道的灰度图转化为三通道的 RGB图 ,但是图像虽然还是灰色的,但是已经是三通道的了
            cv::cvtColor(mImgSegment,mImgSegment_color, CV_GRAY2BGR);

            // 将像素进行映射,使用了Opencv中的LUT函数,运行之后语义分割的图片 有了颜色
            LUT(mImgSegment_color, label_colours, mImgSegment_color_final);
            // 将原图象和分割后的图像 都resize 成相同的尺寸
            cv::resize(mImgSegment, mImgSegment, cv::Size(Camera::width,Camera::height) );
            cv::resize(mImgSegment_color_final, mImgSegment_color_final, cv::Size(Camera::width,Camera::height) );

            std::chrono::steady_clock::time_point t4 = std::chrono::steady_clock::now();
            mSegmentTime+=std::chrono::duration_cast<std::chrono::duration<double> >(t4 - t3).count();
            mSkipIndex=0;
            imgIndex++;
        }

通过caffe框架的Predict函数完成语义分割,得到语义分割的结果mImgSegment,这个图片是灰色的,没有颜色,经过调色版上色之后得到**mImgSegment_color_final。 一会我们要用分割后的图片mImgSegment,去完成运动特征点的删除工作。
SKIP_NUMBER 这个变量的设置,应该是第一幅图片不会进行语义分割,而之后的图片才会进行语义分割。
分割完之后,保存分割的结果,并将对应的标志位设置为1。

        // 保存了上一帧 和本帧的 分割图片mImgSegmentLatest  ,并且告诉Track 新的分割图片已经就绪
        ProduceImgSegment();

3 语义分割线程到此完结,接下来结合语义分割的结果的动态点进行删除
这一步主要是在 CalculEverything这个函数完成的。

// step 5 :  移除 动态的 外点
    mCurrentFrame.CalculEverything(
            mImRGB,                           // 彩色图 
            mImGray,                         // 灰度图
            mImDepth,                       //深度图
            mpSegment->mImgSegmentLatest    // 上面图像的语义分割的结果
            );

在程序里面实际上没有用到 mImRGB 这个变量。

// 3. 1 遍历语义分割之后的图像,当检查出 “人” 跳出循环

 // 1 逐个遍历语义分割之后的图像的像素,当检查出 “人” 跳出循环
    for ( int m=0; m<imS.rows; m+=1 )
    {
        for ( int n=0; n<imS.cols; n+=1 )
        {
            int labelnum = (int)imS.ptr<uchar>(m)[n];
            //The lable of people is 15
            if(labelnum == PEOPLE_LABLE)  
            {
                flagprocess=1;
                break;
            }
        }

        if(flagprocess == 1)
        break;
    }

在语义分割图当中,人的灰度值是15 ,当检测到人时,flagprocess 标志位设为1,只有当检测到人的时候,才会执行CheckMovingKeyPoints 这个函数 去看是否去除特征点。

3.2 去除 人身上的动态点

CheckMovingKeyPoints 这个函数如下,通过分割图imgS 和潜在的动态点 T擦除 关键点mvKeysT 中的位于人身上的动态点。

int ORBextractor::CheckMovingKeyPoints(
        const cv::Mat &imGray,    // 灰度图
        const cv::Mat &imS,       // 分割图
        std::vector<std::vector<cv::KeyPoint>>& mvKeysT,   // 关键点
        std::vector<cv::Point2f> T)                         //光流点T矩阵

3.2.1 逐个遍历T 矩阵的特征点以及其周围15✖15的小方格内的 像素灰度值,只要T矩阵的光流点或者周围15✖15的小方格有一个像素值等于15(语义分割的人的灰度值),则说明动态点落在了人身上,人是运动的。这样的条件是不是有点苛刻呢,毕竟只有一个潜在的运动点落在人身上,就认为整个人是运动的

//检查剔除区域(T_M集中每个像素点及周围15像素),因为T_M 矩阵放置的就是离群点。是不是具有动态先验“人类”的标签
   // 在T_M 离群点的 15*15 的像素块内发现 人,那么flag_orb_mov 设置为1.
	for (int i = 0; i < T.size(); i++)
	{
	    for(int m = -15; m < 15; m++) 
	    {
	        for(int n = -15; n < 15; n++)
	        {
	            //  确定 mx ,my 的范围不能出界
	            int my = ((int)T[i].y + n) ;
	            int mx = ((int)T[i].x + m) ;
		        if( ((int)T[i].y + n) > (Camera::height -1) ) my = (Camera::height - 1) ;
	        	if( ((int)T[i].y + n) < 1 ) my = 0;
		        if( ((int)T[i].x + m) > (Camera::width -1) ) mx = (Camera::width - 1) ;
		        if( ((int)T[i].x + m) < 1 ) mx = 0;
                // The label of peopel is 15
		        if((int)imS.ptr<uchar>(my)[mx] == PEOPLE_LABLE)
		        {
		            flag_orb_mov=1;
		               break;
		        }
	        }
	            if(flag_orb_mov==1)
	                 break;
	     }
	         if(flag_orb_mov==1)
	            break;
	}

3.2.1 既然人是运动的,那么就将提取的ORB特征点位于 人身上的部分全部剔除。

for (int level = 0; level < nlevels; ++level)
            {
                vector<cv::KeyPoint>& mkeypoints = mvKeysT[level];  // 提取每一层的金字塔
		        int nkeypointsLevel = (int)mkeypoints.size();
		        if(nkeypointsLevel==0)
		                continue;
		        if (level != 0)
			        scale = mvScaleFactor[level]; 
		        else
			        scale =1; 
                vector<cv::KeyPoint>::iterator keypoint = mkeypoints.begin();

                // 标签带有先验动态,则在特征点金字塔内删除该特征点
                while(keypoint != mkeypoints.end())
	            {
		             cv::Point2f search_coord = keypoint->pt * scale;
		             // Search in the semantic image
		             if(search_coord.x >= (Camera::width -1)) search_coord.x=(Camera::width -1);
		             if(search_coord.y >= (Camera::height -1)) search_coord.y=(Camera::height -1) ;

		             //  用来访问灰度图像的单个像素。对于灰度图像,每个像素只存储一个值。
		             int label_coord =(int)imS.ptr<uchar>((int)search_coord.y)[(int)search_coord.x];

		             //发现这个特征点的坐标 落在 人 身上,则把这个特征点删除
		             if(label_coord == PEOPLE_LABLE) 
		             {
			            keypoint=mkeypoints.erase(keypoint);		// 将这个特征点删除掉
		             }
		             else
		             {
			            keypoint++;
		             }
	             }
	          }

程序的大致过程是:遍历所有前面Frame初始化提取的ORB特征点,如果这个特征点落在人身上(程序里是 找出特征点在语义分割图imS的位置(u,v),如果这个位置的像素值是15,也就是这个特征点在人身上,那么就在特征点容器里面把这个点 删除 ),剩下的静态点保留在 mvKeysTemp 中。

至此,动态特征点的删除工作就已经做完了,保留的特征点认为是静态的点,可以参与后续的描述子计算,去畸变和Track部分

  • 6
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值