96SEO 2026-02-19 20:09 33
什么是特征点、如何提取和匹配特征点、如何根据配对的特征点估计相机运动。

如何提取、匹配图像特征点然后估计两帧之间的相机运动和场景结构。
【两帧间视觉里程计】也称为
project(vo1)set(CMAKE_CXX_STANDARD
${PROJECT_SOURCE_DIR}/cmake)find_package(OpenCV
REQUIRED)include_directories(${OpenCV_INCLUDE_DIRS}${G2O_INCLUDE_DIRS}${Sophus_INCLUDE_DIRS}/usr/include/eigen3/
add_executable(pose_estimation_2d2d
target_link_libraries(pose_estimation_2d2d
target_link_libraries(triangulation
${OpenCV_LIBS})add_executable(pose_estimation_3d2d
target_link_libraries(pose_estimation_3d2dg2o_core
g2o_stuff${OpenCV_LIBS}${Sophus_LIBRARIES})add_executable(pose_estimation_3d3d
target_link_libraries(pose_estimation_3d3dg2o_core
g2o_stuff${OpenCV_LIBS}${Sophus_LIBRARIES})]]7.2.1
/home/xixi/Downloads/slambook2-master/ch7/orb_cv.cpp:16:31:
opencv4/opencv2/features2d/features2d.hpp
opencv4/opencv2/highgui/highgui.hpp
cv::IMREAD_COLOR);assert(img_1.data
descriptors_2;PtrFeatureDetector
ORB::create();PtrDescriptorExtractor
ORB::create();PtrDescriptorMatcher
DescriptorMatcher::create(BruteForce-Hamming);//--
角点位置chrono::steady_clock::time_point
chrono::steady_clock::now();detector-detect(img_1,
keypoints_1);detector-detect(img_2,
descriptors_1);descriptor-compute(img_2,
descriptors_2);chrono::steady_clock::time_point
chrono::steady_clock::now();chrono::durationdouble
chrono::duration_castchrono::durationdouble(t2
DrawMatchesFlags::DEFAULT);imshow(ORB
chrono::steady_clock::now();matcher-match(descriptors_1,
chrono::steady_clock::now();time_used
chrono::duration_castchrono::durationdouble(t2
minmax_element(matches.begin(),
min_max.second-distance;printf(--
min_dist);//当描述子之间的距离大于两倍的最小距离时,即认为匹配有误.但有时候最小距离会非常小,设置一个经验值30作为下限.std::vectorDMatch
{good_matches.push_back(matches[i]);}}//--
img_goodmatch;drawMatches(img_1,
img_goodmatch);waitKey(0);return
threshold40chrono::steady_clock::time_point
chrono::steady_clock::now();vectorcv::KeyPoint
keypoints1;cv::FAST(first_image,
descriptor1;ComputeORB(first_image,
descriptor2;cv::FAST(second_image,
descriptor2);chrono::steady_clock::time_point
chrono::steady_clock::now();chrono::durationdouble
chrono::duration_castchrono::durationdouble(t2
chrono::steady_clock::now();BfMatch(descriptor1,
chrono::steady_clock::now();time_used
chrono::duration_castchrono::durationdouble(t2
image_show;cv::drawMatches(first_image,
image_show);cv::imshow(matches,
image_show);cv::imwrite(matches.png,
image_show);cv::waitKey(0);cout
--------------------------------------------------------------------------------------------------
outsidebad_points;descriptors.push_back({});continue;}float
d;}descriptors.push_back(desc);}cout
\bm{F}\bm{K}^{-T}\bm{E}\bm{K}^{-1}
/home/xixi/Downloads/slambook2-master/ch7/pose_estimation_2d2d.cpp:36:31:
/home/xixi/Downloads/slambook2-master/ch7/pose_estimation_2d2d.cpp:
pose_estimation_2d2d(std::vectorcv::KeyPoint,
/home/xixi/Downloads/slambook2-master/ch7/pose_estimation_2d2d.cpp:143:61:
dumped)】卡了挺久。
重新复制原版CmakeLists.txt
../2.pngpose_estimation_2d2d.cpp
opencv2/features2d/features2d.hpp
cv;/*****************************************************
**************************************************/void
keypoints_1,std::vectorKeyPoint
pose_estimation_2d2d(std::vectorKeyPoint
keypoints_1,std::vectorKeyPoint
IMREAD_COLOR);assert(img_1.data
matches;find_feature_matches(img_1,
t;pose_estimation_2d2d(keypoints_1,
pixel2cam(keypoints_1[m.queryIdx].pt,
pixel2cam(keypoints_2[m.trainIdx].pt,
keypoints_1,std::vectorKeyPoint
ORB::create();PtrDescriptorExtractor
DescriptorMatcher::create(BruteForce-Hamming);//--
keypoints_1);detector-detect(img_2,
descriptors_1);descriptor-compute(img_2,
min_dist);//当描述子之间的距离大于两倍的最小距离时,即认为匹配有误.但有时候最小距离会非常小,设置一个经验值30作为下限.for
{matches.push_back(match[i]);}}
pose_estimation_2d2d(std::vectorKeyPoint
keypoints_1,std::vectorKeyPoint
把匹配点转换为vectorPoint2f的形式vectorPoint2f
{points1.push_back(keypoints_1[matches[i].queryIdx].pt);points2.push_back(keypoints_2[matches[i].trainIdx].pt);}//--
fundamental_matrix;fundamental_matrix
essential_matrix;essential_matrix
homography_matrix;homography_matrix
此函数仅在Opencv3中提供recoverPose(essential_matrix,
无法获得像素的深度信息需要通过三角测量(Triangulation)或三角化
通过不同位置对同一路标点进行观察从观察到的位置判断路标点的距离。
keypoints_1,std::vectorKeyPoint
cv::IMREAD_COLOR);vectorKeyPoint
matches;find_feature_matches(img_1,
t;pose_estimation_2d2d(keypoints_1,
points;triangulation(keypoints_1,
pixel2cam(keypoints_1[matches[i].queryIdx].pt,
keypoints_1[matches[i].queryIdx].pt,
keypoints_2[matches[i].trainIdx].pt,
img2_plot);cv::waitKey();return
keypoints_1,std::vectorKeyPoint
ORB::create();PtrDescriptorExtractor
DescriptorMatcher::create(BruteForce-Hamming);//--
keypoints_1);detector-detect(img_2,
descriptors_1);descriptor-compute(img_2,
min_dist);//当描述子之间的距离大于两倍的最小距离时,即认为匹配有误.但有时候最小距离会非常小,设置一个经验值30作为下限.for
{matches.push_back(match[i]);}}
把匹配点转换为vectorPoint2f的形式vectorPoint2f
{points1.push_back(keypoints_1[matches[i].queryIdx].pt);points2.push_back(keypoints_2[matches[i].trainIdx].pt);}//--
essential_matrix;essential_matrix
从本质矩阵中恢复旋转和平移信息.recoverPose(essential_matrix,
将像素坐标转换至相机坐标pts_1.push_back(pixel2cam(keypoint_1[m.queryIdx].pt,
K));pts_2.push_back(pixel2cam(keypoint_2[m.trainIdx].pt,
pts_4d;cv::triangulatePoints(T1,
将明显变小。
说明平移较大时在同样的相机分辨率下三角化测量将会更精确。
ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-LppxVVDY4FQ1fZ1n
.error-icon{fill:#552222;}#mermaid-svg-LppxVVDY4FQ1fZ1n
.error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LppxVVDY4FQ1fZ1n
.edge-thickness-normal{stroke-width:2px;}#mermaid-svg-LppxVVDY4FQ1fZ1n
.edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LppxVVDY4FQ1fZ1n
.edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LppxVVDY4FQ1fZ1n
.edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LppxVVDY4FQ1fZ1n
.edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LppxVVDY4FQ1fZ1n
.marker{fill:#333333;stroke:#333333;}#mermaid-svg-LppxVVDY4FQ1fZ1n
.marker.cross{stroke:#333333;}#mermaid-svg-LppxVVDY4FQ1fZ1n
ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LppxVVDY4FQ1fZ1n
ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-LppxVVDY4FQ1fZ1n
text{fill:#333;}#mermaid-svg-LppxVVDY4FQ1fZ1n
span{color:#333;}#mermaid-svg-LppxVVDY4FQ1fZ1n
text,#mermaid-svg-LppxVVDY4FQ1fZ1n
span{fill:#333;color:#333;}#mermaid-svg-LppxVVDY4FQ1fZ1n
rect,#mermaid-svg-LppxVVDY4FQ1fZ1n
circle,#mermaid-svg-LppxVVDY4FQ1fZ1n
ellipse,#mermaid-svg-LppxVVDY4FQ1fZ1n
polygon,#mermaid-svg-LppxVVDY4FQ1fZ1n
path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-LppxVVDY4FQ1fZ1n
.label{text-align:center;}#mermaid-svg-LppxVVDY4FQ1fZ1n
.node.clickable{cursor:pointer;}#mermaid-svg-LppxVVDY4FQ1fZ1n
.arrowheadPath{fill:#333333;}#mermaid-svg-LppxVVDY4FQ1fZ1n
.path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-LppxVVDY4FQ1fZ1n
.flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-LppxVVDY4FQ1fZ1n
.edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-LppxVVDY4FQ1fZ1n
rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-LppxVVDY4FQ1fZ1n
rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-LppxVVDY4FQ1fZ1n
text{fill:#333;}#mermaid-svg-LppxVVDY4FQ1fZ1n
span{color:#333;}#mermaid-svg-LppxVVDY4FQ1fZ1n
div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet
ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80,
#aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-LppxVVDY4FQ1fZ1n
:root{--mermaid-font-family:"trebuchet
1、已知一组3D点的位置以及它们在某个相机中的投影位置求该相机的位姿。
利用更多的信息用迭代的方式对相机位姿进行优化尽可能消除噪声的影响。
/home/xixi/Downloads/slambook2-master/ch7/pose_estimation_3d2d.cpp:37:11:
/home/xixi/Downloads/slambook2-master/ch7/pose_estimation_3d2d.cpp:45:11:
/home/xixi/Downloads/slambook2-master/ch7/pose_estimation_3d2d.cpp:54:31:
/home/xixi/Downloads/slambook2-master/ch7/pose_estimation_3d2d.cpp:64:28:
/usr/local/include/g2o/stuff/tuple_tools.h:41:46:
std::make_index_sequencestd::tuple_size_vstd::decay_tT());
/home/xixi/Downloads/slambook2-master/ch7/pose_estimation_3d2d.cpp:318:10:
g2o::make_uniqueBlockSolverType(g2o::make_uniqueLinearSolverType()));
g2o::BlockSolverg2o::BlockSolverTraits3,
g2o::LinearSolverDenseBlockSolverType::PoseMatrixType
线性求解器类型*/std::unique_ptrg2o::BlockSolverX::LinearSolverType
g2o::LinearSolverDenseg2o::BlockSolverX::PoseMatrixType());//
g2o::OptimizationAlgorithmGaussNewton(g2o::make_uniqueBlockSolverType(g2o::make_uniqueLinearSolverType()));*/std::unique_ptrg2o::BlockSolverX
g2o::BlockSolverX(std::move(linearSolver)));g2o::OptimizationAlgorithmGaussNewton*
g2o::OptimizationAlgorithmGaussNewton(std::move(solver_ptr));g2o::SparseOptimizer
图模型optimizer.setAlgorithm(solver);
设置求解器optimizer.setVerbose(true);
CMakeFiles/pose_estimation_3d2d.dir/pose_estimation_3d2d.cpp.o:
bundleAdjustmentGaussNewton(std::vectorEigen::Matrixdouble,
Eigen::aligned_allocatorEigen::Matrixdouble,
std::vectorEigen::Matrixdouble,
Eigen::aligned_allocatorEigen::Matrixdouble,
pose_estimation_3d2d.cpp:(.text0x2a4f):
Sophus::SE3::operator*(Eigen::Matrixdouble,
pose_estimation_3d2d.cpp:(.text0x3254):
Sophus::SE3::exp(Eigen::Matrixdouble,
add_executable(pose_estimation_3d2d
target_link_libraries(pose_estimation_3d2dg2o_core
g2o_stuff${OpenCV_LIBS}${Sophus_LIBRARIES})cd
../2_depth.pngpose_estimation_3d2d.cpp
opencv4/opencv2/features2d/features2d.hpp
opencv4/opencv2/highgui/highgui.hpp
opencv4/opencv2/calib3d/calib3d.hpp
g2o/core/optimization_algorithm_gauss_newton.h
g2o/solvers/dense/linear_solver_dense.h
keypoints_1,std::vectorKeyPoint
Eigen::aligned_allocatorEigen::Vector2d
Eigen::aligned_allocatorEigen::Vector3d
bundleAdjustmentGaussNewton(const
IMREAD_COLOR);assert(img_1.data
matches;find_feature_matches(img_1,
short(int(keypoints_1[m.queryIdx].pt.y))[int(keypoints_1[m.queryIdx].pt.x)];if
pixel2cam(keypoints_1[m.queryIdx].pt,
K);pts_3d.push_back(Point3f(p1.x
dd));pts_2d.push_back(keypoints_2[m.trainIdx].pt);}cout
endl;chrono::steady_clock::time_point
chrono::steady_clock::now();Mat
r为旋转向量形式用Rodrigues公式转换为矩阵chrono::steady_clock::time_point
chrono::steady_clock::now();chrono::durationdouble
chrono::duration_castchrono::durationdouble(t2
{pts_3d_eigen.push_back(Eigen::Vector3d(pts_3d[i].x,
pts_3d[i].z));pts_2d_eigen.push_back(Eigen::Vector2d(pts_2d[i].x,
chrono::steady_clock::now();bundleAdjustmentGaussNewton(pts_3d_eigen,
chrono::steady_clock::now();time_used
chrono::duration_castchrono::durationdouble(t2
chrono::steady_clock::now();bundleAdjustmentG2O(pts_3d_eigen,
chrono::steady_clock::now();time_used
chrono::duration_castchrono::durationdouble(t2
keypoints_1,std::vectorKeyPoint
ORB::create();PtrDescriptorExtractor
DescriptorMatcher::create(BruteForce-Hamming);//--
keypoints_1);detector-detect(img_2,
descriptors_1);descriptor-compute(img_2,
min_dist);//当描述子之间的距离大于两倍的最小距离时,即认为匹配有误.但有时候最小距离会非常小,设置一个经验值30作为下限.for
{matches.push_back(match[i]);}}
bundleAdjustmentGaussNewton(const
e.squaredNorm();Eigen::Matrixdouble,
public:EIGEN_MAKE_ALIGNED_OPERATOR_NEW;virtual
public:EIGEN_MAKE_ALIGNED_OPERATOR_NEW;EdgeProjection(const
g2o::BlockSolverg2o::BlockSolverTraits3,
g2o::LinearSolverDenseBlockSolverType::PoseMatrixType
线性求解器类型*/std::unique_ptrg2o::BlockSolverX::LinearSolverType
g2o::LinearSolverDenseg2o::BlockSolverX::PoseMatrixType());//
g2o::OptimizationAlgorithmGaussNewton(g2o::make_uniqueBlockSolverType(g2o::make_uniqueLinearSolverType()));*/std::unique_ptrg2o::BlockSolverX
g2o::BlockSolverX(std::move(linearSolver)));g2o::OptimizationAlgorithmGaussNewton*
g2o::OptimizationAlgorithmGaussNewton(std::move(solver_ptr));g2o::SparseOptimizer
图模型optimizer.setAlgorithm(solver);
设置求解器optimizer.setVerbose(true);
vertex_posevertex_pose-setId(0);vertex_pose-setEstimate(Sophus::SE3());optimizer.addVertex(vertex_pose);//
K_eigen);edge-setId(index);edge-setVertex(0,
vertex_pose);edge-setMeasurement(p2d);edge-setInformation(Eigen::Matrix2d::Identity());optimizer.addEdge(edge);index;}chrono::steady_clock::time_point
chrono::steady_clock::now();optimizer.setVerbose(true);optimizer.initializeOptimization();optimizer.optimize(10);chrono::steady_clock::time_point
chrono::steady_clock::now();chrono::durationdouble
chrono::duration_castchrono::durationdouble(t2
vertex_pose-estimate().matrix()
/home/xixi/Downloads/slambook2-master/ch7/pose_estimation_3d3d.cpp:81:50:
Sophus::SO3d::hat(xyz_trans);去掉d改成
/home/xixi/Downloads/slambook2-master/ch7/pose_estimation_3d3d.cpp:
/home/xixi/Downloads/slambook2-master/ch7/pose_estimation_3d3d.cpp:298:41:
pose-estimate().rotationMatrix();|
pose-estimate().rotation_matrix();cd
../2_depth.pngpose_estimation_3d3d.cpp
opencv4/opencv2/features2d/features2d.hpp
opencv4/opencv2/highgui/highgui.hpp
opencv4/opencv2/calib3d/calib3d.hpp
g2o/core/optimization_algorithm_gauss_newton.h
g2o/core/optimization_algorithm_levenberg.h
g2o/solvers/dense/linear_solver_dense.h
keypoints_1,std::vectorKeyPoint
public:EIGEN_MAKE_ALIGNED_OPERATOR_NEW;virtual
public:EIGEN_MAKE_ALIGNED_OPERATOR_NEW;EdgeProjectXYZRGBDPoseOnly(const
pose-estimate();Eigen::Vector3d
_point;_jacobianOplusXi.block3,
-Eigen::Matrix3d::Identity();_jacobianOplusXi.block3,
Sophus::SO3::hat(xyz_trans);}bool
matches;find_feature_matches(img_1,
short(int(keypoints_1[m.queryIdx].pt.y))[int(keypoints_1[m.queryIdx].pt.x)];ushort
short(int(keypoints_2[m.trainIdx].pt.y))[int(keypoints_2[m.trainIdx].pt.x)];if
pixel2cam(keypoints_1[m.queryIdx].pt,
pixel2cam(keypoints_2[m.trainIdx].pt,
5000.0;pts1.push_back(Point3f(p1.x
dd1));pts2.push_back(Point3f(p2.x
keypoints_1,std::vectorKeyPoint
ORB::create();PtrDescriptorExtractor
DescriptorMatcher::create(BruteForce-Hamming);//--
keypoints_1);detector-detect(img_2,
descriptors_1);descriptor-compute(img_2,
min_dist);//当描述子之间的距离大于两倍的最小距离时,即认为匹配有误.但有时候最小距离会非常小,设置一个经验值30作为下限.for
{matches.push_back(match[i]);}}
WEigen::JacobiSVDEigen::Matrix3d
Eigen::ComputeFullV);Eigen::Matrix3d
g2o::BlockSolverg2o::BlockSolverTraits3,
g2o::LinearSolverDenseBlockSolverType::PoseMatrixType
线性求解器类型*/std::unique_ptrg2o::BlockSolverX::LinearSolverType
g2o::LinearSolverDenseg2o::BlockSolverX::PoseMatrixType());//
g2o::OptimizationAlgorithmGaussNewton(g2o::make_uniqueBlockSolverType(g2o::make_uniqueLinearSolverType()));*/std::unique_ptrg2o::BlockSolverX
g2o::BlockSolverX(std::move(linearSolver)));g2o::OptimizationAlgorithmGaussNewton*
g2o::OptimizationAlgorithmGaussNewton(std::move(solver_ptr));g2o::SparseOptimizer
图模型optimizer.setAlgorithm(solver);
设置求解器optimizer.setVerbose(true);
posepose-setId(0);pose-setEstimate(Sophus::SE3());optimizer.addVertex(pose);//
EdgeProjectXYZRGBDPoseOnly(Eigen::Vector3d(pts2[i].x,
pose);edge-setMeasurement(Eigen::Vector3d(pts1[i].x,
pts1[i].z));edge-setInformation(Eigen::Matrix3d::Identity());optimizer.addEdge(edge);}chrono::steady_clock::time_point
chrono::steady_clock::now();optimizer.initializeOptimization();optimizer.optimize(10);chrono::steady_clock::time_point
chrono::steady_clock::now();chrono::durationdouble
chrono::duration_castchrono::durationdouble(t2
pose-estimate().rotation_matrix();Eigen::Vector3d
pose-estimate().translation();R
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback