0
点赞
收藏
分享

微信扫一扫

Far planner系列代码(9)

        今天讲一下论文里的Polygon Extraction部分:

         


Algorithm 1: Create binary image I from points in S

// Algorithm 1: Create binary image I from points in S 
void ContourDetector::UpdateImgMatWithCloud(const PointCloudPtr& pc, cv::Mat& img_mat) {
    int row_idx, col_idx, inf_row, inf_col;
    const std::vector<int> inflate_vec{-1, 0, 1};   //to expand the spot in img
    for (const auto& pcl_p : pc->points) {                                      //把pc->points里的点取出来赋值给pcl_p
        this->PointToImgSub(pcl_p, odom_pos_, row_idx, col_idx, false, false);  //通过一层PointTOImgSub 更新row_idx和col_idx
        if (!this->IsIdxesInImg(row_idx, col_idx)) continue;    //如果该点不在图的范围内,直接跳过

        for (const auto& dr : inflate_vec) {
            for (const auto& dc : inflate_vec) {
                inf_row = row_idx+dr, inf_col = col_idx+dc;
                //这个inf_row 和 inf_col 是干嘛的??把位置放大?1x1的变成3x3的
                if (this->IsIdxesInImg(inf_row, inf_col)) {
                    img_mat.at<float>(inf_row, inf_col) += 1.0; //把图里(inf_row, inf_col)的位置内容+1
                }
            }
        }
    }
    if (!FARUtil::IsStaticEnv) {
        cv::threshold(img_mat, img_mat, cd_params_.kThredValue, 1.0, cv::ThresholdTypes::THRESH_BINARY);
    }
    if (cd_params_.is_save_img) this->SaveCurrentImg(img_mat);
}

        这部分的代码简明扼要,就是从三维点云信息创建二值图像。获取到点云的Point后,通过PointToImgSub得到对应二值图片的位置点,在通过inflate_vec{-1, 0, 1}将区域进行膨胀。也就是原来1x1的点,变成了一个3x3的小方块。

        同时函数还提供了一个开放接口,可以让你看到点云被压缩到二值图像上的画面。

        if (cd_params_.is_save_img) this->SaveCurrentImg(img_mat) 这部分可以去yaml里改配置文件,将is_save_img设置为true。

        这个就是点云映射后的二值图片了。


        之后就要对其进行模糊 

Algorithm 1:Apply average fifilter to generate blurred image Iblur

void ContourDetector::ResizeAndBlurImg(const cv::Mat& img, cv::Mat& Rimg) {
    img.convertTo(Rimg, CV_8UC1, 255);
    cv::resize(Rimg, Rimg, cv::Size(), cd_params_.kRatio, cd_params_.kRatio, 
               cv::InterpolationFlags::INTER_LINEAR);
    cv::boxFilter(Rimg, Rimg, -1, cv::Size(cd_params_.kBlurSize, cd_params_.kBlurSize), cv::Point2i(-1, -1), false);
}

         yaml文件里给的resize_ratio是3.0 ;可以看到首先将img转成CV_8UC1的格式 输出成Rimg,然后将Rimg进行resize。我们来看看同一张图片的对比

        

         左边是没有进行resize和blur的原始二值化图片,右边是进行了resize和blur的二值化图片。原始尺寸是401x401像素,resize后的尺寸为1203x1203像素。

        通过图像的平滑处理,得到新的Rimg。


Algorithm 1:Extract polygons {Pk contour} based on [29]

        这一步就是通过上面的Rimg来进行轮廓的提取 也就是contour。

ExtractRefinedContours(Rimg, img_contours)

        第一步先通过Rimg抽取出contours 这时候的contours还没经过任何过滤,称为img_contours。

void ContourDetector::ExtractRefinedContours(const cv::Mat& imgIn,
                                            std::vector<CVPointStack>& refined_contours) 
{ 

    std::vector<std::vector<cv::Point2i>> raw_contours;
    refined_contours.clear(), refined_hierarchy_.clear();

    //raw_contours定义为“std::vector<std::vector<cv::Point2i>> raw_contours”,
    //是一个双重向量(向量内每个元素保存了一组由连续的Point构成的点的集合的向量),每一组点集就是一个轮廓,有多少轮廓,contours就有多少元素;

    
    cv::findContours(imgIn, raw_contours, refined_hierarchy_, 
                     cv::RetrievalModes::RETR_TREE, //RETR_TREE 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
                     cv::ContourApproximationModes::CHAIN_APPROX_TC89_L1);//定义轮廓的近似方法,取值如下:CV_CHAIN_APPROX_TC89_L1:使用teh-Chinl chain 近似算法;
                     
    refined_contours.resize(raw_contours.size());
    for (std::size_t i=0; i<raw_contours.size(); i++) {
        // using Ramer–Douglas–Peucker algorithm url: https://en.wikipedia.org/wiki/Ramer%E2%80%93Douglas%E2%80%93Peucker_algorithm
        cv::approxPolyDP(raw_contours[i], refined_contours[i], DIST_LIMIT, true);//最小多边形拟合,输入raw_contours  输出refined_contours
    }
    // 过滤掉一些poly,删除父轮廓,得到新的refined_contours
    //这个应该就是Downsample vertices in Pk contour based on [30]
    this->TopoFilterContours(refined_contours); 

    //Check the inner angle of each vertex and eliminate the vertices with inner angle < ζ;
    this->AdjecentDistanceFilter(refined_contours);
    //这两步都是在对contour进行过滤 第一步过滤父轮廓,第二步过滤重叠的向量。
}

        其中,主要是通过findContours函数进行图片轮廓提取,再经过一系列的过滤,最后得到refined_contours。


