多台RealsenseD435生成点云(PCL显示)

PCL为3D点云数据处理提供丰富强大的接口,本文记录利用多台Realsense D435生成并显示点云。代码在显示部分仅适用于小于等于两台设备的接入,大于两台需要稍微修改多台接入的点云处理部分程序。
  • PCL可视化:PCLVisualizer
PCLVisualizer可视化类是PCL中功能最全的可视化类,与CloudViewer可视化类相比,PCLVisualizer使用起来更为复杂,但该类具有更全面的功能,如显示法线、绘制多种形状和多个视口。
  • 主要用法
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer1(new pcl::visualization::PCLVisualizer("3D Viewer"));
viewer1->addCoordinateSystem(0.1);////添加坐标系,输入是坐标系三个圆柱体的规模因子
viewer1->initCameraParameters();////初始化相机角度等参数
viewer1->setBackgroundColor(0, 0, 0);//背景颜色

viewer1->addPointCloud<pcl::PointXYZRGB>(cloud, "cloud1");
viewer1>setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud1");
viewer1->spinOnce(1);
viewer1->removePointCloud("cloud1");////addPointCloud()后需要使用removePointCloud()来清楚指定ID的点云
  • 实验效果:
双台相机点云
#include <iostream>
#include <algorithm> 
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/thread/thread.hpp>
#include <string>
#include "stdafx.h"
#include <map>
#include <mutex>                    // std::mutex, std::lock_guard
#include <cmath>                    // std::ceil

// Intel Realsense Headers
#include <librealsense2/rs.hpp> // Include RealSense Cross Platform API
#include "example.hpp"   

// PCL Headers
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/filters/passthrough.h>
#include <pcl/common/common_headers.h>
#include <pcl/features/normal_3d.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/console/parse.h>
#include <boost/thread/thread.hpp>
#include <pcl/io/io.h>
#include <pcl/visualization/cloud_viewer.h>

// Include OpenCV API
#include <opencv2/opencv.hpp>   
#include "cv-helpers.hpp"

using namespace std;
using namespace cv;

typedef pcl::PointXYZRGB RGB_Cloud;
typedef pcl::PointCloud<RGB_Cloud> point_cloud;
typedef point_cloud::Ptr cloud_pointer;
typedef point_cloud::Ptr prevCloud;
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer1(new pcl::visualization::PCLVisualizer("3D Viewer"));////全局共享指针
boost::shared_ptr<pcl::visualization::PCLVisualizer> viewer2(new pcl::visualization::PCLVisualizer("3D Viewer"));////全局共享指针

////声明
std::tuple<int, int, int> RGB_Texture(rs2::video_frame texture, rs2::texture_coordinate Texture_XY);
cloud_pointer PCL_Conversion(const rs2::points& points, const rs2::video_frame& color);
//boost::shared_ptr<pcl::visualization::PCLVisualizer> rgbVis(pcl::PointCloud<pcl::PointXYZRGB>::ConstPtr cloud);


const std::string no_camera_message = "No camera connected, please connect 1 or more";
const std::string platform_camera_name = "Platform Camera";


class device_container
{
    // Helper struct per pipeline
    struct view_port
    {
        std::map<int, rs2::frame> frames_per_stream;
        rs2::colorizer colorize_frame;
        texture tex;
        rs2::pipeline pipe;
        rs2::pipeline_profile profile;
    };

public:

    void enable_device(rs2::device dev)
    {
        std::string serial_number(dev.get_info(RS2_CAMERA_INFO_SERIAL_NUMBER));
        std::lock_guard<std::mutex> lock(_mutex);

        if (_devices.find(serial_number) != _devices.end())
        {
            return; //already in
        }

        // Ignoring platform cameras (webcams, etc..)
        if (platform_camera_name == dev.get_info(RS2_CAMERA_INFO_NAME))
        {
            return;
        }
        // Create a pipeline from the given device
        rs2::pipeline p;
        rs2::config cfg;
        cfg.enable_device(serial_number);
        cfg.enable_stream(RS2_STREAM_COLOR, 640, 480, RS2_FORMAT_BGR8, 30);
        cfg.enable_stream(RS2_STREAM_INFRARED, 640, 480, RS2_FORMAT_Y8, 30);
        cfg.enable_stream(RS2_STREAM_DEPTH, 640, 480, RS2_FORMAT_Z16, 30);
        // Start the pipeline with the configuration
        rs2::pipeline_profile profile = p.start(cfg);
        // Hold it internally
        _devices.emplace(serial_number, view_port{ {},{},{}, p, profile });

    }

