Skip to content

Commit

Permalink
Add y16 10bit data handling to DDS streams
Browse files Browse the repository at this point in the history
  • Loading branch information
OhadMeir committed Jan 22, 2025
1 parent 7d1fb18 commit 70c6f49
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 27 deletions.
14 changes: 13 additions & 1 deletion src/dds/rs-dds-sensor-proxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <src/context.h>

#include <src/proc/color-formats-converter.h>
#include <src/proc/y16_10msb-to-y16.h>

#include <rsutils/string/nocase.h>
#include <rsutils/json.h>
Expand Down Expand Up @@ -171,11 +172,22 @@ void dds_sensor_proxy::register_basic_converters()
{ RS2_FORMAT_Y8, RS2_STREAM_INFRARED, 1 },
{ RS2_FORMAT_Y8, RS2_STREAM_INFRARED, 2 } },
[]() { return std::make_shared< identity_processing_block >(); } } );
std::string product_line = get_device().get_info( RS2_CAMERA_INFO_PRODUCT_LINE );
bool d400 = product_line.find("D400") != std::string::npos;
_formats_converter.register_converter(
{ { { RS2_FORMAT_Y16, RS2_STREAM_INFRARED } },
{ { RS2_FORMAT_Y16, RS2_STREAM_INFRARED, 1 },
{ RS2_FORMAT_Y16, RS2_STREAM_INFRARED, 2 } },
[]() { return std::make_shared< identity_processing_block >(); } } );
[d400]() -> std::shared_ptr< stream_filter_processing_block >
{
// Y16 is calibration format, sent with 10bit data that needs conversion to 16bit.
// D400 products don't have DDS so we use rs-dds-adapter that already converts.
// Calibration with other products that use rs-dds-adapter is currently not supported.
if( d400 )
return std::make_shared< identity_processing_block >();

return std::make_shared< y16_10msb_to_y16 >();
} } );

// Motion
_formats_converter.register_converter( processing_block_factory::create_id_pbf( RS2_FORMAT_COMBINED_MOTION, RS2_STREAM_MOTION ) );
Expand Down
4 changes: 2 additions & 2 deletions src/ds/d500/d500-device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

#include "proc/depth-formats-converter.h"
#include "proc/y8i-to-y8y8.h"
#include "proc/y16i-to-y10msby10msb.h"
#include "proc/y16i-10msb-to-y16y16.h"

#include <rsutils/type/fourcc.h>
using rs_fourcc = rsutils::type::fourcc;
Expand Down Expand Up @@ -516,7 +516,7 @@ namespace librealsense
depth_sensor.register_processing_block(
{ RS2_FORMAT_Y16I },
{ {RS2_FORMAT_Y16, RS2_STREAM_INFRARED, 1}, {RS2_FORMAT_Y16, RS2_STREAM_INFRARED, 2} },
[]() {return std::make_shared<y16i_to_y10msby10msb>(); }
[]() {return std::make_shared<y16i_10msb_to_y16y16>(); }
);

