特征提取与匹配中使用cv::findFundamentalMat要注意的几点

新版OpenCV将很多C代码封装成C++代码,调用接口改变。处理立体图像对常计算基本矩阵,老版本C代码的RANSAC方法有迭代次数不收敛的bug,影响计算效率。新版本cv::findFundamentalMat函数使用也有问题,传递参数和矩阵类型设置不当会导致程序崩溃,文中给出解决办法和代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        原文地址:http://blog.sina.com.cn/s/blog_4298002e01013w9a.html

         在新版的OpenCV中,很多C代码都被重新封装成了C++代码,相应的其调用接口也发生了改变,而文档中的叙述也越来越不清楚,往往导致使用过程中出现各种问题。

    在处理立体图像对的时候经常会用到对极几何的知识,计算基础矩阵也是很常见的事。OpenCV实现了基本矩阵的算法。对于老版本的C代码,计算基本矩阵的RANSAC方法中有一个迭代次数不收敛的bug,可能导致每次计算的采样次数都达到最大限制的次数,其根源在于计算采样可信度的公式有误,新版本的代码还没有仔细看过,不敢确定是否已经修正了这个bug。但是这个bug并不会对最后的结果造成多大影响,只是会影响计算的效率——原本几次采样即可结束迭代,在这个bug的影响下可能要采样数百次。

    新版本的计算基本矩阵的函数的使用也有一些问题,下面来看看cv::findFundamentalMat函数:

//! the algorithm for finding fundamental matrix寻找基础矩阵算法
enum
{
    FM_7POINT = CV_FM_7POINT, //!< 7-point algorithm
    FM_8POINT = CV_FM_8POINT, //!< 8-point algorithm
    FM_LMEDS = CV_FM_LMEDS,  //!< least-median algorithm
    FM_RANSAC = CV_FM_RANSAC  //!< RANSAC algorithm
};

//! finds fundamental matrix from a set of corresponding 2D points
CV_EXPORTS Mat findFundamentalMat( const Mat& points1, const Mat& points2,
                                     CV_OUT vector<uchar>& mask, int method=FM_RANSAC,
                                     double param1=3., double param2=0.99 );

//! finds fundamental matrix from a set of corresponding 2D points
CV_EXPORTS_W Mat findFundamentalMat( const Mat& points1, const Mat& points2,
                                     int method=FM_RANSAC,
                                     double param1=3., double param2=0.99 );

    上面是OpenCV计算基本矩阵的C++接口,其内部实现仍然是调用的C代码函数,仅仅是在上层进行了一次封装。网上有些文档中说const Mat& points1和points2这两个参数可以直接由vector<Point2f>类型作为传入参数,其实这是错误的。直接用vector<Point2f>传递,在编译时可能不会报错,但是运行时会直接崩溃。因为cv::Mat的构造函数并不会把Point2f转化为两个浮点数存于Mat的两个元素中,而是仍然以Point2f存于Mat的一个元素中,于是findFundamentalMat一读取这个Mat就会出错。

    因此我们最好老老实实地去构建Mat points1和Mat points2。该矩阵的维度可以是2xN,也可以是Nx2的,其中N是特征点的数目。另一个要注意的地方就是该矩阵的类型不能是CV_64F,也就是说findFundamentalMat内部解析points1和points2时是按照float类型去解析的,设为CV_64F将导致读取数据失败,程序崩溃。最好设为CV_32F。下面是从匹配好的特征点计算F的代码示例:

// vector<KeyPoint> m_LeftKey;
// vector<KeyPoint> m_RightKey;
// vector<DMatch> m_Matches;
// 以上三个变量已经被计算出来,分别是提取的关键点及其匹配,下面直接计算F
// 分配空间
int ptCount = (int)m_Matches.size();
Mat p1(ptCount, 2, CV_32F);
Mat p2(ptCount, 2, CV_32F);

// 把Keypoint转换为Mat
Point2f pt;
for (int i=0; i<ptCount; i++)
{
     pt = m_LeftKey[m_Matches[i].queryIdx].pt;
     p1.at<float>(i, 0) = pt.x;
     p1.at<float>(i, 1) = pt.y;
  
     pt = m_RightKey[m_Matches[i].trainIdx].pt;
     p2.at<float>(i, 0) = pt.x;
     p2.at<float>(i, 1) = pt.y;
}