    void remove_devices(const rs2::event_information& info)
    {
        std::lock_guard<std::mutex> lock(_mutex);
        // Go over the list of devices and check if it was disconnected
        auto itr = _devices.begin();
        while (itr != _devices.end())
        {
            if (info.was_removed(itr->second.profile.get_device()))
            {
                itr = _devices.erase(itr);
            }
            else
            {
                ++itr;
            }
        }
    }

    size_t device_count()
    {
        std::lock_guard<std::mutex> lock(_mutex);
        return _devices.size();
    }

    int stream_count()
    {
        std::lock_guard<std::mutex> lock(_mutex);
        int count = 0;
        for (auto&& sn_to_dev : _devices)
        {
            for (auto&& stream : sn_to_dev.second.frames_per_stream)
            {
                if (stream.second)
                {
                    count++;
                }
            }
        }
        return count;
    }

    void poll_frames()
    {
        std::lock_guard<std::mutex> lock(_mutex);
        // Go over all device
        for (auto&& view : _devices)
        {
            // Ask each pipeline if there are new frames available
            rs2::frameset frameset;
            if (view.second.pipe.poll_for_frames(&frameset))
            {
                for (int i = 0; i < frameset.size(); i++)
                {
                    rs2::frame new_frame = frameset[i];
                    int stream_id = new_frame.get_profile().unique_id();
                    //view.second.frames_per_stream[stream_id] = view.second.colorize_frame.process(new_frame); //update view port with the new stream
                    view.second.frames_per_stream[stream_id] = new_frame; ////不将深度图彩色化
                }
            }
        }
    }

    void render_textures()
    {
        std::lock_guard<std::mutex> lock(_mutex);
        int stream_num = 0;
        rs2::colorizer color_map;
        for (auto&& view : _devices)
        {
            // For each device get its frames
            for (auto&& id_to_frame : view.second.frames_per_stream)
            {
                
                if (rs2::video_frame vid_frame = id_to_frame.second.as<rs2::video_frame>())
                {
                    
                    auto format = vid_frame.get_profile().format();
                    auto w = vid_frame.get_width();
                    auto h = vid_frame.get_height();
                    
                    if (format == RS2_FORMAT_BGR8)  ////彩色图
                    {
                        auto colorMat = Mat(Size(w, h), CV_8UC3, (void*)vid_frame.get_data(), Mat::AUTO_STEP);
                        imshow("color1_"+to_string(stream_num) , colorMat);
                    }
                    
                    else if (format == RS2_FORMAT_RGB8) 
                    {
                        auto colorMat = Mat(Size(w, h), CV_8UC3, (void*)vid_frame.get_data(), Mat::AUTO_STEP);
                        cvtColor(colorMat, colorMat, COLOR_RGB2BGR);
                        imshow("color2_" + to_string(stream_num), colorMat);
                    }

                    else if (format == RS2_FORMAT_Z16)
                    {
                        auto depth = vid_frame.apply_filter(color_map);
                        auto colorMat = Mat(Size(w, h), CV_8UC3, (void*)depth.get_data(), Mat::AUTO_STEP);
                        imshow("color_depth_" + to_string(stream_num), colorMat);
                        //auto depthMat = Mat(Size(w, h), CV_16UC1, (void*)vid_frame.get_data(), Mat::AUTO_STEP);
                    }
                                    
                    waitKey(1);
                    
                }
                stream_num++;
                
            }
        }
    }

