小秋SLAM入门实战

小秋SLAM入门实战


C++ 程序使用 OpenCV 可视化和分析两个图像之间特征点的对应关系-LMLPHP

代码功能

  1. 创建图像和生成随机特征点

    • 程序首先创建两个灰度图像(m_image_Left_BGRm_image_Right_BGR),并将它们转换为彩色图像。
    • 然后,生成两组随机特征点(mvKeysmvKeysRight),分别对应于这两个图像。
  2. 关联左右图特征点

    • 使用一个二维向量 vRowIndices 假设存储与左图特征点在 Y 轴上对应的右图特征点索引。这里简单地假设左图中的每个特征点对应于右图中具有相同索引的特征点。
  3. 合并图像并绘制特征点

    • 对于左图的每个特征点,程序创建一张包含左右图的合并图像。
    • 在合并图像上绘制坐标轴,并显示图像的宽度和高度。
    • 在合并图像的左侧部分(对应于 m_image_Left_BGR),标记左图特征点的位置,并在右侧部分(对应于 m_image_Right_BGR)标记对应的右图特征点的位置。
  4. 保存和绘制特征点信息

    • 对于每个左图特征点及其对应的右图特征点,程序绘制一个红色圆圈表示特征点的位置,并添加包含特征点索引和坐标的文本标签。
    • 然后,将包含这些信息的合并图像保存为 PNG 文件,文件名基于左图特征点的索引和对应的右图特征点的索引。
  5. 文件保存路径

    • 保存的文件路径被设置为当前目录下的一个子目录。

总的来说,这个程序用于可视化和分析两个图像之间特征点的对应关系。它在合并的图像上显示了左右图的特征点,以及相关的坐标信息,这在图像处理、立体视觉或特征点匹配分析中非常有用。

源码文件

#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>

#include <string>  // 确保包含这个头文件

using namespace std;

std::vector<cv::KeyPoint> generateRandomKeyPoints(const cv::Mat& image, int numPoints) {
    std::vector<cv::KeyPoint> keypoints;
    cv::RNG rng;
    for (int i = 0; i < numPoints; ++i) {
        cv::Point2f pt(rng.uniform(0.f, (float)image.cols), rng.uniform(0.f, (float)image.rows));
        keypoints.push_back(cv::KeyPoint(pt, 1.f));
    }
    return keypoints;
}

