图像的线特征提取与跟踪

线特征比点特征可以提供更多的约束条件,并且在某些场合下比点特征要鲁棒。

1. LSD: Line Segment Detector线段检测器

  • LSD是一种局部提取直线的算法,速度比Hough要快。 LSD是一种直线检测分割算法,它能在线性的时间内得出亚像素级精度的检测结果。该算法被设计成自适应模式,无需手动调参。
  • 作者将自己的论文(LSD: a Line Segment Detector,2012)及代码( c++)放到自己的主页上:LSD详解
  • opencv使用头文件及命令空间
#include <opencv2/line_descriptor/descriptor.hpp>
using namespace cv::line_descriptor;
  • opencv中线特征的类:KeyLine(点特征:KeyPoints)
  • opencv检测线特征的API接口:
Ptr<line_descriptor::LSDDetector> lsd = line_descriptor::LSDDetector::createLSDDetector();
lsd->detect(img, keylines, 1.2,1);
//@param images input images
//@param keylines set of vectors that will store extracted lines for one or more images
//@param scale scale factor used in pyramids generation
//@param numOctaves number of octaves inside pyramid
//@param masks vector of mask matrices to detect only KeyLines of interest from each input image

2. LBD: line binary descriptor 线二进制描述符

  • opencv描述线特征的二进制描述子
Ptr<BinaryDescriptor> lbd = BinaryDescriptor::createBinaryDescriptor();
lbd->compute(img, keylines, mLdesc);   //mLdesc是矩阵格式

3. 特征匹配器

  • BFMatcher:brute force matcher 暴力匹配器
BFMatcher* bfm = new BFMatcher(NORM_HAMMING, false);
bfm->knnMatch(mLdesc, mLdesc2, lmatches, 2);
  • BinaryDescriptorMatcher:二进制匹配器
Ptr<BinaryDescriptorMatcher> bdm_ = BinaryDescriptorMatcher::createBinaryDescriptorMatcher();
bdm_->match(forwframe_->lbd_descr, curframe_->lbd_descr, lsd_matches);

4. 线特征提取实践

  • 使用 OpenCV下的LSD 提取特征,LBD进行特征描述, KNNMatch做特征描述,配合简单的外点筛选
  • 筛选策略是选择前2个最匹配的点,当bestMatch.distance / betterMatch.distance<0.7时,认为匹配有效
  • 实践代码
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/line_descriptor/descriptor.hpp>

using namespace cv;
using namespace std;
using namespace cv::line_descriptor;

void ExtractLineSegment(const Mat &img, const Mat &image2, vector<KeyLine> &keylines,vector<KeyLine> &keylines2)
{
    Mat mLdesc,mLdesc2;
    vector<vector<DMatch>> lmatches;
    Ptr<BinaryDescriptor> lbd = BinaryDescriptor::createBinaryDescriptor();
    Ptr<line_descriptor::LSDDetector> lsd = line_descriptor::LSDDetector::createLSDDetector();

    lsd->detect(img, keylines, 1.2,1);
    lsd->detect(image2,keylines2,1.2,1);
    int lsdNFeatures = 50;
    if(keylines.size()>lsdNFeatures){
        sort(keylines.begin(), keylines.end(),[](const KeyLine &a,const KeyLine &b){return a.response > b.response;});
        keylines.resize(lsdNFeatures);
        for( int i=0; i<lsdNFeatures; i++)
            keylines[i].class_id = i;
    }
    if(keylines2.size()>lsdNFeatures){
        sort(keylines2.begin(), keylines2.end(), [](const KeyLine &a,const KeyLine &b){return a.response > b.response;});
        keylines2.resize(lsdNFeatures);
        for(int i=0; i<lsdNFeatures; i++)
            keylines2[i].class_id = i;
    }

    lbd->compute(img, keylines, mLdesc);
    lbd->compute(image2,keylines2,mLdesc2);
    BFMatcher* bfm = new BFMatcher(NORM_HAMMING, false);
    bfm->knnMatch(mLdesc, mLdesc2, lmatches, 2);
    vector<DMatch> matches;
    for(size_t i=0;i<lmatches.size();i++)
    {
        const DMatch& bestMatch = lmatches[i][0];
        const DMatch& betterMatch = lmatches[i][1];
        float  distanceRatio = bestMatch.distance / betterMatch.distance;
        if (distanceRatio < 0.7)
            matches.push_back(bestMatch);
    }

    cv::Mat outImg;
    std::vector<char> mask( lmatches.size(), 1 );
    drawLineMatches( img, keylines, image2, keylines2, matches, outImg, Scalar::all( -1 ), Scalar::all( -1 ), mask, DrawLinesMatchesFlags::DEFAULT );
    imshow( "Matches", outImg );
    waitKey(0);
    imwrite("Line_Matcher.png",outImg);
}

