Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

port tests post-processing-from bag to python #12402

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion unit-tests/live/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ set(PP_Rosbag_Recordings_URL https://librealsense.intel.com/rs-tests/Rosbag_unit
# for rec-play/test-non-realtime.py
dl_file( ${PP_Rosbag_Recordings_URL} recordings recording_deadlock.bag OFF )

# for rec-play/test-playback-stress.py
# for rec-play/test-playback-stress.py and for post-processing/post-processing-from-bag.py
dl_file( ${PP_Rosbag_Recordings_URL} recordings all_combinations_depth_color.bag OFF )

# for post-processing/post-processing-from-bag.py
dl_file( ${PP_Rosbag_Recordings_URL} recordings [aligned_2c]_all_combinations_depth_color.bag OFF )

# for post-processing/post-processing-from-bag.py
dl_file( ${PP_Rosbag_Recordings_URL} recordings [aligned_2d]_all_combinations_depth_color.bag OFF )


119 changes: 119 additions & 0 deletions unit-tests/post-processing/test-post-processing-from-bag.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# License: Apache 2.0. See LICENSE file in root directory.
# Copyright(c) 2023 Intel Corporation. All Rights Reserved.

#temporary fix to prevent the test from running on Win_SH_Py_DDS_CI
#test:donotrun:dds

import pyrealsense2 as rs
from rspy import test, repo
import os.path
import time
################################################################################################


def playback_callback(status):
global playback_status
playback_status = status


def validate_ppf_results(result_frame_data, reference_frame_data):
result_bytearray_frame_data, result_profile = result_frame_data
reference_bytearray_frame_data, reference_profile = reference_frame_data

test.check_equal(result_profile.width(), reference_profile.width())
test.check_equal(result_profile.height(), reference_profile.height())
test.check_equal_lists(result_bytearray_frame_data,reference_bytearray_frame_data)


def process_frame(frame, frame_source):
maloel marked this conversation as resolved.
Show resolved Hide resolved
global pre_processed_frames_map
sensor_name = rs.sensor.from_frame(frame).get_info(rs.camera_info.name)
if sensor_name in pre_processed_frames_map:
pre_processed_frames_map[sensor_name].append(frame)
else:
pre_processed_frames_map[sensor_name] = [frame]

if len(pre_processed_frames_map[sensor_name]) == 2:
frameset = frame_source.allocate_composite_frame(pre_processed_frames_map[sensor_name])
fs_processed = process_frame_callback(frameset.as_frameset())
fs_processed_data = bytearray(fs_processed.get_data())
fs_processed_profile = fs_processed.get_profile().as_video_stream_profile()
frames_data_map[sensor_name] = (fs_processed_data, fs_processed_profile)


def load_ref_frames_to_map(frame):
if len(frames_data_map) < len(sensors):
sensor_name = rs.sensor.from_frame(frame).get_info(rs.camera_info.name)
frames_data_map[sensor_name] = (bytearray(frame.get_data()), frame.get_profile().as_video_stream_profile())


def get_frames(callback):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this function, but not with the frame-list logic: it makes it less generic. Why should it know a frames_data_map, etc.? That logic should be in the caller of playback_file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

for s in sensors:
s.open(s.get_stream_profiles())

for s in sensors:
s.start(callback)

while playback_status != rs.playback_status.stopped:
time.sleep(0.25)

for s in sensors:
s.stop()

for s in sensors:
s.close()


def playback_file(file, callback):
global playback_status, sensors
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like the sensors as global here...

playback_status = None
filename = os.path.join(repo.build, 'unit-tests', 'recordings', file)
ctx = rs.context()

dev = ctx.load_device(filename)
dev.set_real_time(False)
dev.set_status_changed_callback(playback_callback)

sensors = dev.query_sensors()

get_frames(callback)


def compare_processed_frames_vs_recorded_frames(file):
# we need processing_block in order to have frame_source.
# frame_source is used to composite frames (by calling allocate_composite_frames function).
global frames_data_map, pre_processed_frames_map, sensors
frame_processor = rs.processing_block(process_frame)
frames_data_map = {}
pre_processed_frames_map = {}
sensors = []
playback_file('all_combinations_depth_color.bag', lambda frame: (frame_processor.invoke(frame)))
processed_frames_data_list = []
for sf in frames_data_map:
processed_frames_data_list.append(frames_data_map[sf])

frames_data_map = {}
playback_file(file, lambda frame: load_ref_frames_to_map(frame))
ref_frame_data_list = []
for sf in frames_data_map:
ref_frame_data_list.append(frames_data_map[sf])

test.check_equal(len(processed_frames_data_list),len(ref_frame_data_list))

for i in range(len(processed_frames_data_list)):
validate_ppf_results(processed_frames_data_list[i], ref_frame_data_list[i])

################################################################################################
with test.closure("Test align depth to color from recording"):
align = rs.align(rs.stream.color)
process_frame_callback = lambda fs: align.process(fs).first_or_default(rs.stream.depth)

compare_processed_frames_vs_recorded_frames("[aligned_2c]_all_combinations_depth_color.bag")
################################################################################################
with test.closure("Test align color to depth from recording"):
align = rs.align(rs.stream.depth)
process_frame_callback = lambda fs: align.process(fs).first_or_default(rs.stream.color)

compare_processed_frames_vs_recorded_frames("[aligned_2d]_all_combinations_depth_color.bag")
################################################################################################
test.print_results_and_exit()
9 changes: 6 additions & 3 deletions unit-tests/py/rspy/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@
#
build = os.path.join( root, 'build' )
if not os.path.isdir( build ):
log.w( 'repo.build directory wasn\'t found' )
log.d( 'repo.root=', root )
build = None
# Under GHA, we use a build directory under this env variable:
build = os.environ.get( 'WIN_BUILD_DIR' )
if not build or not os.path.isdir( build ):
log.w( 'repo.build directory wasn\'t found' )
log.d( 'repo.root=', root )
build = None


def find_pyrs():
Expand Down
2 changes: 1 addition & 1 deletion wrappers/python/pyrs_frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ void init_frame(py::module &m) {
}
}
else
return BufData(const_cast<void*>(self.get_data()), 1, std::string("@B"), 0); };
return BufData(const_cast<void*>(self.get_data()), 1, std::string("@B"), self.get_data_size()); };

