前言
最近想试着在ORB_SLAM2添加线特征,所以先学习了一下如何使用OpenCV提取LSD线特征。
一、OpenCV提取LSD(简单)
OpenCV在imgproc模块实现了一个LineSegmentDetector,实现原理就是常用的LSD算法,使用方法也很简单。
#include <iostream>
#include <string>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
int main() {
// 换成自己的图像路径
std::string image_1_dir = "../data/image1.png";
// 以灰度图读取图像
cv::Mat image_1 = cv::imread(image_1_dir, cv::IMREAD_GRAYSCALE);
// 先看看图像有没有读取到
if(image_1.empty()) {
std::cout << "Unable to load " << image_1_dir << std::endl;
return 1;
}
// 显示原图像
cv::imshow("Source Image 1", image_1);
// 创建LSD
cv::Ptr<cv::LineSegmentDetector> lsd = createLineSegmentDetector(cv::LSD_REFINE_STD);
// 创建一个4维向量的vector,作为后续lsd的输出
std::vector<cv::Vec4f> lines_1;
// lsd检测,检测出的线特征以一个4维向量表示
// 向量中是线段起始端点和终点的坐标值
// [x1, y1, x2, y2], 1是起始点,2是终点
lsd->detect(image_1, lines_1);
// 显示线特征,把原图拷贝下,当然不拷贝也行
cv::Mat image_output = image_1.clone();
// 显示线特征用到lsd中的drawSegment函数
lsd->drawSegments(image_output, lines_1);
cv::imshow("1", image_output);
cv::waitKey(0);
return 0;
}
显示结果
二、OpenCV提取LSD(复杂)
上面的方法,线特征只有两个端点的坐标,信息比较简单。而且这里面也没有LBD描述子相关的计算,这个时候,我们可以使用OpenCV_contrib中的line_descriptor模块。
OpenCV一般包含的是基本模块,而OpenCV_contrib模块中还包含了一些扩展模块,这些扩展模块中会包含一些带专利或收费的模块(当然我们用来学习不需要管这个收不收费),而line_descriptor就是在OpenCV_contrib,下面是这个模块的简介
This module shows how to extract line segments from an image by 2 different methods: First segmenting lines with Line Segment Detector LSDDetector and then (or just) using the Binary Descriptor to get the lines and give them a descriptor – BinaryDescriptor. Finally, we can then match line segments using the BinaryDescriptorMatcher class.
如何安装OpenCV_contrib,大家可以参照这个教程,简单点说就是先下载对应版本的OpenCV_contrib,比如我的是3.4.16版本,然后在OpenCV中cmake时可以指定额外模块的路径,把你的OpenCV_contrib的路径输入进入即可,为了方便,通常是直接把OpenCV_contrib放到OpenCV路径里,之后就可以在代码中正常导入line_descriptor了,代码如下
#include <iostream>
#include <string>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <opencv2/line_descriptor.hpp>
int main() {
std::string image_1_dir = "../data/image1.png";
cv::Mat image_1 = cv::imread(image_1_dir, cv::IMREAD_UNCHANGED);
if(image_1.empty()) {
std::cout << "Unable to load " << image_1_dir << std::endl;
return 1;
}
cv::imshow("Source Image 1", image_1);
// 上面读取图像代码和之前一样
// 创建直线检测器
cv::Ptr<cv::line_descriptor::LSDDetector> lsd = cv::line_descriptor::LSDDetector::createLSDDetector();
// 这个看名字是计算描述子的,但其实里面也有直线检测器,但检测结果和上面那个有些区别,底层实现似乎是不一样的
cv::Ptr<cv::line_descriptor::BinaryDescriptor> lbd = cv::line_descriptor::BinaryDescriptor::createBinaryDescriptor();
// 存储检测出的直线
std::vector<cv::line_descriptor::KeyLine> keylines_1;
// 直线描述子,每一行表示一条直线的描述子
cv::Mat descriptor_1;
// 下面这两个都可以检测直线,但是lsd这个接口有金字塔分层和缩放系数的设置,但下面lbd这个没有,估计是不支持分层检测
// 大家可以试试用这两个不同的检测器检测
lsd->detect(image_1, keylines_1, 1.2, 1);
// lbd->detect(image_1, keylines_1);
// 计算描述子
lbd->compute(image_1, keylines_1, descriptor_1);
// 绘制直线,用蓝色绘制
cv::Mat image_output = image_1.clone();
cv::line_descriptor::drawKeylines(image_1, keylines_1, image_output, cv::Scalar(255, 0, 0));
cv::imshow("1", image_output);
cv::waitKey(0);
return 0;
}
检测结果如下