Opencv计算机视觉编程攻略-第九节 描述和匹配兴趣点

一般而言,如果一个物体在一幅图像中被检测到关键点,那么同一个物体在其他图像中也会检测到同一个关键点。图像匹配是关键点的常用功能之一,它的作用包括关联同一场景的两幅图像、检测图像中事物的发生地点等等。

1.局部模板匹配

凭单个像素就判断两个关键点的相似度显然是不够的,因此要在匹配过程中**考虑每个关键
点周围的图像块,**对图像块中的像素进行逐个比较,但是并不是最可靠的。

第一步,使用FAST 检测器进行关键点提取:

cpp 复制代码
// 定义特征检测器
cv::Ptr<cv::FeatureDetector> ptrDetector; // 泛型检测器指针
ptrDetector= // 这里选用FAST 检测器
cv::FastFeatureDetector::create(80);
// 检测关键点
ptrDetector->detect(image1,keypoints1);
ptrDetector->detect(image2,keypoints2);

第二步,定义匹配框,在每个图像对的关键点之间进行匹配,这里使用逐像素相差匹配:

cpp 复制代码
// 在第二幅图像中找出与第一幅图像中的每个关键点最匹配的
cv::Mat result;
std::vector<cv::DMatch> matches;
// 针对图像一的全部关键点
for (int i=0; i<keypoints1.size(); i++) {
    // 定义图像块
    neighborhood.x = keypoints1[i].pt.x-nsize/2;
    neighborhood.y = keypoints1[i].pt.y-nsize/2;
    // 如果邻域超出图像范围,就继续处理下一个点
    if (neighborhood.x<0 || neighborhood.y<0 ||neighborhood.x+nsize >= image1.cols ||neighborhood.y+nsize >= image1.rows)
        continue;
    // 第一幅图像的块
    patch1 = image1(neighborhood);
    // 存放最匹配的值
    cv::DMatch bestMatch;
    // 针对第二幅图像的全部关键点
    for (int j=0; j<keypoints2.size(); j++) {
        // 定义图像块
        neighborhood.x = keypoints2[j].pt.x-nsize/2;
        neighborhood.y = keypoints2[j].pt.y-nsize/2;
        // 如果邻域超出图像范围,就继续处理下一个点
        if (neighborhood.x<0 || neighborhood.y<0 ||neighborhood.x + nsize >= image2.cols ||neighborhood.y + nsize >= image2.rows)
            continue;
        // 第二幅图像的块
        patch2 = image2(neighborhood);
        // 匹配两个图像块
        cv::matchTemplate(patch1,patch2,result, cv::TM_SQDIFF);
        // 检查是否为最佳匹配
        if (result.at<float>(0,0) < bestMatch.distance) {
            bestMatch.distance= result.at<float>(0,0);
            bestMatch.queryIdx= i;
            bestMatch.trainIdx= j;
        }
                }
        // 添加最佳匹配
        matches.push_back(bestMatch);
    }

第三步,选择置信度最高的一些点,进行展示:

cpp 复制代码
// 提取25 个最佳匹配项
std::nth_element(matches.begin(),matches.begin() + 25,matches.end());
matches.erase(matches.begin() + 25,matches.end());

// 绘制图像
cv::Mat matchImage;
cv::drawMatches(image1,keypoints1, // 第一幅图像
                image2,keypoints2, // 第二幅图像
                matches, // 匹配项的向量
                cv::Scalar(255,255,255), // 线条颜色
                cv::Scalar(255,255,255)); // 点的颜色

上述方法使用图块之间相似度进行评估,也可以使用opencv中的区域模板匹配方法进一步增大搜索精确度:

cpp 复制代码
// 定义搜索区域
cv::Mat roi(image2, // 这里用图像的上半部分
cv::Rect(0,0,image2.cols,image2.rows/2));
// 进行模板匹配
cv::matchTemplate(roi, // 搜索区域
target, // 模板
result, // 结果
cv::TM_SQDIFF); // 相似度
// 找到最相似的位置
double minVal, maxVal;
cv::Point minPt, maxPt;
cv::minMaxLoc(result, &minVal, &maxVal, &minPt, &maxPt);
// 在相似度最高的位置绘制矩形
// 本例中为minPt
cv::rectangle(roi, cv::Rect(minPt.x, minPt.y,
target.cols, target.rows), 255);

2.描述并匹配局部强度值模式

在图像分析中,可以用邻域包含的视觉信息来标识每个特征点,以便区分各个特征点。**特征描述子通常是一个N 维的向量,在光照变化和拍摄角度发生微小扭曲时,它描述特征点的方式不会发生变化,**通常可以用简单的差值矩阵来比较描述子,例如用欧几里得距离等

