【RealSense】深度图和RGB图生成点云

发布时间 2023-06-21 15:41:52作者: 乞力马扎罗山的雪

rgb_depth_2_pointcloud.cpp

#include <iostream>
#include <pcl/console/print.h>
#include <pcl/filters/voxel_grid.h>
#include <opencv2/opencv.hpp>
#include <pcl/common/io.h>
#include <pcl/io/pcd_io.h>
#include <pcl/io/ply_io.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/range_image/range_image.h>
#include <pcl/visualization/range_image_visualizer.h>
#include <pcl/visualization/common/float_image_utils.h> // 保存深度图
#include <pcl/io/png_io.h>                              // 保存深度图
#include <pcl/filters/passthrough.h>
#include <pcl/console/time.h> // TicToc
#include <pcl/point_types.h>
#include <pcl/registration/icp.h>

using namespace std;
using namespace cv;

// 宏定义,简化定义
typedef pcl::PointXYZRGB PointT;
typedef pcl::visualization::PCLVisualizer::Ptr pViewer;

// 此函数采用4x4矩阵的引用,并以人类可读的方式打印刚性变换
void print4x4Matrix(const Eigen::Matrix4d &matrix)
{
    printf("Rotation matrix :\n");
    printf("    | %6.3f %6.3f %6.3f | \n", matrix(0, 0), matrix(0, 1), matrix(0, 2));
    printf("R = | %6.3f %6.3f %6.3f | \n", matrix(1, 0), matrix(1, 1), matrix(1, 2));
    printf("    | %6.3f %6.3f %6.3f | \n", matrix(2, 0), matrix(2, 1), matrix(2, 2));
    printf("Translation vector :\n");
    printf("t = < %6.3f, %6.3f, %6.3f >\n\n", matrix(0, 3), matrix(1, 3), matrix(2, 3));
}

/**
 * 方法1:通过双重循环遍历
 * @brief 将彩色图和深度图合并成点云
 * @param matrix        相机内参矩阵 3✖3
 * @param rgb           彩色图
 * @param depth         深度图
 * @param cloud         输出点云
 */
static void convert(Mat &matrix, Mat &rgb, Mat &depth, pcl::PointCloud<PointT>::Ptr &cloud)
{
    double camera_fx = matrix.at<double>(0, 0);
    double camera_fy = matrix.at<double>(1, 1);
    double camera_cx = matrix.at<double>(0, 2);
    double camera_cy = matrix.at<double>(1, 2);

    cout << "fx: " << camera_fx << endl;
    cout << "fy: " << camera_fy << endl;
    cout << "cx: " << camera_cx << endl;
    cout << "cy: " << camera_cy << endl;

    // 遍历深度图
    for (int v = 0; v < depth.rows; v++)
    {
        for (int u = 0; u < depth.cols; u++)
        {
            // 获取深度图中(m, n)处的值
            ushort d = depth.ptr<ushort>(v)[u];
            // d 可能没有值,若如此,跳过此点, isnan用来判断一个值是否是nan
            if (isnan(d) && abs(d) < 0.0001)
                continue;
            // 若 d 存在值,则向点云增加一个点
            PointT p;

            // 计算这个点的空间坐标
            p.z = double(d) / 1000; // 单位是 m
            p.x = (u - camera_cx) * p.z / camera_fx;
            p.y = (v - camera_cy) * p.z / camera_fy;

            // 从rgb图像中获取它的颜色
            // rgb是三通道的RGB格式图,所以按像下面的顺序获取颜色
            // 这边因为是使用opencv读取图片的,所以三通道顺序应该是bgr
            Vec3b bgr = rgb.at<Vec3b>(v, u); // 此处:表示行,u表示列
            p.b = bgr[0];
            p.g = bgr[1];
            p.r = bgr[2];

            // 把p加入到点云中
            cloud->points.emplace_back(p);
        }
    }

    // 设置并保存点云
    cloud->height = 1;
    cloud->width = cloud->points.size();
    cout << "point cloud size = " << cloud->points.size() << endl;
    cloud->is_dense = false;

    // // 添加下采样
    // pcl::console::print_highlight("Downsampling...\n");
    // pcl::VoxelGrid<PointT> grid;
    // const float leaf = 0.1f;
    // grid.setLeafSize(leaf, leaf, leaf); // 设置下采样率
    // grid.setInputCloud(cloud);          // 输入对象点云
    // grid.filter(*cloud);                // 对象下采样
    // cout << "PointCloud after filtering: " << cloud->width * cloud->height << " data point ("
    //      << pcl::getFieldsList(*cloud) << ")." << endl;

    // 增加直通滤波
    // pcl::console::print_highlight("passThrough");
    // pcl::PassThrough<PointT> pass;
    // pass.setInputCloud(cloud);    // 1. 设置输入源
    // pass.setFilterFieldName("z"); // 2. 设置过滤域名
    // pass.setFilterLimits(0, 0.5); // 3. 设置过滤范围
    // pass.filter(*cloud);          // 4. 执行过滤
    // cout << "PointCloud after passThrough: " << cloud->width * cloud->height << " data point ("
    //      << pcl::getFieldsList(*cloud) << ")." << endl;
}