Algorithm 1:Downsample vertices in Pk contour based on [30];

void ContourDetector::TopoFilterContours(std::vector<CVPointStack>& contoursInOut) {
    std::unordered_set<int> remove_idxs;
    for (int i=0; i<contoursInOut.size(); i++) {
        if (remove_idxs.find(i) != remove_idxs.end()) continue;
        const auto poly = contoursInOut[i];
        //把一组小于3个point的这个index索引值放到无序集合remove_idxs里
        if (poly.size() < 3) {
            remove_idxs.insert(i);
        //大于3个point的组,还要过一层判断 判断机器人是否在poly内部 ,
        //判断所有poly的点和机器人的odom的位置,如果点始终位于构成线段的同一侧, 那么它就是平面的内部点。

        //!点在平面内部 返回1  点在平面外部返回0
        } else if (!FARUtil::PointInsideAPoly(poly, free_odom_resized_)) {
            //如果点在平面外部 就执行InternalContoursIdxs  这个主要是把父轮廓给放到remove_idxs里。
            InternalContoursIdxs(refined_hierarchy_, i, remove_idxs);
        }
    }
    //对remove_idxs进行操作,把不在remove_idxs里面的索引重新push_back到contoursInOut,也就是从refine_contour里把remove_idx里的索引都删除。
    if (!remove_idxs.empty()) {
        std::vector<CVPointStack> temp_contours = contoursInOut;
        contoursInOut.clear();
        for (int i=0; i<temp_contours.size(); i++) {
            if (remove_idxs.find(i) != remove_idxs.end()) continue;
            contoursInOut.push_back(temp_contours[i]);
        }
    }
}

Algorithm 1:eliminate the vertices with inner angle < ζ

void ContourDetector::AdjecentDistanceFilter(std::vector<CVPointStack>& contoursInOut) {
    /* filter out vertices that are overlapped with neighbor */
    std::unordered_set<int> remove_idxs;
    //循环refined_contour
    for (std::size_t i=0; i<contoursInOut.size(); i++) { 
        //取出元素 赋值给c  c应该也是一串
        const auto c = contoursInOut[i];
        const std::size_t c_size = c.size();
        std::size_t refined_idx = 0;
        //第一轮过滤
        for (std::size_t j=0; j<c_size; j++) {
            cv::Point2f p = c[j]; 
            //如果刚开始,refined_idx<1  或者已经进行到中间,p点和前一个点的距离大于DIST_LIMIT 
            if (refined_idx < 1 || FARUtil::PixelDistance(contoursInOut[i][refined_idx-1], p) > DIST_LIMIT) {
                /** Reduce wall nodes */
                //contoursInOut[i] 这是一组点,p是当前contoursInOut[i]里的一个点
                RemoveWallConnection(contoursInOut[i], p, refined_idx);

                contoursInOut[i][refined_idx] = p;// 修改refined_contour了  把一些点过滤掉 然后可能原来是5边形,过滤完就是4边形
                refined_idx ++;
            }
        }
        /**--------------------------------------------------------------------------------------------------------*/
        /** Reduce wall nodes */
        //第二轮过滤 过滤完后refined_idx可能会改变  判断[i]里所有的点和[i]里第一个点
        RemoveWallConnection(contoursInOut[i], contoursInOut[i][0], refined_idx);
        //比如原来一组[i]里第一轮过滤完有8个point,第二轮过滤完剩下7个point 
        contoursInOut[i].resize(refined_idx);       //把point的数量resize掉。

        //refined_idx大于1 且 contoursInOut[i]的第一个元素和最后一个元素的距离小于DIST_LIMIT
        if (refined_idx > 1 && FARUtil::PixelDistance(contoursInOut[i].front(), contoursInOut[i].back()) < DIST_LIMIT) {
            contoursInOut[i].pop_back();//尾删 删除最后一个点
        }
        //如果删完了 或者 没删之前,contoursInOut[i]的点的数量小于3,就把这些点都加到remove_idxs里。
        if (contoursInOut[i].size() < 3) remove_idxs.insert(i);
    }
    if (!remove_idxs.empty()) { // clear contour with vertices size less that 3
        std::vector<CVPointStack> temp_contours = contoursInOut;
        contoursInOut.clear();
        for (int i=0; i<temp_contours.size(); i++) {
            if (remove_idxs.find(i) != remove_idxs.end()) continue;
            contoursInOut.push_back(temp_contours[i]);
        }
    }
}

 


        经过上面几个步骤,得到最终的refined_contour

举报

相关推荐

0 条评论