跳转至

使用OpenCL GPU

1. 启用gpu支持

C++
    // 启用 OpenCL 支持
    cv::ocl::setUseOpenCL(true);   // 设为false则使用cpu

    // 检查 OpenCL 是否成功启用
    if (cv::ocl::haveOpenCL()) {
        std::cout << "OpenCL is supported and enabled." << std::endl;
        // 可选:打印可用的 OpenCL 设备信息
        cv::ocl::Device device = cv::ocl::Device::getDefault();
        std::cout << "Using OpenCL device: " << device.name() << std::endl;
    } else {
        std::cout << "OpenCL is not supported or failed to initialize." << std::endl;
        // 程序可以继续在 CPU 上运行
    }   

2. 确保 OpenCV 编译时启用 OpenCL

  • 编译 OpenCV 时必须开启WITH_OPENCL=ON(默认开启,但需确认),否则所有 OpenCL 接口均无法使用。

  • 可通过代码验证编译配置:

C++
#include <opencv2/core/ocl.hpp>
cout << "OpenCV OpenCL support: " << (cv::ocl::haveOpenCL() ? "Enabled" : "Disabled") << endl;

3. 强制使用 OpenCL 兼容的数据类型

OpenCV 的 OpenCL 加速接口通常只对UMat(统一内存矩阵)有效,需将所有图像和中间数据转为UMat

C++
// 1. 图像读取为UMat
UMat uimg1 = imread("query.png", IMREAD_GRAYSCALE).getUMat(ACCESS_READ);
UMat uimg2 = imread("train.png", IMREAD_GRAYSCALE).getUMat(ACCESS_READ);

// 2. 特征点检测与描述子计算(输入为UMat)
vector<KeyPoint> kp1, kp2;
UMat udes1, udes2;  // 描述子也使用UMat
orb->detectAndCompute(uimg1, UMat(), kp1, udes1);  // 优先调用OpenCL实现(如果存在)
orb->detectAndCompute(uimg2, UMat(), kp2, udes2);

4. example

Bash
#include <opencv2/opencv.hpp>
#include <opencv2/core/ocl.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
    // 启用 OpenCL 支持
    cv::ocl::setUseOpenCL(true);

    // 检查 OpenCL 是否成功启用
    if (cv::ocl::haveOpenCL()) {
        std::cout << "OpenCL is supported and enabled." << std::endl;
        // 可选:打印可用的 OpenCL 设备信息
        cv::ocl::Device device = cv::ocl::Device::getDefault();
        std::cout << "Using OpenCL device: " << device.name() << std::endl;
    } else {
        std::cout << "OpenCL is not supported or failed to initialize." << std::endl;
        // 程序可以继续在 CPU 上运行
    }

    // ---------------------- 1. 读取图像 ----------------------
    // 请确保这两张图片文件与可执行文件在同一目录下,或者提供完整路径
    Mat img1 = imread("query.png", IMREAD_GRAYSCALE);  // 查询图(灰度图)
    Mat img2 = imread("train.png", IMREAD_GRAYSCALE);  // 训练图(灰度图)

    if (img1.empty() || img2.empty()) {
        cout << "错误:无法读取图像,请检查图像路径和文件名是否正确!" << endl;
        return -1;
    }

    // ---------------------- 2. 初始化 ORB 特征检测器 ----------------------
    // 参数顺序和类型严格遵循 OpenCV 4.2 的 API 文档
    Ptr<ORB> orb = ORB::create(
        500,                    // nfeatures: 最大特征点数量
        1.2f,                   // scaleFactor: 尺度因子
        8,                      // nlevels: 金字塔层数
        31,                     // edgeThreshold: 边缘阈值
        0,                      // firstLevel: 第一个金字塔层
        2,                      // WTA_K: 用于计算描述子的点对数量
        ORB::HARRIS_SCORE,      // scoreType: 评分函数类型
        31,                     // patchSize: 计算描述子的补丁大小
        20                      // fastThreshold: FAST 角点检测阈值
    );

    // ---------------------- 3. 提取特征点和描述子 ----------------------
    vector<KeyPoint> kp1, kp2;  // 存储关键点
    Mat des1, des2;             // 存储描述子 (ORB 生成的是 32 位二进制描述子)

