You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

488 lines
22 KiB

// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2017 Intel Corporation. All Rights Reserved.
#pragma once
#include <iostream>
#include <iomanip>
#include <map>
#include <utility>
#include <vector>
#include <librealsense2/rs.hpp>
#include "helper.h"
using namespace helper;
/**
The how_to class provides several functions for common usages of the sensor API
*/
class how_to
{
public:
static rs2::device get_a_realsense_device()
{
// First, create a rs2::context.
// The context represents the current platform with respect to connected devices
rs2::context ctx;
// Using the context we can get all connected devices in a device list
rs2::device_list devices = ctx.query_devices();
rs2::device selected_device;
if (devices.size() == 0)
{
std::cerr << "No device connected, please connect a RealSense device" << std::endl;
//To help with the boilerplate code of waiting for a device to connect
//The SDK provides the rs2::device_hub class
rs2::device_hub device_hub(ctx);
//Using the device_hub we can block the program until a device connects
selected_device = device_hub.wait_for_device();
}
else
{
std::cout << "Found the following devices:\n" << std::endl;
// device_list is a "lazy" container of devices which allows
//The device list provides 2 ways of iterating it
//The first way is using an iterator (in this case hidden in the Range-based for loop)
int index = 0;
for (rs2::device device : devices)
{
std::cout << " " << index++ << " : " << get_device_name(device) << std::endl;
}
uint32_t selected_device_index = get_user_selection("Select a device by index: ");
// The second way is using the subscript ("[]") operator:
if (selected_device_index >= devices.size())
{
throw std::out_of_range("Selected device index is out of range");
}
// Update the selected device
selected_device = devices[selected_device_index];
}
return selected_device;
}
static void print_device_information(const rs2::device& dev)
{
// Each device provides some information on itself
// The different types of available information are represented using the "RS2_CAMERA_INFO_*" enum
std::cout << "Device information: " << std::endl;
//The following code shows how to enumerate all of the RS2_CAMERA_INFO
//Note that all enum types in the SDK start with the value of zero and end at the "*_COUNT" value
for (int i = 0; i < static_cast<int>(RS2_CAMERA_INFO_COUNT); i++)
{
rs2_camera_info info_type = static_cast<rs2_camera_info>(i);
//SDK enum types can be streamed to get a string that represents them
std::cout << " " << std::left << std::setw(20) << info_type << " : ";
//A device might not support all types of RS2_CAMERA_INFO.
//To prevent throwing exceptions from the "get_info" method we first check if the device supports this type of info
if (dev.supports(info_type))
std::cout << dev.get_info(info_type) << std::endl;
else
std::cout << "N/A" << std::endl;
}
}
static std::string get_device_name(const rs2::device& dev)
{
// Each device provides some information on itself, such as name:
std::string name = "Unknown Device";
if (dev.supports(RS2_CAMERA_INFO_NAME))
name = dev.get_info(RS2_CAMERA_INFO_NAME);
// and the serial number of the device:
std::string sn = "########";
if (dev.supports(RS2_CAMERA_INFO_SERIAL_NUMBER))
sn = std::string("#") + dev.get_info(RS2_CAMERA_INFO_SERIAL_NUMBER);
return name + " " + sn;
}
static std::string get_sensor_name(const rs2::sensor& sensor)
{
// Sensors support additional information, such as a human readable name
if (sensor.supports(RS2_CAMERA_INFO_NAME))
return sensor.get_info(RS2_CAMERA_INFO_NAME);
else
return "Unknown Sensor";
}
static rs2::sensor get_a_sensor_from_a_device(const rs2::device& dev)
{
// A rs2::device is a container of rs2::sensors that have some correlation between them.
// For example:
// * A device where all sensors are on a single board
// * A Robot with mounted sensors that share calibration information
// Given a device, we can query its sensors using:
std::vector<rs2::sensor> sensors = dev.query_sensors();
std::cout << "Device consists of " << sensors.size() << " sensors:\n" << std::endl;
int index = 0;
// We can now iterate the sensors and print their names
for (rs2::sensor sensor : sensors)
{
std::cout << " " << index++ << " : " << get_sensor_name(sensor) << std::endl;
}
uint32_t selected_sensor_index = get_user_selection("Select a sensor by index: ");
// The second way is using the subscript ("[]") operator:
if (selected_sensor_index >= sensors.size())
{
throw std::out_of_range("Selected sensor index is out of range");
}
return sensors[selected_sensor_index];
}
static rs2_option get_sensor_option(const rs2::sensor& sensor)
{
// Sensors usually have several options to control their properties
// such as Exposure, Brightness etc.
std::cout << "Sensor supports the following options:\n" << std::endl;
// The following loop shows how to iterate over all available options
// Starting from 0 until RS2_OPTION_COUNT (exclusive)
for (int i = 0; i < static_cast<int>(RS2_OPTION_COUNT); i++)
{
rs2_option option_type = static_cast<rs2_option>(i);
//SDK enum types can be streamed to get a string that represents them
std::cout << " " << i << ": " << option_type;
// To control an option, use the following api:
// First, verify that the sensor actually supports this option
if (sensor.supports(option_type))
{
std::cout << std::endl;
// Get a human readable description of the option
const char* description = sensor.get_option_description(option_type);
std::cout << " Description : " << description << std::endl;
// Get the current value of the option
float current_value = sensor.get_option(option_type);
std::cout << " Current Value : " << current_value << std::endl;
//To change the value of an option, please follow the change_sensor_option() function
}
else
{
std::cout << " is not supported" << std::endl;
}
}
uint32_t selected_sensor_option = get_user_selection("Select an option by index: ");
if (selected_sensor_option >= static_cast<int>(RS2_OPTION_COUNT))
{
throw std::out_of_range("Selected option is out of range");
}
return static_cast<rs2_option>(selected_sensor_option);
}
static float get_depth_units(const rs2::sensor& sensor)
{
//A Depth stream contains an image that is composed of pixels with depth information.
//The value of each pixel is the distance from the camera, in some distance units.
//To get the distance in units of meters, each pixel's value should be multiplied by the sensor's depth scale
//Here is the way to grab this scale value for a "depth" sensor:
if (rs2::depth_sensor dpt_sensor = sensor.as<rs2::depth_sensor>())
{
float scale = dpt_sensor.get_depth_scale();
std::cout << "Scale factor for depth sensor is: " << scale << std::endl;
return scale;
}
else
throw std::runtime_error("Given sensor is not a depth sensor");
}
static void get_field_of_view(const rs2::stream_profile& stream)
{
// A sensor's stream (rs2::stream_profile) is in general a stream of data with no specific type.
// For video streams (streams of images), the sensor that produces the data has a lens and thus has properties such
// as a focal point, distortion, and principal point.
// To get these intrinsics parameters, we need to take a stream and first check if it is a video stream
if (auto video_stream = stream.as<rs2::video_stream_profile>())
{
try
{
//If the stream is indeed a video stream, we can now simply call get_intrinsics()
rs2_intrinsics intrinsics = video_stream.get_intrinsics();
auto principal_point = std::make_pair(intrinsics.ppx, intrinsics.ppy);
auto focal_length = std::make_pair(intrinsics.fx, intrinsics.fy);
rs2_distortion model = intrinsics.model;
std::cout << "Principal Point : " << principal_point.first << ", " << principal_point.second << std::endl;
std::cout << "Focal Length : " << focal_length.first << ", " << focal_length.second << std::endl;
std::cout << "Distortion Model : " << model << std::endl;
std::cout << "Distortion Coefficients : [" << intrinsics.coeffs[0] << "," << intrinsics.coeffs[1] << "," <<
intrinsics.coeffs[2] << "," << intrinsics.coeffs[3] << "," << intrinsics.coeffs[4] << "]" << std::endl;
}
catch (const std::exception& e)
{
std::cerr << "Failed to get intrinsics for the given stream. " << e.what() << std::endl;
}
}
else if (auto motion_stream = stream.as<rs2::motion_stream_profile>())
{
try
{
//If the stream is indeed a motion stream, we can now simply call get_motion_intrinsics()
rs2_motion_device_intrinsic intrinsics = motion_stream.get_motion_intrinsics();
std::cout << " Scale X cross axis cross axis Bias X \n";
std::cout << " cross axis Scale Y cross axis Bias Y \n";
std::cout << " cross axis cross axis Scale Z Bias Z \n";
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 4; j++)
{
std::cout << intrinsics.data[i][j] << " ";
}
std::cout << "\n";
}
std::cout << "Variance of noise for X, Y, Z axis \n";
for (int i = 0; i < 3; i++)
std::cout << intrinsics.noise_variances[i] << " ";
std::cout << "\n";
std::cout << "Variance of bias for X, Y, Z axis \n";
for (int i = 0; i < 3; i++)
std::cout << intrinsics.bias_variances[i] << " ";
std::cout << "\n";
}
catch (const std::exception& e)
{
std::cerr << "Failed to get intrinsics for the given stream. " << e.what() << std::endl;
}
}
else
{
std::cerr << "Given stream profile has no intrinsics data" << std::endl;
}
}
static void get_extrinsics(const rs2::stream_profile& from_stream, const rs2::stream_profile& to_stream)
{
// If the device/sensor that you are using contains more than a single stream, and it was calibrated
// then the SDK provides a way of getting the transformation between any two streams (if such exists)
try
{
// Given two streams, use the get_extrinsics_to() function to get the transformation from the stream to the other stream
rs2_extrinsics extrinsics = from_stream.get_extrinsics_to(to_stream);
std::cout << "Translation Vector : [" << extrinsics.translation[0] << "," << extrinsics.translation[1] << "," << extrinsics.translation[2] << "]\n";
std::cout << "Rotation Matrix : [" << extrinsics.rotation[0] << "," << extrinsics.rotation[3] << "," << extrinsics.rotation[6] << "]\n";
std::cout << " : [" << extrinsics.rotation[1] << "," << extrinsics.rotation[4] << "," << extrinsics.rotation[7] << "]\n";
std::cout << " : [" << extrinsics.rotation[2] << "," << extrinsics.rotation[5] << "," << extrinsics.rotation[8] << "]" << std::endl;
}
catch (const std::exception& e)
{
std::cerr << "Failed to get extrinsics for the given streams. " << e.what() << std::endl;
}
}
static void change_sensor_option(const rs2::sensor& sensor, rs2_option option_type)
{
// Sensors usually have several options to control their properties
// such as Exposure, Brightness etc.
// To control an option, use the following api:
// First, verify that the sensor actually supports this option
if (!sensor.supports(option_type))
{
std::cerr << "This option is not supported by this sensor" << std::endl;
return;
}
// Each option provides its rs2::option_range to provide information on how it can be changed
// To get the supported range of an option we do the following:
std::cout << "Supported range for option " << option_type << ":" << std::endl;
rs2::option_range range = sensor.get_option_range(option_type);
float default_value = range.def;
float maximum_supported_value = range.max;
float minimum_supported_value = range.min;
float difference_to_next_value = range.step;
std::cout << " Min Value : " << minimum_supported_value << std::endl;
std::cout << " Max Value : " << maximum_supported_value << std::endl;
std::cout << " Default Value : " << default_value << std::endl;
std::cout << " Step : " << difference_to_next_value << std::endl;
bool change_option = false;
change_option = prompt_yes_no("Change option's value?");
if (change_option)
{
std::cout << "Enter the new value for this option: ";
float requested_value;
std::cin >> requested_value;
std::cout << std::endl;
// To set an option to a different value, we can call set_option with a new value
try
{
sensor.set_option(option_type, requested_value);
}
catch (const rs2::error& e)
{
// Some options can only be set while the camera is streaming,
// and generally the hardware might fail so it is good practice to catch exceptions from set_option
std::cerr << "Failed to set option " << option_type << ". (" << e.what() << ")" << std::endl;
}
}
}
static rs2::stream_profile choose_a_streaming_profile(const rs2::sensor& sensor)
{
// A Sensor is an object that is capable of streaming one or more types of data.
// For example:
// * A stereo sensor with Left and Right Infrared streams that
// creates a stream of depth images
// * A motion sensor with an Accelerometer and Gyroscope that
// provides a stream of motion information
// Using the sensor we can get all of its streaming profiles
std::vector<rs2::stream_profile> stream_profiles = sensor.get_stream_profiles();
// Usually a sensor provides one or more streams which are identifiable by their stream_type and stream_index
// Each of these streams can have several profiles (e.g FHD/HHD/VGA/QVGA resolution, or 90/60/30 fps, etc..)
//The following code shows how to go over a sensor's stream profiles, and group the profiles by streams.
std::map<std::pair<rs2_stream, int>, int> unique_streams;
for (auto&& sp : stream_profiles)
{
unique_streams[std::make_pair(sp.stream_type(), sp.stream_index())]++;
}
std::cout << "Sensor consists of " << unique_streams.size() << " streams: " << std::endl;
for (size_t i = 0; i < unique_streams.size(); i++)
{
auto it = unique_streams.begin();
std::advance(it, i);
std::cout << " - " << it->first.first << " #" << it->first.second << std::endl;
}
//Next, we go over all the stream profiles and print the details of each one
std::cout << "Sensor provides the following stream profiles:" << std::endl;
int profile_num = 0;
for (rs2::stream_profile stream_profile : stream_profiles)
{
// A Stream is an abstraction for a sequence of data items of a
// single data type, which are ordered according to their time
// of creation or arrival.
// The stream's data types are represented using the rs2_stream
// enumeration
rs2_stream stream_data_type = stream_profile.stream_type();
// The rs2_stream provides only types of data which are
// supported by the RealSense SDK
// For example:
// * rs2_stream::RS2_STREAM_DEPTH describes a stream of depth images
// * rs2_stream::RS2_STREAM_COLOR describes a stream of color images
// * rs2_stream::RS2_STREAM_INFRARED describes a stream of infrared images
// As mentioned, a sensor can have multiple streams.
// In order to distinguish between streams with the same
// stream type we can use the following methods:
// 1) Each stream type can have multiple occurances.
// All streams, of the same type, provided from a single
// device have distinct indices:
int stream_index = stream_profile.stream_index();
// 2) Each stream has a user-friendly name.
// The stream's name is not promised to be unique,
// rather a human readable description of the stream
std::string stream_name = stream_profile.stream_name();
// 3) Each stream in the system, which derives from the same
// rs2::context, has a unique identifier
// This identifier is unique across all streams, regardless of the stream type.
int unique_stream_id = stream_profile.unique_id(); // The unique identifier can be used for comparing two streams
std::cout << std::setw(3) << profile_num << ": " << stream_data_type << " #" << stream_index;
// As noted, a stream is an abstraction.
// In order to get additional data for the specific type of a
// stream, a mechanism of "Is" and "As" is provided:
if (stream_profile.is<rs2::video_stream_profile>()) //"Is" will test if the type tested is of the type given
{
// "As" will try to convert the instance to the given type
rs2::video_stream_profile video_stream_profile = stream_profile.as<rs2::video_stream_profile>();
// After using the "as" method we can use the new data type
// for additinal operations:
std::cout << " (Video Stream: " << video_stream_profile.format() << " " <<
video_stream_profile.width() << "x" << video_stream_profile.height() << "@ " << video_stream_profile.fps() << "Hz)";
}
std::cout << std::endl;
profile_num++;
}
uint32_t selected_profile_index = get_user_selection("Please select the desired streaming profile: ");
if (selected_profile_index >= stream_profiles.size())
{
throw std::out_of_range("Requested profile index is out of range");
}
return stream_profiles[selected_profile_index];
}
static void start_streaming_a_profile(const rs2::sensor& sensor, const rs2::stream_profile& stream_profile)
{
// The sensor controls turning the streaming on and off
// To start streaming, two calls must be made with the following order:
// 1) open(stream_profiles_to_open)
// 2) start(function_to_handle_frames)
// Open can be called with a single profile, or with a collection of profiles
// Calling open() tries to get exclusive access to the sensor.
// Opening a sensor may have side effects such as actually
// running, consume power, produce data, etc.
sensor.open(stream_profile);
std::ostringstream oss;
oss << "Displaying profile " << stream_profile.stream_name();
// In order to begin getting data from the sensor, we need to register a callback to handle frames (data)
// To register a callback, the sensor's start() method should be invoked.
// The start() method takes any type of callable object that takes a frame as its parameter
// NOTE:
// * Since a sensor can stream multiple streams, and start()
// takes a single handler, multiple types of frames can
// arrive to the handler.
// * Different streams' frames arrive on different threads.
// This behavior requires the provided frame handler to the
// start method to be re-entrant
// In this example we have created a class to handle the frames,
// and we capture it by reference inside a C++11 lambda which is passed to the start() function
helper::frame_viewer display(oss.str());
sensor.start([&](rs2::frame f) { display(f); });
// At this point, frames will asynchronously arrive to the callback handler
// This thread will continue to run in parallel.
// To prevent this thread from returning, we block it using the helper wait() function
std::cout << "Streaming profile: " << stream_profile.stream_name() << ". Close display window to continue..." << std::endl;
display.wait();
// To stop streaming, we simply need to call the sensor's stop method
// After returning from the call to stop(), no frames will arrive from this sensor
sensor.stop();
// To complete the stop operation, and release access of the device, we need to call close() per sensor
sensor.close();
}
};