Construct Frameset Object
Hi Realsense Support,
I am running a D435 in single shot mode (activate the sensor, get and image, deactivate the sensor). The first couple of framesets arriving from the sensor do either not contain both requested stream types (RGB and Depth in my case) or contain frames with a timestamp difference being to large to merge them into a frameset automatically. This is causing a delay that I am trying to avoid.
Since there does not seem to be a way to construct a frameset object manually I was looking at the software sensor feature in the SDK. I implemented a function based on this example (https://github.com/IntelRealSense/librealsense/tree/master/examples/software-device). It acquires all images incoming images and constructs a frameset as soon as all requested frames (RGB & Depth) have arrived.
The function looks like this:
void constructFrameset(std::vector<rs2::frame> frames, rs2::frameset &fs)
{
rs2::software_device device_sw;
device_sw.create_matcher(RS2_MATCHER_DLR_C);
rs2::syncer sync;
for(auto frames_iter = frames.begin(); frames_iter != frames.end(); ++frames_iter)
{
sensor_sw = device_sw.add_sensor(rs2_stream_to_string(frames_iter->get_profile().stream_type()));
//create rs2_video_stream using the info from the frame
rs2_video_stream video_stream_synth
{
frame_info taken from frame_iter, stream_type, stream_index, stream_unique_id, etc.
};
sensor_sw.add_video_stream(video_stream_synth);
sensor_sw.open(video_stream_synth);
sensor_sw.start(sync);
//create rs2_software_video_frame using the data from the frame
void* data = const_cast<void*>(frames_iter->get_data());
rs2_software_video_frame sw_video_frame
{
data,
[](void*){},
frames_iter->as<rs2::video_frame>().get_stride_in_bytes(),
frames_iter->as<rs2::video_frame>().get_bytes_per_pixel(),
time_stamp,
frames_iter->as<rs2::video_frame>().get_frame_timestamp_domain(),
frames_iter->as<rs2::video_frame>().get_frame_number(),
video_stream_synth
};
sensor_sw.on_video_frame(sw_video_frame);
}
fs = sync.wait_for_frames();
}
If I provide a Depth and a RGB frame to the function two sensors (RGB and Depth) are created. I confirmed that the frames input to the function are valid. The frameset being created by the syncer, however, only contains a single frame of the type that was added last in the loop (Depth frame first in the frames vector results in a frameset containing only a color frame of size 0, whereas Color frame first in the frames vector results in a frameset containing only a depth frame of size 0).
What am I missing/doing wrong?
-
When capture is activated, if you have auto-exposure enabled then the first several frames are spent settling down the auto-exposure values. You can either disable auto-exposure and use your own exposure values, or use code to tell the camera to skip the first frames by using a 'for' instruction to wait until there is a complete frameset available.
https://github.com/IntelRealSense/librealsense/issues/2269#issuecomment-414211209
-
Hi everyone,
I am also having this kind of problem. The solution provided by MartyG was not very helpful (rs2::software_sensor does not support auto exposure as an option), so I wonder if anyone can provide a better answer.
Have you found a valid solution to this problem, Frank?
I also wrote a program that is based on the software_device example mentioned by Frank (https://github.com/IntelRealSense/librealsense/tree/master/examples/software-device), and my program's structure is very similar to the main function of that example. What I am trying to do is read two images (a depth image encoded as Z16 and a color image encoded as BGR8) from the file system, feed them into their respective sensors, then call sync.wait_for_frames(). What perplexes me is that no matter how many loops I run the frameset I received always only contain 1 frame, and that is the depth frame. The color frame just would not show up. Here is my code:
#include <iostream>
#include <opencv2/opencv.hpp>
#include <librealsense2/rs.hpp>
#include <librealsense2/hpp/rs_internal.hpp>
double W = 640;
double H = 480;
int main() {
rs2::software_device dev;
auto depth_sensor = dev.add_sensor("Depth");
auto color_sensor = dev.add_sensor("Color");
rs2_intrinsics depth_intrinsics{W, H, W / 2, H / 2, 383.859, 383.859, RS2_DISTORTION_BROWN_CONRADY,
{0, 0, 0, 0, 0}};
rs2_intrinsics color_intrinsics{W, H, W / 2, H / 2, 618.212, 618.463, RS2_DISTORTION_INVERSE_BROWN_CONRADY,
{0, 0, 0, 0, 0}};
auto depth_stream = depth_sensor.add_video_stream(
{RS2_STREAM_DEPTH, 0, 0, W, H, 60, 2, RS2_FORMAT_Z16, depth_intrinsics});
auto color_stream = color_sensor.add_video_stream(
{RS2_STREAM_COLOR, 0, 1, W, H, 60, 3, RS2_FORMAT_BGR8, color_intrinsics});
dev.create_matcher(RS2_MATCHER_DEFAULT);
rs2::syncer sync;
depth_sensor.open(depth_stream);
color_sensor.open(color_stream);
depth_sensor.start(sync);
color_sensor.start(sync);
depth_stream.register_extrinsics_to(color_stream, {{1, 0, 0, 0, 1, 0, 0, 0, 1},
{0, 0, 0}});
rs2::frameset fs;
cv::Mat color_image;
cv::Mat depth_image;
//stream in the images
for (int idx = 0; idx < 120; ++idx) {
color_image = cv::imread("/home/username/Desktop/images/color.jpg", cv::IMREAD_COLOR);
depth_image = cv::imread("/home/username/Desktop/images/depth.png", cv::IMREAD_UNCHANGED);
color_sensor.on_video_frame({(void*) color_image.data, // Frame pixels from capture API
[](void*) {}, // Custom deleter (if required)
3 * 640, 3, // Stride and Bytes-per-pixel
(double) (idx) / 60, RS2_TIMESTAMP_DOMAIN_COUNT,
idx, // Timestamp, Frame# for potential sync services
color_stream});
depth_sensor.on_video_frame({(void*) depth_image.data, // Frame pixels from capture API
[](void*) {}, // Custom deleter (if required)
2 * 640, 2, // Stride and Bytes-per-pixel
(double) (idx) / 60, RS2_TIMESTAMP_DOMAIN_COUNT,
idx, // Timestamp, Frame# for potential sync services
depth_stream});
fs = sync.wait_for_frames();
}
return 0;
}Can anyone PLEASE give me some explanation?
-
Hi everybody,
This problem still persists in 2021, but I can propose a simple workaround.
I am also trying to load frames (rgb + depth) from disk and then process them using the standard pipeline by creating frames in a software device. Sadly, the rs-software-device example is not fully functional and fails because the syncer fails to consistently extract the constructed frames.
I do this particularly because I want to do expensive processing after recording at a separate time and am loading frames from raw byte data.
The syncer likes to throw a tantrum particularly when the simulated timestamps don't match the FPS of the software sensor. @Asfrga this is a problem in the code you posted. That's why the example multiplies `ind` with 16, which is roughly 1000/60 (unit milliseconds).
I am not sure under the hood what is preventing the syncer from extracting the frames correctly, I think it has to do with timestamps.
Here is my original implementation:
#include <librealsense2/rs.hpp>
#include <librealsense2/hpp/rs_internal.hpp>
#include <iostream>
#include <vector>
#include <csignal>
#include <unistd.h>
struct synthframe{
int W, H, B;
std::vector<uint8_t> dat;
};
const int CW = 960; //Color Frame
const int CH = 540;
const int CB = 4;
const int DW = 640; //Depth Frame
const int DH = 480;
const int DB = 2;
const rs2_intrinsics intrDepth = { DW, DH,
324.83203125, 254.3828125,
456.63671875 , 457.02734375 ,
RS2_DISTORTION_NONE ,
{ 0,0,0,0,0 } };
const rs2_intrinsics intrColor = { CW, CH,
488.421417236328, 268.80029296875,
673.101379394531, 673.328247070312,
RS2_DISTORTION_INVERSE_BROWN_CONRADY ,
{ 0.142735555768013, -0.466687560081482, -0.000759649323299527, 0.000336397089995444, 0.420803397893906 } };
synthframe cframe; //Color-Frame
synthframe dframe; //Depth-Frame
synthframe& getColor(){
//...
return cframe;
}
synthframe& getDepth(){
//...
return dframe;
}
bool isalive = true;
void sighandler(int signal){
isalive = false;
}
int main(int argc, char * argv[]) try {
signal(SIGINT, &sighandler);
rs2::software_device dev; // Create software-only device
dev.create_matcher(RS2_MATCHER_DEFAULT);
auto depth_sensor = dev.add_sensor("Depth"); // Define single sensor
auto color_sensor = dev.add_sensor("Color"); // Define single sensor
auto depth_stream = depth_sensor.add_video_stream({RS2_STREAM_DEPTH, 0, 0,
DW, DH, 100, DB, RS2_FORMAT_Z16, intrDepth });
depth_sensor.add_read_only_option(RS2_OPTION_DEPTH_UNITS, 0.00025f);
auto color_stream = color_sensor.add_video_stream({RS2_STREAM_COLOR, 1, 1,
CW, CH, 100, CB, RS2_FORMAT_RGBA8, intrColor });
dev.create_matcher(RS2_MATCHER_COUNT);
color_stream.register_extrinsics_to(depth_stream, {
{ 0.999966, -0.00700524, 0.00429877,
0.00689519, 0.999661, 0.0251015,
-0.00447315, -0.025071, 0.999676 },
{-0.000823423033580184, -0.0139116607606411, 0.00651831179857254 } });
depth_stream.register_extrinsics_to(color_stream, {
{ 0.999966 , 0.00689519 , -0.00447315,
-0.00700524 , 0.999661 , -0.025071,
0.00429877 , 0.0251015 , 0.999676 },
{0.000697920040693134, 0.0137490043416619, -0.00686865951865911 } });
depth_sensor.open(depth_stream);
color_sensor.open(color_stream);
int ind = 0;
rs2::syncer sync(2);
color_sensor.start(sync);
depth_sensor.start(sync);
cframe = getColor();
dframe = getDepth();
while(isalive){
color_sensor.on_video_frame({ cframe.dat.data(), // Frame pixels from capture API
[](void*) {cframe.dat.clear();}, // Custom deleter (if required)
CW*CB, CB, // Stride and Bytes-per-pixel
(double)ind*10.0, RS2_TIMESTAMP_DOMAIN_HARDWARE_CLOCK, ind,
color_stream });
depth_sensor.on_video_frame({ dframe.dat.data(), // Frame pixels from capture API
[](void*) {dframe.dat.clear();}, // Custom deleter (if required)
DW*DB, DB, // Stride and Bytes-per-pixel
(double)ind*10.0, RS2_TIMESTAMP_DOMAIN_HARDWARE_CLOCK, ind,
depth_stream });
usleep(100000); //!!!!!!!!
ind++;
rs2::frameset fset;
try{
fset = sync.wait_for_frames();
}
catch(rs2::error e){
std::cout<<e.what()<<std::endl;
}
std::cout<<"Found "<<fset.size()<<" frames"<<std::endl;
if(fset.size() == 1){
std::cout<<"Only Found 1 Frame "<<fset[0].get_profile().stream_type()<<std::endl;
continue;
}
else{
cframe = getColor();
dframe = getDepth();
}
}
return EXIT_SUCCESS;
}
catch (const rs2::error & e){
std::cerr << "RealSense error calling " << e.get_failed_function() << "(" << e.get_failed_args() << "):\n " << e.what() << std::endl;
return EXIT_FAILURE;
}
catch (const std::exception& e){
std::cerr << e.what() << std::endl;
return EXIT_FAILURE;
}Note that adding usleep(1E5) or processing the frames makes the syncer fail.
Solution:
Don't use the syncer. Use a frame queue with a custom bundler as a processing block. Replace this:
rs2::syncer sync(2);
color_sensor.start(sync);
depth_sensor.start(sync);with this:
//Prepare frame-set getter (the frame queue!)
rs2::frame_queue q(2);
std::vector<rs2::frame> bundle;
rs2::processing_block bundler([&](rs2::frame f, rs2::frame_source& src) {
bundle.push_back(f); //add frame to vector
if (bundle.size() == 2){ //or however many when full
auto fs = src.allocate_composite_frame(bundle);
src.frame_ready(fs);
bundle.clear();
}
});
bundler.start(q); //when enough frames are loaded, the queue is ready
depth_sensor.start([&](rs2::frame f){
bundler.invoke(f); //add frame to bundler
});
color_sensor.start([&](rs2::frame f){
bundler.invoke(f);
});Then it stops caring about the timestamp syncing and processing time and just constructs the frameset for you so you can use them.
Please sign in to leave a comment.
Comments
3 comments