    void pointcloud_process() {
        std::lock_guard<std::mutex> lock(_mutex);
        int stream_num = 0;
        rs2::frame color_frame_1, color_frame_2, depth_frame_1, depth_frame_2;
        cloud_pointer cloud1, cloud2;
        for (auto&& view : _devices) ////遍历每个设备
        {
            // For each device get its frames
            for (auto&& id_to_frame : view.second.frames_per_stream) ////每个设备一个stream里有3 帧 数据:深度帧,红外帧,彩色帧
            {
                if (rs2::video_frame vid_frame = id_to_frame.second.as<rs2::video_frame>())
                {

                    auto format = vid_frame.get_profile().format();
                    auto w = vid_frame.get_width();
                    auto h = vid_frame.get_height();
                    int cur_num = stream_num / 3; ////cur_num等于0,1,2属于一个设备,等于3,4,5属于另一个设备,以此类推

                    ////只获取深度帧和彩色帧
                    if (format == RS2_FORMAT_BGR8)  ////彩色帧
                    {
                        if (cur_num == 0) 
                            color_frame_1 = vid_frame;
                        else
                            color_frame_2 = vid_frame;

                    }

                    else if (format == RS2_FORMAT_Z16) ////深度帧
                    {
                        if (cur_num == 0)
                            depth_frame_1 = vid_frame;
                        else
                            depth_frame_2 = vid_frame;
                    }
                }
                stream_num++;
            
            }
        
        }
        
        if (color_frame_1 && depth_frame_1 && color_frame_2 && depth_frame_2) ////若两个设备的深度帧和彩色帧均获取到,则生成点云
        {
            
            pc1.map_to(color_frame_1);
            auto points1 = pc1.calculate(depth_frame_1);
            cloud1 = PCL_Conversion(points1, color_frame_1);
            
        
            pc2.map_to(color_frame_2);
            auto points2 = pc2.calculate(depth_frame_2);
            cloud2 = PCL_Conversion(points2, color_frame_2);
            
            ////可视化
            viewer1->addPointCloud<pcl::PointXYZRGB>(cloud1, "cloud1");
            viewer1->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud1");
            viewer1->spinOnce(1);
            viewer1->removePointCloud("cloud1");
            
            viewer2->addPointCloud<pcl::PointXYZRGB>(cloud2, "cloud2");
            viewer2->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 2, "cloud2");
            viewer2->spinOnce(1);
            viewer2->removePointCloud("cloud2");
            
        }
        
        
        else
        {
            cout << "depth frame and color frame not aligned" << endl;
        }
        


    }
private:
    std::mutex _mutex;
    std::map<std::string, view_port> _devices;

public:
    rs2::pointcloud pc1, pc2;

};



//======================================================
// RGB Texture
// - Function is utilized to extract the RGB data from
// a single point return R, G, and B values. 
// Normals are stored as RGB components and
// correspond to the specific depth (XYZ) coordinate.
// By taking these normals and converting them to
// texture coordinates, the RGB components can be
// "mapped" to each individual point (XYZ).
//======================================================
std::tuple<int, int, int> RGB_Texture(rs2::video_frame texture, rs2::texture_coordinate Texture_XY)
{
    // Get Width and Height coordinates of texture
    int width = texture.get_width();  // Frame width in pixels
    int height = texture.get_height(); // Frame height in pixels

                                       // Normals to Texture Coordinates conversion
    int x_value = min(max(int(Texture_XY.u * width + .5f), 0), width - 1);
    int y_value = min(max(int(Texture_XY.v * height + .5f), 0), height - 1);

    int bytes = x_value * texture.get_bytes_per_pixel();   // Get # of bytes per pixel
    int strides = y_value * texture.get_stride_in_bytes(); // Get line width in bytes
    int Text_Index = (bytes + strides);

    const auto New_Texture = reinterpret_cast<const uint8_t*>(texture.get_data());

    // RGB components to save in tuple
    int NT1 = New_Texture[Text_Index];
    int NT2 = New_Texture[Text_Index + 1];
    int NT3 = New_Texture[Text_Index + 2];

    return std::tuple<int, int, int>(NT1, NT2, NT3);
}