int main() {
    // 创建两个简单的灰度图像,并转换为彩色图像
    cv::Mat m_image_Left_BGR = cv::Mat::zeros(240, 320, CV_8UC1);
    cv::Mat m_image_Right_BGR = cv::Mat::zeros(240, 320, CV_8UC1);
    cv::cvtColor(m_image_Left_BGR, m_image_Left_BGR, cv::COLOR_GRAY2BGR);
    cv::cvtColor(m_image_Right_BGR, m_image_Right_BGR, cv::COLOR_GRAY2BGR);

    // 生成一些随机特征点
    std::vector<cv::KeyPoint> mvKeys = generateRandomKeyPoints(m_image_Left_BGR, 100);
    std::vector<cv::KeyPoint> mvKeysRight = generateRandomKeyPoints(m_image_Right_BGR, 100);

    // 假设 vRowIndices 是一个二维向量,存储与左图特征点在 Y 轴上对应的右图特征点索引
    std::vector<std::vector<size_t>> vRowIndices(m_image_Left_BGR.rows);
    for (int i = 0; i < mvKeys.size(); ++i) {
        vRowIndices[mvKeys[i].pt.y].push_back(i); // 为了演示,我们简单地假设每个左图特征点与相同索引的右图特征点对应
    }

       for(int iL=0; iL<mvKeys.size(); iL++)
        {
            const cv::KeyPoint &kpL = mvKeys[iL];

            // 循环绘制和左目当前特征点 和 vRowIndices[i]
            // 1. 这里生成一张大图包括左右目
            const int height = max(m_image_Left_BGR.rows, m_image_Right_BGR.rows);
            const int width = m_image_Left_BGR.cols + m_image_Right_BGR.cols;
            cv::Mat output(height, width, CV_8UC3, cv::Scalar(0, 0, 0));
            m_image_Left_BGR.copyTo(output(cv::Rect(0, 0, m_image_Left_BGR.cols, m_image_Left_BGR.rows)));
            m_image_Right_BGR.copyTo(output(cv::Rect(m_image_Left_BGR.cols, 0, m_image_Right_BGR.cols, m_image_Right_BGR.rows)));

            // 左目绘制x轴
            cv::line(output, cv::Point(5, 5), cv::Point(m_image_Left_BGR.cols - 100, 5), cv::Scalar(0, 0, 255), 2);
            cv::arrowedLine(output, cv::Point(m_image_Left_BGR.cols - 100, 5), cv::Point(m_image_Left_BGR.cols - 10, 5), cv::Scalar(0, 0, 255), 2, cv::LINE_AA);
            cv::putText(output, "X", cv::Point(m_image_Left_BGR.cols - 150, 30), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 2, cv::LINE_AA);
            cv::putText(output, "Width Col: " + std::to_string(m_image_Left_BGR.cols), cv::Point(m_image_Left_BGR.cols - 150, 50), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 1, cv::LINE_AA);

            // 右目绘制x轴
            cv::line(output, cv::Point(m_image_Left_BGR.cols + 5, 5), cv::Point(m_image_Left_BGR.cols + m_image_Right_BGR.cols - 100, 5), cv::Scalar(0, 0, 255), 2);
            cv::arrowedLine(output, cv::Point(m_image_Left_BGR.cols + m_image_Right_BGR.cols - 100, 5), cv::Point(m_image_Left_BGR.cols + m_image_Right_BGR.cols - 10, 5), cv::Scalar(0, 0, 255), 2, cv::LINE_AA);
            cv::putText(output, "X", cv::Point(m_image_Left_BGR.cols + m_image_Right_BGR.cols - 150, 30), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 2, cv::LINE_AA);
            cv::putText(output, "Width Col: " + std::to_string(m_image_Right_BGR.cols), cv::Point(m_image_Left_BGR.cols + m_image_Right_BGR.cols - 150, 50), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 1, cv::LINE_AA);

            // 左目绘制Y轴
            cv::line(output, cv::Point(5, 5), cv::Point(5, m_image_Left_BGR.rows - 100), cv::Scalar(0, 255, 0), 2);
            cv::arrowedLine(output, cv::Point(5, m_image_Left_BGR.rows - 100), cv::Point(5, m_image_Left_BGR.rows - 5), cv::Scalar(0, 255, 0), 2, cv::LINE_AA);
            cv::putText(output, "Y", cv::Point(10, m_image_Left_BGR.rows - 20), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 255, 0), 2, cv::LINE_AA);
            cv::putText(output, "Height Row: " + std::to_string(m_image_Left_BGR.rows), cv::Point(10, m_image_Left_BGR.rows - 40), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 255, 0), 1, cv::LINE_AA);

            // 右目绘制Y轴
            cv::line(output, cv::Point(m_image_Left_BGR.cols + 5, 5), cv::Point(m_image_Left_BGR.cols + 5, m_image_Right_BGR.rows - 100), cv::Scalar(0, 255, 0), 2);
            cv::arrowedLine(output, cv::Point(m_image_Left_BGR.cols + 5, m_image_Right_BGR.rows - 100), cv::Point(m_image_Left_BGR.cols + 5, m_image_Right_BGR.rows - 5), cv::Scalar(0, 255, 0), 2, cv::LINE_AA);
            cv::putText(output, "Y", cv::Point(m_image_Left_BGR.cols + 10, m_image_Right_BGR.rows - 20), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 255, 0), 2, cv::LINE_AA);
            cv::putText(output, "Height Row: " + std::to_string(m_image_Right_BGR.rows), cv::Point(m_image_Left_BGR.cols + 10, m_image_Right_BGR.rows - 40), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 255, 0), 1, cv::LINE_AA);

            // 2. 这里把左目特征点绘制大图的左目上
            circle(output, kpL.pt, 2, cv::Scalar(255, 0, 0), 2);
            cv::putText(output,"iL " + std::to_string(int(iL)) + ", X " + std::to_string(int(kpL.pt.x)) + ", Y " + std::to_string(int(kpL.pt.y)),
                        cv::Point(kpL.pt.x - 25, kpL.pt.y - 10), cv::FONT_HERSHEY_SIMPLEX, 0.4,
                        cv::Scalar(255, 0, 0), 1);

            for(size_t iC=0; iC<vRowIndices[kpL.pt.y].size(); iC++) {
                const size_t iR = vRowIndices[kpL.pt.y][iC];
                const cv::KeyPoint &kpR = mvKeysRight[iR];

                // 3. 这里把右目特征点绘制大图的右目上,并且每一个特征点是一张图像
                circle(output, (kpR.pt + cv::Point2f((float) m_image_Left_BGR.cols, 0.f)), 2,cv::Scalar(255, 0, 0), 2);
                cv::putText(output,"iC " + std::to_string(int(iC)) + ", X " + std::to_string(int(kpR.pt.x)) + ", Y " + std::to_string(int(kpR.pt.y)),
                            cv::Point(kpR.pt.x + m_image_Left_BGR.cols - 15,kpR.pt.y - 15),
                            cv::FONT_HERSHEY_SIMPLEX, 0.4, cv::Scalar(255, 0, 0), 1);
                cv::imwrite("./" + to_string(iL) + "_" + to_string(iC) + ".png",output);
            }
        }


    return 0;
}

编译文件

cmake_minimum_required(VERSION 3.10)
project(ImagePyramid)

# 设置C++标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# 找到OpenCV包
find_package(OpenCV REQUIRED)

# 包含OpenCV头文件
include_directories(${OpenCV_INCLUDE_DIRS})

# 添加可执行文件
add_executable(ImagePyramid main.cpp)

# 链接OpenCV库
target_link_libraries(ImagePyramid ${OpenCV_LIBS})

01-27 11:48