// 用RANSAC方法计算F
// Mat m_Fundamental;
// 上面这个变量是基本矩阵
// vector<uchar> m_RANSACStatus;
// 上面这个变量已经定义过,用于存储RANSAC后每个点的状态

m_Fundamental = findFundamentalMat(p1, p2, m_RANSACStatus, FM_RANSAC);

// 计算野点个数
int OutlinerCount = 0;
for (int i=0; i<ptCount; i++)
{
     if (m_RANSACStatus[i] == 0) // 状态为0表示野点
     {
          OutlinerCount++;
     }
}

// 计算内点
// vector<Point2f> m_LeftInlier;
// vector<Point2f> m_RightInlier;
// vector<DMatch> m_InlierMatches;
// 上面三个变量用于保存内点和匹配关系
int InlinerCount = ptCount - OutlinerCount;
m_InlierMatches.resize(InlinerCount);
m_LeftInlier.resize(InlinerCount);
m_RightInlier.resize(InlinerCount);
InlinerCount = 0;
for (int i=0; i<ptCount; i++)
{
     if (m_RANSACStatus[i] != 0)
     {
          m_LeftInlier[InlinerCount].x = p1.at<float>(i, 0);
          m_LeftInlier[InlinerCount].y = p1.at<float>(i, 1);
          m_RightInlier[InlinerCount].x = p2.at<float>(i, 0);
          m_RightInlier[InlinerCount].y = p2.at<float>(i, 1);
          m_InlierMatches[InlinerCount].queryIdx = InlinerCount;
          m_InlierMatches[InlinerCount].trainIdx = InlinerCount;
          InlinerCount++;
     }
}

// 把内点转换为drawMatches可以使用的格式
vector<KeyPoint> key1(InlinerCount);
vector<KeyPoint> key2(InlinerCount);
KeyPoint::convert(m_LeftInlier, key1);
KeyPoint::convert(m_RightInlier, key2);

// 显示计算F过后的内点匹配
// Mat m_matLeftImage;
// Mat m_matRightImage;
// 以上两个变量保存的是左右两幅图像
Mat OutImage;
drawMatches(m_matLeftImage, key1, m_matRightImage, key2, m_InlierMatches, OutImage);
cvNamedWindow( "Match features", 1);
cvShowImage("Match features", &(IplImage(OutImage)));
cvWaitKey( 0 );
cvDestroyWindow( "Match features" );

    以上就是核心代码。载入左右两幅图像后,先用http://blog.sina.com.cn/s/blog_4298002e01013w4z.html一文中所述方法提取特征点并匹配,然后用上面的程序计算基本矩阵,并显示内点匹配。如图:

初始匹配:

RANSAC过后的内点匹配:

 