/* rs_frame.hpp */
py::class_<rs2::stream_profile> stream_profile(m, "stream_profile", "Stores details about the profile of a stream.");
Expand Down
4 changes: 2 additions & 2 deletions wrappers/python/pyrs_processing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ void init_processing(py::module &m) {

py::class_<rs2::processing_block, rs2::options> processing_block(m, "processing_block", "Define the processing block workflow, inherit this class to "
"generate your own processing_block.");
processing_block.def(py::init([](std::function<void(rs2::frame, rs2::frame_source&)> processing_function) {
return new rs2::processing_block(processing_function);
processing_block.def(py::init([](std::function<void(rs2::frame, rs2::frame_source*)> processing_function) {
maloel marked this conversation as resolved.
Show resolved Hide resolved
return new rs2::processing_block([=](rs2::frame f, rs2::frame_source& fs) {processing_function(f, &fs); });
}), "processing_function"_a)
.def("start", [](rs2::processing_block& self, std::function<void(rs2::frame)> f) {
self.start(f);
Expand Down
14 changes: 8 additions & 6 deletions wrappers/python/pyrs_sensor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,13 +53,13 @@ void init_sensor(py::module &m) {
.def("close", &rs2::sensor::close, "Close sensor for exclusive access.", py::call_guard<py::gil_scoped_release>())
.def("start", [](const rs2::sensor& self, std::function<void(rs2::frame)> callback) {
self.start(callback);
}, "Start passing frames into user provided callback.", "callback"_a)
}, "Start passing frames into user provided callback.", "callback"_a,py::call_guard< py::gil_scoped_release >())
maloel marked this conversation as resolved.
Show resolved Hide resolved
.def("start", [](const rs2::sensor& self, rs2::syncer& syncer) {
self.start(syncer);
}, "Start passing frames into user provided syncer.", "syncer"_a)
}, "Start passing frames into user provided syncer.", "syncer"_a, py::call_guard< py::gil_scoped_release >())
.def("start", [](const rs2::sensor& self, rs2::frame_queue& queue) {
self.start(queue);
}, "start passing frames into specified frame_queue", "queue"_a)
}, "start passing frames into specified frame_queue", "queue"_a, py::call_guard< py::gil_scoped_release >())
.def("stop", &rs2::sensor::stop, "Stop streaming.", py::call_guard<py::gil_scoped_release>())
.def("get_stream_profiles", &rs2::sensor::get_stream_profiles, "Retrieves the list of stream profiles supported by the sensor.")
.def("get_active_streams", &rs2::sensor::get_active_streams, "Retrieves the list of stream profiles currently streaming on the sensor.")
Expand Down Expand Up @@ -91,9 +91,11 @@ void init_sensor(py::module &m) {
ss << ": \"" << self.get_info( RS2_CAMERA_INFO_NAME ) << "\"";
ss << ">";
return ss.str();
} );
// rs2::sensor_from_frame [frame.def("get_sensor", ...)?
// rs2::sensor==sensor?
} )
.def_static("from_frame", [](rs2::frame frame) {
auto sptr = rs2::sensor_from_frame(frame);
return *sptr;
}, "frame"_a);

py::class_<rs2::roi_sensor, rs2::sensor> roi_sensor(m, "roi_sensor"); // No docstring in C++
roi_sensor.def(py::init<rs2::sensor>(), "sensor"_a)
Expand Down
Loading