//===================================================
//  PCL_Conversion
// - Function is utilized to fill a point cloud
//  object with depth and RGB data from a single
//  frame captured using the Realsense.
//=================================================== 
cloud_pointer PCL_Conversion(const rs2::points& points, const rs2::video_frame& color) {

    // Object Declaration (Point Cloud)
    cloud_pointer cloud(new point_cloud);

    // Declare Tuple for RGB value Storage (<t0>, <t1>, <t2>)
    std::tuple<uint8_t, uint8_t, uint8_t> RGB_Color;

    //================================
    // PCL Cloud Object Configuration
    //================================
    // Convert data captured from Realsense camera to Point Cloud
    auto sp = points.get_profile().as<rs2::video_stream_profile>();

    cloud->width = static_cast<uint32_t>(sp.width());
    cloud->height = static_cast<uint32_t>(sp.height());
    cloud->is_dense = false;
    cloud->points.resize(points.size());

    auto Texture_Coord = points.get_texture_coordinates();
    auto Vertex = points.get_vertices();

    // Iterating through all points and setting XYZ coordinates
    // and RGB values
    for (int i = 0; i < points.size(); i++)
    {
        //===================================
        // Mapping Depth Coordinates
        // - Depth data stored as XYZ values
        //===================================
        cloud->points[i].x = Vertex[i].x;
        cloud->points[i].y = Vertex[i].y;
        cloud->points[i].z = Vertex[i].z;

        // Obtain color texture for specific point
        RGB_Color = RGB_Texture(color, Texture_Coord[i]);

        // Mapping Color (BGR due to Camera Model)
        cloud->points[i].r = get<2>(RGB_Color); // Reference tuple<2>
        cloud->points[i].g = get<1>(RGB_Color); // Reference tuple<1>
        cloud->points[i].b = get<0>(RGB_Color); // Reference tuple<0>

    }

    return cloud; // PCL RGB Point Cloud generated
}