<think>好的,我现在需要回答用户关于特征点融合算法及其实现方法的问题。首先,我需要回顾用户提供的引用内容,看看是否有相关的资料可以引用。用户提到了五个引用,其中引用[1]是关于特征级融合和多知识图谱的,引用[2]到[5]主要涉及图像融合、医学图像融合和多源信息融合。虽然这些引用没有直接提到特征点融合,但可能有一些相关的技术可以借鉴。 首先,特征点融合通常指的是在计算机视觉中,将来自不同图像或传感器的特征点(如SIFT、SURF、ORB等)进行融合,以提高匹配的准确性和鲁棒性。这可能包括特征点的提取、匹配、筛选以及融合策略。 接下来,我需要考虑用户提供的引用内容。引用[1]提到特征级融合和多知识图谱的融合算法,这可能涉及如何将不同来源的特征进行融合,但具体到特征点可能需要进一步关联。引用[3]和[4]提到的图像金字塔分解、预处理、特征提取和重建可能对特征点融合的实现有帮助。引用[5]讨论的多源信息融合方法,特别是关于排除冗余、提高一致性的部分,也可能适用于特征点融合的步骤。 然后,我需要组织回答的结构。用户要求结构清晰,分步骤说明。可能需要先介绍特征点融合的基本流程,然后详细说明每个步骤,如特征提取匹配、筛选和融合策略,最后给出实现方法,比如使用Python或Matlab,并结合引用中的相关技术,比如图像金字塔或特征重建。 同时,用户要求生成相关问题,我需要根据回答内容提出几个相关问题,例如性能优化、应用场景、多模态融合等。 需要注意的是,所有数学公式需要用$...$或$$...$$格式,确保LaTeX正确。例如,在描述特征点匹配时的距离度量可能会用到欧氏距离公式:$d = \sqrt{(x_2 - x_1)^2 + (y_2 - y_1)^2}$。此外,引用需要自然地添加到相关部分的末尾,比如在提到特征筛选时引用[5]中的冗余排除方法。 最后,要确保使用中文回答,避免使用Markdown格式,代码示例用正确的语法高亮,如Python代码块。需要检查是否有遗漏的引用或技术点,确保回答真实可靠,并覆盖用户的需求。</think>### 特征点融合算法及其实现方法 特征点融合是一种通过整合不同图像或传感器提取的特征点信息,提升目标识别、匹配或重建精度的技术。其核心流程包括**特征提取匹配筛选、融合策略**,以下是具体实现步骤: --- #### 1. **特征提取** - **常用算法**:SIFT(尺度不变特征变换)、SURF(加速鲁棒特征)、ORB(定向FAST和旋转BRIEF)等。 - **数学表达**: 例如,SIFT特征通过高斯差分(DoG)检测关键点,并生成128维描述子: $$ D(x,y,\sigma) = (G(x,y,k\sigma) - G(x,y,\sigma)) * I(x,y) $$ 其中$G$为高斯核,$I$为图像,$\sigma$为尺度参数[^2]。 --- #### 2. **特征匹配筛选** - **匹配方法**: 使用欧氏距离或汉明距离衡量特征相似性。例如,ORB特征采用汉明距离: $$ d = \sum_{i=1}^n \text{XOR}(desc1_i, desc2_i) $$ - **筛选策略**: 通过RANSAC(随机抽样一致性)或比率测试(如Lowe's 0.7阈值)剔除误匹配[^5]。 --- #### 3. **融合策略** - **几何一致性融合**: 基于多视图几何约束(如对极几何)校正特征点位置,公式化表示为: $$ x'^T F x = 0 $$ 其中$F$为基础矩阵,$x$和$x'$为匹配点对坐标。 - **加权融合**: 对不同传感器或算法的特征点赋予权重,例如基于置信度: $$ p_{fused} = \sum_{i=1}^n w_i p_i $$ 权重$w_i$可根据特征点响应强度或匹配得分确定[^4]。 --- #### 4. **实现方法(Python示例)** ```python import cv2 import numpy as np # 特征提取(以SIFT为例) sift = cv2.SIFT_create() kp1, desc1 = sift.detectAndCompute(img1, None) kp2, desc2 = sift.detectAndCompute(img2, None) # 特征匹配(FLANN匹配器) flann = cv2.FlannBasedMatcher() matches = flann.knnMatch(desc1, desc2, k=2) # 筛选:Lowe's比率测试 good_matches = [] for m, n in matches: if m.distance < 0.7 * n.distance: good_matches.append(m) # 几何一致性(RANSAC) src_pts = np.float32([kp1[m.queryIdx].pt for m in good_matches]).reshape(-1, 1, 2) dst_pts = np.float32([kp2[m.trainIdx].pt for m in good_matches]).reshape(-1, 1, 2) F, mask = cv2.findFundamentalMat(src_pts, dst_pts, cv2.FM_RANSAC) # 融合后特征点 fused_kp = [kp1[i] for i, m in enumerate(mask) if m[0] == 1] ``` --- #### 5. **关键优化方向** - **多模态融合**:融合可见光红外特征点,增强鲁棒性[^3]。 - **并行计算**:利用GPU加速特征提取匹配(如OpenCV CUDA模块)。 - **动态权重**:根据场景复杂度自适应调整融合权重[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值