본문 바로가기
IntelRealsense & PCL

PCL 설치 및 Intel Realsense 연동

by choice1219 2021. 4. 21.

안녕하세요 VeriLog입니다.

 

이번 포스팅에서는 PCL (Point Cloud Library)를 설치하고, Intel Realsense 카메라와 연동해보려고 합니다.

제가 사용하고 있는 환경은 다음과 같습니다.

 

 - OS: Windows 10

 - 개발언어: C++

 - PCL version: 1.9.1

 

지난 포스팅에서는 D435 스테레오 카메라를 사용한다고 하였는데, 이번 포스팅부터는 L515 라이다를 사용하려고 합니다. (코드는 동일합니다.)

 

1. PCL 다운로드 및 설치

 1) PCL github 접속 (github.com/PointCloudLibrary/pcl/releases)

 

Releases · PointCloudLibrary/pcl

Point Cloud Library (PCL). Contribute to PointCloudLibrary/pcl development by creating an account on GitHub.

github.com

 2) PCL AllInOne.exe 파일 다운로드 및 설치

 - github 접속 후, PCL AllInOne 파일을 다운 및 설치해주세요.

 - 저의 경우, 1.9.1 version을 사용하고 있어서 최신 버젼과 환경 설정에서 조금 다릅니다.

 

2. PCL 환경설정

 1) Visual Studio Project Open

  - 지난 시간에 Intel Realsense를 추가했던 프로젝트를 열어주세요.

2021.03.24 - [IntelRealsense & PCL] - Intel Realsense SDK 설치 방법

 

 2) Visual Studio 속성시트 생성

  - "속성 관리자" > Release 64에서 새로운 속성시트를 생성해주세요. (PCL_Release_x64.props)

 - 속성시트 생성 후, 더블클릭해서 열어주세요. 이후 "C/C++ > 일반 > 추가 포함 디렉터리" 으로 이동해주세요.

 - 이후, PCL과 3rdParty의 include 폴더를 모두 추가해주세요. (경로는 버전에 따라 상의할 수 있습니다.)

 

 - "링커 > 일반 > 추가 라이브러리 디렉터리"로 이동해주세요. 

 - 이후, PCL과 3rdParty의 lib 폴더를 모두 추가해주세요. (경로는 버전에 따라 상의할 수 있습니다.)

 - 마지막으로 "링커 > 입력 > 추가 종속성"으로 이동해주세요.

 - 여기가 제일 힘든 부분입니다. 위에서 추가했던 "lib 폴더들"로 이동하여 모든 .lib파일을 추가해주시면 됩니다. (debug라고 써있는 파일은 추가 안하셔도 됩니다.)

 - 모든 lib 파일을 추가 후, "확인"을 눌러주세요.

 

 3. 코드 작성 및 데모

 - 아래 코드 작성 후, 빌드 시 에러가 없다면 성공적으로 연동이 된 것입니다.

 - 만약, .dll 파일을 찾을 수 없다는 에러가 표시된다면, dll 파일을 찾아서 프로젝트 폴더에 넣어주시면 됩니다.

 (ex. OpenNI2.dll을 찾을 수 없습니다. > OpenNI2.dll 파일 복사 후, librealsense_temp\librealsense_temp에 붙여넣기)

 * 프로젝트 명: librealsense_temp

#pragma warning(disable:4996)

#include <iostream>
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/visualization/cloud_viewer.h>
#include <librealsense2/rs.hpp>
#include <pcl/filters/passthrough.h>
#include <pcl/filters/extract_indices.h>
#include <pcl/filters/voxel_grid.h>
#include <pcl/features/normal_3d.h>
#include <pcl/kdtree/kdtree.h>
#include <pcl/sample_consensus/method_types.h>
#include <pcl/sample_consensus/model_types.h>
#include <pcl/segmentation/sac_segmentation.h>
#include <pcl/segmentation/extract_clusters.h>
#include <pcl/common/io.h>
#include <pcl/filters/radius_outlier_removal.h>

using namespace std;

typedef pcl::PointXYZRGB RGB_Cloud;
typedef pcl::PointCloud<RGB_Cloud> point_cloud;
typedef point_cloud::Ptr cloud_pointer;
typedef point_cloud::Ptr prevCloud;

// Global Variables
string cloudFile; // .pcd file name
string prevCloudFile; // .pcd file name (Old cloud)
int i = 1; // Index for incremental file name

//======================================================
// 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<0>(RGB_Color); // Reference tuple<2>
		cloud->points[i].g = get<1>(RGB_Color); // Reference tuple<1>
		cloud->points[i].b = get<2>(RGB_Color); // Reference tuple<0>

	}

	return cloud; // PCL RGB Point Cloud generated
}


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

	pcl::PointCloud<pcl::PointXYZRGB>::Ptr newCloud(new pcl::PointCloud<pcl::PointXYZRGB>);
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr cluster_result(new pcl::PointCloud<pcl::PointXYZRGB>);
	pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_f(new pcl::PointCloud<pcl::PointXYZRGB>);

	// Declare pointcloud object, for calculating pointclouds and texture mappings
	rs2::pointcloud pc;
	// We want the points object to be persistent so we can display the last cloud when a frame drops
	rs2::points points;

	// Declare RealSense pipeline, encapsulating the actual device and sensors
	rs2::pipeline pipe;

	// Create a configuration for configuring the pipeline with a non default profile
	rs2::config cfg;

	//======================
	// Stream configuration with parameters resolved internally. See enable_stream() overloads for extended usage
	//======================
	cfg.enable_stream(RS2_STREAM_COLOR);
	cfg.enable_stream(RS2_STREAM_INFRARED);
	cfg.enable_stream(RS2_STREAM_DEPTH);

	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
		pipe.wait_for_frames();
		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
		Sleep(1);
		depth_sensor.set_option(RS2_OPTION_LASER_POWER, 0.f); // Disable laser
	}
	
	pcl::visualization::CloudViewer viewer("cloud viewer");

	while (!viewer.wasStopped())
	{
		cloud_pointer cloud;
		
		// Wait for the next set of frames from the camera
		auto frames = pipe.wait_for_frames();
		auto depth = frames.get_depth_frame();
		auto RGB = frames.get_color_frame();

		cout << "get frame ended!" << endl;

		// Map Color texture to each point
		pc.map_to(RGB);

		// Generate the pointcloud and texture mappings
		points = pc.calculate(depth);

		cloud = PCL_Conversion(points, RGB);
					
		viewer.showCloud(cloud);

	}


	return (0);

}

(코드 출처: Intel Realsense 공식 홈페이지)

 

 - 아래는 데모 영상입니다.

 

 

다음 포스팅에서는 Realsense와 PCL을 활용한 다양한 예제들을 수행해보려고 합니다. 

'IntelRealsense & PCL' 카테고리의 다른 글

Intel RealSense L515에서 데이터 추출하기  (11) 2021.04.21
Intel Realsense SDK 설치 방법  (12) 2021.03.24