//boost::mutex updateModelMutex;

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

    bool captureLoop = true; // Loop control for generating point clouds
    device_container connected_devices;

    rs2::context ctx;    // Create librealsense context for managing devices

                         // Register callback for tracking which devices are currently connected
    ctx.set_devices_changed_callback([&](rs2::event_information& info)
    {
        connected_devices.remove_devices(info);
        for (auto&& dev : info.get_new_devices())
        {
            connected_devices.enable_device(dev);
        }
    });

    // Initial population of the device list
    for (auto&& dev : ctx.query_devices()) // Query the list of connected RealSense devices
    {
        connected_devices.enable_device(dev);
    }

    if ((int)connected_devices.device_count() == 0)
    {
        
        cerr << no_camera_message << endl;
        return  EXIT_FAILURE;
    }
    ////单台接入点云显示
    else if ((int)connected_devices.device_count() == 1)
    {
        rs2::pointcloud pc;
        rs2::pipeline pipe;
        rs2::config cfg;
        rs2::colorizer color_map;
        cfg.enable_stream(RS2_STREAM_COLOR, 640, 480, RS2_FORMAT_BGR8, 30);
        cfg.enable_stream(RS2_STREAM_INFRARED, 640, 480, RS2_FORMAT_Y8, 30);
        cfg.enable_stream(RS2_STREAM_DEPTH, 640, 480, RS2_FORMAT_Z16, 30);
        rs2::pipeline_profile selection = pipe.start(cfg);
        rs2::device selected_device = selection.get_device();
        auto depth_sensor = selected_device.first<rs2::depth_sensor>();

        if (depth_sensor.supports(RS2_OPTION_EMITTER_ENABLED))
        {
            depth_sensor.set_option(RS2_OPTION_EMITTER_ENABLED, 1.f); // Enable emitter
            depth_sensor.set_option(RS2_OPTION_EMITTER_ENABLED, 0.f); // Disable emitter
        }
        if (depth_sensor.supports(RS2_OPTION_LASER_POWER))
        {
            // Query min and max values:
            auto range = depth_sensor.get_option_range(RS2_OPTION_LASER_POWER);
            depth_sensor.set_option(RS2_OPTION_LASER_POWER, range.max); // Set max power
            depth_sensor.set_option(RS2_OPTION_LASER_POWER, 0.f); // Disable laser
        }

        // Wait for frames from the camera to settle
        for (int i = 0; i < 30; i++) {
            auto frames = pipe.wait_for_frames(); //Drop several frames for auto-exposure
        }
        ////单台相机

        viewer1->addCoordinateSystem(0.1);
        viewer1->initCameraParameters();
        viewer1->setBackgroundColor(0, 0, 0);

        while (captureLoop) {
            //// 单台相机
            auto frames = pipe.wait_for_frames();
            auto depth = frames.get_depth_frame();
            auto depth1 = depth.apply_filter(color_map);
            auto RGB = frames.get_color_frame();
            auto rgbMat = frame_to_mat(RGB);
            auto depthMat = frame_to_mat(depth1);
            //imshow("rgb", rgbMat);
            //imshow("depth", depthMat);
            //waitKey(1);
            //// 单台相机


            pc.map_to(RGB);
            auto points = pc.calculate(depth);
            cloud_pointer cloud = PCL_Conversion(points, RGB);
            ////可视化
            viewer1->addPointCloud<pcl::PointXYZRGB>(cloud, "cloud1");
            viewer1->setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 1, "cloud1");
            viewer1->spinOnce(1);
            viewer1->removePointCloud("cloud1");

        }
    }
    ////多台相机接入,这里是两台
    else
    {       
        viewer1->addCoordinateSystem(0.1);
        viewer1->initCameraParameters();
        viewer1->setBackgroundColor(0, 0, 0);
        viewer2->addCoordinateSystem(0.1);
        viewer2->initCameraParameters();
        viewer2->setBackgroundColor(0, 0, 0);


        // Loop and take frame captures upon user input
        while (captureLoop) {


            ////多台相机
            connected_devices.poll_frames();
            auto total_number_of_streams = connected_devices.stream_count();

            //connected_devices.render_textures(); ////显示深度图和彩色图
            connected_devices.pointcloud_process();////点云处理

                                                   ////多台相机
        }

        //========================================
        // Filter PointCloud (PassThrough Method)
        //========================================
        /*
        pcl::PassThrough<pcl::PointXYZRGB> Cloud_Filter; // Create the filtering object
        Cloud_Filter.setInputCloud(cloud);           // Input generated cloud to filter
        Cloud_Filter.setFilterFieldName("z");        // Set field name to Z-coordinate
        Cloud_Filter.setFilterLimits(0.0, 1.0);      // Set accepted interval values
        Cloud_Filter.filter(*newCloud);              // Filtered Cloud Outputted
        */
    }

    cout << "Exiting Program... " << endl;
    return EXIT_SUCCESS;
    
}

catch (const rs2::error & e)
{
    std::cerr << "RealSense error calling " << e.get_failed_function() << "(" << e.get_failed_args() << "):\n    " << e.what() << std::endl;
    return EXIT_FAILURE;
}
catch (const std::exception& e)
{
    std::cerr << e.what() << std::endl;
    return EXIT_FAILURE;
}


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,711评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,079评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,194评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,089评论 1 286
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,197评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,306评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,338评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,119评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,541评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,846评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,014评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,694评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,322评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,026评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,257评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,863评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,895评论 2 351

推荐阅读更多精彩内容

  • 点云(Point Cloud) 接触三维重建或者立体视觉相关问题的时候,点云是第一个需要掌握的概念。简单来讲,点云...
    羲牧阅读 9,871评论 5 10
  • 摘要: 一、新产品发布1、云防火墙 商业化 发布云防火墙是一款云环境下的防火墙产品,首创了基于业务可视的结果,实现...
    猫耳呀阅读 698评论 0 1
  • 小序:汉江流经小城,人称其为汉江河。 河出鲋愚山,四季无暑寒。 渔夫歌其上,农...
    W和W阅读 864评论 2 6
  • 正确的生活态度 就是认真做好手里的每一件事 温柔地对待每一个人 包括自己 提醒自己记得吃饭 学会在工作生活中获取能...
    瀓sky阅读 357评论 0 0
  • 练习 42:SQL 删除 原文:EExercise 42: Deleting with SQL 译者:飞龙 协议:...
    布客飞龙阅读 424评论 0 2