// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2017 Intel Corporation. All Rights Reserved.
#ifndef LIBREALSENSE_RS2_PROCESSING_HPP
#define LIBREALSENSE_RS2_PROCESSING_HPP
#include "rs_types.hpp"
#include "rs_frame.hpp"
#include "rs_options.hpp"
namespace rs2
{
/**
* The source used to generate frames, which is usually done by the low level driver for each sensor. frame_source is one of the parameters
* of processing_block's callback function, which can be used to re-generate the frame and via frame_ready invoke another callback function
* to notify application frame is ready. Please refer to "video_processing_thread" code snippet in rs-measure.cpp for a detailed usage example.
*/
class frame_source
{
public:
/**
* Allocate a new video frame with given params
*
* \param[in] profile Stream profile going to allocate.
* \param[in] original Original frame, if new_bpp, new_width, new_height or new_stride is zero, newly created frame will base on original frame's metadata to allocate new frame. If frame_type is RS2_EXTENSION_DEPTH_FRAME, the original of the returned frame will be set to it.
* \param[in] new_bpp Frame bit per pixel to create.
* \param[in] new_width Frame width to create.
* \param[in] new_height Frame height to create.
* \param[in] new_stride Frame stride to create.
* \param[in] frame_type Which frame type are going to create.
* \return The allocated frame
*/
frame allocate_video_frame(const stream_profile& profile,
const frame& original,
int new_bpp = 0,
int new_width = 0,
int new_height = 0,
int new_stride = 0,
rs2_extension frame_type = RS2_EXTENSION_VIDEO_FRAME) const
{
rs2_error* e = nullptr;
auto result = rs2_allocate_synthetic_video_frame(_source, profile.get(),
original.get(), new_bpp, new_width, new_height, new_stride, frame_type, &e);
error::handle(e);
return result;
}
/**
* Allocate a new motion frame with given params
*
* \param[in] profile Stream profile going to allocate.
* \param[in] original Original frame.
* \param[in] frame_type Which frame type are going to create.
* \return The allocated frame
*/
frame allocate_motion_frame(const stream_profile& profile,
const frame& original,
rs2_extension frame_type = RS2_EXTENSION_MOTION_FRAME) const
{
rs2_error* e = nullptr;
auto result = rs2_allocate_synthetic_motion_frame(_source, profile.get(),
original.get(), frame_type, &e);
error::handle(e);
return result;
}
frame allocate_points(const stream_profile& profile,
const frame& original) const
{
rs2_error* e = nullptr;
auto result = rs2_allocate_points(_source, profile.get(), original.get(), &e);
error::handle(e);
return result;
}
/**
* Allocate composite frame with given params
*
* \param[in] frames Frame vecotor used to create composite frame, the size of composite frame will be the same as vector size.
* \return The allocated composite frame
*/
frame allocate_composite_frame(std::vector frames) const
{
rs2_error* e = nullptr;
std::vector refs(frames.size(), (rs2_frame*)nullptr);
for (size_t i = 0; i < frames.size(); i++)
std::swap(refs[i], frames[i].frame_ref);
auto result = rs2_allocate_composite_frame(_source, refs.data(), (int)refs.size(), &e);
error::handle(e);
return result;
}
/**
* Invoke the callback funtion informing the frame is ready.
*
* \param[in] frames frame to send to callback function.
*/
void frame_ready(frame result) const
{
rs2_error* e = nullptr;
rs2_synthetic_frame_ready(_source, result.get(), &e);
error::handle(e);
result.frame_ref = nullptr;
}
rs2_source* _source;
private:
template
friend class frame_processor_callback;
frame_source(rs2_source* source) : _source(source) {}
frame_source(const frame_source&) = delete;
};
template
class frame_processor_callback : public rs2_frame_processor_callback
{
T on_frame_function;
public:
explicit frame_processor_callback(T on_frame) : on_frame_function(on_frame) {}
void on_frame(rs2_frame* f, rs2_source * source) override
{
frame_source src(source);
frame frm(f);
on_frame_function(std::move(frm), src);
}
void release() override { delete this; }
};
class frame_queue
{
public:
/**
* create frame queue. frame queues are the simplest x-platform synchronization primitive provided by librealsense
* to help developers who are not using async APIs
* param[in] capacity size of the frame queue
* param[in] keep_frames if set to true, the queue automatically calls keep() on every frame enqueued into it.
*/
explicit frame_queue(unsigned int capacity, bool keep_frames = false) : _capacity(capacity), _keep(keep_frames)
{
rs2_error* e = nullptr;
_queue = std::shared_ptr(
rs2_create_frame_queue(capacity, &e),
rs2_delete_frame_queue);
error::handle(e);
}
frame_queue() : frame_queue(1) {}
/**
* enqueue new frame into the queue
* \param[in] f - frame handle to enqueue (this operation passed ownership to the queue)
*/
void enqueue(frame f) const
{
if (_keep) f.keep();
rs2_enqueue_frame(f.frame_ref, _queue.get()); // noexcept
f.frame_ref = nullptr; // frame has been essentially moved from
}
/**
* wait until new frame becomes available in the queue and dequeue it
* \return frame handle to be released using rs2_release_frame
*/
frame wait_for_frame(unsigned int timeout_ms = 5000) const
{
rs2_error* e = nullptr;
auto frame_ref = rs2_wait_for_frame(_queue.get(), timeout_ms, &e);
error::handle(e);
return{ frame_ref };
}
/**
* poll if a new frame is available and dequeue if it is
* \param[out] f - frame handle
* \return true if new frame was stored to f
*/
template
typename std::enable_if::value, bool>::type poll_for_frame(T* output) const
{
rs2_error* e = nullptr;
rs2_frame* frame_ref = nullptr;
auto res = rs2_poll_for_frame(_queue.get(), &frame_ref, &e);
error::handle(e);
frame f{ frame_ref };
if (res) *output = f;
return res > 0;
}
template
typename std::enable_if::value, bool>::type try_wait_for_frame(T* output, unsigned int timeout_ms = 5000) const
{
rs2_error* e = nullptr;
rs2_frame* frame_ref = nullptr;
auto res = rs2_try_wait_for_frame(_queue.get(), timeout_ms, &frame_ref, &e);
error::handle(e);
frame f{ frame_ref };
if (res) *output = f;
return res > 0;
}
/**
* Does the same thing as enqueue function.
*/
void operator()(frame f) const
{
enqueue(std::move(f));
}
/**
* Return the capacity of the queue
* \return capacity size
*/
size_t size() const
{
rs2_error* e = nullptr;
auto res = rs2_frame_queue_size(_queue.get(), &e);
error::handle(e);
return static_cast(res);
}
/**
* Return the capacity of the queue
* \return capacity size
*/
size_t capacity() const { return _capacity; }
/**
* Return whether or not the queue calls keep on enqueued frames
* \return keeping frames
*/
bool keep_frames() const { return _keep; }
/**
* Provide a getter for underlying rs2_frame_queue object. Used to invoke C-API that require C-type parameters in signature
* \return keeping frames
*/
std::shared_ptr get() { return _queue; }
private:
std::shared_ptr _queue;
size_t _capacity;
bool _keep;
};
/**
* Define the processing block flow, inherit this class to generate your own processing_block. Please refer to the viewer class in examples.hpp for a detailed usage example.
*/
class processing_block : public options
{
public:
using options::supports;
/**
* Start the processing block with callback function on_frame to inform the application the frame is processed.
*
* \param[in] on_frame callback function for notifying the frame to be processed is ready.
*/
template
void start(S on_frame)
{
rs2_error* e = nullptr;
rs2_start_processing(get(), new frame_callback(on_frame), &e);
error::handle(e);
}
/**
* Does the same thing as "start" function
*
* \param[in] on_frame address of callback function for noticing the frame to be processed is ready.
* \return address of callback function.
*/
template
S& operator>>(S& on_frame)
{
start(on_frame);
return on_frame;
}
/**
* Ask processing block to process the frame
*
* \param[in] on_frame frame to be processed.
*/
void invoke(frame f) const
{
rs2_frame* ptr = nullptr;
std::swap(f.frame_ref, ptr);
rs2_error* e = nullptr;
rs2_process_frame(get(), ptr, &e);
error::handle(e);
}
/**
* constructor with already created low level processing block assigned.
*
* \param[in] block - low level rs2_processing_block created before.
*/
processing_block(std::shared_ptr block)
: options((rs2_options*)block.get()), _block(block)
{
}
/**
* constructor with callback function on_frame in rs2_frame_processor_callback structure assigned.
*
* \param[in] processing_function - function pointer of on_frame function in rs2_frame_processor_callback structure, which will be called back by invoke function .
*/
template
processing_block(S processing_function)
{
rs2_error* e = nullptr;
_block = std::shared_ptr(
rs2_create_processing_block(new frame_processor_callback(processing_function), &e),
rs2_delete_processing_block);
options::operator=(_block);
error::handle(e);
}
operator rs2_options*() const { return (rs2_options*)get(); }
rs2_processing_block* get() const { return _block.get(); }
/**
* Check if a specific camera info field is supported.
* \param[in] info the parameter to check for support
* \return true if the parameter both exists and well-defined for the specific processing_block
*/
bool supports(rs2_camera_info info) const
{
rs2_error* e = nullptr;
auto is_supported = rs2_supports_processing_block_info(_block.get(), info, &e);
error::handle(e);
return is_supported > 0;
}
/**
* Retrieve camera specific information, like versions of various internal components.
* \param[in] info camera info type to retrieve
* \return the requested camera info string, in a format specific to the processing_block model
*/
const char* get_info(rs2_camera_info info) const
{
rs2_error* e = nullptr;
auto result = rs2_get_processing_block_info(_block.get(), info, &e);
error::handle(e);
return result;
}
protected:
void register_simple_option(rs2_option option_id, option_range range) {
rs2_error * e = nullptr;
rs2_processing_block_register_simple_option(_block.get(), option_id,
range.min, range.max, range.step, range.def, &e);
error::handle(e);
}
std::shared_ptr _block;
};
/**
* Define the filter workflow, inherit this class to generate your own filter. Refer to the viewer class in examples.hpp for a more detailed example.
*/
class filter : public processing_block, public filter_interface
{
public:
/**
* Ask processing block to process the frame and poll the processed frame from internal queue
*
* \param[in] on_frame frame to be processed.
* return processed frame
*/
rs2::frame process(rs2::frame frame) const override
{
invoke(frame);
rs2::frame f;
if (!_queue.poll_for_frame(&f))
throw std::runtime_error("Error occured during execution of the processing block! See the log for more info");
return f;
}
/**
* constructor with already created low level processing block assigned.
*
* \param[in] block - low level rs2_processing_block created before.
*/
filter(std::shared_ptr block, int queue_size = 1)
: processing_block(block),
_queue(queue_size)
{
start(_queue);
}
/**
* constructor with callback function on_frame in rs2_frame_processor_callback structure assigned.
*
* \param[in] processing_function - function pointer of on_frame function in rs2_frame_processor_callback structure, which will be called back by invoke function .
*/
template
filter(S processing_function, int queue_size = 1) :
processing_block(processing_function),
_queue(queue_size)
{
start(_queue);
}
frame_queue get_queue() { return _queue; }
rs2_processing_block* get() const { return _block.get(); }
template
bool is() const
{
T extension(*this);
return extension;
}
template
T as() const
{
T extension(*this);
return extension;
}
operator bool() const { return _block.get() != nullptr; }
protected:
frame_queue _queue;
};
/**
* Generates 3D point clouds based on a depth frame. Can also map textures from a color frame.
*/
class pointcloud : public filter
{
public:
/**
* create pointcloud instance
*/
pointcloud() : filter(init(), 1) {}
pointcloud(rs2_stream stream, int index = 0) : filter(init(), 1)
{
set_option(RS2_OPTION_STREAM_FILTER, float(stream));
set_option(RS2_OPTION_STREAM_INDEX_FILTER, float(index));
}
/**
* Generate the pointcloud and texture mappings of depth map.
*
* \param[in] depth - the depth frame to generate point cloud and texture.
* \return points instance.
*/
points calculate(frame depth) const
{
auto res = process(depth);
if (res.as())
return res;
if (auto set = res.as