Example of Building a Simple Pipeline
The code sample Metavision Filtering found in the Core module will be used here to show how to build a simple pipeline with linear connections. We don’t implement any custom stage in this sample: this example includes only stages that already exist in Metavision SDK.
The sample is aimed at acquiring events data (from a live camera or an event file), applying filters on them (ROI and polarity filters), generating a frame out of filtered data and displaying this frame.
The pipeline can be represented by this graph:
The sample includes only the main function that creates the pipeline and connects the stages:
int main(int argc, char *argv[]) {
std::string event_file_path;
const std::string short_program_desc("Code sample showing how the pipeline utility can be used to "
"create a simple application to filter and display events.\n");
const std::string long_program_desc(short_program_desc + "Available keyboard options:\n"
" - r - toggle the ROI filter algorithm\n"
" - p - show only events of positive polarity\n"
" - n - show only events of negative polarity\n"
" - a - show all events\n"
" - q - quit the application\n");
po::options_description options_desc("Options");
// clang-format off
options_desc.add_options()
("help,h", "Produce help message.")
("input-event-file,i", po::value<std::string>(&event_file_path), "Path to input event file (RAW or HDF5). If not specified, the camera live stream is used.")
;
// clang-format on
po::variables_map vm;
try {
po::store(po::command_line_parser(argc, argv).options(options_desc).run(), vm);
po::notify(vm);
} catch (po::error &e) {
MV_LOG_ERROR() << short_program_desc;
MV_LOG_ERROR() << options_desc;
MV_LOG_ERROR() << "Parsing error:" << e.what();
return 1;
}
if (vm.count("help")) {
MV_LOG_INFO() << short_program_desc;
MV_LOG_INFO() << options_desc;
return 0;
}
MV_LOG_INFO() << long_program_desc;
// A pipeline for which all added stages will automatically be run in their own processing threads (if applicable)
Metavision::Pipeline p(true);
// Construct a camera from a recording or a live stream
Metavision::Camera cam;
if (!event_file_path.empty()) {
cam = Metavision::Camera::from_file(event_file_path);
} else {
cam = Metavision::Camera::from_first_available();
}
const unsigned short width = cam.geometry().width();
const unsigned short height = cam.geometry().height();
/// Pipeline
//
// 0 (Camera) -->-- 1 (ROI) -->-- 2 (Polarity) -->-- 3 (Frame Generation) -->-- 4 (Display)
//
// 0) Stage producing events from a camera
auto &cam_stage = p.add_stage(std::make_unique<Metavision::CameraStage>(std::move(cam)));
// 1) Stage wrapping an ROI filter algorithm
auto &roi_stage = p.add_algorithm_stage(
std::make_unique<Metavision::RoiFilterAlgorithm>(150, 150, width - 150, height - 150, false), cam_stage, false);
// 2) Stage wrapping a polarity filter algorithm
auto &pol_stage = p.add_algorithm_stage(std::make_unique<Metavision::PolarityFilterAlgorithm>(0), roi_stage, false);
// 3) Stage generating a frame from filtered events using accumulation time of 30ms
auto &frame_stage = p.add_stage(std::make_unique<Metavision::FrameGenerationStage>(width, height, 30), pol_stage);
// 4) Stage displaying the frame
auto &disp_stage =
p.add_stage(std::make_unique<Metavision::FrameDisplayStage>("CD events", width, height), frame_stage);
disp_stage.set_key_callback([&](Metavision::UIKeyEvent key, int scancode, Metavision::UIAction action, int mods) {
if (action == Metavision::UIAction::RELEASE) {
switch (key) {
case Metavision::UIKeyEvent::KEY_A:
// show all events
pol_stage.set_enabled(false);
break;
case Metavision::UIKeyEvent::KEY_N:
// show only negative events
pol_stage.set_enabled(true);
pol_stage.algo().set_polarity(0);
break;
case Metavision::UIKeyEvent::KEY_P:
// show only positive events
pol_stage.set_enabled(true);
pol_stage.algo().set_polarity(1);
break;
case Metavision::UIKeyEvent::KEY_R:
// toggle ROI filter
roi_stage.set_enabled(!roi_stage.is_enabled());
break;
}
}
});
// Run the pipeline
p.run();
return 0;
}
Instantiating a Pipeline
First, we instantiate the Pipeline
using Pipeline::Pipeline(bool auto_detach)
constructor:
Metavision::Pipeline p(true);
We pass auto_detach
argument as true to make the pipeline run all stages in their own processing threads.
Adding Stages to the Pipeline
Once the pipeline is instantiated, we add stages to it.
The first stage is a CameraStage
, which is used to produce CD events from a camera or a RAW file.
This stage is added to the pipeline by calling
Pipeline::add_stage(std::unique_ptr<Stage> &&stage)
function. Also, we pass the Camera
instance (that is already instantiated in this
example) using std::move(cam) to transfer the ownership of the camera smart pointer:
// 0) Stage producing events from a camera
auto &cam_stage = p.add_stage(std::make_unique<Metavision::CameraStage>(std::move(cam)));
Next, we add the RoiFilterAlgorithm
to apply ROI-filtering and acquire
only events in the given Region Of Interest:
// 1) Stage wrapping an ROI filter algorithm
auto &roi_stage = p.add_algorithm_stage(
std::make_unique<Metavision::RoiFilterAlgorithm>(150, 150, width - 150, height - 150, false), cam_stage, false);
Here, we use
Pipeline::add_algorithm_stage(std::unique_ptr<Algorithm> &&algo, BaseStage &prev_stage, bool enabled = true)
function to add the stage, as we are adding a synchronous algorithm to the pipeline.
This stage is deliberately disabled here by setting enabled argument to false.
In the main loop, you can see how it can be dynamically activated, by pressing “r” key on keyboard when the sample is
running.
Then, we add PolarityFilterAlgorithm
to filter events and keep only
events of the given polarity. Also this stage is deliberately disabled here by setting enabled argument to false
and can be activated dynamically by pressing the “n” or “p” keys on the keyboard (“p” key will keep only
events with positive polarity and remove other events, and “n” key will keep only events with negative polarity).
91 92 | // 2) Stage wrapping a polarity filter algorithm auto &pol_stage = p.add_algorithm_stage(std::make_unique<Metavision::PolarityFilterAlgorithm>(0), roi_stage, false); |
Then, we add FrameGenerationStage
to generate a frame from events.
94 95 | // 3) Stage generating a frame from filtered events using accumulation time of 30ms auto &frame_stage = p.add_stage(std::make_unique<Metavision::FrameGenerationStage>(width, height, 30), pol_stage); |
Finally, as the last stage, we add FrameDisplayStage
to display the frame
on the screen.
97 98 99 | // 4) Stage displaying the frame auto &disp_stage = p.add_stage(std::make_unique<Metavision::FrameDisplayStage>("CD events", width, height), frame_stage); |
Running the Pipeline
Finally, when the pipeline is set up, it is started by calling
Pipeline::run()
from the main thread,
which will keep running until completion or until the pipeline is cancelled.
p.run();
After this first example, you can move to a more complex example of Pipeline.