当前位置: 首页 > article >正文

opencv-FindHomography接口-C语言实现

什么是单应性矩阵?

单应性矩阵(Homography)是一个 3x3 的变换矩阵,用于描述两个平面之间的投影变换。在计算机视觉中,它通常用于将一张图像中的点映射到另一张图像中的对应点,尤其是在两张图像拍摄的是同一个平面但视角不同的情况下。

找到单应性矩阵的步骤

  1. 找到对应点

    • 需要至少 4 对对应点,这些点分别来自两张图像,并且在现实世界中位于同一个平面上。

    • 设第一张图像中的点为 (𝑥1,𝑦1),(𝑥2,𝑦2),…,(𝑥𝑛,𝑦𝑛)(x1​,y1​),(x2​,y2​),…,(xn​,yn​),第二张图像中的对应点为 (𝑥1′,𝑦1′),(𝑥2′,𝑦2′),…,(𝑥𝑛′,𝑦𝑛′)(x1′​,y1′​),(x2′​,y2′​),…,(xn′​,yn′​)。

  2. 建立单应性方程

    • 单应性矩阵 𝐻H 是一个 3x3 的矩阵:

      𝐻=[ℎ11ℎ12ℎ13ℎ21ℎ22ℎ23ℎ31ℎ32ℎ33]H=​h11​h21​h31​​h12​h22​h32​​h13​h23​h33​​​
    • 对于每一对点,满足以下关系:

      [𝑥′𝑦′1]∼𝐻[𝑥𝑦1]​x′y′1​​∼H​xy1​​

      其中 ∼∼ 表示“相等,允许一个比例因子”。

  3. 转换为线性方程组

    • 将上述关系展开,得到:

      𝑥′=ℎ11𝑥+ℎ12𝑦+ℎ13ℎ31𝑥+ℎ32𝑦+ℎ33x′=h31​x+h32​y+h33​h11​x+h12​y+h13​​𝑦′=ℎ21𝑥+ℎ22𝑦+ℎ23ℎ31𝑥+ℎ32𝑦+ℎ33y′=h31​x+h32​y+h33​h21​x+h22​y+h23​​
    • 将其重写为线性方程组的形式:

      {𝑥′(ℎ31𝑥+ℎ32𝑦+ℎ33)=ℎ11𝑥+ℎ12𝑦+ℎ13𝑦′(ℎ31𝑥+ℎ32𝑦+ℎ33)=ℎ21𝑥+ℎ22𝑦+ℎ23{x′(h31​x+h32​y+h33​)=h11​x+h12​y+h13​y′(h31​x+h32​y+h33​)=h21​x+h22​y+h23​​
    • 最终可以写成矩阵形式 𝐴ℎ=0Ah=0,其中 𝐴A 是一个 2𝑛×92n×9 的矩阵,ℎh 是单应性矩阵 𝐻H 的 9 个元素组成的向量。

  4. 求解方程组

    • 使用 奇异值分解(SVD) 或其他方法求解 ℎh。

    • 对 𝐻H 进行归一化,通常令 ℎ33=1h33​=1。

  5. 优化单应性矩阵

    • 可以使用 RANSAC(随机采样一致性)算法来去除异常点,提高鲁棒性。

下面是pytho的简单实现:

import cv2
import numpy as np

# 定义图像1和图像2中的对应点
pts1 = np.array([[50, 50], [200, 50], [50, 200], [200, 200]], dtype=np.float32)
pts2 = np.array([[70, 60], [220, 70], [60, 210], [210, 220]], dtype=np.float32)

# 计算单应性矩阵
H, _ = cv2.findHomography(pts1, pts2)

print("单应性矩阵 H:")
print(H)

单应性矩阵的应用

  1. 图像拼接:将多张图像拼接成全景图。

  2. 增强现实(AR):将虚拟物体叠加到现实世界的平面上。

  3. 透视校正:校正图像中的透视变形(例如,将倾斜拍摄的文档图像校正为正面视角)。

下面我们来根据算法逻辑实现FindHomography并通过实现RANSAC算法来求解H矩阵:

下面是main.cpp

#include <opencv2/opencv.hpp>
#include <iostream>
#include <fstream>
#include "get_H.h"
using namespace cv;


int main() {
  

    // 遍历图像对imgId<-->modID
    for (int i = 0; i < 163; ++i) {

        std::string imagepath1 = image_path + std::to_string(modid[i].src) + ".bmp"; // src
        std::string imagepath2 = image_path + std::to_string(modid[i].dst) + ".bmp"; // dst

        cv::Mat srcImage = cv::imread(imagepath1, cv::IMREAD_GRAYSCALE);
        cv::Mat dstImage = cv::imread(imagepath2, cv::IMREAD_GRAYSCALE);

        if (srcImage.empty() || dstImage.empty()) {
            std::cerr << "Could not open or find the images!" << std::endl;
            return -1;
        }

        // 使用 SIFT 检测特征点
        cv::Ptr<cv::SIFT> sift = cv::SIFT::create();

        // 检测关键点并计算描述符
        std::vector<KeyPoint> keypoints1, keypoints2;
        Mat descriptors1, descriptors2;
        sift->detectAndCompute(srcImage, noArray(), keypoints1, descriptors1);
        sift->detectAndCompute(dstImage, noArray(), keypoints2, descriptors2);

        // 使用 BFMatcher 进行特征匹配
        BFMatcher matcher(NORM_L2);
        std::vector<DMatch> matches;
        matcher.match(descriptors1, descriptors2, matches);

        // 对匹配结果排序
        std::sort(matches.begin(), matches.end(), [](const DMatch &a, const DMatch &b) {
            return a.distance < b.distance;
        });

        // 过滤匹配点,选取前 50 个最佳匹配
        const int numGoodMatches = 50;
        std::vector<DMatch> goodMatches(matches.begin(), matches.begin() + std::min(numGoodMatches, (int)matches.size()));

        // 提取匹配点
        std::vector<Point2f> srcPoints, dstPoints;
        for (const auto& match : goodMatches) {
            srcPoints.push_back(keypoints1[match.queryIdx].pt);
            dstPoints.push_back(keypoints2[match.trainIdx].pt);
        }


      

        GuassHonophgy *Guaa = new GuassHonophgy();
        int numpoints = srcPoints.size();

        point_32F SRC_P[numpoints];
        point_32F DST_P[numpoints];

        for (int i = 0; i < numpoints; ++i) {
            SRC_P[i].x = srcPoints[i].x;
            SRC_P[i].y = srcPoints[i].y;


            DST_P[i].x = dstPoints[i].x;
            DST_P[i].y = dstPoints[i].y;
        }


        float H[9] = {0.f};

        bool result = Guaa->MyFindHomography(SRC_P,DST_P,H,numpoints,mycv::RANSAC);


    
    }

    return 0;
}

结果与opencv比对:

结果基本一致。。。。


http://www.kler.cn/a/513735.html

相关文章:

  • MYSQL数据库基础-01.数据库的基本操作
  • 【云原生布道系列】第三篇:“软”饭“硬”吃的计算
  • iOS UIScrollView的一个特性
  • (一)相机标定——四大坐标系的介绍、对应转换、畸变原理以及OpenCV完整代码实战(C++版)
  • 2024人工智能AI+制造业应用落地研究报告汇总PDF洞察(附原数据表)
  • 计算机网络 (46)简单网络管理协议SNMP
  • 靠右行驶数学建模分析(2014MCM美赛A题)
  • 日本IT|集成测试(結合テスト)的含义
  • office 2019 关闭word窗口后卡死未响应
  • 全新推理模型 DeepSeek-R1 问世,全面对标 OpenAI o1
  • “深入浅出”系列之C++:(10)nlohmann Json库
  • 【gopher的java学习笔记】Java中Mapper与Entity的关系详解
  • 虚拟mock
  • 学Python的人…
  • 【Spring Boot】Spring AOP动态代理,以及静态代理
  • 代码随想录刷题day13|(链表篇)24.两两交换链表中的结点
  • github无法访问配置
  • ubuntu24 springboot jar设置宕机重启
  • 【2024年华为OD机试】(C/D卷,200分)- 5G网络建设 (JavaScriptJava PythonC/C++)
  • Qt中自定义信号与槽
  • JAVA基础语句整理
  • 【JsonPath】JsonPath常用示例
  • Linux和Windows系统之间实现文件共享
  • 【STL】list 双向循环链表的使用介绍
  • 后盾人JS -- Set与WeakSet类型在JavaScript中的使用
  • 《鸿蒙Next原生应用的独特用户体验之旅》