forked from IntelRealSense/librealsense
-
Notifications
You must be signed in to change notification settings - Fork 0
/
api_how_to.h
487 lines (414 loc) · 22.3 KB
/
api_how_to.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
// 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();
}
};