pid_hex_str = rsutils::string::from() << std::uppercase << rsutils::string::hexdump( _pid );
Expand Down
6 changes: 4 additions & 2 deletions src/proc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ target_sources(${LRS_TARGET}
"${CMAKE_CURRENT_LIST_DIR}/y8i-to-y8y8.cpp"
"${CMAKE_CURRENT_LIST_DIR}/y12i-to-y16y16.cpp"
"${CMAKE_CURRENT_LIST_DIR}/y12i-to-y16y16-mipi.cpp"
"${CMAKE_CURRENT_LIST_DIR}/y16i-to-y10msby10msb.cpp"
"${CMAKE_CURRENT_LIST_DIR}/y16i-10msb-to-y16y16.cpp"
"${CMAKE_CURRENT_LIST_DIR}/y16_10msb-to-y16.cpp"
"${CMAKE_CURRENT_LIST_DIR}/identity-processing-block.cpp"
"${CMAKE_CURRENT_LIST_DIR}/threshold.cpp"
"${CMAKE_CURRENT_LIST_DIR}/rates-printer.cpp"
Expand Down Expand Up @@ -61,7 +62,8 @@ target_sources(${LRS_TARGET}
"${CMAKE_CURRENT_LIST_DIR}/y8i-to-y8y8.h"
"${CMAKE_CURRENT_LIST_DIR}/y12i-to-y16y16.h"
"${CMAKE_CURRENT_LIST_DIR}/y12i-to-y16y16-mipi.h"
"${CMAKE_CURRENT_LIST_DIR}/y16i-to-y10msby10msb.h"
"${CMAKE_CURRENT_LIST_DIR}/y16i-10msb-to-y16y16.h"
"${CMAKE_CURRENT_LIST_DIR}/y16_10msb-to-y16.h"
"${CMAKE_CURRENT_LIST_DIR}/identity-processing-block.h"
"${CMAKE_CURRENT_LIST_DIR}/threshold.h"
"${CMAKE_CURRENT_LIST_DIR}/rates-printer.h"
Expand Down
36 changes: 36 additions & 0 deletions src/proc/y16_10msb-to-y16.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2022 Intel Corporation. All Rights Reserved.

#include "y16_10msb-to-y16.h"
#include "stream.h"
// TODO - CUDA
//#ifdef RS2_USE_CUDA
//#include "cuda/cuda-conversion.cuh"
//#endif

namespace librealsense
{
y16_10msb_to_y16::y16_10msb_to_y16() : functional_processing_block( "Y16 10msb to Y16 Transform", RS2_FORMAT_Y16, RS2_STREAM_INFRARED )
{
}

void y16_10msb_to_y16::process_function( uint8_t * const dest[], const uint8_t * source, int width, int height, int actual_size, int input_size)
{
// TODO - CUDA
// #ifdef RS2_USE_CUDA
// rscuda::y16_from_y16_10msb_cuda( dest, count, reinterpret_cast< const uint16_t * >( source ) );
// #else
uint16_t * dest_16_bit = reinterpret_cast< uint16_t * >( dest[0] );
const uint16_t * source_16_bit = reinterpret_cast< const uint16_t * >( source );
for( size_t i = 0; i < width * height; ++i )
{
// Since the data is received only in 10 bits, and the conversion is to 16 bits,
// the range moves from [0 : 2^10-1] to [0 : 2^16-1], so the values should be converted accordingly:
// x in range [0 : 2^10-1] is converted to y = x * (2^16-1)/(2^10-1) approx = x * (64 + 1/16)
// And x * (64 + 1/16) = x * 64 + x * 1/16 = x << 6 | x >> 4
// This operation is done using shiftings to make it more efficient, and with a non-significant accuracy loss.
dest_16_bit[i] = source_16_bit[i] << 6 | source_16_bit[i] >> 4;
}
// #endif
}
}
19 changes: 19 additions & 0 deletions src/proc/y16_10msb-to-y16.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2025 Intel Corporation. All Rights Reserved.

#pragma once

#include "synthetic-stream.h"

namespace librealsense
{
class y16_10msb_to_y16 : public functional_processing_block
{
public:
y16_10msb_to_y16();

protected:
void process_function( uint8_t * const dest[], const uint8_t * source, int width, int height, int actual_size, int input_size ) override;
};
}

Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// License: Apache 2.0. See LICENSE file in root directory.
// Copyright(c) 2022 Intel Corporation. All Rights Reserved.

#include "y16i-to-y10msby10msb.h"
#include "y16i-10msb-to-y16y16.h"
#include "stream.h"
// CUDA TODO
//#ifdef RS2_USE_CUDA
Expand All @@ -11,38 +11,38 @@
namespace librealsense
{
struct y16i_pixel { uint16_t left : 16, right : 16;
// explanation of "return x << 6 | x >> 4" :
// Explanation of "return x << 6 | x >> 4" :
// Since the data is received only in 10 bits, and the conversion is to 16 bits,
// the range moves from [0 : 2^10-1] to [0 : 2^16-1], so the values should be converted accordingly:
// x is range [0 : 2^10-1] is converted to y = x * (2^16-1)/(2^10-1) approx= x * (64 + 1/16)
// x in range [0 : 2^10-1] is converted to y = x * (2^16-1)/(2^10-1) approx = x * (64 + 1/16)
// And x * (64 + 1/16) = x * 64 + x * 1/16 = x << 6 | x >> 4
// This operation is done using shiftings to make it more efficient, and with a non-significant accuracy loss.
uint16_t l() const { return left << 6 | left >> 4; }
uint16_t r() const { return right << 6 | right >> 4; }
};
void unpack_y10msb_y10msb_from_y16i( uint8_t * const dest[], const uint8_t * source, int width, int height, int actual_size)
void unpack_y16_y16_from_y16i_10msb( uint8_t * const dest[], const uint8_t * source, int width, int height, int actual_size)
{
auto count = width * height;
// CUDA TODO
//#ifdef RS2_USE_CUDA
// rscuda::split_frame_y10msb_y10msb_from_y16i_cuda(dest, count, reinterpret_cast<const y12i_pixel*>(source));
// rscuda::split_frame_y16_16_from_y16i_10msb_cuda(dest, count, reinterpret_cast<const y16i_pixel*>(source));
//#else
split_frame(dest, count, reinterpret_cast<const y16i_pixel*>(source),
[](const y16i_pixel& p) -> uint16_t { return (p.l()); },
[](const y16i_pixel& p) -> uint16_t { return (p.r()); });
//#endif
}

y16i_to_y10msby10msb::y16i_to_y10msby10msb(int left_idx, int right_idx)
: y16i_to_y10msby10msb("Y16I to Y10msbL Y10msbR Transform", left_idx, right_idx) {}
y16i_10msb_to_y16y16::y16i_10msb_to_y16y16(int left_idx, int right_idx)
: y16i_10msb_to_y16y16("Y16I 10msb to Y16L Y16R Transform", left_idx, right_idx) {}

y16i_to_y10msby10msb::y16i_to_y10msby10msb(const char* name, int left_idx, int right_idx)
y16i_10msb_to_y16y16::y16i_10msb_to_y16y16(const char* name, int left_idx, int right_idx)
: interleaved_functional_processing_block(name, RS2_FORMAT_Y16I, RS2_FORMAT_Y16, RS2_STREAM_INFRARED, RS2_EXTENSION_VIDEO_FRAME, 1,
RS2_FORMAT_Y16, RS2_STREAM_INFRARED, RS2_EXTENSION_VIDEO_FRAME, 2)
{}

void y16i_to_y10msby10msb::process_function( uint8_t * const dest[], const uint8_t * source, int width, int height, int actual_size, int input_size)
void y16i_10msb_to_y16y16::process_function( uint8_t * const dest[], const uint8_t * source, int width, int height, int actual_size, int input_size)
{
unpack_y10msb_y10msb_from_y16i(dest, source, width, height, actual_size);
unpack_y16_y16_from_y16i_10msb(dest, source, width, height, actual_size);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@

namespace librealsense
{
class y16i_to_y10msby10msb : public interleaved_functional_processing_block
class y16i_10msb_to_y16y16 : public interleaved_functional_processing_block
{
public:
y16i_to_y10msby10msb(int left_idx = 1, int right_idx = 2);
y16i_10msb_to_y16y16(int left_idx = 1, int right_idx = 2);

protected:
y16i_to_y10msby10msb(const char* name, int left_idx, int right_idx);
y16i_10msb_to_y16y16(const char* name, int left_idx, int right_idx);
void process_function( uint8_t * const dest[], const uint8_t * source, int width, int height, int actual_size, int input_size) override;
};
}
Expand Down
3 changes: 2 additions & 1 deletion unit-tests/dds/formats-conversion-server.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@

device_info = dds.message.device_info.from_json({
"name": "formats-conversion-device",
"topic-root": "root_123"
"topic-root": "root_123",
"product-line": "D400"
})

# Used to created a device_server per test case, but it currently creates problems when creating a second device while
Expand Down
8 changes: 5 additions & 3 deletions unit-tests/dds/test-librs-options.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
participant.init( 123, 'server' )

with test.closure( 'Create the server' ):
device_info = dds.message.device_info()
device_info.name = 'Options device'
device_info.topic_root = 'librs-options/device'
device_info = dds.message.device_info.from_json({
"name": "Options device",
"topic-root": "librs-options/device",
"product-line": "D400"
})
s1p1 = dds.video_stream_profile( 9, dds.video_encoding.rgb, 10, 10 )
s1profiles = [s1p1]
s1 = dds.color_stream_server( 's1', 'sensor' )
Expand Down
10 changes: 5 additions & 5 deletions unit-tests/dds/test-stream-sensor-bridge.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ def find_active_profile( stream_name ):
raise KeyError( f"can't find '{sensor_name}' profile for stream '{stream_name}'" )

def find_server_profile( stream_name, profile_string ):
by_string = f"<'{stream_name}' {profile_string}>"
by_string = f"<'{stream_name}' {profile_string}>"
for profile in servers[stream_name].profiles():
if profile.to_string() == by_string:
return profile
Expand Down Expand Up @@ -331,12 +331,12 @@ def find_server_profile( stream_name, profile_string ):
bridge.open( find_server_profile( 'Infrared_1', '1280x800 mono8 @ 30 Hz' ))
bridge.close( servers['Infrared_1'] )
bridge.close( servers['Infrared_1'] )
bridge.open( find_server_profile( 'Infrared_1', '1280x800 Y16 @ 25 Hz' ))
bridge.open( find_server_profile( 'Infrared_1', '1280x800 mono16 @ 25 Hz' ))
bridge.reset()
bridge.open( find_server_profile( 'Infrared_1', '1280x800 Y16 @ 15 Hz' ))
bridge.open( find_server_profile( 'Infrared_1', '1280x800 mono16 @ 15 Hz' ))
test.check_throws( lambda:
bridge.open( find_server_profile( 'Infrared_1', '1280x800 Y16 @ 25 Hz' )),
RuntimeError, "profile <'Infrared_1' 1280x800 Y16 @ 25 Hz> is incompatible with already-open <'Infrared_1' 1280x800 Y16 @ 15 Hz>" )
bridge.open( find_server_profile( 'Infrared_1', '1280x800 mono16 @ 25 Hz' )),
RuntimeError, "profile <'Infrared_1' 1280x800 mono16 @ 25 Hz> is incompatible with already-open <'Infrared_1' 1280x800 mono16 @ 15 Hz>" )
reset()
#
#############################################################################################
Expand Down

0 comments on commit 70c6f49

Please sign in to comment.