int main(int argc, char**argv)
{
    if(argc != 3){
        cerr << endl << "Usage: ./Line path_to_image1 path_to_image2" << endl;
        return 1;
    }
    string imagePath1=string(argv[1]);
    string imagePath2=string(argv[2]);
    cout<<"import two images"<<endl;
    Mat image1=imread(imagePath1);
    Mat image2=imread(imagePath2);

    imshow("img1",image1);
    imshow("img2",image2);
    waitKey(0);
    destroyWindow("img1");
    destroyWindow("img2");

    vector<KeyLine> keylines,keylines2;
    ExtractLineSegment(image1,image2,keylines,keylines2);
    return 0;
}
  • 实验结果:
    在这里插入图片描述

5. 基于线特征的位姿估计

比较著名的点线特征融合的VSLAM方案:

  • PL-SLAM: a Stereo SLAM System through the Combination of Points and Line Segments(双目纯视觉)
    开源代码
  • PL-VIO: Tightly-Coupled Monocular Visual–Inertial Odometry Using Point and Line Features(单目视觉惯性)
    开源代码

PL-VIO分析

  • 整个代码基于vins-mono的基础上改动,主要表现在除了原有的feature_tracker节点外,独立新增了一个line_feature_tracker节点(用于提取和发布线特征);并充实了feature_manager(添加了linefeature)和后端对线特征的ceres优化类
(1)特征点提取
  • 特征点筛选
lsd[i].octave == 0 && lsd[i].lineLength >= 30  //线特征点的质量要求

 lsd_matches[i].distance < MATCHES_DIST_THRESHOLD   
(serr.dot(serr) < 60 * 60) && (eerr.dot(eerr) < 60 * 60))  
//匹配的距离小于阈值且两端点误差小于阈值,认为匹配成功,未匹配成功的线线特征作为新的特征点加入(单帧特征点不超过50)
(2)线特征的三角化
  • 实质是求了空间中直线的 Plücker(普吕克)坐标
  • Plücker coordinates
  • 用普吕克坐标系的优势在于可以用简单的几个运算就能处理常见的相交问题,特别在判断相交的问题上。在实际编程中去除了除法运算,所以效率有很大的提升。
  • 用普吕克坐标而非笛卡尔坐标系的2个3d坐标表示,估计是其在损失函数中能简单的量化出残差
(3)线特征的参数化和残差雅克比求导
  • 空间中的直线具有4个自由度,而普吕克坐标表示的线特征具有6个参数,这样就会导致过参数化,过参数化在优化的时候就需要采用带约束的优化,不太方便。所以需要在ceres中仿照四元数一样书写过参数类ceres::LocalParameterization()
  • 参数化和求导的相机解释与推导
  • 18
    点赞
  • 124
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
特征提取跟踪图像处理和计算机视觉中的重要任务。在Matlab中,可以使用各种方法进行线特征提取跟踪的关联。以下是一种常用的方法: 1. 使用Hough变换进行线特征提取: ```matlab % 读取图像 image = imread('image.jpg'); % 将图像转换为灰度图像 grayImage = rgb2gray(image); % 使用Canny边缘检测算法检测图像边缘 edgeImage = edge(grayImage, 'canny'); % 使用Hough变换检测直线 [H, theta, rho] = hough(edgeImage); peaks = houghpeaks(H, 10); % 选择前10个峰值 lines = houghlines(edgeImage, theta, rho, peaks); % 绘制检测到的直线 figure, imshow(image), hold on for k = 1:length(lines) xy = [lines(k).point1; lines(k).point2]; plot(xy(:,1), xy(:,2), 'LineWidth', 2, 'Color', 'green'); end ``` 2. 使用光流法进行线特征跟踪: ```matlab % 读取视频 video = VideoReader('video.mp4'); % 读取第一帧图像 frame1 = readFrame(video); frame1Gray = rgb2gray(frame1); % 使用角点检测算法检测图像中的角点 points1 = detectHarrisFeatures(frame1Gray); % 初始化光流估计器 opticalFlow = opticalFlowLK('NoiseThreshold', 0.01); % 循环处理每一帧图像 while hasFrame(video) frame2 = readFrame(video); frame2Gray = rgb2gray(frame2); % 估计光流 flow = estimateFlow(opticalFlow, frame1Gray); % 选择角点在当前帧中的位置 points2 = points1.Location + flow.Velocity; % 绘制跟踪线 figure, imshow(frame2), hold on plot(points2(:,1), points2(:,2), 'r.'); % 更新角点位置 points1 = detectHarrisFeatures(frame2Gray); points1 = points1.selectStrongest(100); % 更新当前帧 frame1Gray = frame2Gray; end ``` 这些方法可以用于提取和跟踪图像中的线特征。你可以根据具体的需求选择适合的方法进行特征提取跟踪
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值