int main(int argc, char *argv[])
{

    cout << "opencv version = " << CV_VERSION << endl;
    cout << "pcl version = " << PCL_VERSION << endl;

    cv::Mat cameraMatrix = (Mat_<double>(3, 3) << 619.058195277193, 0, 319.512748241151, 0, 620.152772683851, 224.326042533956, 0, 0, 1); // realsense rgb相机的内参

    Mat rgb1 = imread("/home/bck18vm/桌面/dataset/1.10/rgb_31.png", IMREAD_UNCHANGED);
    Mat depth1 = imread("/home/bck18vm/桌面/dataset/1.10/depth_31.png", IMREAD_UNCHANGED);
    Mat rgb2 = imread("/home/bck18vm/桌面/dataset/1.10/rgb_32.png", IMREAD_UNCHANGED);
    Mat depth2 = imread("/home/bck18vm/桌面/dataset/1.10/depth_32.png", IMREAD_UNCHANGED);

    pcl::PointCloud<PointT>::Ptr pCloud1(new pcl::PointCloud<PointT>);
    pcl::PointCloud<PointT>::Ptr pCloud2(new pcl::PointCloud<PointT>);
    pcl::PointCloud<PointT>::Ptr pCloudicp(new pcl::PointCloud<PointT>);

    convert(cameraMatrix, rgb1, depth1, pCloud1);
    convert(cameraMatrix, rgb2, depth2, pCloud2);

    // 点云可视化
    pViewer viewer(new pcl::visualization::PCLVisualizer("viewer"));
    viewer->setBackgroundColor(255, 255, 255); // 显示白色
    // 添加点云
    viewer->addPointCloud<PointT>(pCloud1, "cloud1");
    viewer->addPointCloud<PointT>(pCloud2, "cloud2");

    // 保存点云
    pcl::io::savePCDFile("../1.pcd", *pCloud1);
    pcl::io::savePCDFile("../2.pcd", *pCloud2);

    // 添加坐标系
    viewer->addCoordinateSystem(1);
    while (!viewer->wasStopped())
    {
        viewer->spinOnce();
    }

    return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
project(2pointCloud)

# set(CMAKE_BUILD_TYPE "Release")
set(CMAKE_BUILD_TYPE "Debug")
add_definitions("-DENABLE_SSE")
set(CMAKE_CXX_FLAGS "-std=c++14 -O2 ${SSE_FLAGS} -msse4")

find_package(OpenCV 4 REQUIRED)

include_directories(
        ${OpenCV_INCLUDE_DIRS}
        "/usr/include/eigen3/"
)
 
# 带有点云相关的
# PCL
find_package(PCL REQUIRED)
include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})
# 从rgb 和 的depth 生成点云
add_executable(rgb_depth_2_pointcloud rgb_depth_2_pointcloud.cpp)
target_link_libraries(rgb_depth_2_pointcloud ${PCL_LIBRARIES} ${OpenCV_LIBS})