图像的线特征提取与跟踪
线特征⽐点特征可以提供更多的约束条件,并且在某些场合下⽐点特征要鲁棒。
1. LSD: Line Segment Detector线段检测器
LSD是⼀种局部提取直线的算法,速度⽐Hough要快。 LSD是⼀种直线检测分割算法,它能在线性的时间内得出亚像素级精度的检测结果。该算法被设计成⾃适应模式,⽆需⼿动调参。
作者将⾃⼰的论⽂(LSD: a Line Segment Detector,2012)及代码( c++)放到⾃⼰的主页上:
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 t of vectors that will store extracted lines for one or more images
//@param scale scale factor ud 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,fal);
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(), d(),[](const KeyLine &a,const KeyLine &b){spon > b.respon;});
for(int i=0; i<lsdNFeatures; i++)
keylines[i].class_id = i;
}
if(keylines2.size()>lsdNFeatures){
sort(keylines2.begin(), d(),[](const KeyLine &a,const KeyLine &b){spon > b.respon;});
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,fal);
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;
return1;
}
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);
return0;
}
实验结果:
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
(rr.dot(rr)<60*60)&&(eerr.dot(eerr)<60*60))
//匹配的距离⼩于阈值且两端点误差⼩于阈值,认为匹配成功,未匹配成功的线线特征作为新的特征点加⼊(单帧特征点不超过50)
(2)线特征的三⾓化
实质是求了空间中直线的 Plücker(普吕克)坐标
⽤普吕克坐标系的优势在于可以⽤简单的⼏个运算就能处理常见的相交问题,特别在判断相交的问题上。在实际编程中去除了除法运算,所以效率有很⼤的提升。
⽤普吕克坐标⽽⾮笛卡尔坐标系的2个3d坐标表⽰,估计是其在损失函数中能简单的量化出残差
(3)线特征的参数化和残差雅克⽐求导
空间中的直线具有4个⾃由度,⽽普吕克坐标表⽰的线特征具有6个参数,这样就会导致过参数化,过参数化在优化的时候就需要采⽤带约束的优化,不太⽅便。所以需要在ceres中仿照四元数⼀样书写过参数类ceres::LocalParameterization()