//    orb->detectAndCompute(img1, Mat(), kp1, des1);  // 提取查询图的特征
//    orb->detectAndCompute(img2, Mat(), kp2, des2);  // 提取训练图的特征

    UMat uimg1, uimg2;
    img1.copyTo(uimg1);
    img2.copyTo(uimg2);

    // 提取特征时使用UMat
    orb->detectAndCompute(uimg1, UMat(), kp1, des1);
    orb->detectAndCompute(uimg2, UMat(), kp2, des2);

    cout << "查询图检测到的特征点数量:" << kp1.size() << endl;
    cout << "训练图检测到的特征点数量:" << kp2.size() << endl;

    // ---------------------- 4. 方案1:BF 暴力匹配 ----------------------
    // ORB 描述子是二进制的,因此使用汉明距离 (NORM_HAMMING) 进行匹配
    BFMatcher bf_matcher(NORM_HAMMING, true); // true 表示启用交叉验证,匹配更稳定
    vector<DMatch> bf_matches;
    bf_matcher.match(des1, des2, bf_matches);  // 执行匹配

    // 使用 Ratio Test 筛选出优质匹配
    double min_dist = 1e9;
    for (const auto& m : bf_matches) {
        if (m.distance < min_dist) min_dist = m.distance;
    }
    vector<DMatch> good_bf_matches;
    for (const auto& m : bf_matches) {
        // 设定一个合理的阈值,这里使用 2 * min_dist 或 30.0 中的较大值
        if (m.distance <= max(2.0 * min_dist, 30.0)) {
            good_bf_matches.push_back(m);
        }
    }

    cout << "BF 匹配后筛选出的优质特征点数量:" << good_bf_matches.size() << endl;

    // ---------------------- 5. 方案2:FLANN 快速匹配 ----------------------
    // 对于大规模特征点匹配,FLANN 比 BF 匹配快得多
    // 为二进制描述子配置 FLANN 参数
    Ptr<flann::IndexParams> index_params = makePtr<flann::LshIndexParams>(6, 12, 1);
    Ptr<flann::SearchParams> search_params = makePtr<flann::SearchParams>(50); // checks=50

    FlannBasedMatcher flann_matcher(index_params, search_params);
    vector<vector<DMatch>> flann_matches;
    // 使用 k-NN 匹配,k=2,即每个查询点返回两个最佳匹配
    flann_matcher.knnMatch(des1, des2, flann_matches, 2);

    // 使用 Ratio Test 筛选优质匹配
    vector<DMatch> good_flann_matches;
    for (const auto& m_n : flann_matches) {
        // 如果最佳匹配的距离远小于次佳匹配的距离,则认为是一个好的匹配
        if (m_n[0].distance < 0.75 * m_n[1].distance) {
            good_flann_matches.push_back(m_n[0]);
        }
    }

    cout << "FLANN 匹配后筛选出的优质特征点数量:" << good_flann_matches.size() << endl;
#if 0
    // ---------------------- 6. 可视化匹配结果 ----------------------
    Mat bf_match_img, flann_match_img;

    // --- 方案一:使用最简洁的 drawMatches 调用 ---
    // 只提供必要的参数,颜色和绘制选项使用默认值,避免参数不匹配问题
    drawMatches(img1, kp1, img2, kp2, good_bf_matches, bf_match_img);
    drawMatches(img1, kp1, img2, kp2, good_flann_matches, flann_match_img);

    // 显示结果窗口
    imshow("BF Matcher Results", bf_match_img);
    imshow("FLANN Matcher Results", flann_match_img);

    cout << "\n程序运行完毕。" << endl;
    cout << "请在图像窗口中按任意键退出程序。" << endl;

    // 等待按键,然后关闭所有窗口
    waitKey(0);
    destroyAllWindows();
#endif
    return 0;
}