基于特征的方法都包含一个检测器和一个描述子组件,与cv::Feature2D 相关的类也一样,它们都有一个检测函数(用于检测兴趣点)和一个计算函数(用于计算兴趣点的描述子)cv::SURF

和cv::SIFT,检测流程和上述一致。

cpp 复制代码
// 1. 定义关键点的容器
std::vector<cv::KeyPoint> keypoints1;
std::vector<cv::KeyPoint> keypoints2;

// 2. 定义特征检测器
cv::Ptr<cv::Feature2D> ptrFeature2D =
cv::xfeatures2d::SURF::create(2000.0);

// 3. 检测关键点
ptrFeature2D->detect(image1,keypoints1);
ptrFeature2D->detect(image2,keypoints2);

// 4. 提取描述子
cv::Mat descriptors1;
cv::Mat descriptors2;
ptrFeature2D->compute(image1,keypoints1,descriptors1);
ptrFeature2D->compute(image2,keypoints2,descriptors2);

// 5. 构造匹配器
cv::BFMatcher matcher(cv::NORM_L2);

cv::BFMatcher matcher2(cv::NORM_L2, // 度量差距
true); // 可以开启 交叉检查标志


// 匹配两幅图像的描述子
std::vector<cv::DMatch> matches;
matcher.match(descriptors1,descriptors2, matches);

好的特征描述子不受照明和视角微小变动的影响,也不受图像中噪声的影响,因此它们通常

基于局部强度值的差值,SURF 描述子在关键点周围局部地应用下面的简易内核:

第一个内核度量水平方向的局部强度值差值(标为dx),第二个内核度量垂直方向的差值(标为dy)。通常将用于提取描述子向量的邻域尺寸定为特征值缩放因子的20 倍(即20σ)。然后把这个正方形区域划分成更小的4×4 子区域。对于每个子区域,在5×5 等分的位置上(用尺寸为2σ的内核)计算内核反馈值(dx 和dy)。

使用SURF 和SIFT 的特征和描述子可以进行尺度无关的匹配,能够取得较好的效果。

3.用二值描述子匹配关键点

上述描述子是浮点数类型的向量,大小为64、128等,这导致对它们的操作将耗资巨大,为了减少内存使用、降低计算量,人们引入了将一组比特位(0 和1)组合成二值描述子的概念。这里的难点在于,既要易于计算,又要在场景和视角变化时保持鲁棒性。

cpp 复制代码
// 1. 定义特征检测器/描述子
// Construct the ORB feature object
cv::Ptr<cv::Feature2D> feature = cv::ORB::create(60);
// 大约60 个特征点
// 检测并描述关键点
// 2. 检测ORB 特征
feature->detectAndCompute(image1, cv::noArray(),
keypoints1, descriptors1);
feature->detectAndCompute(image2, cv::noArray(),
keypoints2, descriptors2);

// 3. 构建匹配器
cv::BFMatcher matcher(cv::NORM_HAMMING); // 二值描述子一律使用Hamming 规范】

// 4.匹配两幅图像的描述子
std::vector<cv::DMatch> matches;
matcher.match(descriptors1, descriptors2, matches);

ORB 算法在多个尺度下检测特征点,这些特征点含有方向。基于这些特征点,ORB 描述子通过简单比较强度值,提取出每个关键点的表征,在BRIEF 描述子的基础上构建的,然后在关键点周围的邻域内随机选取一对像素点,创建一个二值描述子。

比较这两个像素点的强度值,如果第一个点的强度值较大,就把对应描述子的位(bit)设为1,否则就设为0。对一批随机像素点对进行上述处理,就产生了一个由若干位(bit)组成的描述子,通常采用128 到512 位(成对地测试)。

相关推荐
耘瞳科技3 小时前
喜讯 | 耘瞳科技视觉检测与测量装备荣膺“2024机器视觉创新产品TOP10”
人工智能·科技·视觉检测
__Benco5 小时前
OpenHarmony子系统开发 - DFX(一)
人工智能·harmonyos
小西几哦5 小时前
3D点云配准RPM-Net模型解读(附论文+源码)
人工智能·pytorch·3d
CareyWYR5 小时前
每周AI论文速递(250331-250404)
人工智能
码视野5 小时前
基于快速开发平台与智能手表的区域心电监测与AI预警系统(源码+论文+部署讲解等)
人工智能·智能手表·毕业论文·计算机论文·物联网论文
skywalk81636 小时前
OpenRouter开源的AI大模型路由工具,统一API调用
服务器·前端·人工智能·openrouter
ejinxian6 小时前
大模型应用初学指南
人工智能·大模型·向量数据库
秋96 小时前
使用人工智能大模型kimi,如何免费高效制作PPT?
人工智能·kimi·制作ppt
IT古董7 小时前
【漫话机器学习系列】181.没有免费的午餐定理(NFL)
人工智能·机器学习