Delete redundant launch files
@@ -1,22 +0,0 @@
|
||||
# Sample launch for OrbbecSDK_ROS2
|
||||
|
||||
These simple examples demonstrate how to easily use the camera with OrbbecSDK_ROS2
|
||||
|
||||
## List of Examples:
|
||||
|
||||
| Name | Description | Experience Level |
|
||||
| :---------------------------------------------------------------------------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------- |
|
||||
| [Start_camera_node](./start_camera_node) | Hhow to launch the camera node with a colored point cloud feature enabled using OrbbecSDK_ROS2. | ⭐️ |
|
||||
| [Align_depth_color](./align_depth_color) | Align the depth image with the color image to create an overlay image. | ⭐️ |
|
||||
| [Point_cloud](./point_cloud) | How to enable point cloud data output from the camera node and visualize it using RViz2 | ⭐️ |
|
||||
| [Lower_cpu_usage](./lower_cpu_usage) | How to reduce CPU usage for one or more cameras in a ROS 2 environment. | ⭐️ |
|
||||
| [Net_camera](./net_camera) | How to use Net camera in OrbbecSDK_ROS2(Only Femto Mega nad Gemini 335Le) | ⭐️ |
|
||||
| [Gmsl_camera](./gmsl_camera) | How to use GMSL camera.(Only Gemini 335Lg) | ⭐️ |
|
||||
| [Benchmark](./benchmark) | The goal of this tool is to benchmark the performance of various OrbbecSDK_ROS2 camera configurations. | ⭐️⭐️ |
|
||||
| [Disparity_search_offset](./disparity_search_offset) | Use the disparity_search_offset function in the Gemini330 series cameras (minimum camera firmware version[1.4.60](https://www.orbbec.com/docs/g330-firmware-release/)). | ⭐️⭐️ |
|
||||
| [Interleave_ae_mode](./interleave_ae_mode) | How to use interleave_ae in Gemini 330 series cameras (minimum camera firmware version[1.4.00](https://www.orbbec.com/docs/g330-firmware-release/)) | ⭐️⭐️ |
|
||||
| [Multi_camera_synced](./multi_camera_synced) | How to use multi-camera synced with OrbbecSDK_ROS2 | ⭐️⭐️ |
|
||||
| [Multi_camera_synced_verification_tool](./multi_camera_synced_verification_tool) | How to verify the synchronization accuracy of multi-camera synchronization. | ⭐️⭐️⭐️ |
|
||||
|
||||
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
## Aligning Depth to Color in ROS 2
|
||||
|
||||
This section explains how to align depth images with color images to create an overlay image using ROS 2. This is particularly useful for applications requiring synchronized visual information from different sensor modalities.
|
||||
|
||||
### Commands to Align and View Depth and Color Images
|
||||
|
||||
1. **Basic Depth to Color Alignment:**
|
||||
|
||||
To simply align the depth image to the color image, use the following command:
|
||||
|
||||
```bash
|
||||
ros2 launch orbbec_camera gemini_330_series.launch.py depth_registration:=true
|
||||
```
|
||||
|
||||
This command activates the depth registration feature without opening a viewer.
|
||||
2. **Viewing Depth to Color Overlay:**
|
||||
|
||||
If you wish to view the depth to color overlay, you need to enable the viewer by using the command below:
|
||||
|
||||
```bash
|
||||
ros2 launch orbbec_camera gemini_330_series.launch.py depth_registration:=true enable_d2c_viewer:=true
|
||||
```
|
||||
|
||||
This launches the camera node with depth to color registration and opens a viewer to display the overlay image.
|
||||
|
||||
### Selecting Topics in RViz2
|
||||
|
||||
To visualize the aligned images in RViz2:
|
||||
|
||||
1. Launch RViz2 after running one of the above commands.
|
||||
2. Select the topic for the depth to color overlay image. An example topic selection is shown here:
|
||||
|
||||

|
||||
|
||||
### Example of Depth to Color Overlay
|
||||
|
||||
After selecting the appropriate topic in RViz2, you will be able to see the depth to color overlay image. Here's what it might look like:
|
||||
|
||||

|
||||
|
Before Width: | Height: | Size: 736 KiB |
|
Before Width: | Height: | Size: 70 KiB |
@@ -1,51 +0,0 @@
|
||||
# Ob_benchmark tool
|
||||
|
||||
> The goal of this tool is to benchmark the performance of various OrbbecSDK_ROS2 camera configurations. The benchmark results depend on the camera and settings used.(Currently only works with ROS2 Humble)
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
### Tool Configuration ([start_benchmark_params.json](../../config/tools/startbenchmark/start_benchmark_params.json))
|
||||
|
||||
```json
|
||||
{
|
||||
"start_benchmark_params": {
|
||||
"camera_name": [
|
||||
"camera_01",
|
||||
"camera_02",
|
||||
"camera_03",
|
||||
"camera_04"
|
||||
],
|
||||
"process_name": "component_conta",
|
||||
"switch_cycle": 300,
|
||||
"test_cycle": 1,
|
||||
"skip_number": 30
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
* `camera_name`: Names of the cameras to be configured. Example: `"camera_01"`, `"camera_02"`, etc.
|
||||
* `process_name`: The name of the process to be monitored. For example, `"component_conta"` will monitor the data of the container process.
|
||||
* `switch_cycle`: The cycle time for switching configurations, in seconds. For example, setting it to `300` means the configuration will switch every 300 seconds.
|
||||
* `test_cycle`: The testing cycle, in seconds. For example, setting it to `1` means the tool will collect data for the monitored process every 1 second.
|
||||
* `skip_number`: The number of data points to skip. For example, setting it to `30` means that the first 30 data points will be ignored.
|
||||
|
||||
### Camera configuration (launch files)
|
||||
|
||||
In the launch folder, there are multiple\.launch.py files (`ob_benchmark_0.launch.py`, `ob_benchmark_1.launch.py`, ..., `ob_benchmark_19.launch.py`). Each file corresponds to a different camera configuration.
|
||||
|
||||
### Running the ob_benchmark tool
|
||||
|
||||
To run the tool, use the following commands:
|
||||
|
||||
```bash
|
||||
source install/setup.bash
|
||||
ros2 run orbbec_camera ob_benchmark_node
|
||||
```
|
||||
|
||||
### Output Data Files
|
||||
|
||||
The output data files will be stored in the ob_benchmark folder with filenames like `0.csv`, `1.csv`, ..., 19.csv. For example:
|
||||
|
||||
* `0.csv` contains data from the `ob_benchmark_0.launch.py` configuration.
|
||||
* `1.csv` contains data from the `ob_benchmark_1.launch.py` configuration.
|
||||
@@ -1,318 +0,0 @@
|
||||
import os
|
||||
import yaml
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument, OpaqueFunction, GroupAction
|
||||
from launch.substitutions import LaunchConfiguration
|
||||
from launch_ros.actions import PushRosNamespace, ComposableNodeContainer, Node
|
||||
from launch_ros.descriptions import ComposableNode
|
||||
from launch.conditions import UnlessCondition
|
||||
from launch_ros.actions import LoadComposableNodes
|
||||
|
||||
def load_yaml(file_path):
|
||||
with open(file_path, 'r') as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
def merge_params(default_params, yaml_params):
|
||||
for key, value in yaml_params.items():
|
||||
if key in default_params:
|
||||
default_params[key] = value
|
||||
return default_params
|
||||
|
||||
|
||||
def convert_value(value):
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
if value.lower() == 'true':
|
||||
return True
|
||||
elif value.lower() == 'false':
|
||||
return False
|
||||
return value
|
||||
|
||||
|
||||
def load_parameters(context, args):
|
||||
default_params = {arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args}
|
||||
config_file_path = LaunchConfiguration('config_file_path').perform(context)
|
||||
if config_file_path:
|
||||
yaml_params = load_yaml(config_file_path)
|
||||
default_params = merge_params(default_params, yaml_params)
|
||||
skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
|
||||
return {
|
||||
key: (value if key in skip_convert else convert_value(value))
|
||||
for key, value in default_params.items()
|
||||
}
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
args = [
|
||||
DeclareLaunchArgument('camera_name', default_value='camera'),
|
||||
DeclareLaunchArgument('depth_registration', default_value='true'),
|
||||
DeclareLaunchArgument('serial_number', default_value=''),
|
||||
DeclareLaunchArgument('usb_port', default_value=''),
|
||||
DeclareLaunchArgument('device_num', default_value='1'),
|
||||
DeclareLaunchArgument('upgrade_firmware', default_value=''),
|
||||
DeclareLaunchArgument('preset_firmware_path', default_value=''),
|
||||
DeclareLaunchArgument('load_config_json_file_path', default_value=''),
|
||||
DeclareLaunchArgument('export_config_json_file_path', default_value=''),
|
||||
DeclareLaunchArgument('uvc_backend', default_value='libuvc'),#libuvc or v4l2
|
||||
DeclareLaunchArgument('point_cloud_qos', default_value='default'),
|
||||
DeclareLaunchArgument('enable_point_cloud', default_value='true'),
|
||||
DeclareLaunchArgument('enable_colored_point_cloud', default_value='false'),
|
||||
DeclareLaunchArgument('cloud_frame_id', default_value=''),
|
||||
DeclareLaunchArgument('connection_delay', default_value='10'),
|
||||
DeclareLaunchArgument('color_width', default_value='0'),
|
||||
DeclareLaunchArgument('color_height', default_value='0'),
|
||||
DeclareLaunchArgument('color_fps', default_value='0'),
|
||||
DeclareLaunchArgument('color_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_color', default_value='true'),
|
||||
DeclareLaunchArgument('color_qos', default_value='default'),
|
||||
DeclareLaunchArgument('color_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('enable_color_auto_exposure_priority', default_value='false'),
|
||||
DeclareLaunchArgument('color_rotation', default_value='-1'),#color rotation degree : 0, 90, 180, 270
|
||||
DeclareLaunchArgument('color_flip', default_value='false'),
|
||||
DeclareLaunchArgument('color_mirror', default_value='false'),
|
||||
DeclareLaunchArgument('color_ae_roi_left', default_value='-1'),
|
||||
DeclareLaunchArgument('color_ae_roi_left', default_value='-1'),
|
||||
DeclareLaunchArgument('color_ae_roi_right', default_value='-1'),
|
||||
DeclareLaunchArgument('color_ae_roi_top', default_value='-1'),
|
||||
DeclareLaunchArgument('color_ae_roi_bottom', default_value='-1'),
|
||||
DeclareLaunchArgument('color_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('color_gain', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_color_auto_white_balance', default_value='true'),
|
||||
DeclareLaunchArgument('color_white_balance', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_color_auto_exposure', default_value='true'),
|
||||
DeclareLaunchArgument('color_ae_max_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('color_brightness', default_value='-1'),
|
||||
DeclareLaunchArgument('color_sharpness', default_value='-1'),
|
||||
DeclareLaunchArgument('color_gamma', default_value='-1'),
|
||||
DeclareLaunchArgument('color_saturation', default_value='-1'),
|
||||
DeclareLaunchArgument('color_constrast', default_value='-1'),
|
||||
DeclareLaunchArgument('color_hue', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_color_backlight_compenstation', default_value='false'),
|
||||
DeclareLaunchArgument('color_powerline_freq', default_value=''),#disable ,50hz ,60hz ,auto
|
||||
DeclareLaunchArgument('enable_color_decimation_filter', default_value='false'),
|
||||
DeclareLaunchArgument('color_decimation_filter_scale', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_width', default_value='0'),
|
||||
DeclareLaunchArgument('depth_height', default_value='0'),
|
||||
DeclareLaunchArgument('depth_fps', default_value='0'),
|
||||
DeclareLaunchArgument('depth_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_depth', default_value='true'),
|
||||
DeclareLaunchArgument('depth_qos', default_value='default'),
|
||||
DeclareLaunchArgument('depth_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('enable_depth_auto_exposure_priority', default_value='false'),
|
||||
DeclareLaunchArgument('depth_precision', default_value=''),
|
||||
DeclareLaunchArgument('depth_rotation', default_value='-1'),#depth rotation degree : 0, 90, 180, 270
|
||||
DeclareLaunchArgument('depth_flip', default_value='false'),
|
||||
DeclareLaunchArgument('depth_mirror', default_value='false'),
|
||||
DeclareLaunchArgument('depth_ae_roi_left', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_ae_roi_right', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_ae_roi_top', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_ae_roi_bottom', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_brightness', default_value='-1'),
|
||||
DeclareLaunchArgument('left_ir_width', default_value='0'),
|
||||
DeclareLaunchArgument('left_ir_height', default_value='0'),
|
||||
DeclareLaunchArgument('left_ir_fps', default_value='0'),
|
||||
DeclareLaunchArgument('left_ir_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_left_ir', default_value='false'),
|
||||
DeclareLaunchArgument('left_ir_qos', default_value='default'),
|
||||
DeclareLaunchArgument('left_ir_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('left_ir_rotation', default_value='-1'),#left_ir rotation degree : 0, 90, 180, 270
|
||||
DeclareLaunchArgument('left_ir_flip', default_value='false'),
|
||||
DeclareLaunchArgument('left_ir_mirror', default_value='false'),
|
||||
DeclareLaunchArgument('enable_left_ir_sequence_id_filter', default_value='false'),
|
||||
DeclareLaunchArgument('left_ir_sequence_id_filter_id', default_value='-1'),
|
||||
DeclareLaunchArgument('right_ir_width', default_value='0'),
|
||||
DeclareLaunchArgument('right_ir_height', default_value='0'),
|
||||
DeclareLaunchArgument('right_ir_fps', default_value='0'),
|
||||
DeclareLaunchArgument('right_ir_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_right_ir', default_value='false'),
|
||||
DeclareLaunchArgument('right_ir_qos', default_value='default'),
|
||||
DeclareLaunchArgument('right_ir_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('right_ir_rotation', default_value='-1'),#right_ir rotation degree : 0, 90, 180, 270
|
||||
DeclareLaunchArgument('right_ir_flip', default_value='false'),
|
||||
DeclareLaunchArgument('right_ir_mirror', default_value='false'),
|
||||
DeclareLaunchArgument('enable_right_ir_sequence_id_filter', default_value='false'),
|
||||
DeclareLaunchArgument('right_ir_sequence_id_filter_id', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_ir_auto_exposure', default_value='true'),
|
||||
DeclareLaunchArgument('ir_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('ir_gain', default_value='-1'),
|
||||
DeclareLaunchArgument('ir_ae_max_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('ir_brightness', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_sync_output_accel_gyro', default_value='false'),
|
||||
DeclareLaunchArgument('enable_accel', default_value='false'),
|
||||
DeclareLaunchArgument('enable_accel_data_correction', default_value='true'),
|
||||
DeclareLaunchArgument('accel_rate', default_value='200hz'),
|
||||
DeclareLaunchArgument('accel_range', default_value='4g'),
|
||||
DeclareLaunchArgument('enable_gyro', default_value='false'),
|
||||
DeclareLaunchArgument('enable_gyro_data_correction', default_value='true'),
|
||||
DeclareLaunchArgument('gyro_rate', default_value='200hz'),
|
||||
DeclareLaunchArgument('gyro_range', default_value='1000dps'),
|
||||
DeclareLaunchArgument('liner_accel_cov', default_value='0.01'),
|
||||
DeclareLaunchArgument('angular_vel_cov', default_value='0.01'),
|
||||
DeclareLaunchArgument('publish_tf', default_value='true'),
|
||||
DeclareLaunchArgument('tf_publish_rate', default_value='0.0'),
|
||||
DeclareLaunchArgument('ir_info_url', default_value=''),
|
||||
DeclareLaunchArgument('color_info_url', default_value=''),
|
||||
# Network device settings: default enumerate_net_device is set to true, which will automatically enumerate network devices
|
||||
# If you do not want to automatically enumerate network devices,
|
||||
# you can set enumerate_net_device to true, net_device_ip to the device's IP address, and net_device_port to the default value of 8090
|
||||
DeclareLaunchArgument('enumerate_net_device', default_value='false'),
|
||||
DeclareLaunchArgument('net_device_ip', default_value=''),
|
||||
DeclareLaunchArgument('net_device_port', default_value='0'),
|
||||
DeclareLaunchArgument('exposure_range_mode', default_value='default'),#default, ultimate or regular
|
||||
DeclareLaunchArgument('log_level', default_value='none'),
|
||||
DeclareLaunchArgument('enable_publish_extrinsic', default_value='false'),
|
||||
DeclareLaunchArgument('enable_d2c_viewer', default_value='false'),
|
||||
DeclareLaunchArgument('disaparity_to_depth_mode', default_value='HW'),
|
||||
DeclareLaunchArgument('enable_ldp', default_value='true'),
|
||||
DeclareLaunchArgument('ldp_power_level', default_value='-1'),
|
||||
DeclareLaunchArgument('sync_mode', default_value='standalone'),
|
||||
DeclareLaunchArgument('depth_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('color_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('trigger2image_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('trigger_out_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('trigger_out_enabled', default_value='true'),
|
||||
DeclareLaunchArgument('frames_per_trigger', default_value='2'),
|
||||
DeclareLaunchArgument('software_trigger_period', default_value='33'), # ms
|
||||
DeclareLaunchArgument('enable_ptp_config', default_value='false'),#Only for Gemini 335Le
|
||||
DeclareLaunchArgument('enable_frame_sync', default_value='true'),
|
||||
DeclareLaunchArgument('ordered_pc', default_value='false'),
|
||||
DeclareLaunchArgument('enable_depth_scale', default_value='true'),
|
||||
DeclareLaunchArgument('enable_decimation_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_hdr_merge', default_value='false'),
|
||||
DeclareLaunchArgument('enable_sequence_id_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_threshold_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_hardware_noise_removal_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_noise_removal_filter', default_value='true'),
|
||||
DeclareLaunchArgument('enable_spatial_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_temporal_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_disaparity_to_depth', default_value='true'),
|
||||
DeclareLaunchArgument('enable_hole_filling_filter', default_value='false'),
|
||||
DeclareLaunchArgument('decimation_filter_scale', default_value='-1'),
|
||||
DeclareLaunchArgument('sequence_id_filter_id', default_value='-1'),
|
||||
DeclareLaunchArgument('threshold_filter_max', default_value='-1'),
|
||||
DeclareLaunchArgument('threshold_filter_min', default_value='-1'),
|
||||
DeclareLaunchArgument('hardware_noise_removal_filter_threshold', default_value='-1.0'),
|
||||
DeclareLaunchArgument('noise_removal_filter_min_diff', default_value='256'),
|
||||
DeclareLaunchArgument('noise_removal_filter_max_size', default_value='80'),
|
||||
DeclareLaunchArgument('spatial_filter_alpha', default_value='-1.0'),
|
||||
DeclareLaunchArgument('spatial_filter_diff_threshold', default_value='-1'),
|
||||
DeclareLaunchArgument('spatial_filter_magnitude', default_value='-1'),
|
||||
DeclareLaunchArgument('spatial_filter_radius', default_value='-1'),
|
||||
DeclareLaunchArgument('temporal_filter_diff_threshold', default_value='-1.0'),
|
||||
DeclareLaunchArgument('temporal_filter_weight', default_value='-1.0'),
|
||||
DeclareLaunchArgument('hole_filling_filter_mode', default_value=''),
|
||||
DeclareLaunchArgument('hdr_merge_exposure_1', default_value='-1'),
|
||||
DeclareLaunchArgument('hdr_merge_gain_1', default_value='-1'),
|
||||
DeclareLaunchArgument('hdr_merge_exposure_2', default_value='-1'),
|
||||
DeclareLaunchArgument('hdr_merge_gain_2', default_value='-1'),
|
||||
DeclareLaunchArgument('align_mode', default_value='SW'),
|
||||
DeclareLaunchArgument('align_target_stream', default_value='COLOR'),# COLOR or DEPTH
|
||||
DeclareLaunchArgument('diagnostic_period', default_value='1.0'),
|
||||
DeclareLaunchArgument('enable_laser', default_value='true'),
|
||||
DeclareLaunchArgument('depth_precision', default_value=''),
|
||||
DeclareLaunchArgument('device_preset', default_value='Default'),
|
||||
DeclareLaunchArgument('retry_on_usb3_detection_failure', default_value='false'),
|
||||
DeclareLaunchArgument('laser_energy_level', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_sync_host_time', default_value='true'),
|
||||
DeclareLaunchArgument('time_domain', default_value='global'),# global, device, system
|
||||
DeclareLaunchArgument('enable_color_undistortion', default_value='false'),
|
||||
DeclareLaunchArgument('config_file_path', default_value=''),
|
||||
DeclareLaunchArgument('enable_heartbeat', default_value='false'),
|
||||
DeclareLaunchArgument('gmsl_trigger_fps', default_value='3000'),
|
||||
DeclareLaunchArgument('enable_gmsl_trigger', default_value='false'),
|
||||
DeclareLaunchArgument('disparity_range_mode', default_value='-1'),
|
||||
DeclareLaunchArgument('disparity_search_offset', default_value='-1'),
|
||||
DeclareLaunchArgument('disparity_offset_config', default_value='false'),
|
||||
DeclareLaunchArgument('offset_index0', default_value='-1'),
|
||||
DeclareLaunchArgument('offset_index1', default_value='-1'),
|
||||
DeclareLaunchArgument('frame_aggregate_mode', default_value='ANY'), # full_frame, color_frame, ANY or disable
|
||||
DeclareLaunchArgument('interleave_ae_mode', default_value='laser'), # 'hdr' or 'laser'
|
||||
DeclareLaunchArgument('interleave_frame_enable', default_value='false'),
|
||||
DeclareLaunchArgument('interleave_skip_enable', default_value='false'),
|
||||
DeclareLaunchArgument('interleave_skip_index', default_value='1'), # 0:skip pattern ir 1: skip flood ir
|
||||
|
||||
DeclareLaunchArgument('hdr_index1_laser_control', default_value='1'),#interleave_hdr_param
|
||||
DeclareLaunchArgument('hdr_index1_depth_exposure', default_value='1'),
|
||||
DeclareLaunchArgument('hdr_index1_depth_gain', default_value='16'),
|
||||
DeclareLaunchArgument('hdr_index1_ir_brightness', default_value='20'),
|
||||
DeclareLaunchArgument('hdr_index1_ir_ae_max_exposure', default_value='2000'),
|
||||
DeclareLaunchArgument('hdr_index0_laser_control', default_value='1'),
|
||||
DeclareLaunchArgument('hdr_index0_depth_exposure', default_value='7500'),
|
||||
DeclareLaunchArgument('hdr_index0_depth_gain', default_value='16'),
|
||||
DeclareLaunchArgument('hdr_index0_ir_brightness', default_value='60'),
|
||||
DeclareLaunchArgument('hdr_index0_ir_ae_max_exposure', default_value='10000'),
|
||||
|
||||
DeclareLaunchArgument('laser_index1_laser_control', default_value='0'),#interleave_laser_param
|
||||
DeclareLaunchArgument('laser_index1_depth_exposure', default_value='3000'),
|
||||
DeclareLaunchArgument('laser_index1_depth_gain', default_value='16'),
|
||||
DeclareLaunchArgument('laser_index1_ir_brightness', default_value='60'),
|
||||
DeclareLaunchArgument('laser_index1_ir_ae_max_exposure', default_value='17000'),
|
||||
DeclareLaunchArgument('laser_index0_laser_control', default_value='1'),
|
||||
DeclareLaunchArgument('laser_index0_depth_exposure', default_value='3000'),
|
||||
DeclareLaunchArgument('laser_index0_depth_gain', default_value='16'),
|
||||
DeclareLaunchArgument('laser_index0_ir_brightness', default_value='60'),
|
||||
DeclareLaunchArgument('laser_index0_ir_ae_max_exposure', default_value='30000'),
|
||||
DeclareLaunchArgument('use_intra_process_comms', default_value='false'),
|
||||
DeclareLaunchArgument('attach_component_container_enable', default_value='false'),
|
||||
DeclareLaunchArgument('attach_component_container_name', default_value='orbbec_container'),
|
||||
|
||||
]
|
||||
|
||||
def get_params(context, args):
|
||||
return [load_parameters(context, args)]
|
||||
|
||||
def create_node_action(context, args):
|
||||
params = get_params(context, args)
|
||||
ros_distro = os.environ.get("ROS_DISTRO", "humble")
|
||||
if ros_distro == "foxy":
|
||||
return [
|
||||
Node(
|
||||
package="orbbec_camera",
|
||||
executable="orbbec_camera_node",
|
||||
name="ob_camera_node",
|
||||
namespace=LaunchConfiguration("camera_name"),
|
||||
parameters=params,
|
||||
output="screen",
|
||||
)
|
||||
]
|
||||
else:
|
||||
attach_to_shared_component_container_arg = LaunchConfiguration('attach_to_shared_component_container', default=False)
|
||||
component_container_name_arg = LaunchConfiguration('component_container_name', default='orbbec_container')
|
||||
|
||||
orbbec_container = Node(
|
||||
name=component_container_name_arg,
|
||||
package='rclcpp_components',
|
||||
executable='component_container_mt',
|
||||
output='screen',
|
||||
condition=UnlessCondition(attach_to_shared_component_container_arg)
|
||||
)
|
||||
return [
|
||||
orbbec_container,
|
||||
LoadComposableNodes(
|
||||
target_container=component_container_name_arg,
|
||||
composable_node_descriptions=[
|
||||
ComposableNode(
|
||||
namespace=LaunchConfiguration("camera_name"),
|
||||
name=LaunchConfiguration("camera_name"),
|
||||
package='orbbec_camera',
|
||||
plugin='orbbec_camera::OBCameraNodeDriver',
|
||||
parameters=params,
|
||||
extra_arguments=[{'use_intra_process_comms': LaunchConfiguration("use_intra_process_comms")}],
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
return LaunchDescription(
|
||||
args + [
|
||||
OpaqueFunction(function=lambda context: create_node_action(context, args))
|
||||
]
|
||||
)
|
||||
@@ -1,197 +0,0 @@
|
||||
import os
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
from launch import LaunchDescription
|
||||
from launch_ros.actions import Node,LoadComposableNodes
|
||||
from launch.actions import IncludeLaunchDescription, GroupAction, TimerAction
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
from launch.actions import DeclareLaunchArgument
|
||||
from launch.conditions import UnlessCondition, IfCondition
|
||||
from launch.substitutions import LaunchConfiguration
|
||||
from launch.substitutions import TextSubstitution
|
||||
from launch_ros.descriptions import ComposableNode
|
||||
|
||||
def load_yaml(file_path):
|
||||
with open(file_path, 'r') as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
def merge_params(default_params, yaml_params):
|
||||
for key, value in yaml_params.items():
|
||||
if key in default_params:
|
||||
default_params[key] = value
|
||||
return default_params
|
||||
|
||||
|
||||
def convert_value(value):
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
if value.lower() == 'true':
|
||||
return True
|
||||
elif value.lower() == 'false':
|
||||
return False
|
||||
return value
|
||||
|
||||
|
||||
def load_parameters(context, args):
|
||||
default_params = {arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args}
|
||||
config_file_path = LaunchConfiguration('config_file_path').perform(context)
|
||||
if config_file_path:
|
||||
yaml_params = load_yaml(config_file_path)
|
||||
default_params = merge_params(default_params, yaml_params)
|
||||
skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
|
||||
return {
|
||||
key: (value if key in skip_convert else convert_value(value))
|
||||
for key, value in default_params.items()
|
||||
}
|
||||
def generate_launch_description():
|
||||
# Include launch files
|
||||
package_dir = get_package_share_directory("orbbec_camera")
|
||||
launch_file_dir = os.path.join(package_dir, "examples/benchmark")
|
||||
|
||||
attach_to_shared_component_container_arg = LaunchConfiguration('attach_to_shared_component_container', default=False)
|
||||
component_container_name_arg = LaunchConfiguration('component_container_name', default='shared_orbbec_container')
|
||||
|
||||
shared_orbbec_container = Node(
|
||||
name=component_container_name_arg,
|
||||
package="rclcpp_components",
|
||||
executable="component_container_mt",
|
||||
output="screen",
|
||||
condition=UnlessCondition(attach_to_shared_component_container_arg),
|
||||
)
|
||||
start_benchmark = LoadComposableNodes(
|
||||
target_container=component_container_name_arg,
|
||||
composable_node_descriptions=[
|
||||
ComposableNode(
|
||||
namespace="start_benchmark",
|
||||
name="start_benchmark",
|
||||
package='orbbec_camera',
|
||||
plugin='orbbec_camera::tools::StartBenchmark',
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
attach_to_shared_component_container_arg = TextSubstitution(text="true")
|
||||
|
||||
args = [
|
||||
DeclareLaunchArgument('uvc_backend', default_value='libuvc'),
|
||||
DeclareLaunchArgument('depth_registration', default_value='false'),
|
||||
DeclareLaunchArgument('color_width', default_value='848'),
|
||||
DeclareLaunchArgument('color_height', default_value='480'),
|
||||
DeclareLaunchArgument('color_fps', default_value='30'),
|
||||
DeclareLaunchArgument('color_format', default_value='MJPG'),
|
||||
DeclareLaunchArgument('enable_color', default_value='true'),
|
||||
DeclareLaunchArgument('depth_width', default_value='848'),
|
||||
DeclareLaunchArgument('depth_height', default_value='480'),
|
||||
DeclareLaunchArgument('depth_fps', default_value='30'),
|
||||
DeclareLaunchArgument('depth_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_depth', default_value='true'),
|
||||
DeclareLaunchArgument('left_ir_width', default_value='848'),
|
||||
DeclareLaunchArgument('left_ir_height', default_value='480'),
|
||||
DeclareLaunchArgument('left_ir_fps', default_value='30'),
|
||||
DeclareLaunchArgument('left_ir_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_left_ir', default_value='true'),
|
||||
DeclareLaunchArgument('right_ir_width', default_value='848'),
|
||||
DeclareLaunchArgument('right_ir_height', default_value='480'),
|
||||
DeclareLaunchArgument('right_ir_fps', default_value='30'),
|
||||
DeclareLaunchArgument('right_ir_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_right_ir', default_value='true'),
|
||||
DeclareLaunchArgument('enable_point_cloud', default_value='false'),
|
||||
DeclareLaunchArgument('enable_colored_point_cloud', default_value='false'),
|
||||
DeclareLaunchArgument('enable_decimation_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_hdr_merge', default_value='false'),
|
||||
DeclareLaunchArgument('enable_sequence_id_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_threshold_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_hardware_noise_removal_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_noise_removal_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_spatial_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_temporal_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_hole_filling_filter', default_value='false'),
|
||||
]
|
||||
launch1_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, "gemini_330_series_benchmark.launch.py")
|
||||
),
|
||||
launch_arguments={
|
||||
"camera_name": "camera_01",
|
||||
"usb_port": "2-7",
|
||||
"device_num": "4",
|
||||
"sync_mode": "standalone",
|
||||
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
|
||||
"component_container_name": component_container_name_arg,
|
||||
}.items(),
|
||||
)
|
||||
launch2_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, "gemini_330_series_benchmark.launch.py")
|
||||
),
|
||||
launch_arguments={
|
||||
"camera_name": "camera_02",
|
||||
"usb_port": "2-2",
|
||||
"device_num": "4",
|
||||
"sync_mode": "standalone",
|
||||
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
|
||||
"component_container_name": component_container_name_arg,
|
||||
}.items(),
|
||||
)
|
||||
launch3_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, "gemini_330_series_benchmark.launch.py")
|
||||
),
|
||||
launch_arguments={
|
||||
"camera_name": "camera_03",
|
||||
"usb_port": "2-1",
|
||||
"device_num": "4",
|
||||
"sync_mode": "standalone",
|
||||
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
|
||||
"component_container_name": component_container_name_arg,
|
||||
}.items(),
|
||||
)
|
||||
launch4_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, "gemini_330_series_benchmark.launch.py")
|
||||
),
|
||||
launch_arguments={
|
||||
"camera_name": "camera_04",
|
||||
"usb_port": "2-3",
|
||||
"device_num": "4",
|
||||
"sync_mode": "standalone",
|
||||
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
|
||||
"component_container_name": component_container_name_arg,
|
||||
}.items(),
|
||||
)
|
||||
|
||||
delayed_left_camera = TimerAction(
|
||||
period=2.0,
|
||||
actions=args+[launch1_include],
|
||||
)
|
||||
delayed_right_camera = TimerAction(
|
||||
period=4.0,
|
||||
actions=args+[launch2_include],
|
||||
)
|
||||
delayed_rear_camera = TimerAction(
|
||||
period=6.0,
|
||||
actions=args+[launch3_include],
|
||||
)
|
||||
delayed_front_camera = TimerAction(
|
||||
period=8.0,
|
||||
actions=args+[launch4_include],
|
||||
)
|
||||
ld = LaunchDescription(
|
||||
[
|
||||
shared_orbbec_container,
|
||||
start_benchmark,
|
||||
delayed_left_camera,
|
||||
delayed_right_camera,
|
||||
delayed_rear_camera,
|
||||
delayed_front_camera,
|
||||
]
|
||||
)
|
||||
|
||||
return ld
|
||||
@@ -1,197 +0,0 @@
|
||||
import os
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
from launch import LaunchDescription
|
||||
from launch_ros.actions import Node,LoadComposableNodes
|
||||
from launch.actions import IncludeLaunchDescription, GroupAction, TimerAction
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
from launch.actions import DeclareLaunchArgument
|
||||
from launch.conditions import UnlessCondition, IfCondition
|
||||
from launch.substitutions import LaunchConfiguration
|
||||
from launch.substitutions import TextSubstitution
|
||||
from launch_ros.descriptions import ComposableNode
|
||||
|
||||
def load_yaml(file_path):
|
||||
with open(file_path, 'r') as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
def merge_params(default_params, yaml_params):
|
||||
for key, value in yaml_params.items():
|
||||
if key in default_params:
|
||||
default_params[key] = value
|
||||
return default_params
|
||||
|
||||
|
||||
def convert_value(value):
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
if value.lower() == 'true':
|
||||
return True
|
||||
elif value.lower() == 'false':
|
||||
return False
|
||||
return value
|
||||
|
||||
|
||||
def load_parameters(context, args):
|
||||
default_params = {arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args}
|
||||
config_file_path = LaunchConfiguration('config_file_path').perform(context)
|
||||
if config_file_path:
|
||||
yaml_params = load_yaml(config_file_path)
|
||||
default_params = merge_params(default_params, yaml_params)
|
||||
skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
|
||||
return {
|
||||
key: (value if key in skip_convert else convert_value(value))
|
||||
for key, value in default_params.items()
|
||||
}
|
||||
def generate_launch_description():
|
||||
# Include launch files
|
||||
package_dir = get_package_share_directory("orbbec_camera")
|
||||
launch_file_dir = os.path.join(package_dir, "examples/benchmark")
|
||||
|
||||
attach_to_shared_component_container_arg = LaunchConfiguration('attach_to_shared_component_container', default=False)
|
||||
component_container_name_arg = LaunchConfiguration('component_container_name', default='shared_orbbec_container')
|
||||
|
||||
shared_orbbec_container = Node(
|
||||
name=component_container_name_arg,
|
||||
package="rclcpp_components",
|
||||
executable="component_container_mt",
|
||||
output="screen",
|
||||
condition=UnlessCondition(attach_to_shared_component_container_arg),
|
||||
)
|
||||
start_benchmark = LoadComposableNodes(
|
||||
target_container=component_container_name_arg,
|
||||
composable_node_descriptions=[
|
||||
ComposableNode(
|
||||
namespace="start_benchmark",
|
||||
name="start_benchmark",
|
||||
package='orbbec_camera',
|
||||
plugin='orbbec_camera::tools::StartBenchmark',
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
attach_to_shared_component_container_arg = TextSubstitution(text="true")
|
||||
|
||||
args = [
|
||||
DeclareLaunchArgument('uvc_backend', default_value='libuvc'),
|
||||
DeclareLaunchArgument('depth_registration', default_value='false'),
|
||||
DeclareLaunchArgument('color_width', default_value='848'),
|
||||
DeclareLaunchArgument('color_height', default_value='480'),
|
||||
DeclareLaunchArgument('color_fps', default_value='30'),
|
||||
DeclareLaunchArgument('color_format', default_value='MJPG'),
|
||||
DeclareLaunchArgument('enable_color', default_value='true'),
|
||||
DeclareLaunchArgument('depth_width', default_value='848'),
|
||||
DeclareLaunchArgument('depth_height', default_value='480'),
|
||||
DeclareLaunchArgument('depth_fps', default_value='30'),
|
||||
DeclareLaunchArgument('depth_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_depth', default_value='true'),
|
||||
DeclareLaunchArgument('left_ir_width', default_value='848'),
|
||||
DeclareLaunchArgument('left_ir_height', default_value='480'),
|
||||
DeclareLaunchArgument('left_ir_fps', default_value='30'),
|
||||
DeclareLaunchArgument('left_ir_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_left_ir', default_value='true'),
|
||||
DeclareLaunchArgument('right_ir_width', default_value='848'),
|
||||
DeclareLaunchArgument('right_ir_height', default_value='480'),
|
||||
DeclareLaunchArgument('right_ir_fps', default_value='30'),
|
||||
DeclareLaunchArgument('right_ir_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_right_ir', default_value='true'),
|
||||
DeclareLaunchArgument('enable_point_cloud', default_value='false'),
|
||||
DeclareLaunchArgument('enable_colored_point_cloud', default_value='false'),
|
||||
DeclareLaunchArgument('enable_decimation_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_hdr_merge', default_value='false'),
|
||||
DeclareLaunchArgument('enable_sequence_id_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_threshold_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_hardware_noise_removal_filter', default_value='true'),
|
||||
DeclareLaunchArgument('enable_noise_removal_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_spatial_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_temporal_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_hole_filling_filter', default_value='false'),
|
||||
]
|
||||
launch1_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, "gemini_330_series_benchmark.launch.py")
|
||||
),
|
||||
launch_arguments={
|
||||
"camera_name": "camera_01",
|
||||
"usb_port": "2-7",
|
||||
"device_num": "4",
|
||||
"sync_mode": "standalone",
|
||||
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
|
||||
"component_container_name": component_container_name_arg,
|
||||
}.items(),
|
||||
)
|
||||
launch2_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, "gemini_330_series_benchmark.launch.py")
|
||||
),
|
||||
launch_arguments={
|
||||
"camera_name": "camera_02",
|
||||
"usb_port": "2-2",
|
||||
"device_num": "4",
|
||||
"sync_mode": "standalone",
|
||||
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
|
||||
"component_container_name": component_container_name_arg,
|
||||
}.items(),
|
||||
)
|
||||
launch3_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, "gemini_330_series_benchmark.launch.py")
|
||||
),
|
||||
launch_arguments={
|
||||
"camera_name": "camera_03",
|
||||
"usb_port": "2-1",
|
||||
"device_num": "4",
|
||||
"sync_mode": "standalone",
|
||||
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
|
||||
"component_container_name": component_container_name_arg,
|
||||
}.items(),
|
||||
)
|
||||
launch4_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, "gemini_330_series_benchmark.launch.py")
|
||||
),
|
||||
launch_arguments={
|
||||
"camera_name": "camera_04",
|
||||
"usb_port": "2-3",
|
||||
"device_num": "4",
|
||||
"sync_mode": "standalone",
|
||||
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
|
||||
"component_container_name": component_container_name_arg,
|
||||
}.items(),
|
||||
)
|
||||
|
||||
delayed_left_camera = TimerAction(
|
||||
period=2.0,
|
||||
actions=args+[launch1_include],
|
||||
)
|
||||
delayed_right_camera = TimerAction(
|
||||
period=4.0,
|
||||
actions=args+[launch2_include],
|
||||
)
|
||||
delayed_rear_camera = TimerAction(
|
||||
period=6.0,
|
||||
actions=args+[launch3_include],
|
||||
)
|
||||
delayed_front_camera = TimerAction(
|
||||
period=8.0,
|
||||
actions=args+[launch4_include],
|
||||
)
|
||||
ld = LaunchDescription(
|
||||
[
|
||||
shared_orbbec_container,
|
||||
start_benchmark,
|
||||
delayed_left_camera,
|
||||
delayed_right_camera,
|
||||
delayed_rear_camera,
|
||||
delayed_front_camera,
|
||||
]
|
||||
)
|
||||
|
||||
return ld
|
||||
@@ -1,47 +0,0 @@
|
||||
# Disparity_search_offset
|
||||
|
||||
> This section describes how to use the disparity_search_offset function in the Gemini330 series cameras (minimum camera firmware version [1.4.60](https://www.orbbec.com/docs/g330-firmware-release/)).Disparity_search_offset is effective only for1280×720, 1280×800 and 640×400 resolutions of depth stream.
|
||||
|
||||
## Function Introduction
|
||||
|
||||
The definition of disparity search range: For any pixel *(u_l, v)* in the left image, by default, the corresponding disparity search range in the right image is *[ (u_l - 255, v)*, *(u_l, v) ]*, where the disparity search length is 256 and the maximum integer disparity is 255. If the starting point of the search is adjusted to *[ (u_l - 255 - offset, v)*, *(u_l - offset, v) ]*, the offset is defined as the disparity shift. Therefore, our disparity search range configuration includes both the disparity search length and the search position offset (which can also be referred to as the disparity shift).
|
||||
|
||||

|
||||
|
||||
## Parameter Introduction
|
||||
|
||||
The disparity_search_offset related parameters are set in [gemini_330_series.launch.py](../../launch/gemini_330_series.launch.py)
|
||||
|
||||
* `disparity_range_mode` : Disparity search length,can only be set to 64, 128 and 256.
|
||||
|
||||
* `disparity_search_offset` : Disparity search offset value,Disparity search offset value, can be set from 0 to 127.
|
||||
|
||||
* `disparity_offset_config` : Disparity search offset interleave frames.
|
||||
|
||||
* `offset_index0` : Frame 0 disparity search offset value.
|
||||
|
||||
* `offset_index1` : Frame 1 disparity search offset value.
|
||||
|
||||
| disparity range mode | disparity search offset | Minimum depth of inclined wall (mm) |
|
||||
| :------------------: | :---------------------: | :---------------------------------: |
|
||||
| 64 | 85 | Gemini 335L 388-406 |
|
||||
| 64 | 127 | Gemini 335L 302-317 |
|
||||
|
||||
| disparity range mode | disparity search offset | Minimum depth of inclined wall (mm) |
|
||||
| :------------------: | :---------------------: | :---------------------------------------------: |
|
||||
| 128 | 0 | Gemini 335 233-249<br />Gemini 335L 453-475 |
|
||||
| 128 | 45 | Gemini 335 172-184<br />Gemini 335L 334-349 |
|
||||
| 128 | 127 | Gemini 335 117-125<br />Gemini 335L 226-236 |
|
||||
|
||||
| disparity range mode | disparity search offset | Minimum depth of inclined wall (mm) |
|
||||
| :------------------: | :---------------------: | :---------------------------------: |
|
||||
| 256 | 85 | Gemini 335L 169-178 |
|
||||
| 256 | 127 | Gemini 335L 151-158 |
|
||||
|
||||
## Run the launch
|
||||
|
||||
Setting the disparity_search_offset parameter,`colcon build` again and run launch
|
||||
|
||||
```bash
|
||||
ros2 launch orbbec_camera gemini_330_series.launch.py
|
||||
```
|
||||
|
Before Width: | Height: | Size: 11 KiB |
@@ -1,50 +0,0 @@
|
||||
# GMSL_camera
|
||||
|
||||
> This section describes how to use GMSL camera in OrbbecSDK_ROS2.Currently, only Gemini 335Lg GMSL devices are supported, and other GMSL devices will be supported in the near future.
|
||||
|
||||
## Single GMSL camera
|
||||
|
||||
The usage of GMSL camera in OrbbecSDK_ROS2 is the same as that of Gemini 330 series camera via USB.
|
||||
|
||||
```bash
|
||||
ros2 launch orbbec_camera gemini_330_gmsl.launch.py
|
||||
```
|
||||
|
||||
## Multi GMSL camera
|
||||
|
||||
To get the `usb_port` of the GMSL camera, plug in the camera and run the following command in the terminal:
|
||||
|
||||
```bash
|
||||
ros2 run orbbec_camera list_devices_node
|
||||
```
|
||||
|
||||
For example, the obtained gmsl camera `usb_port`: `gmsl2-1`
|
||||
|
||||
Go to the [multi_gmsl_camera.launch.py](./multi_gmsl_camera.launch.py) file and change the `usb_port`.
|
||||
|
||||
```bash
|
||||
ros2 launch orbbec_camera multi_gmsl_camera.launch.py
|
||||
```
|
||||
|
||||
> Note: By default, multi_gmsl_camera.launch.py only starts color and left_ir. If you want to start other sensors, please go to [camera_secondary_params.yaml](../../config/camera_secondary_params.yaml) to modify them.
|
||||
|
||||
## Multi GMSL camera synced
|
||||
|
||||
First, please see how to use [multi_camera_synced](../multi_camera_synced/README.MD).
|
||||
|
||||
In addition, GMSL multi-camera synced does not require Multi-Camera Sync Hub Pro, so there is no need to set the `primary` mode. Each GMSL camera is `secondary`.
|
||||
|
||||
### Additional Parameter Settings
|
||||
|
||||
* `gmsl_trigger_fps` : set hardware soc trigger source frame rate.
|
||||
* `enable_gmsl_trigger` : enable hardware soc trigger.
|
||||
|
||||
### Run the launch
|
||||
|
||||
Please refer to the configuration in [multi_gmsl_camera_synced.launch.py.](multi_gmsl_camera_synced.launch.py)
|
||||
|
||||
```bash
|
||||
ros2 launch orbbec_camera multi_gmsl_camera_synced.launch.py
|
||||
```
|
||||
|
||||
> Note: By default, multi_gmsl_camera_synced.launch.py only starts color and left_ir. If you want to start other sensors, please go to [camera_secondary_params.yaml](../../config/camera_secondary_params.yaml) and [camera_params.yaml](../../config/camera_params.yaml) to modify them.
|
||||
@@ -1,318 +0,0 @@
|
||||
import os
|
||||
import yaml
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument, OpaqueFunction, GroupAction
|
||||
from launch.substitutions import LaunchConfiguration
|
||||
from launch_ros.actions import PushRosNamespace, ComposableNodeContainer, Node
|
||||
from launch_ros.descriptions import ComposableNode
|
||||
from launch.conditions import UnlessCondition
|
||||
from launch_ros.actions import LoadComposableNodes
|
||||
|
||||
def load_yaml(file_path):
|
||||
with open(file_path, 'r') as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
def merge_params(default_params, yaml_params):
|
||||
for key, value in yaml_params.items():
|
||||
if key in default_params:
|
||||
default_params[key] = value
|
||||
return default_params
|
||||
|
||||
|
||||
def convert_value(value):
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
if value.lower() == 'true':
|
||||
return True
|
||||
elif value.lower() == 'false':
|
||||
return False
|
||||
return value
|
||||
|
||||
|
||||
def load_parameters(context, args):
|
||||
default_params = {arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args}
|
||||
config_file_path = LaunchConfiguration('config_file_path').perform(context)
|
||||
if config_file_path:
|
||||
yaml_params = load_yaml(config_file_path)
|
||||
default_params = merge_params(default_params, yaml_params)
|
||||
skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
|
||||
return {
|
||||
key: (value if key in skip_convert else convert_value(value))
|
||||
for key, value in default_params.items()
|
||||
}
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
args = [
|
||||
DeclareLaunchArgument('camera_name', default_value='camera'),
|
||||
DeclareLaunchArgument('depth_registration', default_value='true'),
|
||||
DeclareLaunchArgument('serial_number', default_value=''),
|
||||
DeclareLaunchArgument('usb_port', default_value=''),
|
||||
DeclareLaunchArgument('device_num', default_value='1'),
|
||||
DeclareLaunchArgument('upgrade_firmware', default_value=''),
|
||||
DeclareLaunchArgument('preset_firmware_path', default_value=''),
|
||||
DeclareLaunchArgument('load_config_json_file_path', default_value=''),
|
||||
DeclareLaunchArgument('export_config_json_file_path', default_value=''),
|
||||
DeclareLaunchArgument('uvc_backend', default_value='libuvc'),#libuvc or v4l2
|
||||
DeclareLaunchArgument('point_cloud_qos', default_value='default'),
|
||||
DeclareLaunchArgument('enable_point_cloud', default_value='true'),
|
||||
DeclareLaunchArgument('enable_colored_point_cloud', default_value='false'),
|
||||
DeclareLaunchArgument('cloud_frame_id', default_value=''),
|
||||
DeclareLaunchArgument('connection_delay', default_value='10'),
|
||||
DeclareLaunchArgument('color_width', default_value='0'),
|
||||
DeclareLaunchArgument('color_height', default_value='0'),
|
||||
DeclareLaunchArgument('color_fps', default_value='0'),
|
||||
DeclareLaunchArgument('color_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_color', default_value='true'),
|
||||
DeclareLaunchArgument('color_qos', default_value='default'),
|
||||
DeclareLaunchArgument('color_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('enable_color_auto_exposure_priority', default_value='false'),
|
||||
DeclareLaunchArgument('color_rotation', default_value='-1'),#color rotation degree : 0, 90, 180, 270
|
||||
DeclareLaunchArgument('color_flip', default_value='false'),
|
||||
DeclareLaunchArgument('color_mirror', default_value='false'),
|
||||
DeclareLaunchArgument('color_ae_roi_left', default_value='-1'),
|
||||
DeclareLaunchArgument('color_ae_roi_left', default_value='-1'),
|
||||
DeclareLaunchArgument('color_ae_roi_right', default_value='-1'),
|
||||
DeclareLaunchArgument('color_ae_roi_top', default_value='-1'),
|
||||
DeclareLaunchArgument('color_ae_roi_bottom', default_value='-1'),
|
||||
DeclareLaunchArgument('color_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('color_gain', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_color_auto_white_balance', default_value='true'),
|
||||
DeclareLaunchArgument('color_white_balance', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_color_auto_exposure', default_value='true'),
|
||||
DeclareLaunchArgument('color_ae_max_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('color_brightness', default_value='-1'),
|
||||
DeclareLaunchArgument('color_sharpness', default_value='-1'),
|
||||
DeclareLaunchArgument('color_gamma', default_value='-1'),
|
||||
DeclareLaunchArgument('color_saturation', default_value='-1'),
|
||||
DeclareLaunchArgument('color_constrast', default_value='-1'),
|
||||
DeclareLaunchArgument('color_hue', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_color_backlight_compenstation', default_value='false'),
|
||||
DeclareLaunchArgument('color_powerline_freq', default_value=''),#disable ,50hz ,60hz ,auto
|
||||
DeclareLaunchArgument('enable_color_decimation_filter', default_value='false'),
|
||||
DeclareLaunchArgument('color_decimation_filter_scale', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_width', default_value='0'),
|
||||
DeclareLaunchArgument('depth_height', default_value='0'),
|
||||
DeclareLaunchArgument('depth_fps', default_value='0'),
|
||||
DeclareLaunchArgument('depth_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_depth', default_value='true'),
|
||||
DeclareLaunchArgument('depth_qos', default_value='default'),
|
||||
DeclareLaunchArgument('depth_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('enable_depth_auto_exposure_priority', default_value='false'),
|
||||
DeclareLaunchArgument('depth_precision', default_value=''),
|
||||
DeclareLaunchArgument('depth_rotation', default_value='-1'),#depth rotation degree : 0, 90, 180, 270
|
||||
DeclareLaunchArgument('depth_flip', default_value='false'),
|
||||
DeclareLaunchArgument('depth_mirror', default_value='false'),
|
||||
DeclareLaunchArgument('depth_ae_roi_left', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_ae_roi_right', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_ae_roi_top', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_ae_roi_bottom', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_brightness', default_value='-1'),
|
||||
DeclareLaunchArgument('left_ir_width', default_value='0'),
|
||||
DeclareLaunchArgument('left_ir_height', default_value='0'),
|
||||
DeclareLaunchArgument('left_ir_fps', default_value='0'),
|
||||
DeclareLaunchArgument('left_ir_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_left_ir', default_value='false'),
|
||||
DeclareLaunchArgument('left_ir_qos', default_value='default'),
|
||||
DeclareLaunchArgument('left_ir_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('left_ir_rotation', default_value='-1'),#left_ir rotation degree : 0, 90, 180, 270
|
||||
DeclareLaunchArgument('left_ir_flip', default_value='false'),
|
||||
DeclareLaunchArgument('left_ir_mirror', default_value='false'),
|
||||
DeclareLaunchArgument('enable_left_ir_sequence_id_filter', default_value='false'),
|
||||
DeclareLaunchArgument('left_ir_sequence_id_filter_id', default_value='-1'),
|
||||
DeclareLaunchArgument('right_ir_width', default_value='0'),
|
||||
DeclareLaunchArgument('right_ir_height', default_value='0'),
|
||||
DeclareLaunchArgument('right_ir_fps', default_value='0'),
|
||||
DeclareLaunchArgument('right_ir_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_right_ir', default_value='false'),
|
||||
DeclareLaunchArgument('right_ir_qos', default_value='default'),
|
||||
DeclareLaunchArgument('right_ir_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('right_ir_rotation', default_value='-1'),#right_ir rotation degree : 0, 90, 180, 270
|
||||
DeclareLaunchArgument('right_ir_flip', default_value='false'),
|
||||
DeclareLaunchArgument('right_ir_mirror', default_value='false'),
|
||||
DeclareLaunchArgument('enable_right_ir_sequence_id_filter', default_value='false'),
|
||||
DeclareLaunchArgument('right_ir_sequence_id_filter_id', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_ir_auto_exposure', default_value='true'),
|
||||
DeclareLaunchArgument('ir_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('ir_gain', default_value='-1'),
|
||||
DeclareLaunchArgument('ir_ae_max_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('ir_brightness', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_sync_output_accel_gyro', default_value='false'),
|
||||
DeclareLaunchArgument('enable_accel', default_value='false'),
|
||||
DeclareLaunchArgument('enable_accel_data_correction', default_value='true'),
|
||||
DeclareLaunchArgument('accel_rate', default_value='200hz'),
|
||||
DeclareLaunchArgument('accel_range', default_value='4g'),
|
||||
DeclareLaunchArgument('enable_gyro', default_value='false'),
|
||||
DeclareLaunchArgument('enable_gyro_data_correction', default_value='true'),
|
||||
DeclareLaunchArgument('gyro_rate', default_value='200hz'),
|
||||
DeclareLaunchArgument('gyro_range', default_value='1000dps'),
|
||||
DeclareLaunchArgument('liner_accel_cov', default_value='0.01'),
|
||||
DeclareLaunchArgument('angular_vel_cov', default_value='0.01'),
|
||||
DeclareLaunchArgument('publish_tf', default_value='true'),
|
||||
DeclareLaunchArgument('tf_publish_rate', default_value='0.0'),
|
||||
DeclareLaunchArgument('ir_info_url', default_value=''),
|
||||
DeclareLaunchArgument('color_info_url', default_value=''),
|
||||
# Network device settings: default enumerate_net_device is set to true, which will automatically enumerate network devices
|
||||
# If you do not want to automatically enumerate network devices,
|
||||
# you can set enumerate_net_device to true, net_device_ip to the device's IP address, and net_device_port to the default value of 8090
|
||||
DeclareLaunchArgument('enumerate_net_device', default_value='false'),
|
||||
DeclareLaunchArgument('net_device_ip', default_value=''),
|
||||
DeclareLaunchArgument('net_device_port', default_value='0'),
|
||||
DeclareLaunchArgument('exposure_range_mode', default_value='default'),#default, ultimate or regular
|
||||
DeclareLaunchArgument('log_level', default_value='none'),
|
||||
DeclareLaunchArgument('enable_publish_extrinsic', default_value='false'),
|
||||
DeclareLaunchArgument('enable_d2c_viewer', default_value='false'),
|
||||
DeclareLaunchArgument('disaparity_to_depth_mode', default_value='HW'),
|
||||
DeclareLaunchArgument('enable_ldp', default_value='true'),
|
||||
DeclareLaunchArgument('ldp_power_level', default_value='-1'),
|
||||
DeclareLaunchArgument('sync_mode', default_value='standalone'),
|
||||
DeclareLaunchArgument('depth_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('color_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('trigger2image_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('trigger_out_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('trigger_out_enabled', default_value='true'),
|
||||
DeclareLaunchArgument('frames_per_trigger', default_value='2'),
|
||||
DeclareLaunchArgument('software_trigger_period', default_value='33'), # ms
|
||||
DeclareLaunchArgument('enable_ptp_config', default_value='false'),#Only for Gemini 335Le
|
||||
DeclareLaunchArgument('enable_frame_sync', default_value='true'),
|
||||
DeclareLaunchArgument('ordered_pc', default_value='false'),
|
||||
DeclareLaunchArgument('enable_depth_scale', default_value='true'),
|
||||
DeclareLaunchArgument('enable_decimation_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_hdr_merge', default_value='false'),
|
||||
DeclareLaunchArgument('enable_sequence_id_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_threshold_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_hardware_noise_removal_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_noise_removal_filter', default_value='true'),
|
||||
DeclareLaunchArgument('enable_spatial_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_temporal_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_disaparity_to_depth', default_value='true'),
|
||||
DeclareLaunchArgument('enable_hole_filling_filter', default_value='false'),
|
||||
DeclareLaunchArgument('decimation_filter_scale', default_value='-1'),
|
||||
DeclareLaunchArgument('sequence_id_filter_id', default_value='-1'),
|
||||
DeclareLaunchArgument('threshold_filter_max', default_value='-1'),
|
||||
DeclareLaunchArgument('threshold_filter_min', default_value='-1'),
|
||||
DeclareLaunchArgument('hardware_noise_removal_filter_threshold', default_value='-1.0'),
|
||||
DeclareLaunchArgument('noise_removal_filter_min_diff', default_value='256'),
|
||||
DeclareLaunchArgument('noise_removal_filter_max_size', default_value='80'),
|
||||
DeclareLaunchArgument('spatial_filter_alpha', default_value='-1.0'),
|
||||
DeclareLaunchArgument('spatial_filter_diff_threshold', default_value='-1'),
|
||||
DeclareLaunchArgument('spatial_filter_magnitude', default_value='-1'),
|
||||
DeclareLaunchArgument('spatial_filter_radius', default_value='-1'),
|
||||
DeclareLaunchArgument('temporal_filter_diff_threshold', default_value='-1.0'),
|
||||
DeclareLaunchArgument('temporal_filter_weight', default_value='-1.0'),
|
||||
DeclareLaunchArgument('hole_filling_filter_mode', default_value=''),
|
||||
DeclareLaunchArgument('hdr_merge_exposure_1', default_value='-1'),
|
||||
DeclareLaunchArgument('hdr_merge_gain_1', default_value='-1'),
|
||||
DeclareLaunchArgument('hdr_merge_exposure_2', default_value='-1'),
|
||||
DeclareLaunchArgument('hdr_merge_gain_2', default_value='-1'),
|
||||
DeclareLaunchArgument('align_mode', default_value='SW'),
|
||||
DeclareLaunchArgument('align_target_stream', default_value='COLOR'),# COLOR or DEPTH
|
||||
DeclareLaunchArgument('diagnostic_period', default_value='1.0'),
|
||||
DeclareLaunchArgument('enable_laser', default_value='true'),
|
||||
DeclareLaunchArgument('depth_precision', default_value=''),
|
||||
DeclareLaunchArgument('device_preset', default_value='Default'),
|
||||
DeclareLaunchArgument('retry_on_usb3_detection_failure', default_value='false'),
|
||||
DeclareLaunchArgument('laser_energy_level', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_sync_host_time', default_value='true'),
|
||||
DeclareLaunchArgument('time_domain', default_value='global'),# global, device, system
|
||||
DeclareLaunchArgument('enable_color_undistortion', default_value='false'),
|
||||
DeclareLaunchArgument('config_file_path', default_value=''),
|
||||
DeclareLaunchArgument('enable_heartbeat', default_value='false'),
|
||||
DeclareLaunchArgument('gmsl_trigger_fps', default_value='3000'),
|
||||
DeclareLaunchArgument('enable_gmsl_trigger', default_value='false'),
|
||||
DeclareLaunchArgument('disparity_range_mode', default_value='-1'),
|
||||
DeclareLaunchArgument('disparity_search_offset', default_value='-1'),
|
||||
DeclareLaunchArgument('disparity_offset_config', default_value='false'),
|
||||
DeclareLaunchArgument('offset_index0', default_value='-1'),
|
||||
DeclareLaunchArgument('offset_index1', default_value='-1'),
|
||||
DeclareLaunchArgument('frame_aggregate_mode', default_value='ANY'), # full_frame, color_frame, ANY or disable
|
||||
DeclareLaunchArgument('interleave_ae_mode', default_value='laser'), # 'hdr' or 'laser'
|
||||
DeclareLaunchArgument('interleave_frame_enable', default_value='false'),
|
||||
DeclareLaunchArgument('interleave_skip_enable', default_value='false'),
|
||||
DeclareLaunchArgument('interleave_skip_index', default_value='1'), # 0:skip pattern ir 1: skip flood ir
|
||||
|
||||
DeclareLaunchArgument('hdr_index1_laser_control', default_value='1'),#interleave_hdr_param
|
||||
DeclareLaunchArgument('hdr_index1_depth_exposure', default_value='1'),
|
||||
DeclareLaunchArgument('hdr_index1_depth_gain', default_value='16'),
|
||||
DeclareLaunchArgument('hdr_index1_ir_brightness', default_value='20'),
|
||||
DeclareLaunchArgument('hdr_index1_ir_ae_max_exposure', default_value='2000'),
|
||||
DeclareLaunchArgument('hdr_index0_laser_control', default_value='1'),
|
||||
DeclareLaunchArgument('hdr_index0_depth_exposure', default_value='7500'),
|
||||
DeclareLaunchArgument('hdr_index0_depth_gain', default_value='16'),
|
||||
DeclareLaunchArgument('hdr_index0_ir_brightness', default_value='60'),
|
||||
DeclareLaunchArgument('hdr_index0_ir_ae_max_exposure', default_value='10000'),
|
||||
|
||||
DeclareLaunchArgument('laser_index1_laser_control', default_value='0'),#interleave_laser_param
|
||||
DeclareLaunchArgument('laser_index1_depth_exposure', default_value='3000'),
|
||||
DeclareLaunchArgument('laser_index1_depth_gain', default_value='16'),
|
||||
DeclareLaunchArgument('laser_index1_ir_brightness', default_value='60'),
|
||||
DeclareLaunchArgument('laser_index1_ir_ae_max_exposure', default_value='17000'),
|
||||
DeclareLaunchArgument('laser_index0_laser_control', default_value='1'),
|
||||
DeclareLaunchArgument('laser_index0_depth_exposure', default_value='3000'),
|
||||
DeclareLaunchArgument('laser_index0_depth_gain', default_value='16'),
|
||||
DeclareLaunchArgument('laser_index0_ir_brightness', default_value='60'),
|
||||
DeclareLaunchArgument('laser_index0_ir_ae_max_exposure', default_value='30000'),
|
||||
DeclareLaunchArgument('use_intra_process_comms', default_value='false'),
|
||||
DeclareLaunchArgument('attach_component_container_enable', default_value='false'),
|
||||
DeclareLaunchArgument('attach_component_container_name', default_value='orbbec_container'),
|
||||
|
||||
]
|
||||
|
||||
def get_params(context, args):
|
||||
return [load_parameters(context, args)]
|
||||
|
||||
def create_node_action(context, args):
|
||||
params = get_params(context, args)
|
||||
ros_distro = os.environ.get("ROS_DISTRO", "humble")
|
||||
if ros_distro == "foxy":
|
||||
return [
|
||||
Node(
|
||||
package="orbbec_camera",
|
||||
executable="orbbec_camera_node",
|
||||
name="ob_camera_node",
|
||||
namespace=LaunchConfiguration("camera_name"),
|
||||
parameters=params,
|
||||
output="screen",
|
||||
)
|
||||
]
|
||||
else:
|
||||
attach_to_shared_component_container_arg = LaunchConfiguration('attach_to_shared_component_container', default=False)
|
||||
component_container_name_arg = LaunchConfiguration('component_container_name', default='orbbec_container')
|
||||
|
||||
orbbec_container = Node(
|
||||
name=component_container_name_arg,
|
||||
package='rclcpp_components',
|
||||
executable='component_container_mt',
|
||||
output='screen',
|
||||
condition=UnlessCondition(attach_to_shared_component_container_arg)
|
||||
)
|
||||
return [
|
||||
orbbec_container,
|
||||
LoadComposableNodes(
|
||||
target_container=component_container_name_arg,
|
||||
composable_node_descriptions=[
|
||||
ComposableNode(
|
||||
namespace=LaunchConfiguration("camera_name"),
|
||||
name=LaunchConfiguration("camera_name"),
|
||||
package='orbbec_camera',
|
||||
plugin='orbbec_camera::OBCameraNodeDriver',
|
||||
parameters=params,
|
||||
extra_arguments=[{'use_intra_process_comms': LaunchConfiguration("use_intra_process_comms")}],
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
return LaunchDescription(
|
||||
args + [
|
||||
OpaqueFunction(function=lambda context: create_node_action(context, args))
|
||||
]
|
||||
)
|
||||
@@ -1,108 +0,0 @@
|
||||
import os
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
from launch import LaunchDescription
|
||||
from launch_ros.actions import Node, LoadComposableNodes
|
||||
from launch.actions import IncludeLaunchDescription, GroupAction, TimerAction
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
from launch.actions import DeclareLaunchArgument
|
||||
from launch.conditions import UnlessCondition, IfCondition
|
||||
from launch.substitutions import LaunchConfiguration
|
||||
from launch.substitutions import TextSubstitution
|
||||
from launch_ros.descriptions import ComposableNode
|
||||
|
||||
|
||||
def load_yaml(file_path):
|
||||
with open(file_path, "r") as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
def merge_params(default_params, yaml_params):
|
||||
for key, value in yaml_params.items():
|
||||
if key in default_params:
|
||||
default_params[key] = value
|
||||
return default_params
|
||||
|
||||
|
||||
def convert_value(value):
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
if value.lower() == "true":
|
||||
return True
|
||||
elif value.lower() == "false":
|
||||
return False
|
||||
return value
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
# Include launch files
|
||||
package_dir = get_package_share_directory("orbbec_camera")
|
||||
launch_file_dir = os.path.join(
|
||||
package_dir, "examples/gmsl_camera"
|
||||
)
|
||||
config_file_dir = os.path.join(package_dir, "config")
|
||||
secondary_config_file_path = os.path.join(config_file_dir, "camera_secondary_params.yaml")
|
||||
|
||||
attach_to_shared_component_container_arg = LaunchConfiguration(
|
||||
"attach_to_shared_component_container", default=False
|
||||
)
|
||||
component_container_name_arg = LaunchConfiguration(
|
||||
"component_container_name", default="shared_orbbec_container"
|
||||
)
|
||||
|
||||
shared_orbbec_container = Node(
|
||||
name=component_container_name_arg,
|
||||
package="rclcpp_components",
|
||||
executable="component_container_mt",
|
||||
output="screen",
|
||||
condition=UnlessCondition(attach_to_shared_component_container_arg),
|
||||
)
|
||||
|
||||
attach_to_shared_component_container_arg = TextSubstitution(text="true")
|
||||
|
||||
launch1_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, "gemini_330_gmsl.launch.py")
|
||||
),
|
||||
launch_arguments={
|
||||
"camera_name": "camera_01",
|
||||
"usb_port": "gmsl2-1",
|
||||
"device_num": "2",
|
||||
"sync_mode": "standalone",
|
||||
"config_file_path": secondary_config_file_path,
|
||||
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
|
||||
"component_container_name": component_container_name_arg,
|
||||
}.items(),
|
||||
)
|
||||
|
||||
launch2_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, "gemini_330_gmsl.launch.py")
|
||||
),
|
||||
launch_arguments={
|
||||
"camera_name": "camera_02",
|
||||
"usb_port": "gmsl2-3",
|
||||
"device_num": "2",
|
||||
"sync_mode": "standalone",
|
||||
"config_file_path": secondary_config_file_path,
|
||||
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
|
||||
"component_container_name": component_container_name_arg,
|
||||
}.items(),
|
||||
)
|
||||
|
||||
# Launch description
|
||||
ld = LaunchDescription(
|
||||
[
|
||||
shared_orbbec_container,
|
||||
TimerAction(period=0.0, actions=[GroupAction([launch2_include])]),
|
||||
TimerAction(period=2.0, actions=[GroupAction([launch1_include])]),
|
||||
]
|
||||
)
|
||||
|
||||
return ld
|
||||
@@ -1,110 +0,0 @@
|
||||
import os
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
from launch import LaunchDescription
|
||||
from launch_ros.actions import Node, LoadComposableNodes
|
||||
from launch.actions import IncludeLaunchDescription, GroupAction, TimerAction
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
from launch.actions import DeclareLaunchArgument
|
||||
from launch.conditions import UnlessCondition, IfCondition
|
||||
from launch.substitutions import LaunchConfiguration
|
||||
from launch.substitutions import TextSubstitution
|
||||
from launch_ros.descriptions import ComposableNode
|
||||
|
||||
|
||||
def load_yaml(file_path):
|
||||
with open(file_path, "r") as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
def merge_params(default_params, yaml_params):
|
||||
for key, value in yaml_params.items():
|
||||
if key in default_params:
|
||||
default_params[key] = value
|
||||
return default_params
|
||||
|
||||
|
||||
def convert_value(value):
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
if value.lower() == "true":
|
||||
return True
|
||||
elif value.lower() == "false":
|
||||
return False
|
||||
return value
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
# Include launch files
|
||||
package_dir = get_package_share_directory("orbbec_camera")
|
||||
launch_file_dir = os.path.join(
|
||||
package_dir, "examples/gmsl_camera"
|
||||
)
|
||||
config_file_dir = os.path.join(package_dir, "config")
|
||||
secondary_config_file_path = os.path.join(config_file_dir, "camera_secondary_params.yaml")
|
||||
|
||||
attach_to_shared_component_container_arg = LaunchConfiguration(
|
||||
"attach_to_shared_component_container", default=False
|
||||
)
|
||||
component_container_name_arg = LaunchConfiguration(
|
||||
"component_container_name", default="shared_orbbec_container"
|
||||
)
|
||||
|
||||
shared_orbbec_container = Node(
|
||||
name=component_container_name_arg,
|
||||
package="rclcpp_components",
|
||||
executable="component_container_mt",
|
||||
output="screen",
|
||||
condition=UnlessCondition(attach_to_shared_component_container_arg),
|
||||
)
|
||||
|
||||
attach_to_shared_component_container_arg = TextSubstitution(text="true")
|
||||
|
||||
launch1_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, "gemini_330_gmsl.launch.py")
|
||||
),
|
||||
launch_arguments={
|
||||
"camera_name": "camera_01",
|
||||
"usb_port": "gmsl2-1",
|
||||
"device_num": "2",
|
||||
"sync_mode": "secondary_synced",
|
||||
"gmsl_trigger_fps": "3000",
|
||||
"enable_gmsl_trigger": "true",
|
||||
"config_file_path": secondary_config_file_path,
|
||||
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
|
||||
"component_container_name": component_container_name_arg,
|
||||
}.items(),
|
||||
)
|
||||
|
||||
launch2_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, "gemini_330_gmsl.launch.py")
|
||||
),
|
||||
launch_arguments={
|
||||
"camera_name": "camera_02",
|
||||
"usb_port": "gmsl2-3",
|
||||
"device_num": "2",
|
||||
"sync_mode": "secondary_synced",
|
||||
"config_file_path": secondary_config_file_path,
|
||||
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
|
||||
"component_container_name": component_container_name_arg,
|
||||
}.items(),
|
||||
)
|
||||
|
||||
# Launch description
|
||||
ld = LaunchDescription(
|
||||
[
|
||||
shared_orbbec_container,
|
||||
TimerAction(period=0.0, actions=[GroupAction([launch2_include])]),
|
||||
TimerAction(period=2.0, actions=[GroupAction([launch1_include])]),
|
||||
]
|
||||
)
|
||||
|
||||
return ld
|
||||
@@ -1,81 +0,0 @@
|
||||
# Using interleave_ae with Gemini330 series cameras
|
||||
|
||||
> This section describes how to use interleave_ae in Gemini 330 series cameras (minimum camera firmware version [1.4.00](https://www.orbbec.com/docs/g330-firmware-release/))
|
||||
|
||||
## Parameter Introduction
|
||||
|
||||
The interleave_ae related parameters are set in [gemini_330_series.launch.py](../../launch/gemini_330_series.launch.py)
|
||||
|
||||
* `interleave_ae_mode` : Set laser or hdr interleave.
|
||||
|
||||
* `interleave_frame_enable` : enable interleave frame mode.
|
||||
|
||||
* `interleave_skip_enable` : enable skip frame mode.
|
||||
|
||||
* `interleave_skip_index` : Set 0 for skip pattern ir, set 1 for skip flood ir.
|
||||
|
||||
### interleave hdr
|
||||
|
||||
When the `interleave_ae_mode` parameter is set to `hdr` and `interleave_frame_enable `is set to `true`, interleave hdr will be enabled
|
||||
|
||||
* `hdr_index1_laser_control` : Frame 1 laser switch settings.
|
||||
|
||||
* `hdr_index1_depth_exposure` : Frame 1 depth exposure value setting, not in AE mode.
|
||||
|
||||
* `hdr_index1_depth_gain` : Frame 1 depth gain value setting, not in AE mode.
|
||||
|
||||
* `hdr_index1_ir_brightness` : Frame 1 ir gain value setting.
|
||||
|
||||
* `hdr_index1_ir_ae_max_exposure` : Frame 1 ir maximum exposure value setting in AE (auto exposure).
|
||||
|
||||
* `hdr_index0_laser_control`: Frame 0 laser switch settings.
|
||||
|
||||
* `hdr_index0_depth_exposure`: Frame 0 depth exposure value setting, not in AE mode.
|
||||
|
||||
* `hdr_index0_depth_gain` : Frame 0 depth gain value setting, not in AE mode.
|
||||
|
||||
* `hdr_index0_ir_brightness` : Frame 0 ir gain value setting.
|
||||
|
||||
* `hdr_index0_ir_ae_max_exposure` : Frame 0 ir maximum exposure value setting in AE (auto exposure).
|
||||
|
||||
### interleave laser
|
||||
|
||||
When the `interleave_ae_mode` parameter is set to `laser` and `interleave_frame_enable `is set to `true`, interleave laser will be enabled
|
||||
|
||||
* `laser_index1_laser_control` : Frame 1 laser switch settings.
|
||||
|
||||
* `laser_index1_depth_exposure` : Frame 1 depth exposure value setting, not in AE mode.
|
||||
|
||||
* `laser_index1_depth_gain` : Frame 1 depth gain value setting, not in AE mode.
|
||||
|
||||
* `laser_index1_ir_brightness` : Frame 1 ir gain value setting.
|
||||
|
||||
* `laser_index1_ir_ae_max_exposure` : Frame 1 ir maximum exposure value setting in AE (auto exposure).
|
||||
|
||||
* `laser_index0_laser_control` : Frame 0 laser switch settings.
|
||||
|
||||
* `laser_index0_depth_exposure` : Frame 0 depth exposure value setting, not in AE mode.
|
||||
|
||||
* `laser_index0_depth_gain` : Frame 0 depth gain value setting, not in AE mode.
|
||||
|
||||
* `laser_index0_ir_brightness` : Frame 0 ir gain value setting.
|
||||
|
||||
* `laser_index0_ir_ae_max_exposure` : Frame 0 ir maximum exposure value setting in AE (auto exposure).
|
||||
|
||||
## Run the launch
|
||||
|
||||
Setting the interleave_ae parameter,`colcon build` again and run launch
|
||||
|
||||
```bash
|
||||
ros2 launch orbbec_camera gemini_330_series.launch.py
|
||||
```
|
||||
|
||||
#### Example Visualization
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
## Multi_camera_synced + Interleave_ae
|
||||
|
||||
Please refer to [multi_camera_synced](../multi_camera_synced/README.MD) and [Parameter Introduction](#parameter-introduction)
|
||||
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 234 KiB |
@@ -1,86 +0,0 @@
|
||||
## Reducing CPU Usage with Orbbec ROS Package
|
||||
|
||||
This document outlines strategies for minimizing CPU usage in the **OrbbecSDK_ROS2 v2** environment when using **Gemini 330 series cameras**. The firmware version must be **no lower than 1.4.10**, and `device` should be set to **Default**.
|
||||
|
||||
### Recommended Settings for Lower CPU Usage
|
||||
|
||||
To achieve the lowest possible CPU usage in OrbbecSDK_ROS2, it is recommended to configure the following parameters.
|
||||
|
||||
| Parameter | Recommendation | Note |
|
||||
| :--------------: | :------------------------------------: | :--------------------------------------------: |
|
||||
| `uvc_backend` | `v4l2` | Lower CPU usage compared to `libuvc` |
|
||||
| `color_format` | `RGB` | Lower CPU usage than `MJPG` |
|
||||
| `filter` | Only `hardware_noise_removal_filter` | Other filters significantly increase CPU usage |
|
||||
|
||||
### Launch Files Used for Testing
|
||||
|
||||
* `gemini_330_series_lower_cpu_usage.launch.py`
|
||||
* `multi_camera_lower_cpu_usage.launch.py`
|
||||
|
||||
### Test environment
|
||||
|
||||
#### Hardware Configuration
|
||||
|
||||
* **CPU**: Intel i7-8700 @ 3.20GHz
|
||||
* **Memory**: 24 GB
|
||||
* **Storage**: Micron 2200S NVMe 256GB
|
||||
* **GPU**: NVIDIA GeForce GTX 1660Ti
|
||||
* **OS**: Ubuntu22.04
|
||||
|
||||
#### ROS Configuration
|
||||
|
||||
* **ROS Version**: ROS2 Humble
|
||||
* **SDK Version**: OrbbecSDK_ROS2 v2.2.1
|
||||
|
||||
#### Camera Setup
|
||||
|
||||
* Devices: 2x Gemini 335, 1x Gemini 336, 1x Gemini 336L
|
||||
* Firmware Version: 1.4.10
|
||||
|
||||
### Test Setup
|
||||
|
||||
* **Stream Settings:**
|
||||
* Depth / IR Left / IR Right: 848×480 @ 30fps
|
||||
* Color: 848×480 @ 30fps
|
||||
|
||||
Note: The following CPU usage data focuses on `uvc_backend`, `color_format` and various filter combinations.
|
||||
|
||||
### Test Results
|
||||
|
||||
### 1. `uvc_backend` Comparison (RGB format)
|
||||
|
||||
| libuvc CPU Usage | v4l2 CPU Usage | Absolute Change |
|
||||
| :--------------: | :------------: | :-------------: |
|
||||
| 182.8% | 118.8% | -64.0% |
|
||||
|
||||
The CPU usage can be significantly reduced with v4l2 backend. In our implementation, v4l2 works without requiring any patches to the Linux kernel, allowing users to easily switch between v4l2 and libuvc and maintaining full compatibility with standard Linux distributions.
|
||||
|
||||
### 2. `color_format` Comparison (MJPG vs RGB)
|
||||
|
||||
| Backend | MJPG CPU Usage | RGB CPU Usage | Absolute Change |
|
||||
| :-----: | :------------: | :-----------: | :-------------: |
|
||||
| libuvc | 347.7% | 182.8% | -164.9% |
|
||||
| v4l2 | 170.0% | 118.8% | -51.2% |
|
||||
|
||||
The CPU usage can be reduced if the RGB format is selected instead of MJPG, since the decoding of MJPG image will consume the host CPU resource.
|
||||
|
||||
### 3. Filter Configuration Impact
|
||||
|
||||
| Filters Applied | libuvc CPU Usage | CPU Usage Increase | v4l2 CPU Usage | CPU Usage Increase |
|
||||
| ----------------------------------------------------- | ---------------- | ------------------ | -------------- | ------------------ |
|
||||
| No Filter (benchmark) | 182.8% | 0.0%(benchmark) | 118.8% | 0.0%(benchmark) |
|
||||
| `(software)noise_removal_filter` | 218.0% | +35.2% | 128.5% | +9.7% |
|
||||
| `(software)noise_removal_filter + spatial_filter` | 469.6% | +286.8% | 336.7% | +217.9% |
|
||||
| `hardware_noise_removal_filter` | 186.3% | +3.5% | 115.4% | -3.4% |
|
||||
| `hardware_noise_removal_filter + spatial_filter` | 251.3% | +68.5% | 152.5% | +33.7% |
|
||||
|
||||
Based on the test results, using only the `hardware_noise_removal_filter` results in a negligible change in CPU usage for both `libuvc` (+3.5%) and `v4l2` (-3.4%) compared to the no-filter benchmark, as this filter runs internally on the camera hardware. In contrast, other filters execute on the host system. Adding the `spatial_filter` to the hardware filter leads to a moderate increase in CPU usage, while applying the software-based `noise_removal_filter` —either alone or combined with `spatial_filter` —significantly increases CPU load. To maintain low CPU usage, it is recommended to avoid software-based filters and rely solely on the `hardware_noise_removal_filter`.
|
||||
|
||||
## Further Optimization
|
||||
|
||||
| Parameter | Recommendation | Note |
|
||||
| :----------------------------: | :----------------------------------------------: | :---------------------------------------------: |
|
||||
| `depth_registration` | `false` or `true` with `align_mode=HW` | Software alignment consumes more CPU |
|
||||
| `enable_point_cloud` | `false` | Disabling point cloud reduces CPU usage |
|
||||
| `enable_colored_point_cloud` | `false` | Disabling colored point cloud reduces CPU usage |
|
||||
|
||||
@@ -1,262 +0,0 @@
|
||||
import os
|
||||
import yaml
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument, OpaqueFunction, GroupAction
|
||||
from launch.substitutions import LaunchConfiguration
|
||||
from launch_ros.actions import PushRosNamespace, ComposableNodeContainer, Node
|
||||
from launch_ros.descriptions import ComposableNode
|
||||
from launch.conditions import UnlessCondition
|
||||
from launch_ros.actions import LoadComposableNodes
|
||||
|
||||
def load_yaml(file_path):
|
||||
with open(file_path, 'r') as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
def merge_params(default_params, yaml_params):
|
||||
for key, value in yaml_params.items():
|
||||
if key in default_params:
|
||||
default_params[key] = value
|
||||
return default_params
|
||||
|
||||
|
||||
def convert_value(value):
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
if value.lower() == 'true':
|
||||
return True
|
||||
elif value.lower() == 'false':
|
||||
return False
|
||||
return value
|
||||
|
||||
|
||||
def load_parameters(context, args):
|
||||
default_params = {arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args}
|
||||
config_file_path = LaunchConfiguration('config_file_path').perform(context)
|
||||
if config_file_path:
|
||||
yaml_params = load_yaml(config_file_path)
|
||||
default_params = merge_params(default_params, yaml_params)
|
||||
skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
|
||||
return {
|
||||
key: (value if key in skip_convert else convert_value(value))
|
||||
for key, value in default_params.items()
|
||||
}
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
args = [
|
||||
DeclareLaunchArgument('camera_name', default_value='camera'),
|
||||
DeclareLaunchArgument('depth_registration', default_value='false'),
|
||||
DeclareLaunchArgument('serial_number', default_value=''),
|
||||
DeclareLaunchArgument('usb_port', default_value=''),
|
||||
DeclareLaunchArgument('device_num', default_value='1'),
|
||||
DeclareLaunchArgument('uvc_backend', default_value='v4l2'),#libuvc or v4l2
|
||||
DeclareLaunchArgument('point_cloud_qos', default_value='default'),
|
||||
DeclareLaunchArgument('enable_point_cloud', default_value='false'),
|
||||
DeclareLaunchArgument('enable_colored_point_cloud', default_value='false'),
|
||||
DeclareLaunchArgument('cloud_frame_id', default_value=''),
|
||||
DeclareLaunchArgument('connection_delay', default_value='10'),
|
||||
DeclareLaunchArgument('color_width', default_value='0'),
|
||||
DeclareLaunchArgument('color_height', default_value='0'),
|
||||
DeclareLaunchArgument('color_fps', default_value='0'),
|
||||
DeclareLaunchArgument('color_format', default_value='RGB'),
|
||||
DeclareLaunchArgument('enable_color', default_value='true'),
|
||||
DeclareLaunchArgument('color_qos', default_value='default'),
|
||||
DeclareLaunchArgument('color_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('enable_color_auto_exposure', default_value='true'),
|
||||
DeclareLaunchArgument('color_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('color_gain', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_color_auto_white_balance', default_value='true'),
|
||||
DeclareLaunchArgument('color_white_balance', default_value='-1'),
|
||||
DeclareLaunchArgument('color_ae_max_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('color_brightness', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_width', default_value='0'),
|
||||
DeclareLaunchArgument('depth_height', default_value='0'),
|
||||
DeclareLaunchArgument('depth_fps', default_value='0'),
|
||||
DeclareLaunchArgument('depth_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_depth', default_value='true'),
|
||||
DeclareLaunchArgument('depth_qos', default_value='default'),
|
||||
DeclareLaunchArgument('depth_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('left_ir_width', default_value='0'),
|
||||
DeclareLaunchArgument('left_ir_height', default_value='0'),
|
||||
DeclareLaunchArgument('left_ir_fps', default_value='0'),
|
||||
DeclareLaunchArgument('left_ir_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_left_ir', default_value='false'),
|
||||
DeclareLaunchArgument('left_ir_qos', default_value='default'),
|
||||
DeclareLaunchArgument('left_ir_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('right_ir_width', default_value='0'),
|
||||
DeclareLaunchArgument('right_ir_height', default_value='0'),
|
||||
DeclareLaunchArgument('right_ir_fps', default_value='0'),
|
||||
DeclareLaunchArgument('right_ir_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_right_ir', default_value='false'),
|
||||
DeclareLaunchArgument('right_ir_qos', default_value='default'),
|
||||
DeclareLaunchArgument('right_ir_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('enable_ir_auto_exposure', default_value='true'),
|
||||
DeclareLaunchArgument('ir_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('ir_gain', default_value='-1'),
|
||||
DeclareLaunchArgument('ir_ae_max_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('ir_brightness', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_sync_output_accel_gyro', default_value='false'),
|
||||
DeclareLaunchArgument('enable_accel', default_value='false'),
|
||||
DeclareLaunchArgument('accel_rate', default_value='200hz'),
|
||||
DeclareLaunchArgument('accel_range', default_value='4g'),
|
||||
DeclareLaunchArgument('enable_gyro', default_value='false'),
|
||||
DeclareLaunchArgument('gyro_rate', default_value='200hz'),
|
||||
DeclareLaunchArgument('gyro_range', default_value='1000dps'),
|
||||
DeclareLaunchArgument('liner_accel_cov', default_value='0.01'),
|
||||
DeclareLaunchArgument('angular_vel_cov', default_value='0.01'),
|
||||
DeclareLaunchArgument('publish_tf', default_value='true'),
|
||||
DeclareLaunchArgument('tf_publish_rate', default_value='0.0'),
|
||||
DeclareLaunchArgument('ir_info_url', default_value=''),
|
||||
DeclareLaunchArgument('color_info_url', default_value=''),
|
||||
DeclareLaunchArgument('log_level', default_value='none'),
|
||||
DeclareLaunchArgument('enable_publish_extrinsic', default_value='false'),
|
||||
DeclareLaunchArgument('enable_d2c_viewer', default_value='false'),
|
||||
DeclareLaunchArgument('disaparity_to_depth_mode', default_value='HW'),
|
||||
DeclareLaunchArgument('enable_ldp', default_value='true'),
|
||||
DeclareLaunchArgument('sync_mode', default_value='standalone'),
|
||||
DeclareLaunchArgument('depth_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('color_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('trigger2image_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('trigger_out_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('trigger_out_enabled', default_value='true'),
|
||||
DeclareLaunchArgument('frames_per_trigger', default_value='2'),
|
||||
DeclareLaunchArgument('software_trigger_period', default_value='33'), # ms
|
||||
DeclareLaunchArgument('enable_frame_sync', default_value='true'),
|
||||
DeclareLaunchArgument('ordered_pc', default_value='false'),
|
||||
DeclareLaunchArgument('enable_depth_scale', default_value='true'),
|
||||
DeclareLaunchArgument('enable_decimation_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_hdr_merge', default_value='false'),
|
||||
DeclareLaunchArgument('enable_sequence_id_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_threshold_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_hardware_noise_removal_filter', default_value='true'),
|
||||
DeclareLaunchArgument('enable_noise_removal_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_spatial_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_temporal_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_hole_filling_filter', default_value='false'),
|
||||
DeclareLaunchArgument('decimation_filter_scale', default_value='-1'),
|
||||
DeclareLaunchArgument('sequence_id_filter_id', default_value='-1'),
|
||||
DeclareLaunchArgument('threshold_filter_max', default_value='-1'),
|
||||
DeclareLaunchArgument('threshold_filter_min', default_value='-1'),
|
||||
DeclareLaunchArgument('noise_removal_filter_min_diff', default_value='256'),
|
||||
DeclareLaunchArgument('noise_removal_filter_max_size', default_value='80'),
|
||||
DeclareLaunchArgument('spatial_filter_alpha', default_value='-1.0'),
|
||||
DeclareLaunchArgument('spatial_filter_diff_threshold', default_value='-1'),
|
||||
DeclareLaunchArgument('spatial_filter_magnitude', default_value='-1'),
|
||||
DeclareLaunchArgument('spatial_filter_radius', default_value='-1'),
|
||||
DeclareLaunchArgument('temporal_filter_diff_threshold', default_value='-1.0'),
|
||||
DeclareLaunchArgument('temporal_filter_weight', default_value='-1.0'),
|
||||
DeclareLaunchArgument('hole_filling_filter_mode', default_value=''),
|
||||
DeclareLaunchArgument('hdr_merge_exposure_1', default_value='-1'),
|
||||
DeclareLaunchArgument('hdr_merge_gain_1', default_value='-1'),
|
||||
DeclareLaunchArgument('hdr_merge_exposure_2', default_value='-1'),
|
||||
DeclareLaunchArgument('hdr_merge_gain_2', default_value='-1'),
|
||||
DeclareLaunchArgument('align_mode', default_value='SW'),
|
||||
DeclareLaunchArgument('diagnostic_period', default_value='1.0'),
|
||||
DeclareLaunchArgument('enable_laser', default_value='true'),
|
||||
DeclareLaunchArgument('depth_precision', default_value=''),
|
||||
DeclareLaunchArgument('device_preset', default_value='Default'),
|
||||
DeclareLaunchArgument('retry_on_usb3_detection_failure', default_value='false'),
|
||||
DeclareLaunchArgument('laser_energy_level', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_sync_host_time', default_value='true'),
|
||||
DeclareLaunchArgument('time_domain', default_value='global'),# global, device, system
|
||||
DeclareLaunchArgument('enable_color_undistortion', default_value='false'),
|
||||
DeclareLaunchArgument('config_file_path', default_value=''),
|
||||
DeclareLaunchArgument('enable_heartbeat', default_value='false'),
|
||||
DeclareLaunchArgument('gmsl_trigger_fps', default_value='3000'),
|
||||
DeclareLaunchArgument('enable_gmsl_trigger', default_value='false'),
|
||||
DeclareLaunchArgument('disparity_range_mode', default_value='-1'),
|
||||
DeclareLaunchArgument('disparity_search_offset', default_value='-1'),
|
||||
DeclareLaunchArgument('disparity_offset_config', default_value='false'),
|
||||
DeclareLaunchArgument('offset_index0', default_value='-1'),
|
||||
DeclareLaunchArgument('offset_index1', default_value='-1'),
|
||||
DeclareLaunchArgument('frame_aggregate_mode', default_value='ANY'), # full_frame, color_frame, ANY or disable
|
||||
DeclareLaunchArgument('interleave_ae_mode', default_value='laser'), # 'hdr' or 'laser'
|
||||
DeclareLaunchArgument('interleave_frame_enable', default_value='false'),
|
||||
DeclareLaunchArgument('interleave_skip_enable', default_value='false'),
|
||||
DeclareLaunchArgument('interleave_skip_index', default_value='1'), # 0:skip pattern ir 1: skip flood ir
|
||||
|
||||
DeclareLaunchArgument('hdr_index1_laser_control', default_value='1'),#interleave_hdr_param
|
||||
DeclareLaunchArgument('hdr_index1_depth_exposure', default_value='1'),
|
||||
DeclareLaunchArgument('hdr_index1_depth_gain', default_value='16'),
|
||||
DeclareLaunchArgument('hdr_index1_ir_brightness', default_value='20'),
|
||||
DeclareLaunchArgument('hdr_index1_ir_ae_max_exposure', default_value='2000'),
|
||||
DeclareLaunchArgument('hdr_index0_laser_control', default_value='1'),
|
||||
DeclareLaunchArgument('hdr_index0_depth_exposure', default_value='7500'),
|
||||
DeclareLaunchArgument('hdr_index0_depth_gain', default_value='16'),
|
||||
DeclareLaunchArgument('hdr_index0_ir_brightness', default_value='60'),
|
||||
DeclareLaunchArgument('hdr_index0_ir_ae_max_exposure', default_value='10000'),
|
||||
|
||||
DeclareLaunchArgument('laser_index1_laser_control', default_value='0'),#interleave_laser_param
|
||||
DeclareLaunchArgument('laser_index1_depth_exposure', default_value='3000'),
|
||||
DeclareLaunchArgument('laser_index1_depth_gain', default_value='16'),
|
||||
DeclareLaunchArgument('laser_index1_ir_brightness', default_value='60'),
|
||||
DeclareLaunchArgument('laser_index1_ir_ae_max_exposure', default_value='17000'),
|
||||
DeclareLaunchArgument('laser_index0_laser_control', default_value='1'),
|
||||
DeclareLaunchArgument('laser_index0_depth_exposure', default_value='3000'),
|
||||
DeclareLaunchArgument('laser_index0_depth_gain', default_value='16'),
|
||||
DeclareLaunchArgument('laser_index0_ir_brightness', default_value='60'),
|
||||
DeclareLaunchArgument('laser_index0_ir_ae_max_exposure', default_value='30000'),
|
||||
DeclareLaunchArgument('use_intra_process_comms', default_value='false'),
|
||||
DeclareLaunchArgument('attach_component_container_enable', default_value='false'),
|
||||
DeclareLaunchArgument('attach_component_container_name', default_value='orbbec_container'),
|
||||
|
||||
]
|
||||
|
||||
def get_params(context, args):
|
||||
return [load_parameters(context, args)]
|
||||
|
||||
def create_node_action(context, args):
|
||||
params = get_params(context, args)
|
||||
ros_distro = os.environ.get("ROS_DISTRO", "humble")
|
||||
if ros_distro == "foxy":
|
||||
return [
|
||||
Node(
|
||||
package="orbbec_camera",
|
||||
executable="orbbec_camera_node",
|
||||
name="ob_camera_node",
|
||||
namespace=LaunchConfiguration("camera_name"),
|
||||
parameters=params,
|
||||
output="screen",
|
||||
)
|
||||
]
|
||||
else:
|
||||
attach_to_shared_component_container_arg = LaunchConfiguration('attach_to_shared_component_container', default=False)
|
||||
component_container_name_arg = LaunchConfiguration('component_container_name', default='orbbec_container')
|
||||
|
||||
orbbec_container = Node(
|
||||
name=component_container_name_arg,
|
||||
package='rclcpp_components',
|
||||
executable='component_container_mt',
|
||||
output='screen',
|
||||
condition=UnlessCondition(attach_to_shared_component_container_arg)
|
||||
)
|
||||
return [
|
||||
orbbec_container,
|
||||
LoadComposableNodes(
|
||||
target_container=component_container_name_arg,
|
||||
composable_node_descriptions=[
|
||||
ComposableNode(
|
||||
namespace=LaunchConfiguration("camera_name"),
|
||||
name=LaunchConfiguration("camera_name"),
|
||||
package='orbbec_camera',
|
||||
plugin='orbbec_camera::OBCameraNodeDriver',
|
||||
parameters=params,
|
||||
extra_arguments=[{'use_intra_process_comms': LaunchConfiguration("use_intra_process_comms")}],
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
return LaunchDescription(
|
||||
args + [
|
||||
OpaqueFunction(function=lambda context: create_node_action(context, args))
|
||||
]
|
||||
)
|
||||
@@ -1,150 +0,0 @@
|
||||
import os
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
from launch import LaunchDescription
|
||||
from launch_ros.actions import Node,LoadComposableNodes
|
||||
from launch.actions import IncludeLaunchDescription, GroupAction, TimerAction
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
from launch.actions import DeclareLaunchArgument
|
||||
from launch.conditions import UnlessCondition, IfCondition
|
||||
from launch.substitutions import LaunchConfiguration
|
||||
from launch.substitutions import TextSubstitution
|
||||
from launch_ros.descriptions import ComposableNode
|
||||
|
||||
def load_yaml(file_path):
|
||||
with open(file_path, 'r') as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
def merge_params(default_params, yaml_params):
|
||||
for key, value in yaml_params.items():
|
||||
if key in default_params:
|
||||
default_params[key] = value
|
||||
return default_params
|
||||
|
||||
|
||||
def convert_value(value):
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
if value.lower() == 'true':
|
||||
return True
|
||||
elif value.lower() == 'false':
|
||||
return False
|
||||
return value
|
||||
|
||||
|
||||
def load_parameters(context, args):
|
||||
default_params = {arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args}
|
||||
config_file_path = LaunchConfiguration('config_file_path').perform(context)
|
||||
if config_file_path:
|
||||
yaml_params = load_yaml(config_file_path)
|
||||
default_params = merge_params(default_params, yaml_params)
|
||||
skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
|
||||
return {
|
||||
key: (value if key in skip_convert else convert_value(value))
|
||||
for key, value in default_params.items()
|
||||
}
|
||||
def generate_launch_description():
|
||||
# Include launch files
|
||||
package_dir = get_package_share_directory("orbbec_camera")
|
||||
launch_file_dir = os.path.join(package_dir, "examples/lower_cpu_usage")
|
||||
|
||||
attach_to_shared_component_container_arg = LaunchConfiguration('attach_to_shared_component_container', default=False)
|
||||
component_container_name_arg = LaunchConfiguration('component_container_name', default='shared_orbbec_container')
|
||||
|
||||
shared_orbbec_container = Node(
|
||||
name=component_container_name_arg,
|
||||
package="rclcpp_components",
|
||||
executable="component_container_mt",
|
||||
output="screen",
|
||||
condition=UnlessCondition(attach_to_shared_component_container_arg),
|
||||
)
|
||||
|
||||
attach_to_shared_component_container_arg = TextSubstitution(text="true")
|
||||
|
||||
launch1_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, "gemini_330_series_lower_cpu_usage.launch.py")
|
||||
),
|
||||
launch_arguments={
|
||||
"camera_name": "camera_01",
|
||||
"usb_port": "2-7",
|
||||
"device_num": "4",
|
||||
"sync_mode": "standalone",
|
||||
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
|
||||
"component_container_name": component_container_name_arg,
|
||||
}.items(),
|
||||
)
|
||||
launch2_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, "gemini_330_series_lower_cpu_usage.launch.py")
|
||||
),
|
||||
launch_arguments={
|
||||
"camera_name": "camera_02",
|
||||
"usb_port": "2-2",
|
||||
"device_num": "4",
|
||||
"sync_mode": "standalone",
|
||||
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
|
||||
"component_container_name": component_container_name_arg,
|
||||
}.items(),
|
||||
)
|
||||
launch3_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, "gemini_330_series_lower_cpu_usage.launch.py")
|
||||
),
|
||||
launch_arguments={
|
||||
"camera_name": "camera_03",
|
||||
"usb_port": "2-6",
|
||||
"device_num": "4",
|
||||
"sync_mode": "standalone",
|
||||
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
|
||||
"component_container_name": component_container_name_arg,
|
||||
}.items(),
|
||||
)
|
||||
launch4_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, "gemini_330_series_lower_cpu_usage.launch.py")
|
||||
),
|
||||
launch_arguments={
|
||||
"camera_name": "camera_04",
|
||||
"usb_port": "2-3",
|
||||
"device_num": "4",
|
||||
"sync_mode": "standalone",
|
||||
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
|
||||
"component_container_name": component_container_name_arg,
|
||||
}.items(),
|
||||
)
|
||||
|
||||
delayed_left_camera = TimerAction(
|
||||
period=2.0,
|
||||
actions=[launch1_include],
|
||||
)
|
||||
delayed_right_camera = TimerAction(
|
||||
period=4.0,
|
||||
actions=[launch2_include],
|
||||
)
|
||||
delayed_rear_camera = TimerAction(
|
||||
period=6.0,
|
||||
actions=[launch3_include],
|
||||
)
|
||||
delayed_front_camera = TimerAction(
|
||||
period=8.0,
|
||||
actions=[launch4_include],
|
||||
)
|
||||
ld = LaunchDescription(
|
||||
[
|
||||
shared_orbbec_container,
|
||||
TimerAction(period=0.0, actions=[GroupAction([delayed_left_camera])]),
|
||||
TimerAction(period=2.0, actions=[GroupAction([delayed_right_camera])]),
|
||||
TimerAction(period=4.0, actions=[GroupAction([delayed_rear_camera])]),
|
||||
TimerAction(period=6.0, actions=[GroupAction([delayed_front_camera])]),
|
||||
]
|
||||
)
|
||||
|
||||
return ld
|
||||
@@ -1,91 +0,0 @@
|
||||
# Multi-Camera
|
||||
|
||||
- To get the `usb_port` of the camera, plug in the camera and run the following command in the terminal:
|
||||
|
||||
```bash
|
||||
ros2 run orbbec_camera list_devices_node
|
||||
```
|
||||
|
||||
- Set the `device_num` parameter to the number of cameras you have.
|
||||
- Go to the `OrbbecSDK_ROS2/launch/multi_xxx.launch.py` file and change the `usb_port`.
|
||||
- Don't forget to put the `include` tag inside the `group` tag.
|
||||
Otherwise, the parameter values of different cameras may become contaminated.
|
||||
|
||||
```python
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, GroupAction, ExecuteProcess
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
from launch_ros.actions import Node
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
import os
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
# Include launch files
|
||||
package_dir = get_package_share_directory('orbbec_camera')
|
||||
launch_file_dir = os.path.join(package_dir, 'launch')
|
||||
launch1_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, 'gemini2L.launch.py')
|
||||
),
|
||||
launch_arguments={
|
||||
'camera_name': 'camera_01',
|
||||
'usb_port': '6-2.4.4.2', # replace your usb port here
|
||||
'device_num': '2'
|
||||
}.items()
|
||||
)
|
||||
|
||||
launch2_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, 'gemini2L.launch.py')
|
||||
),
|
||||
launch_arguments={
|
||||
'camera_name': 'camera_02',
|
||||
'usb_port': '6-2.4.1', # replace your usb port here
|
||||
'device_num': '2'
|
||||
}.items()
|
||||
)
|
||||
|
||||
# If you need more cameras, just add more launch_include here, and change the usb_port and device_num
|
||||
|
||||
# Launch description
|
||||
ld = LaunchDescription([
|
||||
GroupAction([launch1_include]),
|
||||
GroupAction([launch2_include]),
|
||||
])
|
||||
|
||||
return ld
|
||||
|
||||
```
|
||||
|
||||
- To launch the cameras, run the following command:
|
||||
|
||||
```bash
|
||||
ros2 launch orbbec_camera multi_camera.launch.py
|
||||
```
|
||||
|
||||
## No Data Stream from Multiple Cameras
|
||||
|
||||
**Insufficient Power Supply**:
|
||||
|
||||
- Ensure that each camera is connected to a separate hub.
|
||||
- Use a powered hub to provide sufficient power to each camera.
|
||||
|
||||
**High Resolution**:
|
||||
|
||||
- Try lowering the resolution to resolve data stream issues.
|
||||
|
||||
**Increase usbfs_memory_mb Value**:
|
||||
|
||||
- Increase the `usbfs_memory_mb` value to 128MB (this is a reference value and can be adjusted based on your system’s needs)
|
||||
by running the following command:
|
||||
|
||||
```bash
|
||||
echo 128 | sudo tee /sys/module/usbcore/parameters/usbfs_memory_mb
|
||||
```
|
||||
|
||||
- To make this change permanent, check [this link](https://github.com/OpenKinect/libfreenect2/issues/807).
|
||||
|
||||
## Image topic frame rate too low from Multiple Cameras
|
||||
|
||||
Refer to the [Fast DDS Configuration](./docs/fastdds_tuning.md) file.
|
||||
@@ -1,55 +0,0 @@
|
||||
# Multi_camera synced Instructions
|
||||
|
||||
> The purpose of this document is to explain how to use multi-camera synced with OrbbecSDK_ROS2
|
||||
|
||||
## Setup instructions
|
||||
|
||||
* Please read the Multi-Camera Synchronization Setup Guide:[Multi-Camera Synchronization Setup](https://www.orbbec.com/docs/set-up-cameras-for-external-synchronization_v1-2/)
|
||||
* Make sure the camera is correctly connected to the multi-camera synchronizer.
|
||||
|
||||

|
||||
|
||||
### Checking camera port with OrbbecSDK_ROS2
|
||||
|
||||
```bash
|
||||
ros2 run orbbec_camera list_devices_node
|
||||
```
|
||||
|
||||
### OrbbecSDK_ROS2 multi-camera synced configuration
|
||||
|
||||
Open multi_camera_synced.launch.py, and configure the camera settings as shown below:
|
||||
|
||||

|
||||
|
||||
1. `gemini_330_series.launch.py` is the launch file for starting the camera.
|
||||
|
||||
2. Set `camera_name` to `G330_0`. For example, the published color image topic will be `/G330_0/color/image_raw`.
|
||||
|
||||
3. Set `usb_port` to `2-2`, indicating that the camera device on port `2-2` is being used. This value can be found in the output of the `ros2 run orbbec_camera list_devices_node` command.
|
||||
|
||||
4. Set `device_num` to `2`, meaning two cameras will be used.
|
||||
|
||||
5. Set `sync_mode` to `primary` to indicate that the `2-7` camera device is in primary mode. The multi-camera sync mode options can be found in the figure below.
|
||||
|
||||
6. Parameters from the `config_file_path` can override the parameters set in `gemini_330_series.launch.py` (optional).
|
||||
|
||||
7. For slave cameras, set `trigger_out_enabled` to false.
|
||||
|
||||
| **Pattern Nam**e | **Setting effect description** |
|
||||
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| free_run | -Support different frame rate settings<br />The -8-pin synchronization interface does not support external output of synchronization-related signals |
|
||||
| standalone(default) | ● Same as Primary by default<br />● Built-in RGBD frame synchronization<br />● 8-pin synchronous interface does not output signals to the outside by default |
|
||||
| primary | ● Set as primary camera<br />● 8-pin synchronous interface output signal to external device |
|
||||
| secondary | ● Set as secondary (passive synchronization; When there is a hardware continuous trigger signal input from the outside and the continuous trigger signal matches the currently set frame rate, the image is collected according to the external trigger signal; When there is no external trigger signal, the flow is stopped)<br />● 8-pin synchronous interface output signal to external device |
|
||||
| secondary_synced | ● Set to secondary synchronization (passive synchronization; When there is a hardware continuous trigger signal input from the outside and the continuous trigger signal matches the currently set frame rate, the image is collected according to the external trigger signal; When there is no external trigger signal, the image is collected according to the internal trigger signal at the set frame rate)<br />● 8-pin synchronous interface output signal to external device |
|
||||
| hardware_triggering | ● Set as hardware trigger (passive trigger; When there is a hardware trigger signal input from the outside and the trigger signal time interval is not less than the current upper limit, the image is collected according to the external trigger signal; When there is no external trigger signal, the image is not collected)<br />● 8-pin synchronous interface output signal to external device |
|
||||
| software_triggering | ● Set as software trigger (passive trigger; When there is a trigger command input from the host computer and the trigger command time interval is not less than the current upper limit, the image is collected according to the trigger command; When there is no trigger command, the image is not collected)<br />● 8-pin synchronous interface output signal to external device |
|
||||
|
||||
* The master camera should be launched last.
|
||||
* Ideally, there should be a 2-second delay between starting each camera.
|
||||
|
||||
### Run the following command to start the multi-camera synced
|
||||
|
||||
```bash
|
||||
ros2 launch orbbec_camera multi_camera_synced.launch.py
|
||||
```
|
||||
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 87 KiB |
@@ -1,159 +0,0 @@
|
||||
# Multi_camera_synced_verification_tool
|
||||
|
||||
> This article will introduce how to verify the synchronization accuracy of multi-camera synchronization.
|
||||
>
|
||||
> First, please see how to use [multi_camera_synced](../multi_camera_synced/README.MD).
|
||||
|
||||
## Directory Structure
|
||||
|
||||
```plaintext
|
||||
.
|
||||
├── multicamera_sync/
|
||||
│ ├── output/
|
||||
│ ├──20250218102900/
|
||||
│ ├──DevicesInfo.txt
|
||||
│ ├──StreamProfileInfo.txt
|
||||
│ ├── Python/
|
||||
│ ├──Config.ini
|
||||
├── gemini_330_series_synced_verify.launch.py
|
||||
├── multi_camera_synced_verify.launch.py
|
||||
```
|
||||
|
||||
### multicamera_sync
|
||||
|
||||
* `gemini_330_series_synced_verify.launch.py` : Single camera runs launch, which provides the camera running node for multi_camera_synced_verify.launch.py.
|
||||
* `multi_camera_synced_verify.launch.py` : Multi camer synced + launch of save_rgbir tool.
|
||||
|
||||
#### output
|
||||
|
||||
> The `ouput` folder is the folder where the camera pictures are output
|
||||
|
||||
In the output example provided
|
||||
|
||||
* `20250218102900` : Represents the camera image information collected at 10:29:00 on February 18, 2025.
|
||||
* `TotalModeFrames` : Camera image information storage directory.
|
||||
* `results-2025-02-25_163648` : The result after analyzing and matching the image information of TotalModeFrames.
|
||||
* `DevicesInfo.txt` : Camera equipment basic information (need to be modified).
|
||||
* `StreamProfileInfo.txt` : Camera video stream information (no need to modify).
|
||||
|
||||
#### Python
|
||||
|
||||
* `Config.ini` : Configuration file for Python analysis script (need to be modified).
|
||||
|
||||
## Preparation for operation
|
||||
|
||||
### save_rgbir node
|
||||
|
||||
Edit multi_camera_synced_verify.launch.py and fill in the activated camera device,we can find that save_rgbir is started at the end.
|
||||
|
||||
> IMPORTANT
|
||||
>
|
||||
> If you are using ROS 2 Foxy, you need to launch the save_rgbir node first.
|
||||
|
||||
```python
|
||||
# Launch description
|
||||
ld = LaunchDescription(
|
||||
[
|
||||
shared_orbbec_container,
|
||||
TimerAction(period=0.0, actions=[GroupAction([launch2_include])]),
|
||||
TimerAction(period=2.0, actions=[GroupAction([launch1_include])]),
|
||||
# The primary camera should be launched at last
|
||||
TimerAction(period=6.0, actions=[GroupAction([save_rgbir])]),
|
||||
# save_rgbir is synced verification tool
|
||||
]
|
||||
)
|
||||
|
||||
return ld
|
||||
```
|
||||
|
||||
save_rgbir is a tool for saving images. The configuration file of this tool is in [multi_save_rgbir_params.json](../../config/tools/multisavergbir/multi_save_rgbir_params.json).
|
||||
|
||||
```json
|
||||
{
|
||||
"save_rgbir_params": {
|
||||
"time_domain": "global",
|
||||
"usb_ports": [
|
||||
"2-1",
|
||||
"2-3"
|
||||
],
|
||||
"camera_name": [
|
||||
"camera_01",
|
||||
"camera_02"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* `time_domain` : Timestamp Type
|
||||
* `usb_ports` : "primary", "secondary 1", "secondary 2", "secondary 3", fill in as many usb_ports as there are cameras
|
||||
* `camera_name` : The name of the camera setting, for example: camera_01
|
||||
|
||||
### DevicesInfo.txt
|
||||
|
||||
Edit DevicesInfo.txt. Only the `primarySerialNumber`, `index` and `serialNumber` parameters need to be changed. Other parameters do not need to be changed.Refer to the example of [20250218102900](./multicamera_sync/output/20250218102900).
|
||||
|
||||
* `primarySerialNumber` : The SN serial number of the primary camera
|
||||
* `index` : Camera index
|
||||
* `serialNumber` : The SN serial number of the camera
|
||||
|
||||
### Config.ini
|
||||
|
||||
Edit Config.ini.Modify `frameRate` and `tspRangeThreshold`.
|
||||
|
||||
## Run this example
|
||||
|
||||
### Run launch and save camera pictures
|
||||
|
||||
* First terminal
|
||||
|
||||
```bash
|
||||
ros2 launch orbbec_camera multi_camera_synced_verify.launch.py
|
||||
```
|
||||
|
||||
* Second terminal
|
||||
|
||||
```bash
|
||||
ros2 service call /save_rgbir/start_capture orbbec_camera_msgs/srv/SetInt32 '{data: 100}'
|
||||
```
|
||||
|
||||
When the terminal displays "over", the image is saved.A new multicamera_sync folder will be generated under the workspace.
|
||||
|
||||
#### Camera pictures naming format
|
||||
|
||||
Take [color_SNCP1E5420006D_Index0_g1739874543227_f0_s1739874543327_e50_d16_.jpg](./multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543227_f0_s1739874543327_e50_d16_.jpg) as an example
|
||||
|
||||
* `color` : This image is a snapshot of the color stream.
|
||||
* `SNCP1E5420006D` : The camera's SN serial number is CP1E5420006D.
|
||||
* `Index0` : Camera 0 (usually referred to primary camera).
|
||||
* `g1739874543227` : “g” represents the global timestamp, which means the global timestamp of this frame is 1739874543227.
|
||||
* `f0` : The 0th picture of the color stream acquisition of this camera.
|
||||
* `s1739874543327` : "s" represents the timestamp of the current system, which means the current system timestamp of this frame is 1739874543327.
|
||||
* `e50` : The exposure of this frame is 50.
|
||||
* `d16` : The gain of this frame is 16.
|
||||
|
||||
### Analyzing camera image data
|
||||
|
||||
You need to copy the modified [Python folder](./multicamera_sync/Python) to the new multi_camera_synced subdirectory, and copy the modified [DevicesInfo.txt ](./multicamera_sync/output/20250218102900/DevicesInfo.txt)and [StreamProfileInfo.txt](./multicamera_sync/output/20250218102900/StreamProfileInfo.txt) to the same level directory as the TotalModeFrames folder.
|
||||
|
||||
* Finally everything is ready, run the python script
|
||||
|
||||
```bash
|
||||
cd multicamera_sync/Python
|
||||
python3 SyncFramesMain.py
|
||||
```
|
||||
|
||||
After the operation is successful, you can view the synchronization effect in the `results folder`
|
||||
|
||||
## Files that need to be changed
|
||||
|
||||
### Analysis tools
|
||||
|
||||
* [multi_save_rgbir_params.json](../../config/tools/multisavergbir/multi_save_rgbir_params.json)
|
||||
* [DevicesInfo.txt ](./multicamera_sync/output/20250218102900/DevicesInfo.txt)
|
||||
* [Config.ini](./multicamera_sync/Python/Config.ini)
|
||||
|
||||
### Camera Configuration
|
||||
|
||||
[camera_params.yaml](../../config/camera_params.yaml)(Camera startup parameter settings)
|
||||
|
||||
[multi_camera_synced_verify.launch.py](./multi_camera_synced_verify.launch.py)
|
||||
@@ -1,318 +0,0 @@
|
||||
import os
|
||||
import yaml
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument, OpaqueFunction, GroupAction
|
||||
from launch.substitutions import LaunchConfiguration
|
||||
from launch_ros.actions import PushRosNamespace, ComposableNodeContainer, Node
|
||||
from launch_ros.descriptions import ComposableNode
|
||||
from launch.conditions import UnlessCondition
|
||||
from launch_ros.actions import LoadComposableNodes
|
||||
|
||||
def load_yaml(file_path):
|
||||
with open(file_path, 'r') as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
def merge_params(default_params, yaml_params):
|
||||
for key, value in yaml_params.items():
|
||||
if key in default_params:
|
||||
default_params[key] = value
|
||||
return default_params
|
||||
|
||||
|
||||
def convert_value(value):
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
if value.lower() == 'true':
|
||||
return True
|
||||
elif value.lower() == 'false':
|
||||
return False
|
||||
return value
|
||||
|
||||
|
||||
def load_parameters(context, args):
|
||||
default_params = {arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args}
|
||||
config_file_path = LaunchConfiguration('config_file_path').perform(context)
|
||||
if config_file_path:
|
||||
yaml_params = load_yaml(config_file_path)
|
||||
default_params = merge_params(default_params, yaml_params)
|
||||
skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
|
||||
return {
|
||||
key: (value if key in skip_convert else convert_value(value))
|
||||
for key, value in default_params.items()
|
||||
}
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
args = [
|
||||
DeclareLaunchArgument('camera_name', default_value='camera'),
|
||||
DeclareLaunchArgument('depth_registration', default_value='true'),
|
||||
DeclareLaunchArgument('serial_number', default_value=''),
|
||||
DeclareLaunchArgument('usb_port', default_value=''),
|
||||
DeclareLaunchArgument('device_num', default_value='1'),
|
||||
DeclareLaunchArgument('upgrade_firmware', default_value=''),
|
||||
DeclareLaunchArgument('preset_firmware_path', default_value=''),
|
||||
DeclareLaunchArgument('load_config_json_file_path', default_value=''),
|
||||
DeclareLaunchArgument('export_config_json_file_path', default_value=''),
|
||||
DeclareLaunchArgument('uvc_backend', default_value='libuvc'),#libuvc or v4l2
|
||||
DeclareLaunchArgument('point_cloud_qos', default_value='default'),
|
||||
DeclareLaunchArgument('enable_point_cloud', default_value='true'),
|
||||
DeclareLaunchArgument('enable_colored_point_cloud', default_value='false'),
|
||||
DeclareLaunchArgument('cloud_frame_id', default_value=''),
|
||||
DeclareLaunchArgument('connection_delay', default_value='10'),
|
||||
DeclareLaunchArgument('color_width', default_value='0'),
|
||||
DeclareLaunchArgument('color_height', default_value='0'),
|
||||
DeclareLaunchArgument('color_fps', default_value='0'),
|
||||
DeclareLaunchArgument('color_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_color', default_value='true'),
|
||||
DeclareLaunchArgument('color_qos', default_value='default'),
|
||||
DeclareLaunchArgument('color_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('enable_color_auto_exposure_priority', default_value='false'),
|
||||
DeclareLaunchArgument('color_rotation', default_value='-1'),#color rotation degree : 0, 90, 180, 270
|
||||
DeclareLaunchArgument('color_flip', default_value='false'),
|
||||
DeclareLaunchArgument('color_mirror', default_value='false'),
|
||||
DeclareLaunchArgument('color_ae_roi_left', default_value='-1'),
|
||||
DeclareLaunchArgument('color_ae_roi_left', default_value='-1'),
|
||||
DeclareLaunchArgument('color_ae_roi_right', default_value='-1'),
|
||||
DeclareLaunchArgument('color_ae_roi_top', default_value='-1'),
|
||||
DeclareLaunchArgument('color_ae_roi_bottom', default_value='-1'),
|
||||
DeclareLaunchArgument('color_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('color_gain', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_color_auto_white_balance', default_value='true'),
|
||||
DeclareLaunchArgument('color_white_balance', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_color_auto_exposure', default_value='true'),
|
||||
DeclareLaunchArgument('color_ae_max_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('color_brightness', default_value='-1'),
|
||||
DeclareLaunchArgument('color_sharpness', default_value='-1'),
|
||||
DeclareLaunchArgument('color_gamma', default_value='-1'),
|
||||
DeclareLaunchArgument('color_saturation', default_value='-1'),
|
||||
DeclareLaunchArgument('color_constrast', default_value='-1'),
|
||||
DeclareLaunchArgument('color_hue', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_color_backlight_compenstation', default_value='false'),
|
||||
DeclareLaunchArgument('color_powerline_freq', default_value=''),#disable ,50hz ,60hz ,auto
|
||||
DeclareLaunchArgument('enable_color_decimation_filter', default_value='false'),
|
||||
DeclareLaunchArgument('color_decimation_filter_scale', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_width', default_value='0'),
|
||||
DeclareLaunchArgument('depth_height', default_value='0'),
|
||||
DeclareLaunchArgument('depth_fps', default_value='0'),
|
||||
DeclareLaunchArgument('depth_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_depth', default_value='true'),
|
||||
DeclareLaunchArgument('depth_qos', default_value='default'),
|
||||
DeclareLaunchArgument('depth_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('enable_depth_auto_exposure_priority', default_value='false'),
|
||||
DeclareLaunchArgument('depth_precision', default_value=''),
|
||||
DeclareLaunchArgument('depth_rotation', default_value='-1'),#depth rotation degree : 0, 90, 180, 270
|
||||
DeclareLaunchArgument('depth_flip', default_value='false'),
|
||||
DeclareLaunchArgument('depth_mirror', default_value='false'),
|
||||
DeclareLaunchArgument('depth_ae_roi_left', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_ae_roi_right', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_ae_roi_top', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_ae_roi_bottom', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_brightness', default_value='-1'),
|
||||
DeclareLaunchArgument('left_ir_width', default_value='0'),
|
||||
DeclareLaunchArgument('left_ir_height', default_value='0'),
|
||||
DeclareLaunchArgument('left_ir_fps', default_value='0'),
|
||||
DeclareLaunchArgument('left_ir_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_left_ir', default_value='false'),
|
||||
DeclareLaunchArgument('left_ir_qos', default_value='default'),
|
||||
DeclareLaunchArgument('left_ir_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('left_ir_rotation', default_value='-1'),#left_ir rotation degree : 0, 90, 180, 270
|
||||
DeclareLaunchArgument('left_ir_flip', default_value='false'),
|
||||
DeclareLaunchArgument('left_ir_mirror', default_value='false'),
|
||||
DeclareLaunchArgument('enable_left_ir_sequence_id_filter', default_value='false'),
|
||||
DeclareLaunchArgument('left_ir_sequence_id_filter_id', default_value='-1'),
|
||||
DeclareLaunchArgument('right_ir_width', default_value='0'),
|
||||
DeclareLaunchArgument('right_ir_height', default_value='0'),
|
||||
DeclareLaunchArgument('right_ir_fps', default_value='0'),
|
||||
DeclareLaunchArgument('right_ir_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_right_ir', default_value='false'),
|
||||
DeclareLaunchArgument('right_ir_qos', default_value='default'),
|
||||
DeclareLaunchArgument('right_ir_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('right_ir_rotation', default_value='-1'),#right_ir rotation degree : 0, 90, 180, 270
|
||||
DeclareLaunchArgument('right_ir_flip', default_value='false'),
|
||||
DeclareLaunchArgument('right_ir_mirror', default_value='false'),
|
||||
DeclareLaunchArgument('enable_right_ir_sequence_id_filter', default_value='false'),
|
||||
DeclareLaunchArgument('right_ir_sequence_id_filter_id', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_ir_auto_exposure', default_value='true'),
|
||||
DeclareLaunchArgument('ir_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('ir_gain', default_value='-1'),
|
||||
DeclareLaunchArgument('ir_ae_max_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('ir_brightness', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_sync_output_accel_gyro', default_value='false'),
|
||||
DeclareLaunchArgument('enable_accel', default_value='false'),
|
||||
DeclareLaunchArgument('enable_accel_data_correction', default_value='true'),
|
||||
DeclareLaunchArgument('accel_rate', default_value='200hz'),
|
||||
DeclareLaunchArgument('accel_range', default_value='4g'),
|
||||
DeclareLaunchArgument('enable_gyro', default_value='false'),
|
||||
DeclareLaunchArgument('enable_gyro_data_correction', default_value='true'),
|
||||
DeclareLaunchArgument('gyro_rate', default_value='200hz'),
|
||||
DeclareLaunchArgument('gyro_range', default_value='1000dps'),
|
||||
DeclareLaunchArgument('liner_accel_cov', default_value='0.01'),
|
||||
DeclareLaunchArgument('angular_vel_cov', default_value='0.01'),
|
||||
DeclareLaunchArgument('publish_tf', default_value='true'),
|
||||
DeclareLaunchArgument('tf_publish_rate', default_value='0.0'),
|
||||
DeclareLaunchArgument('ir_info_url', default_value=''),
|
||||
DeclareLaunchArgument('color_info_url', default_value=''),
|
||||
# Network device settings: default enumerate_net_device is set to true, which will automatically enumerate network devices
|
||||
# If you do not want to automatically enumerate network devices,
|
||||
# you can set enumerate_net_device to true, net_device_ip to the device's IP address, and net_device_port to the default value of 8090
|
||||
DeclareLaunchArgument('enumerate_net_device', default_value='false'),
|
||||
DeclareLaunchArgument('net_device_ip', default_value=''),
|
||||
DeclareLaunchArgument('net_device_port', default_value='0'),
|
||||
DeclareLaunchArgument('exposure_range_mode', default_value='default'),#default, ultimate or regular
|
||||
DeclareLaunchArgument('log_level', default_value='none'),
|
||||
DeclareLaunchArgument('enable_publish_extrinsic', default_value='false'),
|
||||
DeclareLaunchArgument('enable_d2c_viewer', default_value='false'),
|
||||
DeclareLaunchArgument('disaparity_to_depth_mode', default_value='HW'),
|
||||
DeclareLaunchArgument('enable_ldp', default_value='true'),
|
||||
DeclareLaunchArgument('ldp_power_level', default_value='-1'),
|
||||
DeclareLaunchArgument('sync_mode', default_value='standalone'),
|
||||
DeclareLaunchArgument('depth_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('color_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('trigger2image_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('trigger_out_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('trigger_out_enabled', default_value='true'),
|
||||
DeclareLaunchArgument('frames_per_trigger', default_value='2'),
|
||||
DeclareLaunchArgument('software_trigger_period', default_value='33'), # ms
|
||||
DeclareLaunchArgument('enable_ptp_config', default_value='false'),#Only for Gemini 335Le
|
||||
DeclareLaunchArgument('enable_frame_sync', default_value='true'),
|
||||
DeclareLaunchArgument('ordered_pc', default_value='false'),
|
||||
DeclareLaunchArgument('enable_depth_scale', default_value='true'),
|
||||
DeclareLaunchArgument('enable_decimation_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_hdr_merge', default_value='false'),
|
||||
DeclareLaunchArgument('enable_sequence_id_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_threshold_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_hardware_noise_removal_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_noise_removal_filter', default_value='true'),
|
||||
DeclareLaunchArgument('enable_spatial_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_temporal_filter', default_value='false'),
|
||||
DeclareLaunchArgument('enable_disaparity_to_depth', default_value='true'),
|
||||
DeclareLaunchArgument('enable_hole_filling_filter', default_value='false'),
|
||||
DeclareLaunchArgument('decimation_filter_scale', default_value='-1'),
|
||||
DeclareLaunchArgument('sequence_id_filter_id', default_value='-1'),
|
||||
DeclareLaunchArgument('threshold_filter_max', default_value='-1'),
|
||||
DeclareLaunchArgument('threshold_filter_min', default_value='-1'),
|
||||
DeclareLaunchArgument('hardware_noise_removal_filter_threshold', default_value='-1.0'),
|
||||
DeclareLaunchArgument('noise_removal_filter_min_diff', default_value='256'),
|
||||
DeclareLaunchArgument('noise_removal_filter_max_size', default_value='80'),
|
||||
DeclareLaunchArgument('spatial_filter_alpha', default_value='-1.0'),
|
||||
DeclareLaunchArgument('spatial_filter_diff_threshold', default_value='-1'),
|
||||
DeclareLaunchArgument('spatial_filter_magnitude', default_value='-1'),
|
||||
DeclareLaunchArgument('spatial_filter_radius', default_value='-1'),
|
||||
DeclareLaunchArgument('temporal_filter_diff_threshold', default_value='-1.0'),
|
||||
DeclareLaunchArgument('temporal_filter_weight', default_value='-1.0'),
|
||||
DeclareLaunchArgument('hole_filling_filter_mode', default_value=''),
|
||||
DeclareLaunchArgument('hdr_merge_exposure_1', default_value='-1'),
|
||||
DeclareLaunchArgument('hdr_merge_gain_1', default_value='-1'),
|
||||
DeclareLaunchArgument('hdr_merge_exposure_2', default_value='-1'),
|
||||
DeclareLaunchArgument('hdr_merge_gain_2', default_value='-1'),
|
||||
DeclareLaunchArgument('align_mode', default_value='SW'),
|
||||
DeclareLaunchArgument('align_target_stream', default_value='COLOR'),# COLOR or DEPTH
|
||||
DeclareLaunchArgument('diagnostic_period', default_value='1.0'),
|
||||
DeclareLaunchArgument('enable_laser', default_value='true'),
|
||||
DeclareLaunchArgument('depth_precision', default_value=''),
|
||||
DeclareLaunchArgument('device_preset', default_value='Default'),
|
||||
DeclareLaunchArgument('retry_on_usb3_detection_failure', default_value='false'),
|
||||
DeclareLaunchArgument('laser_energy_level', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_sync_host_time', default_value='true'),
|
||||
DeclareLaunchArgument('time_domain', default_value='global'),# global, device, system
|
||||
DeclareLaunchArgument('enable_color_undistortion', default_value='false'),
|
||||
DeclareLaunchArgument('config_file_path', default_value=''),
|
||||
DeclareLaunchArgument('enable_heartbeat', default_value='false'),
|
||||
DeclareLaunchArgument('gmsl_trigger_fps', default_value='3000'),
|
||||
DeclareLaunchArgument('enable_gmsl_trigger', default_value='false'),
|
||||
DeclareLaunchArgument('disparity_range_mode', default_value='-1'),
|
||||
DeclareLaunchArgument('disparity_search_offset', default_value='-1'),
|
||||
DeclareLaunchArgument('disparity_offset_config', default_value='false'),
|
||||
DeclareLaunchArgument('offset_index0', default_value='-1'),
|
||||
DeclareLaunchArgument('offset_index1', default_value='-1'),
|
||||
DeclareLaunchArgument('frame_aggregate_mode', default_value='ANY'), # full_frame, color_frame, ANY or disable
|
||||
DeclareLaunchArgument('interleave_ae_mode', default_value='laser'), # 'hdr' or 'laser'
|
||||
DeclareLaunchArgument('interleave_frame_enable', default_value='false'),
|
||||
DeclareLaunchArgument('interleave_skip_enable', default_value='false'),
|
||||
DeclareLaunchArgument('interleave_skip_index', default_value='1'), # 0:skip pattern ir 1: skip flood ir
|
||||
|
||||
DeclareLaunchArgument('hdr_index1_laser_control', default_value='1'),#interleave_hdr_param
|
||||
DeclareLaunchArgument('hdr_index1_depth_exposure', default_value='1'),
|
||||
DeclareLaunchArgument('hdr_index1_depth_gain', default_value='16'),
|
||||
DeclareLaunchArgument('hdr_index1_ir_brightness', default_value='20'),
|
||||
DeclareLaunchArgument('hdr_index1_ir_ae_max_exposure', default_value='2000'),
|
||||
DeclareLaunchArgument('hdr_index0_laser_control', default_value='1'),
|
||||
DeclareLaunchArgument('hdr_index0_depth_exposure', default_value='7500'),
|
||||
DeclareLaunchArgument('hdr_index0_depth_gain', default_value='16'),
|
||||
DeclareLaunchArgument('hdr_index0_ir_brightness', default_value='60'),
|
||||
DeclareLaunchArgument('hdr_index0_ir_ae_max_exposure', default_value='10000'),
|
||||
|
||||
DeclareLaunchArgument('laser_index1_laser_control', default_value='0'),#interleave_laser_param
|
||||
DeclareLaunchArgument('laser_index1_depth_exposure', default_value='3000'),
|
||||
DeclareLaunchArgument('laser_index1_depth_gain', default_value='16'),
|
||||
DeclareLaunchArgument('laser_index1_ir_brightness', default_value='60'),
|
||||
DeclareLaunchArgument('laser_index1_ir_ae_max_exposure', default_value='17000'),
|
||||
DeclareLaunchArgument('laser_index0_laser_control', default_value='1'),
|
||||
DeclareLaunchArgument('laser_index0_depth_exposure', default_value='3000'),
|
||||
DeclareLaunchArgument('laser_index0_depth_gain', default_value='16'),
|
||||
DeclareLaunchArgument('laser_index0_ir_brightness', default_value='60'),
|
||||
DeclareLaunchArgument('laser_index0_ir_ae_max_exposure', default_value='30000'),
|
||||
DeclareLaunchArgument('use_intra_process_comms', default_value='false'),
|
||||
DeclareLaunchArgument('attach_component_container_enable', default_value='false'),
|
||||
DeclareLaunchArgument('attach_component_container_name', default_value='orbbec_container'),
|
||||
|
||||
]
|
||||
|
||||
def get_params(context, args):
|
||||
return [load_parameters(context, args)]
|
||||
|
||||
def create_node_action(context, args):
|
||||
params = get_params(context, args)
|
||||
ros_distro = os.environ.get("ROS_DISTRO", "humble")
|
||||
if ros_distro == "foxy":
|
||||
return [
|
||||
Node(
|
||||
package="orbbec_camera",
|
||||
executable="orbbec_camera_node",
|
||||
name="ob_camera_node",
|
||||
namespace=LaunchConfiguration("camera_name"),
|
||||
parameters=params,
|
||||
output="screen",
|
||||
)
|
||||
]
|
||||
else:
|
||||
attach_to_shared_component_container_arg = LaunchConfiguration('attach_to_shared_component_container', default=False)
|
||||
component_container_name_arg = LaunchConfiguration('component_container_name', default='orbbec_container')
|
||||
|
||||
orbbec_container = Node(
|
||||
name=component_container_name_arg,
|
||||
package='rclcpp_components',
|
||||
executable='component_container_mt',
|
||||
output='screen',
|
||||
condition=UnlessCondition(attach_to_shared_component_container_arg)
|
||||
)
|
||||
return [
|
||||
orbbec_container,
|
||||
LoadComposableNodes(
|
||||
target_container=component_container_name_arg,
|
||||
composable_node_descriptions=[
|
||||
ComposableNode(
|
||||
namespace=LaunchConfiguration("camera_name"),
|
||||
name=LaunchConfiguration("camera_name"),
|
||||
package='orbbec_camera',
|
||||
plugin='orbbec_camera::OBCameraNodeDriver',
|
||||
parameters=params,
|
||||
extra_arguments=[{'use_intra_process_comms': LaunchConfiguration("use_intra_process_comms")}],
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
return LaunchDescription(
|
||||
args + [
|
||||
OpaqueFunction(function=lambda context: create_node_action(context, args))
|
||||
]
|
||||
)
|
||||
@@ -1,207 +0,0 @@
|
||||
import os
|
||||
import yaml
|
||||
from launch import LaunchDescription
|
||||
from launch.actions import DeclareLaunchArgument, OpaqueFunction, GroupAction
|
||||
from launch.substitutions import LaunchConfiguration
|
||||
from launch_ros.actions import PushRosNamespace, ComposableNodeContainer, Node
|
||||
from launch_ros.descriptions import ComposableNode
|
||||
from launch.conditions import UnlessCondition
|
||||
from launch_ros.actions import LoadComposableNodes
|
||||
|
||||
def load_yaml(file_path):
|
||||
with open(file_path, 'r') as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
def merge_params(default_params, yaml_params):
|
||||
for key, value in yaml_params.items():
|
||||
if key in default_params:
|
||||
default_params[key] = value
|
||||
return default_params
|
||||
|
||||
|
||||
def convert_value(value):
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
if value.lower() == 'true':
|
||||
return True
|
||||
elif value.lower() == 'false':
|
||||
return False
|
||||
return value
|
||||
|
||||
|
||||
def load_parameters(context, args):
|
||||
default_params = {arg.name: LaunchConfiguration(arg.name).perform(context) for arg in args}
|
||||
config_file_path = LaunchConfiguration('config_file_path').perform(context)
|
||||
if config_file_path:
|
||||
yaml_params = load_yaml(config_file_path)
|
||||
default_params = merge_params(default_params, yaml_params)
|
||||
skip_convert = {'config_file_path', 'usb_port', 'serial_number'}
|
||||
return {
|
||||
key: (value if key in skip_convert else convert_value(value))
|
||||
for key, value in default_params.items()
|
||||
}
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
args = [
|
||||
DeclareLaunchArgument('camera_name', default_value='camera'),
|
||||
DeclareLaunchArgument('depth_registration', default_value='true'),
|
||||
DeclareLaunchArgument('serial_number', default_value=''),
|
||||
DeclareLaunchArgument('usb_port', default_value=''),
|
||||
DeclareLaunchArgument('device_num', default_value='1'),
|
||||
DeclareLaunchArgument('uvc_backend', default_value='libuvc'),#libuvc or v4l2
|
||||
DeclareLaunchArgument('product_id', default_value=''),
|
||||
DeclareLaunchArgument('enable_point_cloud', default_value='true'),
|
||||
DeclareLaunchArgument('cloud_frame_id', default_value=''),
|
||||
DeclareLaunchArgument('enable_colored_point_cloud', default_value='false'),
|
||||
DeclareLaunchArgument('point_cloud_qos', default_value='default'),
|
||||
DeclareLaunchArgument('connection_delay', default_value='100'),
|
||||
DeclareLaunchArgument('color_width', default_value='0'),
|
||||
DeclareLaunchArgument('color_height', default_value='0'),
|
||||
DeclareLaunchArgument('color_fps', default_value='0'),
|
||||
DeclareLaunchArgument('color_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_color', default_value='true'),
|
||||
DeclareLaunchArgument('color_flip', default_value='false'),
|
||||
DeclareLaunchArgument('color_qos', default_value='default'),
|
||||
DeclareLaunchArgument('color_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('enable_color_auto_exposure', default_value='true'),
|
||||
DeclareLaunchArgument('color_ae_max_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('color_ae_roi_left', default_value='-1'),
|
||||
DeclareLaunchArgument('color_ae_roi_right', default_value='-1'),
|
||||
DeclareLaunchArgument('color_ae_roi_top', default_value='-1'),
|
||||
DeclareLaunchArgument('color_ae_roi_bottom', default_value='-1'),
|
||||
DeclareLaunchArgument('color_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('color_gain', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_color_auto_white_balance', default_value='true'),
|
||||
DeclareLaunchArgument('color_white_balance', default_value='-1'),
|
||||
DeclareLaunchArgument('color_brightness', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_width', default_value='0'),
|
||||
DeclareLaunchArgument('depth_height', default_value='0'),
|
||||
DeclareLaunchArgument('depth_fps', default_value='0'),
|
||||
DeclareLaunchArgument('depth_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_depth', default_value='true'),
|
||||
DeclareLaunchArgument('depth_flip', default_value='false'),
|
||||
DeclareLaunchArgument('depth_qos', default_value='default'),
|
||||
DeclareLaunchArgument('depth_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('depth_ae_roi_left', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_ae_roi_right', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_ae_roi_top', default_value='-1'),
|
||||
DeclareLaunchArgument('depth_ae_roi_bottom', default_value='-1'),
|
||||
DeclareLaunchArgument('ir_width', default_value='0'),
|
||||
DeclareLaunchArgument('ir_height', default_value='0'),
|
||||
DeclareLaunchArgument('ir_fps', default_value='0'),
|
||||
DeclareLaunchArgument('ir_format', default_value='ANY'),
|
||||
DeclareLaunchArgument('enable_ir', default_value='true'),
|
||||
DeclareLaunchArgument('flip_ir', default_value='false'),
|
||||
DeclareLaunchArgument('ir_qos', default_value='default'),
|
||||
DeclareLaunchArgument('ir_camera_info_qos', default_value='default'),
|
||||
DeclareLaunchArgument('enable_ir_auto_exposure', default_value='true'),
|
||||
DeclareLaunchArgument('ir_ae_max_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('ir_exposure', default_value='-1'),
|
||||
DeclareLaunchArgument('ir_gain', default_value='-1'),
|
||||
DeclareLaunchArgument('ir_brightness', default_value='-1'),
|
||||
DeclareLaunchArgument('config_file_path', default_value=''),
|
||||
DeclareLaunchArgument('enable_sync_output_accel_gyro', default_value='true'),
|
||||
DeclareLaunchArgument('enable_accel', default_value='false'),
|
||||
DeclareLaunchArgument('accel_rate', default_value='100hz'),
|
||||
DeclareLaunchArgument('accel_range', default_value='4g'),
|
||||
DeclareLaunchArgument('enable_gyro', default_value='false'),
|
||||
DeclareLaunchArgument('gyro_rate', default_value='100hz'),
|
||||
DeclareLaunchArgument('gyro_range', default_value='1000dps'),
|
||||
DeclareLaunchArgument('liner_accel_cov', default_value='0.01'),
|
||||
DeclareLaunchArgument('angular_vel_cov', default_value='0.01'),
|
||||
DeclareLaunchArgument('publish_tf', default_value='true'),
|
||||
DeclareLaunchArgument('tf_publish_rate', default_value='0.0'),
|
||||
DeclareLaunchArgument('ir_info_url', default_value=''),
|
||||
DeclareLaunchArgument('color_info_url', default_value=''),
|
||||
DeclareLaunchArgument('log_level', default_value='none'),
|
||||
DeclareLaunchArgument('enable_publish_extrinsic', default_value='false'),
|
||||
DeclareLaunchArgument('enable_d2c_viewer', default_value='false'),
|
||||
DeclareLaunchArgument('enable_ldp', default_value='true'),
|
||||
DeclareLaunchArgument('enable_decimation_filter', default_value='false'),
|
||||
DeclareLaunchArgument('decimation_filter_scale', default_value='-1'),
|
||||
# Configure the path for depth filter file, for example: /config/depthfilter/Gemini2_v1.7.json
|
||||
DeclareLaunchArgument('depth_filter_config', default_value=''),
|
||||
# Depth work mode support is as follows:
|
||||
# Unbinned Dense Default
|
||||
# Unbinned Sparse Default
|
||||
# Binned Sparse Default
|
||||
# Obstacle Avoidance
|
||||
DeclareLaunchArgument('depth_work_mode', default_value=''),
|
||||
DeclareLaunchArgument('sync_mode', default_value='standalone'),
|
||||
DeclareLaunchArgument('depth_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('color_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('trigger2image_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('trigger_out_delay_us', default_value='0'),
|
||||
DeclareLaunchArgument('trigger_out_enabled', default_value='false'),
|
||||
DeclareLaunchArgument('enable_frame_sync', default_value='true'),
|
||||
DeclareLaunchArgument('ordered_pc', default_value='false'),
|
||||
DeclareLaunchArgument('enable_depth_scale', default_value='true'),
|
||||
DeclareLaunchArgument('align_mode', default_value='SW'),
|
||||
DeclareLaunchArgument('retry_on_usb3_detection_failure', default_value='false'),
|
||||
DeclareLaunchArgument('laser_energy_level', default_value='-1'),
|
||||
DeclareLaunchArgument('enable_heartbeat', default_value='false'),
|
||||
DeclareLaunchArgument('time_domain', default_value='global'),
|
||||
DeclareLaunchArgument('use_intra_process_comms', default_value='false'),
|
||||
DeclareLaunchArgument('attach_component_container_enable', default_value='false'),
|
||||
DeclareLaunchArgument('attach_component_container_name', default_value='orbbec_container'),
|
||||
]
|
||||
|
||||
def get_params(context, args):
|
||||
return [load_parameters(context, args)]
|
||||
|
||||
def create_node_action(context, args):
|
||||
params = get_params(context, args)
|
||||
ros_distro = os.environ.get("ROS_DISTRO", "humble")
|
||||
if ros_distro == "foxy":
|
||||
return [
|
||||
Node(
|
||||
package="orbbec_camera",
|
||||
executable="orbbec_camera_node",
|
||||
name="ob_camera_node",
|
||||
namespace=LaunchConfiguration("camera_name"),
|
||||
parameters=params,
|
||||
output="screen",
|
||||
)
|
||||
]
|
||||
else:
|
||||
attach_to_shared_component_container_arg = LaunchConfiguration('attach_to_shared_component_container', default=False)
|
||||
component_container_name_arg = LaunchConfiguration('component_container_name', default='orbbec_container')
|
||||
|
||||
orbbec_container = Node(
|
||||
name=component_container_name_arg,
|
||||
package='rclcpp_components',
|
||||
executable='component_container_mt',
|
||||
output='screen',
|
||||
condition=UnlessCondition(attach_to_shared_component_container_arg)
|
||||
)
|
||||
return [
|
||||
orbbec_container,
|
||||
LoadComposableNodes(
|
||||
target_container=component_container_name_arg,
|
||||
composable_node_descriptions=[
|
||||
ComposableNode(
|
||||
namespace=LaunchConfiguration("camera_name"),
|
||||
name=LaunchConfiguration("camera_name"),
|
||||
package='orbbec_camera',
|
||||
plugin='orbbec_camera::OBCameraNodeDriver',
|
||||
parameters=params,
|
||||
extra_arguments=[{'use_intra_process_comms': LaunchConfiguration("use_intra_process_comms")}],
|
||||
)
|
||||
]
|
||||
)
|
||||
]
|
||||
|
||||
return LaunchDescription(
|
||||
args + [
|
||||
OpaqueFunction(function=lambda context: create_node_action(context, args))
|
||||
]
|
||||
)
|
||||
@@ -1,126 +0,0 @@
|
||||
import os
|
||||
from ament_index_python.packages import get_package_share_directory
|
||||
from launch import LaunchDescription
|
||||
from launch_ros.actions import Node, LoadComposableNodes
|
||||
from launch.actions import IncludeLaunchDescription, GroupAction, TimerAction
|
||||
from launch.launch_description_sources import PythonLaunchDescriptionSource
|
||||
from launch.actions import DeclareLaunchArgument
|
||||
from launch.conditions import UnlessCondition, IfCondition
|
||||
from launch.substitutions import LaunchConfiguration
|
||||
from launch.substitutions import TextSubstitution
|
||||
from launch_ros.descriptions import ComposableNode
|
||||
|
||||
|
||||
def load_yaml(file_path):
|
||||
with open(file_path, "r") as f:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
def merge_params(default_params, yaml_params):
|
||||
for key, value in yaml_params.items():
|
||||
if key in default_params:
|
||||
default_params[key] = value
|
||||
return default_params
|
||||
|
||||
|
||||
def convert_value(value):
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
return int(value)
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
pass
|
||||
if value.lower() == "true":
|
||||
return True
|
||||
elif value.lower() == "false":
|
||||
return False
|
||||
return value
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
# Include launch files
|
||||
package_dir = get_package_share_directory("orbbec_camera")
|
||||
launch_file_dir = os.path.join(
|
||||
package_dir, "examples/multi_camera_synced_verification_tool"
|
||||
)
|
||||
config_file_dir = os.path.join(package_dir, "config")
|
||||
config_file_path = os.path.join(config_file_dir, "camera_params.yaml")
|
||||
secondary_config_file_path = os.path.join(config_file_dir, "camera_secondary_params.yaml")
|
||||
|
||||
attach_to_shared_component_container_arg = LaunchConfiguration(
|
||||
"attach_to_shared_component_container", default=False
|
||||
)
|
||||
component_container_name_arg = LaunchConfiguration(
|
||||
"component_container_name", default="shared_orbbec_container"
|
||||
)
|
||||
|
||||
shared_orbbec_container = Node(
|
||||
name=component_container_name_arg,
|
||||
package="rclcpp_components",
|
||||
executable="component_container_mt",
|
||||
output="screen",
|
||||
condition=UnlessCondition(attach_to_shared_component_container_arg),
|
||||
)
|
||||
|
||||
save_rgbir = LoadComposableNodes(
|
||||
target_container=component_container_name_arg,
|
||||
composable_node_descriptions=[
|
||||
ComposableNode(
|
||||
namespace="save_rgbir",
|
||||
name="save_rgbir",
|
||||
package="orbbec_camera",
|
||||
plugin="orbbec_camera::tools::MultiCameraSubscriber",
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
attach_to_shared_component_container_arg = TextSubstitution(text="true")
|
||||
|
||||
launch1_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, "gemini_330_series_synced_verify.launch.py")
|
||||
),
|
||||
launch_arguments={
|
||||
"camera_name": "camera_01",
|
||||
"usb_port": "2-1",
|
||||
"device_num": "2",
|
||||
"sync_mode": "primary",
|
||||
"config_file_path": config_file_path,
|
||||
"trigger_out_enabled": "true",
|
||||
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
|
||||
"component_container_name": component_container_name_arg,
|
||||
}.items(),
|
||||
)
|
||||
|
||||
launch2_include = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(launch_file_dir, "gemini_330_series_synced_verify.launch.py")
|
||||
),
|
||||
launch_arguments={
|
||||
"camera_name": "camera_02",
|
||||
"usb_port": "2-3",
|
||||
"device_num": "2",
|
||||
"sync_mode": "secondary_synced",
|
||||
"config_file_path": secondary_config_file_path,
|
||||
"trigger_out_enabled": "false",
|
||||
"attach_to_shared_component_container": attach_to_shared_component_container_arg,
|
||||
"component_container_name": component_container_name_arg,
|
||||
}.items(),
|
||||
)
|
||||
|
||||
# Launch description
|
||||
ld = LaunchDescription(
|
||||
[
|
||||
shared_orbbec_container,
|
||||
TimerAction(period=0.0, actions=[GroupAction([launch2_include])]),
|
||||
TimerAction(period=2.0, actions=[GroupAction([launch1_include])]),
|
||||
# The primary camera should be launched at last
|
||||
TimerAction(period=6.0, actions=[GroupAction([save_rgbir])]),
|
||||
# save_rgbir is synced verification tool
|
||||
]
|
||||
)
|
||||
|
||||
return ld
|
||||
@@ -1,6 +0,0 @@
|
||||
## config_frameMatch.py
|
||||
|
||||
Definition of 3 folders:
|
||||
matchFrames: data with card control within half frame.
|
||||
notMatchFrames: data with card control not within half frame, not matched.
|
||||
abnormal: data within the group that exceeds tspRangeThreshold setting
|
||||
@@ -1,5 +0,0 @@
|
||||
[Parameter]
|
||||
# Unit: FPS, please fill in according to the actual frame rate of the dataset, otherwise it will lead to inaccurate matching
|
||||
frameRate=30
|
||||
# Unit: ms. When the timestamp range of a group of data frames after matching is greater than or equal to tspRangeThreshold, the file name will be marked
|
||||
tspRangeThreshold=4
|
||||
@@ -1,97 +0,0 @@
|
||||
import os
|
||||
import subprocess
|
||||
import json
|
||||
import platform
|
||||
|
||||
def list_subdirectories(path):
|
||||
# 遍历指定目录
|
||||
for f in os.listdir(path):
|
||||
# 判断是否是子目录
|
||||
if os.path.isdir(os.path.join(path, f)):
|
||||
yield f
|
||||
|
||||
def read_pid_from_device_json(frames_dir):
|
||||
file_path = frames_dir + "/DevicesInfo.txt"
|
||||
if not os.path.exists(file_path):
|
||||
print(file_path + " not exists. Please check it")
|
||||
return ""
|
||||
|
||||
with open(file_path, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not 'devicePid' in data:
|
||||
print("Not found 'devicePid' in " + file_path)
|
||||
return ""
|
||||
|
||||
return data['devicePid']
|
||||
|
||||
def execute_sync_width_pid(frames_dir, pid):
|
||||
sync_script_file=""
|
||||
if pid.lower() == "0x0675":
|
||||
sync_script_file="script/config_frameMatch_Gemini2VL.py"
|
||||
elif pid.lower() == "0x0670" or pid.lower() == "0x0701":
|
||||
sync_script_file="script/config_frameMatch-systemTimestamp.py"
|
||||
elif pid.lower() == "0x0660":
|
||||
sync_script_file="script/config_frameMatch-Astra2_new.py"
|
||||
elif pid.lower() == "0x0803" or pid.lower() == "0x0807" or pid.lower() == "0x0801" or pid.lower() == "0x0805" or pid.lower() == "0x080b" or pid.lower() == "0x080e":
|
||||
sync_script_file="script/config_frameMatch_g330.py"
|
||||
else:
|
||||
sync_script_file="script/config_frameMatch.py"
|
||||
|
||||
if len(sync_script_file) > 0:
|
||||
print(f"execute synchronize frames. script_file={sync_script_file}, frames_dir={frames_dir}")
|
||||
if platform.system() == "Windows":
|
||||
output = subprocess.check_output(["python", sync_script_file, os.path.abspath(f"{frames_dir}"), os.getcwd()])
|
||||
print(output.decode("gbk"))
|
||||
else:
|
||||
output = subprocess.check_output(["python3", sync_script_file, os.path.abspath(f"{frames_dir}"), os.getcwd()])
|
||||
print(output.decode("utf-8"))
|
||||
else:
|
||||
print("Invalid pid=" + pid + ", not match sync frame script")
|
||||
|
||||
def sync_frames(frames_dir):
|
||||
pid = read_pid_from_device_json(frames_dir)
|
||||
if len(pid) <= 0:
|
||||
print("Get pid failed.")
|
||||
return
|
||||
|
||||
execute_sync_width_pid(frames_dir, pid)
|
||||
|
||||
def main():
|
||||
# 指定目录路径
|
||||
frames_output_path = os.path.abspath("../output")
|
||||
|
||||
# 获取所有子目录
|
||||
subdirectories = list(list_subdirectories(frames_output_path))
|
||||
|
||||
if not subdirectories:
|
||||
print("No subdirectories found in the specified directory.")
|
||||
|
||||
# 打印子目录列表并让用户选择
|
||||
for i, subdir in enumerate(subdirectories):
|
||||
print(f"{i + 1}. {subdir}")
|
||||
|
||||
# 获取用户输入
|
||||
choice = input("Please select a subdirectory by number (or 'q' to quit): ")
|
||||
|
||||
if choice.lower() == 'q':
|
||||
return
|
||||
|
||||
# 验证用户输入
|
||||
is_index_valid = False
|
||||
try:
|
||||
index = int(choice) - 1
|
||||
if 0 <= index < len(subdirectories):
|
||||
is_index_valid = True
|
||||
else:
|
||||
print(f"Invalid choice. index={index}. Please enter a number between 1 and the total number of subdirectories.")
|
||||
except ValueError:
|
||||
print("Invalid choice(ValueException). Please enter a number between 1 and the total number of subdirectories")
|
||||
if is_index_valid:
|
||||
selected_subdir = subdirectories[index]
|
||||
print(f"You selected the subdirectory: {selected_subdir}")
|
||||
sync_frames(f"{frames_output_path }/{selected_subdir}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,385 +0,0 @@
|
||||
import os.path
|
||||
import argparse
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import configparser
|
||||
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
## Config.ini中配置
|
||||
# 单位:FPS,请依据数据集实际帧率填写,否则会导致匹配不准确
|
||||
frameRate = -1
|
||||
# 单位:ms,当匹配后某组数据帧的时间戳极差大于等于tspRangeThreshold,文件名会增加标注
|
||||
tspRangeThreshold = -1
|
||||
|
||||
## Python脚本自动解析, 不要修改
|
||||
primaryId = '-1' #主机ID
|
||||
mainPythonWorkPath = "" # SyncFramesMain.py的工作目录
|
||||
sourceFramesPath = "" # 请勿修改,固定值,TotalMode目录路径
|
||||
sourceRootPath = "" # 存放TotalMode的根目录
|
||||
resultPath = "" # 保存sync分析结果
|
||||
streamProfileDict = dict() # 保存Python结果
|
||||
|
||||
class PictureInfo(object):
|
||||
class Struct(object):
|
||||
def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
|
||||
self.deviceId = deviceId
|
||||
self.sensorType = sensorType
|
||||
self.syncTimeStamp = syncTimeStamp
|
||||
self.picturePath = picturePath
|
||||
self.deviceIdFull = deviceIdFull
|
||||
self.fileExt = fileExt
|
||||
|
||||
def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
|
||||
return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt)
|
||||
|
||||
class StreamProfileInfo:
|
||||
def __init__(self, sensorType, width, height, format, fps):
|
||||
self.sensorType = sensorType
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.format = format
|
||||
self.fps = fps
|
||||
|
||||
def initPrimaryId():
|
||||
global primaryId
|
||||
|
||||
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
|
||||
if not os.path.exists(deviceInfoPath):
|
||||
print(f"Initialize primaryId failed. {deviceInfoPath} not exists")
|
||||
return False
|
||||
|
||||
with open(deviceInfoPath, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not 'devices' in data:
|
||||
print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'")
|
||||
return False
|
||||
|
||||
for item in data['devices']:
|
||||
if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']:
|
||||
primaryId = str(item['index'])
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def initSyncConfigParams():
|
||||
global frameRate, tspRangeThreshold
|
||||
|
||||
configPath = f"{mainPythonWorkPath}/Config.ini"
|
||||
if not os.path.exists(configPath):
|
||||
print(f"Initliaze synchronized config parameter failed. {configPath} not exists.")
|
||||
return False
|
||||
|
||||
# 创建ConfigParser对象
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
# 读取ini文件
|
||||
config.read(configPath, 'utf-8')
|
||||
|
||||
if not config.has_option('Parameter', 'frameRate'):
|
||||
print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}")
|
||||
return False
|
||||
|
||||
if not config.has_option('Parameter', 'tspRangeThreshold'):
|
||||
print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}")
|
||||
return False
|
||||
|
||||
frameRate = config.getfloat('Parameter', 'frameRate')
|
||||
if frameRate <= 0 or frameRate >= 1000:
|
||||
print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}")
|
||||
return False
|
||||
|
||||
tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold')
|
||||
if tspRangeThreshold <= 0:
|
||||
print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def getDeviceCount():
|
||||
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
|
||||
if not os.path.exists(deviceInfoPath):
|
||||
print(f"Get device count failed. {deviceInfoPath} not exists")
|
||||
return 0
|
||||
|
||||
with open(deviceInfoPath, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not 'devices' in data:
|
||||
print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'")
|
||||
return 0
|
||||
|
||||
return len(data['devices'])
|
||||
|
||||
def initProfileInfoDict():
|
||||
global streamProfileDict
|
||||
|
||||
streamProfileDict = dict()
|
||||
profilePath = f"{sourceRootPath}/StreamProfileInfo.txt"
|
||||
if not os.path.exists(profilePath):
|
||||
print(f"Initialize profile information dictionary failed. {profilePath} not exists")
|
||||
return False
|
||||
|
||||
with open(profilePath, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not 'streamProfiles' in data:
|
||||
print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'")
|
||||
return False
|
||||
|
||||
for item in data['streamProfiles']:
|
||||
if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item:
|
||||
profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps'])
|
||||
streamProfileDict.setdefault(item['sensorType'], profileInfo)
|
||||
else:
|
||||
print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}")
|
||||
|
||||
return len(streamProfileDict.items()) > 0
|
||||
|
||||
def initResultDir():
|
||||
global resultPath
|
||||
timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S")
|
||||
resultPath = f"{sourceRootPath}/results-{timeText}"
|
||||
if not os.path.exists(resultPath):
|
||||
os.makedirs(resultPath)
|
||||
|
||||
def isFrameFile(fileName):
|
||||
fileExt = os.path.splitext(fileName)
|
||||
if len(fileExt) < 2:
|
||||
return False
|
||||
|
||||
frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"}
|
||||
return fileExt[1] in frameExts
|
||||
|
||||
def initPictureInfoDictionary(rootFilePath, pictureInfoDict):
|
||||
frameFileCount = 0
|
||||
for dirpath, dirnames, filenames in os.walk(rootFilePath):
|
||||
# print(f"dirpath={dirpath}, dirpath.basename=" + os.path.basename(dirpath))
|
||||
for fileName in filenames:
|
||||
filePath = os.path.join(dirpath, fileName)
|
||||
if not isFrameFile(fileName):
|
||||
continue
|
||||
|
||||
frameFileCount += 1
|
||||
fileNameNoExt = os.path.splitext(fileName)[0]
|
||||
pictureNameList = fileNameNoExt.split('_')
|
||||
deviceId = pictureNameList[2][5:]
|
||||
sensorType = pictureNameList[0].replace('#', '_')
|
||||
syncTimeStamp = int(pictureNameList[3][1:])
|
||||
|
||||
# 创建结构体
|
||||
pictureInfo = PictureInfo()
|
||||
info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1])
|
||||
|
||||
# 格式化字典一个key对应一个数组
|
||||
pictureInfoDict.setdefault(deviceId, []).append(info)
|
||||
|
||||
if 0 == frameFileCount:
|
||||
print("initialize pictureInfoDict failed. Not found frame file")
|
||||
|
||||
def matchFrame(pictureInfoDict):
|
||||
global primaryId, frameRate
|
||||
global streamProfileDict
|
||||
|
||||
# 获取比较图像数组,取第一个设备的Color数组来进行比较
|
||||
compareList = []
|
||||
for key in pictureInfoDict:
|
||||
listTmp = list(pictureInfoDict[key])
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == 'color':
|
||||
compareList.append(info)
|
||||
|
||||
sorted_dict = dict(sorted(pictureInfoDict.items(), key=lambda x: x[1], reverse=False))
|
||||
pictureInfoDict = sorted_dict
|
||||
|
||||
# 根据开流情况动态分析,减少循环次数
|
||||
hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict
|
||||
hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict
|
||||
hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict
|
||||
|
||||
# 匹配相邻帧
|
||||
resultDict = {}
|
||||
consumedList = []
|
||||
dictIndex = 0
|
||||
frameTspGap = int(1000.0/frameRate+0.5)
|
||||
for comparePic in compareList:
|
||||
resultDict.setdefault(dictIndex, []).append(comparePic)
|
||||
for key in pictureInfoDict:
|
||||
listTmp = list(pictureInfoDict[key])
|
||||
# 对比彩色
|
||||
if hasColorProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'color' and info.syncTimeStamp >= comparePic.syncTimeStamp and info.syncTimeStamp - comparePic.syncTimeStamp <= frameTspGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# 对Depth
|
||||
if hasDepthProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'depth' and info.syncTimeStamp >= comparePic.syncTimeStamp and info.syncTimeStamp - comparePic.syncTimeStamp <= frameTspGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# IR
|
||||
if hasIRProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'ir' and info.syncTimeStamp >= comparePic.syncTimeStamp and info.syncTimeStamp - comparePic.syncTimeStamp <= frameTspGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# 计算偏差
|
||||
for info in resultDict[dictIndex]:
|
||||
info.tspDiff = info.syncTimeStamp - comparePic.syncTimeStamp
|
||||
|
||||
dictIndex += 1
|
||||
|
||||
return resultDict
|
||||
|
||||
|
||||
def safe_make_dir(path):
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
|
||||
def handleSyncFrames():
|
||||
global resultPath
|
||||
global sourceFramesPath
|
||||
global streamProfileDict
|
||||
|
||||
fp = open(f"{resultPath}/frameMatchLog.txt", "w")
|
||||
|
||||
if not initProfileInfoDict():
|
||||
print("Initialize stream profile dictionary failed. exit")
|
||||
return
|
||||
print(f"Stream profiles: {streamProfileDict}")
|
||||
streamProfileCount = len(streamProfileDict.items())
|
||||
|
||||
pictureInfoDict = {}
|
||||
|
||||
initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict)
|
||||
if len(pictureInfoDict) <= 0:
|
||||
print("init pictureInfoDict failed. pictureInfoDict.len = 0")
|
||||
return
|
||||
|
||||
# # Dump pictureInfoDict
|
||||
# for key in pictureInfoDict:
|
||||
# print("===================key:%s" % key)
|
||||
# listTmp = list(pictureInfoDict[key])
|
||||
# for info in listTmp:
|
||||
# print("=====================value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (info.deviceId,info.sensorType,info.syncTimeStamp,info.picturePath))
|
||||
|
||||
deviceCount = getDeviceCount()
|
||||
print("=================device count:%d" % deviceCount)
|
||||
|
||||
resultDict = {}
|
||||
resultDict = matchFrame(pictureInfoDict)
|
||||
|
||||
for key in resultDict:
|
||||
fp.write("===================result key:%s\n" % key)
|
||||
print("===================result key:%s" % key)
|
||||
listTmp = list(resultDict[key])
|
||||
for info in listTmp:
|
||||
fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % (
|
||||
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
|
||||
print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (
|
||||
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
|
||||
|
||||
matchPath = f"{resultPath}/matchFrames/"
|
||||
safe_make_dir(matchPath)
|
||||
notMatchPath = f"{resultPath}/notMatchFrames/"
|
||||
abnormalPath = f"{resultPath}/abnormal/"
|
||||
|
||||
for key in resultDict:
|
||||
listTmp = list(resultDict[key])
|
||||
if (len(listTmp) == (deviceCount * streamProfileCount)):
|
||||
haveAbnormalData = False
|
||||
for info in listTmp:
|
||||
if info.tspDiff >= tspRangeThreshold:
|
||||
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
haveAbnormalData = True
|
||||
else:
|
||||
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
|
||||
if haveAbnormalData:
|
||||
safe_make_dir(abnormalPath)
|
||||
for info in listTmp:
|
||||
if info.tspDiff >= tspRangeThreshold:
|
||||
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
else:
|
||||
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
else:
|
||||
safe_make_dir(notMatchPath)
|
||||
for info in listTmp:
|
||||
newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
|
||||
shutil.copy(info.picturePath, newFilePath)
|
||||
|
||||
def main():
|
||||
if not initPrimaryId():
|
||||
print("Initialize primaryId failed. exit.")
|
||||
return
|
||||
|
||||
if not initSyncConfigParams():
|
||||
print("Initialized Sync config parameter failed. exit.")
|
||||
return
|
||||
|
||||
initResultDir()
|
||||
|
||||
print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}")
|
||||
handleSyncFrames()
|
||||
|
||||
return
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Astra2'")
|
||||
parser.add_argument('frames_dir', type=str, help="Frames directory")
|
||||
parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory")
|
||||
args = parser.parse_args()
|
||||
|
||||
sourceRootPath = args.frames_dir
|
||||
sourceFramesPath = f"{args.frames_dir}/TotalModeFrames"
|
||||
mainPythonWorkPath = args.main_script_root_dir
|
||||
print(f"Start of Astra2 synchronize frame. sourceRootPath={sourceRootPath}")
|
||||
|
||||
main()
|
||||
|
||||
print("Finish of Astra2 synchronize frame.")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,408 +0,0 @@
|
||||
import os.path
|
||||
import argparse
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import configparser
|
||||
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
## Config.ini中配置
|
||||
# 单位:FPS,请依据数据集实际帧率填写,否则会导致匹配不准确
|
||||
frameRate = -1
|
||||
# 单位:ms,当匹配后某组数据帧的时间戳极差大于等于tspRangeThreshold,文件名会增加标注
|
||||
tspRangeThreshold = -1
|
||||
|
||||
## Python脚本自动解析, 不要修改
|
||||
primaryId = '-1' #主机ID
|
||||
mainPythonWorkPath = "" # SyncFramesMain.py的工作目录
|
||||
sourceFramesPath = "" # 请勿修改,固定值,TotalMode目录路径
|
||||
sourceRootPath = "" # 存放TotalMode的根目录
|
||||
resultPath = "" # 保存sync分析结果
|
||||
streamProfileDict = dict() # 保存Python结果
|
||||
|
||||
class PictureInfo(object):
|
||||
class Struct(object):
|
||||
def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
|
||||
self.deviceId = deviceId
|
||||
self.sensorType = sensorType
|
||||
self.syncTimeStamp = syncTimeStamp
|
||||
self.picturePath = picturePath
|
||||
self.deviceIdFull = deviceIdFull
|
||||
self.fileExt = fileExt
|
||||
|
||||
def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
|
||||
return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt)
|
||||
|
||||
class StreamProfileInfo:
|
||||
def __init__(self, sensorType, width, height, format, fps):
|
||||
self.sensorType = sensorType
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.format = format
|
||||
self.fps = fps
|
||||
|
||||
def initPrimaryId():
|
||||
global primaryId
|
||||
|
||||
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
|
||||
if not os.path.exists(deviceInfoPath):
|
||||
print(f"Initialize primaryId failed. {deviceInfoPath} not exists")
|
||||
return False
|
||||
|
||||
with open(deviceInfoPath, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not 'devices' in data:
|
||||
print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'")
|
||||
return False
|
||||
|
||||
for item in data['devices']:
|
||||
if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']:
|
||||
primaryId = str(item['index'])
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def initSyncConfigParams():
|
||||
global frameRate, tspRangeThreshold
|
||||
|
||||
configPath = f"{mainPythonWorkPath}/Config.ini"
|
||||
if not os.path.exists(configPath):
|
||||
print(f"Initliaze synchronized config parameter failed. {configPath} not exists.")
|
||||
return False
|
||||
|
||||
# 创建ConfigParser对象
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
# 读取ini文件
|
||||
config.read(configPath, 'utf-8')
|
||||
|
||||
if not config.has_option('Parameter', 'frameRate'):
|
||||
print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}")
|
||||
return False
|
||||
|
||||
if not config.has_option('Parameter', 'tspRangeThreshold'):
|
||||
print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}")
|
||||
return False
|
||||
|
||||
frameRate = config.getfloat('Parameter', 'frameRate')
|
||||
if frameRate <= 0 or frameRate >= 1000:
|
||||
print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}")
|
||||
return False
|
||||
|
||||
tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold')
|
||||
if tspRangeThreshold <= 0:
|
||||
print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def getDeviceCount():
|
||||
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
|
||||
if not os.path.exists(deviceInfoPath):
|
||||
print(f"Get device count failed. {deviceInfoPath} not exists")
|
||||
return 0
|
||||
|
||||
with open(deviceInfoPath, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not 'devices' in data:
|
||||
print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'")
|
||||
return 0
|
||||
|
||||
return len(data['devices'])
|
||||
|
||||
def initProfileInfoDict():
|
||||
global streamProfileDict
|
||||
|
||||
streamProfileDict = dict()
|
||||
profilePath = f"{sourceRootPath}/StreamProfileInfo.txt"
|
||||
if not os.path.exists(profilePath):
|
||||
print(f"Initialize profile information dictionary failed. {profilePath} not exists")
|
||||
return False
|
||||
|
||||
with open(profilePath, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not 'streamProfiles' in data:
|
||||
print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'")
|
||||
return False
|
||||
|
||||
for item in data['streamProfiles']:
|
||||
if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item:
|
||||
profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps'])
|
||||
streamProfileDict.setdefault(item['sensorType'], profileInfo)
|
||||
else:
|
||||
print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}")
|
||||
|
||||
return len(streamProfileDict.items()) > 0
|
||||
|
||||
def initResultDir():
|
||||
global resultPath
|
||||
timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S")
|
||||
resultPath = f"{sourceRootPath}/results-{timeText}"
|
||||
if not os.path.exists(resultPath):
|
||||
os.makedirs(resultPath)
|
||||
|
||||
def isFrameFile(fileName):
|
||||
fileExt = os.path.splitext(fileName)
|
||||
if len(fileExt) < 2:
|
||||
return False
|
||||
|
||||
frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"}
|
||||
return fileExt[1] in frameExts
|
||||
|
||||
def initPictureInfoDictionary(rootFilePath, pictureInfoDict):
|
||||
frameFileCount = 0
|
||||
for dirpath, dirnames, filenames in os.walk(rootFilePath):
|
||||
# print(f"dirpath={dirpath}, dirpath.basename=" + os.path.basename(dirpath))
|
||||
for fileName in filenames:
|
||||
filePath = os.path.join(dirpath, fileName)
|
||||
if not isFrameFile(fileName):
|
||||
continue
|
||||
|
||||
frameFileCount += 1
|
||||
fileNameNoExt = os.path.splitext(fileName)[0]
|
||||
pictureNameList = fileNameNoExt.split('_')
|
||||
deviceId = pictureNameList[2][5:]
|
||||
sensorType = pictureNameList[0].replace('#', '_')
|
||||
syncTimeStamp = int(pictureNameList[5][1:])
|
||||
|
||||
# 创建结构体
|
||||
pictureInfo = PictureInfo()
|
||||
info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1])
|
||||
|
||||
# 格式化字典一个key对应一个数组
|
||||
pictureInfoDict.setdefault(deviceId, []).append(info)
|
||||
|
||||
if 0 == frameFileCount:
|
||||
print("initialize pictureInfoDict failed. Not found frame file")
|
||||
|
||||
|
||||
def matchFrame(pictureInfoDict):
|
||||
global primaryId, frameRate
|
||||
global streamProfileDict
|
||||
|
||||
# 获取比较图像数组,取第一个设备的Color数组来进行比较
|
||||
compareList = []
|
||||
for key in pictureInfoDict:
|
||||
listTmp = list(pictureInfoDict[key])
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == 'color':
|
||||
compareList.append(info)
|
||||
|
||||
# 根据开流情况动态分析,减少循环次数
|
||||
hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict
|
||||
hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict
|
||||
hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict
|
||||
hasIRLeftProfile = 'OB_SENSOR_IR_LEFT' in streamProfileDict
|
||||
hasIRRightProfile = 'OB_SENSOR_IR_RIGHT' in streamProfileDict
|
||||
|
||||
# 匹配相邻帧
|
||||
resultDict = {}
|
||||
consumedList = []
|
||||
dictIndex = 0
|
||||
frameTspHalfGap = int(1000.0/frameRate/2.0+0.5)
|
||||
for comparePic in compareList:
|
||||
resultDict.setdefault(dictIndex, []).append(comparePic)
|
||||
for key in pictureInfoDict:
|
||||
listTmp = list(pictureInfoDict[key])
|
||||
# 对比彩色
|
||||
if hasColorProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'color' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# 对Depth
|
||||
if hasDepthProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'depth' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# IR
|
||||
if hasIRProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'ir' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# 左IR
|
||||
if hasIRLeftProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'ir_left' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# 右IR
|
||||
if hasIRRightProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'ir_right' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# 计算偏差
|
||||
minTsp = min(resultDict[dictIndex], key=lambda x: x.syncTimeStamp)
|
||||
for info in resultDict[dictIndex]:
|
||||
info.tspDiff = info.syncTimeStamp - minTsp.syncTimeStamp
|
||||
|
||||
dictIndex += 1
|
||||
|
||||
return resultDict
|
||||
|
||||
|
||||
def safe_make_dir(path):
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
|
||||
def handleSyncFrames():
|
||||
global resultPath
|
||||
global sourceFramesPath
|
||||
global streamProfileDict
|
||||
|
||||
fp = open(f"{resultPath}/frameMatchLog.txt", "w")
|
||||
|
||||
if not initProfileInfoDict():
|
||||
print("Initialize stream profile dictionary failed. exit")
|
||||
return
|
||||
print(f"Stream profiles: {streamProfileDict}")
|
||||
streamProfileCount = len(streamProfileDict.items())
|
||||
|
||||
pictureInfoDict = {}
|
||||
|
||||
initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict)
|
||||
if len(pictureInfoDict) <= 0:
|
||||
print("init pictureInfoDict failed. pictureInfoDict.len = 0")
|
||||
return
|
||||
|
||||
# # Dump pictureInfoDict
|
||||
# for key in pictureInfoDict:
|
||||
# print("===================key:%s" % key)
|
||||
# listTmp = list(pictureInfoDict[key])
|
||||
# for info in listTmp:
|
||||
# print("=====================value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (info.deviceId,info.sensorType,info.syncTimeStamp,info.picturePath))
|
||||
|
||||
deviceCount = getDeviceCount()
|
||||
print("=================device count:%d" % deviceCount)
|
||||
|
||||
resultDict = {}
|
||||
resultDict = matchFrame(pictureInfoDict)
|
||||
|
||||
for key in resultDict:
|
||||
fp.write("===================result key:%s\n" % key)
|
||||
print("===================result key:%s" % key)
|
||||
listTmp = list(resultDict[key])
|
||||
for info in listTmp:
|
||||
fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % (
|
||||
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
|
||||
print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (
|
||||
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
|
||||
|
||||
matchPath = f"{resultPath}/matchFrames/"
|
||||
safe_make_dir(matchPath)
|
||||
notMatchPath = f"{resultPath}/notMatchFrames/"
|
||||
abnormalPath = f"{resultPath}/abnormal/"
|
||||
|
||||
for key in resultDict:
|
||||
listTmp = list(resultDict[key])
|
||||
if (len(listTmp) == (deviceCount * streamProfileCount)):
|
||||
haveAbnormalData = False
|
||||
for info in listTmp:
|
||||
if info.tspDiff >= tspRangeThreshold:
|
||||
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
haveAbnormalData = True
|
||||
else:
|
||||
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
|
||||
if haveAbnormalData:
|
||||
safe_make_dir(abnormalPath)
|
||||
for info in listTmp:
|
||||
if info.tspDiff >= tspRangeThreshold:
|
||||
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
else:
|
||||
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
else:
|
||||
safe_make_dir(notMatchPath)
|
||||
for info in listTmp:
|
||||
newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
|
||||
shutil.copy(info.picturePath, newFilePath)
|
||||
|
||||
def main():
|
||||
if not initPrimaryId():
|
||||
print("Initialize primaryId failed. exit.")
|
||||
return
|
||||
|
||||
if not initSyncConfigParams():
|
||||
print("Initialized Sync config parameter failed. exit.")
|
||||
return
|
||||
|
||||
initResultDir()
|
||||
|
||||
print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}")
|
||||
handleSyncFrames()
|
||||
|
||||
return
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Gemini 2'")
|
||||
parser.add_argument('frames_dir', type=str, help="Frames directory")
|
||||
parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory")
|
||||
args = parser.parse_args()
|
||||
|
||||
sourceRootPath = args.frames_dir
|
||||
sourceFramesPath = f"{args.frames_dir}/TotalModeFrames"
|
||||
mainPythonWorkPath = args.main_script_root_dir
|
||||
print(f"Start of Gemini2 synchronize frame. sourceRootPath={sourceRootPath}")
|
||||
|
||||
main()
|
||||
|
||||
print("Finish of Gemini2 synchronize frame.")
|
||||
@@ -1,430 +0,0 @@
|
||||
import os.path
|
||||
import argparse
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import configparser
|
||||
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
## Config.ini中配置
|
||||
# 单位:FPS,请依据数据集实际帧率填写,否则会导致匹配不准确
|
||||
frameRate = -1
|
||||
# 单位:ms,当匹配后某组数据帧的时间戳极差大于等于tspRangeThreshold,文件名会增加标注
|
||||
tspRangeThreshold = -1
|
||||
|
||||
## Python脚本自动解析, 不要修改
|
||||
primaryId = '-1' #主机ID
|
||||
mainPythonWorkPath = "" # SyncFramesMain.py的工作目录
|
||||
sourceFramesPath = "" # 请勿修改,固定值,TotalMode目录路径
|
||||
sourceRootPath = "" # 存放TotalMode的根目录
|
||||
resultPath = "" # 保存sync分析结果
|
||||
streamProfileDict = dict() # 保存Python结果
|
||||
|
||||
class PictureInfo(object):
|
||||
class Struct(object):
|
||||
def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
|
||||
self.deviceId = deviceId
|
||||
self.sensorType = sensorType
|
||||
self.syncTimeStamp = syncTimeStamp
|
||||
self.picturePath = picturePath
|
||||
self.deviceIdFull = deviceIdFull
|
||||
self.fileExt = fileExt
|
||||
|
||||
def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
|
||||
return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt)
|
||||
|
||||
class StreamProfileInfo:
|
||||
def __init__(self, sensorType, width, height, format, fps):
|
||||
self.sensorType = sensorType
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.format = format
|
||||
self.fps = fps
|
||||
|
||||
def initPrimaryId():
|
||||
global primaryId
|
||||
|
||||
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
|
||||
if not os.path.exists(deviceInfoPath):
|
||||
print(f"Initialize primaryId failed. {deviceInfoPath} not exists")
|
||||
return False
|
||||
|
||||
with open(deviceInfoPath, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not 'devices' in data:
|
||||
print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'")
|
||||
return False
|
||||
|
||||
for item in data['devices']:
|
||||
if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']:
|
||||
primaryId = str(item['index'])
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def initSyncConfigParams():
|
||||
global frameRate, tspRangeThreshold
|
||||
|
||||
configPath = f"{mainPythonWorkPath}/Config.ini"
|
||||
if not os.path.exists(configPath):
|
||||
print(f"Initliaze synchronized config parameter failed. {configPath} not exists.")
|
||||
return False
|
||||
|
||||
# 创建ConfigParser对象
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
# 读取ini文件
|
||||
config.read(configPath, 'utf-8')
|
||||
|
||||
if not config.has_option('Parameter', 'frameRate'):
|
||||
print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}")
|
||||
return False
|
||||
|
||||
if not config.has_option('Parameter', 'tspRangeThreshold'):
|
||||
print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}")
|
||||
return False
|
||||
|
||||
frameRate = config.getfloat('Parameter', 'frameRate')
|
||||
if frameRate <= 0 or frameRate >= 1000:
|
||||
print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}")
|
||||
return False
|
||||
|
||||
tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold')
|
||||
if tspRangeThreshold <= 0:
|
||||
print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def getDeviceCount():
|
||||
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
|
||||
if not os.path.exists(deviceInfoPath):
|
||||
print(f"Get device count failed. {deviceInfoPath} not exists")
|
||||
return 0
|
||||
|
||||
with open(deviceInfoPath, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not 'devices' in data:
|
||||
print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'")
|
||||
return 0
|
||||
|
||||
return len(data['devices'])
|
||||
|
||||
def initProfileInfoDict():
|
||||
global streamProfileDict
|
||||
|
||||
streamProfileDict = dict()
|
||||
profilePath = f"{sourceRootPath}/StreamProfileInfo.txt"
|
||||
if not os.path.exists(profilePath):
|
||||
print(f"Initialize profile information dictionary failed. {profilePath} not exists")
|
||||
return False
|
||||
|
||||
with open(profilePath, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not 'streamProfiles' in data:
|
||||
print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'")
|
||||
return False
|
||||
|
||||
for item in data['streamProfiles']:
|
||||
if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item:
|
||||
profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps'])
|
||||
streamProfileDict.setdefault(item['sensorType'], profileInfo)
|
||||
else:
|
||||
print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}")
|
||||
|
||||
return len(streamProfileDict.items()) > 0
|
||||
|
||||
def initResultDir():
|
||||
global resultPath
|
||||
timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S")
|
||||
resultPath = f"{sourceRootPath}/results-{timeText}"
|
||||
if not os.path.exists(resultPath):
|
||||
os.makedirs(resultPath)
|
||||
|
||||
def isFrameFile(fileName):
|
||||
fileExt = os.path.splitext(fileName)
|
||||
if len(fileExt) < 2:
|
||||
return False
|
||||
|
||||
frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"}
|
||||
return fileExt[1] in frameExts
|
||||
|
||||
def extract_d_number(filename):
|
||||
"""提取文件名中以'd'开头的数字部分"""
|
||||
match_d = re.search(r'd(\d+)', filename)
|
||||
if match_d:
|
||||
return int(match_d.group(1))
|
||||
|
||||
match_g = re.search(r'g(\d+)', filename)
|
||||
if match_g:
|
||||
return int(match_g.group(1))
|
||||
|
||||
return float('inf')
|
||||
|
||||
def initPictureInfoDictionary(rootFilePath, pictureInfoDict):
|
||||
frameFileCount = 0
|
||||
for dirpath, dirnames, filenames in os.walk(rootFilePath):
|
||||
filenames.sort(key=extract_d_number) # 排序,按文件名中的'd'后面的数字从小到大排序
|
||||
for fileName in filenames:
|
||||
filePath = os.path.join(dirpath, fileName)
|
||||
if not isFrameFile(fileName):
|
||||
continue
|
||||
|
||||
frameFileCount += 1
|
||||
fileNameNoExt = os.path.splitext(fileName)[0]
|
||||
pictureNameList = fileNameNoExt.split('_')
|
||||
deviceId = pictureNameList[2][5:]
|
||||
sensorType = pictureNameList[0].replace('#', '_')
|
||||
syncTimeStamp = int(pictureNameList[3][1:])
|
||||
|
||||
# 创建结构体
|
||||
pictureInfo = PictureInfo()
|
||||
info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1])
|
||||
|
||||
# 格式化字典一个key对应一个数组
|
||||
pictureInfoDict.setdefault(deviceId, []).append(info)
|
||||
|
||||
if 0 == frameFileCount:
|
||||
print("initialize pictureInfoDict failed. Not found frame file")
|
||||
|
||||
|
||||
def matchFrame(pictureInfoDict):
|
||||
global primaryId, frameRate
|
||||
global streamProfileDict
|
||||
|
||||
# 获取比较图像数组,取第一个设备的Color数组来进行比较
|
||||
compareList = []
|
||||
for key in pictureInfoDict:
|
||||
listTmp = list(pictureInfoDict[key])
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == 'ir_left':
|
||||
compareList.append(info)
|
||||
|
||||
# 根据开流情况动态分析,减少循环次数
|
||||
hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict
|
||||
hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict
|
||||
hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict
|
||||
hasIRLeftProfile = 'OB_SENSOR_IR_LEFT' in streamProfileDict
|
||||
hasIRRightProfile = 'OB_SENSOR_IR_RIGHT' in streamProfileDict
|
||||
|
||||
# 匹配相邻帧
|
||||
resultDict = {}
|
||||
consumedList = []
|
||||
dictIndex = 0
|
||||
frameTspHalfGap = int(1000.0/frameRate/2.0+0.5)
|
||||
for comparePic in compareList:
|
||||
resultDict.setdefault(dictIndex, []).append(comparePic)
|
||||
for key in pictureInfoDict:
|
||||
listTmp = list(pictureInfoDict[key])
|
||||
# 对比彩色
|
||||
if hasColorProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'color' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# 对Depth
|
||||
if hasDepthProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'depth' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
print("=====================result value : jjjjjjjjjjjjjjj:%s" % (info.syncTimeStamp))
|
||||
break
|
||||
|
||||
# IR
|
||||
if hasIRProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'ir' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# 对左IR
|
||||
if hasIRLeftProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'ir_left' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# 对右IR
|
||||
if hasIRRightProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'ir_right' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# 计算偏差
|
||||
minTsp = min(resultDict[dictIndex], key=lambda x: x.syncTimeStamp)
|
||||
for info in resultDict[dictIndex]:
|
||||
info.tspDiff = info.syncTimeStamp - minTsp.syncTimeStamp
|
||||
|
||||
dictIndex += 1
|
||||
|
||||
return resultDict
|
||||
|
||||
|
||||
def safe_make_dir(path):
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
|
||||
def handleSyncFrames():
|
||||
global resultPath
|
||||
global sourceFramesPath
|
||||
global streamProfileDict
|
||||
|
||||
fp = open(f"{resultPath}/frameMatchLog.txt", "w")
|
||||
|
||||
if not initProfileInfoDict():
|
||||
print("Initialize stream profile dictionary failed. exit")
|
||||
return
|
||||
print(f"Stream profiles: {streamProfileDict}")
|
||||
streamProfileCount = len(streamProfileDict.items())
|
||||
|
||||
pictureInfoDict = {}
|
||||
|
||||
initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict)
|
||||
if len(pictureInfoDict) <= 0:
|
||||
print("init pictureInfoDict failed. pictureInfoDict.len = 0")
|
||||
return
|
||||
|
||||
# # Dump pictureInfoDict
|
||||
# for key in pictureInfoDict:
|
||||
# print("===================key:%s" % key)
|
||||
# listTmp = list(pictureInfoDict[key])
|
||||
# for info in listTmp:
|
||||
# print("=====================value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (info.deviceId,info.sensorType,info.syncTimeStamp,info.picturePath))
|
||||
|
||||
deviceCount = getDeviceCount()
|
||||
print("=================device count:%d" % deviceCount)
|
||||
|
||||
resultDict = {}
|
||||
resultDict = matchFrame(pictureInfoDict)
|
||||
|
||||
for key in resultDict:
|
||||
fp.write("===================result key:%s\n" % key)
|
||||
print("===================result key:%s" % key)
|
||||
listTmp = list(resultDict[key])
|
||||
for info in listTmp:
|
||||
fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % (
|
||||
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
|
||||
print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (
|
||||
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
|
||||
|
||||
matchPath = f"{resultPath}/matchFrames/"
|
||||
safe_make_dir(matchPath)
|
||||
notMatchPath = f"{resultPath}/notMatchFrames/"
|
||||
abnormalPath = f"{resultPath}/abnormal/"
|
||||
|
||||
for key in resultDict:
|
||||
listTmp = list(resultDict[key])
|
||||
# print("***********")
|
||||
# print(listTmp)
|
||||
if (len(listTmp) == (deviceCount * streamProfileCount)):
|
||||
haveAbnormalData = False
|
||||
for info in listTmp:
|
||||
if info.tspDiff >= tspRangeThreshold:
|
||||
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
haveAbnormalData = True
|
||||
else:
|
||||
# print("***********")
|
||||
# print(info.picturePath)
|
||||
# print("###########")
|
||||
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
# print(path)
|
||||
|
||||
if haveAbnormalData:
|
||||
safe_make_dir(abnormalPath)
|
||||
for info in listTmp:
|
||||
if info.tspDiff >= tspRangeThreshold:
|
||||
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
else:
|
||||
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
else:
|
||||
safe_make_dir(notMatchPath)
|
||||
for info in listTmp:
|
||||
newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
|
||||
shutil.copy(info.picturePath, newFilePath)
|
||||
|
||||
def main():
|
||||
if not initPrimaryId():
|
||||
print("Initialize primaryId failed. exit.")
|
||||
return
|
||||
|
||||
if not initSyncConfigParams():
|
||||
print("Initialized Sync config parameter failed. exit.")
|
||||
return
|
||||
|
||||
initResultDir()
|
||||
|
||||
print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}")
|
||||
handleSyncFrames()
|
||||
|
||||
return
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Devices'")
|
||||
parser.add_argument('frames_dir', type=str, help="Frames directory")
|
||||
parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory")
|
||||
args = parser.parse_args()
|
||||
|
||||
sourceRootPath = args.frames_dir
|
||||
sourceFramesPath = f"{args.frames_dir}/TotalModeFrames"
|
||||
mainPythonWorkPath = args.main_script_root_dir
|
||||
print(f"Start of CommonFrameMatch synchronize frame. sourceRootPath={sourceRootPath}")
|
||||
|
||||
main()
|
||||
|
||||
print("Finish of CommonFrameMatch synchronize frame.")
|
||||
@@ -1,408 +0,0 @@
|
||||
import os.path
|
||||
import argparse
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import configparser
|
||||
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
## Config.ini中配置
|
||||
# 单位:FPS,请依据数据集实际帧率填写,否则会导致匹配不准确
|
||||
frameRate = -1
|
||||
# 单位:ms,当匹配后某组数据帧的时间戳极差大于等于tspRangeThreshold,文件名会增加标注
|
||||
tspRangeThreshold = -1
|
||||
|
||||
## Python脚本自动解析, 不要修改
|
||||
primaryId = '-1' #主机ID
|
||||
mainPythonWorkPath = "" # SyncFramesMain.py的工作目录
|
||||
sourceFramesPath = "" # 请勿修改,固定值,TotalMode目录路径
|
||||
sourceRootPath = "" # 存放TotalMode的根目录
|
||||
resultPath = "" # 保存sync分析结果
|
||||
streamProfileDict = dict() # 保存Python结果
|
||||
|
||||
class PictureInfo(object):
|
||||
class Struct(object):
|
||||
def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
|
||||
self.deviceId = deviceId
|
||||
self.sensorType = sensorType
|
||||
self.syncTimeStamp = syncTimeStamp
|
||||
self.picturePath = picturePath
|
||||
self.deviceIdFull = deviceIdFull
|
||||
self.fileExt = fileExt
|
||||
|
||||
def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt):
|
||||
return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt)
|
||||
|
||||
class StreamProfileInfo:
|
||||
def __init__(self, sensorType, width, height, format, fps):
|
||||
self.sensorType = sensorType
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.format = format
|
||||
self.fps = fps
|
||||
|
||||
def initPrimaryId():
|
||||
global primaryId
|
||||
|
||||
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
|
||||
if not os.path.exists(deviceInfoPath):
|
||||
print(f"Initialize primaryId failed. {deviceInfoPath} not exists")
|
||||
return False
|
||||
|
||||
with open(deviceInfoPath, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not 'devices' in data:
|
||||
print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'")
|
||||
return False
|
||||
|
||||
for item in data['devices']:
|
||||
if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']:
|
||||
primaryId = str(item['index'])
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def initSyncConfigParams():
|
||||
global frameRate, tspRangeThreshold
|
||||
|
||||
configPath = f"{mainPythonWorkPath}/Config.ini"
|
||||
if not os.path.exists(configPath):
|
||||
print(f"Initliaze synchronized config parameter failed. {configPath} not exists.")
|
||||
return False
|
||||
|
||||
# 创建ConfigParser对象
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
# 读取ini文件
|
||||
config.read(configPath, 'utf-8')
|
||||
|
||||
if not config.has_option('Parameter', 'frameRate'):
|
||||
print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}")
|
||||
return False
|
||||
|
||||
if not config.has_option('Parameter', 'tspRangeThreshold'):
|
||||
print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}")
|
||||
return False
|
||||
|
||||
frameRate = config.getfloat('Parameter', 'frameRate')
|
||||
if frameRate <= 0 or frameRate >= 1000:
|
||||
print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}")
|
||||
return False
|
||||
|
||||
tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold')
|
||||
if tspRangeThreshold <= 0:
|
||||
print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def getDeviceCount():
|
||||
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
|
||||
if not os.path.exists(deviceInfoPath):
|
||||
print(f"Get device count failed. {deviceInfoPath} not exists")
|
||||
return 0
|
||||
|
||||
with open(deviceInfoPath, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not 'devices' in data:
|
||||
print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'")
|
||||
return 0
|
||||
|
||||
return len(data['devices'])
|
||||
|
||||
def initProfileInfoDict():
|
||||
global streamProfileDict
|
||||
|
||||
streamProfileDict = dict()
|
||||
profilePath = f"{sourceRootPath}/StreamProfileInfo.txt"
|
||||
if not os.path.exists(profilePath):
|
||||
print(f"Initialize profile information dictionary failed. {profilePath} not exists")
|
||||
return False
|
||||
|
||||
with open(profilePath, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not 'streamProfiles' in data:
|
||||
print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'")
|
||||
return False
|
||||
|
||||
for item in data['streamProfiles']:
|
||||
if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item:
|
||||
profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps'])
|
||||
streamProfileDict.setdefault(item['sensorType'], profileInfo)
|
||||
else:
|
||||
print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}")
|
||||
|
||||
return len(streamProfileDict.items()) > 0
|
||||
|
||||
def initResultDir():
|
||||
global resultPath
|
||||
timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S")
|
||||
resultPath = f"{sourceRootPath}/results-{timeText}"
|
||||
if not os.path.exists(resultPath):
|
||||
os.makedirs(resultPath)
|
||||
|
||||
def isFrameFile(fileName):
|
||||
fileExt = os.path.splitext(fileName)
|
||||
if len(fileExt) < 2:
|
||||
return False
|
||||
|
||||
frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"}
|
||||
return fileExt[1] in frameExts
|
||||
|
||||
def initPictureInfoDictionary(rootFilePath, pictureInfoDict):
|
||||
frameFileCount = 0
|
||||
for dirpath, dirnames, filenames in os.walk(rootFilePath):
|
||||
# print(f"dirpath={dirpath}, dirpath.basename=" + os.path.basename(dirpath))
|
||||
for fileName in filenames:
|
||||
filePath = os.path.join(dirpath, fileName)
|
||||
if not isFrameFile(fileName):
|
||||
continue
|
||||
|
||||
frameFileCount += 1
|
||||
fileNameNoExt = os.path.splitext(fileName)[0]
|
||||
pictureNameList = fileNameNoExt.split('_')
|
||||
deviceId = pictureNameList[2][5:]
|
||||
sensorType = pictureNameList[0].replace('#', '_')
|
||||
syncTimeStamp = int(pictureNameList[3][1:])
|
||||
|
||||
# 创建结构体
|
||||
pictureInfo = PictureInfo()
|
||||
info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1])
|
||||
|
||||
# 格式化字典一个key对应一个数组
|
||||
pictureInfoDict.setdefault(deviceId, []).append(info)
|
||||
|
||||
if 0 == frameFileCount:
|
||||
print("initialize pictureInfoDict failed. Not found frame file")
|
||||
|
||||
|
||||
def matchFrame(pictureInfoDict):
|
||||
global primaryId, frameRate
|
||||
global streamProfileDict
|
||||
|
||||
# 获取比较图像数组,取第一个设备的Color数组来进行比较
|
||||
compareList = []
|
||||
for key in pictureInfoDict:
|
||||
listTmp = list(pictureInfoDict[key])
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == 'color':
|
||||
compareList.append(info)
|
||||
|
||||
# 根据开流情况动态分析,减少循环次数
|
||||
hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict
|
||||
hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict
|
||||
hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict
|
||||
hasIRLeftProfile = 'OB_SENSOR_IR_LEFT' in streamProfileDict
|
||||
hasIRRightProfile = 'OB_SENSOR_IR_RIGHT' in streamProfileDict
|
||||
|
||||
# 匹配相邻帧
|
||||
resultDict = {}
|
||||
consumedList = []
|
||||
dictIndex = 0
|
||||
frameTspHalfGap = int(1000.0/frameRate/2.0+0.5)
|
||||
for comparePic in compareList:
|
||||
resultDict.setdefault(dictIndex, []).append(comparePic)
|
||||
for key in pictureInfoDict:
|
||||
listTmp = list(pictureInfoDict[key])
|
||||
# 对比彩色
|
||||
if hasColorProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'color' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# 对Depth
|
||||
if hasDepthProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'depth' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# IR
|
||||
if hasIRProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'ir' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# 对左IR
|
||||
if hasIRLeftProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'ir_left' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# 对右IR
|
||||
if hasIRRightProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'ir_right' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# 计算偏差
|
||||
minTsp = min(resultDict[dictIndex], key=lambda x: x.syncTimeStamp)
|
||||
for info in resultDict[dictIndex]:
|
||||
info.tspDiff = info.syncTimeStamp - minTsp.syncTimeStamp
|
||||
|
||||
dictIndex += 1
|
||||
|
||||
return resultDict
|
||||
|
||||
|
||||
def safe_make_dir(path):
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
|
||||
def handleSyncFrames():
|
||||
global resultPath
|
||||
global sourceFramesPath
|
||||
global streamProfileDict
|
||||
|
||||
fp = open(f"{resultPath}/frameMatchLog.txt", "w")
|
||||
|
||||
if not initProfileInfoDict():
|
||||
print("Initialize stream profile dictionary failed. exit")
|
||||
return
|
||||
print(f"Stream profiles: {streamProfileDict}")
|
||||
streamProfileCount = len(streamProfileDict.items())
|
||||
|
||||
pictureInfoDict = {}
|
||||
|
||||
initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict)
|
||||
if len(pictureInfoDict) <= 0:
|
||||
print("init pictureInfoDict failed. pictureInfoDict.len = 0")
|
||||
return
|
||||
|
||||
# # Dump pictureInfoDict
|
||||
# for key in pictureInfoDict:
|
||||
# print("===================key:%s" % key)
|
||||
# listTmp = list(pictureInfoDict[key])
|
||||
# for info in listTmp:
|
||||
# print("=====================value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (info.deviceId,info.sensorType,info.syncTimeStamp,info.picturePath))
|
||||
|
||||
deviceCount = getDeviceCount()
|
||||
print("=================device count:%d" % deviceCount)
|
||||
|
||||
resultDict = {}
|
||||
resultDict = matchFrame(pictureInfoDict)
|
||||
|
||||
for key in resultDict:
|
||||
fp.write("===================result key:%s\n" % key)
|
||||
print("===================result key:%s" % key)
|
||||
listTmp = list(resultDict[key])
|
||||
for info in listTmp:
|
||||
fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % (
|
||||
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
|
||||
print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (
|
||||
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
|
||||
|
||||
matchPath = f"{resultPath}/matchFrames/"
|
||||
safe_make_dir(matchPath)
|
||||
notMatchPath = f"{resultPath}/notMatchFrames/"
|
||||
abnormalPath = f"{resultPath}/abnormal/"
|
||||
|
||||
for key in resultDict:
|
||||
listTmp = list(resultDict[key])
|
||||
if (len(listTmp) == (deviceCount * streamProfileCount)):
|
||||
haveAbnormalData = False
|
||||
for info in listTmp:
|
||||
if info.tspDiff >= tspRangeThreshold:
|
||||
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
haveAbnormalData = True
|
||||
else:
|
||||
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
|
||||
if haveAbnormalData:
|
||||
safe_make_dir(abnormalPath)
|
||||
for info in listTmp:
|
||||
if info.tspDiff >= tspRangeThreshold:
|
||||
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
else:
|
||||
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
else:
|
||||
safe_make_dir(notMatchPath)
|
||||
for info in listTmp:
|
||||
newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + info.fileExt
|
||||
shutil.copy(info.picturePath, newFilePath)
|
||||
|
||||
def main():
|
||||
if not initPrimaryId():
|
||||
print("Initialize primaryId failed. exit.")
|
||||
return
|
||||
|
||||
if not initSyncConfigParams():
|
||||
print("Initialized Sync config parameter failed. exit.")
|
||||
return
|
||||
|
||||
initResultDir()
|
||||
|
||||
print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}")
|
||||
handleSyncFrames()
|
||||
|
||||
return
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Gemini 2 VL'")
|
||||
parser.add_argument('frames_dir', type=str, help="Frames directory")
|
||||
parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory")
|
||||
args = parser.parse_args()
|
||||
|
||||
sourceRootPath = args.frames_dir
|
||||
sourceFramesPath = f"{args.frames_dir}/TotalModeFrames"
|
||||
mainPythonWorkPath = args.main_script_root_dir
|
||||
print(f"Start of Gemini2VL synchronize frame. sourceRootPath={sourceRootPath}")
|
||||
|
||||
main()
|
||||
|
||||
print("Finish of Gemini2VL synchronize frame.")
|
||||
@@ -1,412 +0,0 @@
|
||||
import os.path
|
||||
import argparse
|
||||
import sys
|
||||
import json
|
||||
import shutil
|
||||
import configparser
|
||||
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
|
||||
## Config.ini中配置
|
||||
# 单位:FPS,请依据数据集实际帧率填写,否则会导致匹配不准确
|
||||
frameRate = -1
|
||||
# 单位:ms,当匹配后某组数据帧的时间戳极差大于等于tspRangeThreshold,文件名会增加标注
|
||||
tspRangeThreshold = -1
|
||||
|
||||
## Python脚本自动解析, 不要修改
|
||||
primaryId = '-1' #主机ID
|
||||
mainPythonWorkPath = "" # SyncFramesMain.py的工作目录
|
||||
sourceFramesPath = "" # 请勿修改,固定值,TotalMode目录路径
|
||||
sourceRootPath = "" # 存放TotalMode的根目录
|
||||
resultPath = "" # 保存sync分析结果
|
||||
streamProfileDict = dict() # 保存Python结果
|
||||
|
||||
class PictureInfo(object):
|
||||
class Struct(object):
|
||||
def __init__(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt, exp, gain):
|
||||
self.deviceId = deviceId
|
||||
self.sensorType = sensorType
|
||||
self.syncTimeStamp = syncTimeStamp
|
||||
self.picturePath = picturePath
|
||||
self.deviceIdFull = deviceIdFull
|
||||
self.fileExt = fileExt
|
||||
self.exp = exp
|
||||
self.gain = gain
|
||||
|
||||
def make_struct(self, deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt, exp, gain):
|
||||
return self.Struct(deviceId, sensorType, syncTimeStamp, picturePath, deviceIdFull, fileExt, exp, gain)
|
||||
|
||||
class StreamProfileInfo:
|
||||
def __init__(self, sensorType, width, height, format, fps):
|
||||
self.sensorType = sensorType
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.format = format
|
||||
self.fps = fps
|
||||
|
||||
def initPrimaryId():
|
||||
global primaryId
|
||||
|
||||
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
|
||||
if not os.path.exists(deviceInfoPath):
|
||||
print(f"Initialize primaryId failed. {deviceInfoPath} not exists")
|
||||
return False
|
||||
|
||||
with open(deviceInfoPath, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not 'devices' in data:
|
||||
print(f"Initialize primaryId failed. {deviceInfoPath} not contain item 'devices'")
|
||||
return False
|
||||
|
||||
for item in data['devices']:
|
||||
if 'isPrimaryDevice' in item and 'index' in item and item['isPrimaryDevice']:
|
||||
primaryId = str(item['index'])
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def initSyncConfigParams():
|
||||
global frameRate, tspRangeThreshold
|
||||
|
||||
configPath = f"{mainPythonWorkPath}/Config.ini"
|
||||
if not os.path.exists(configPath):
|
||||
print(f"Initliaze synchronized config parameter failed. {configPath} not exists.")
|
||||
return False
|
||||
|
||||
# 创建ConfigParser对象
|
||||
config = configparser.ConfigParser()
|
||||
|
||||
# 读取ini文件
|
||||
config.read(configPath, 'utf-8')
|
||||
|
||||
if not config.has_option('Parameter', 'frameRate'):
|
||||
print(f"Initliaze synchronized config parameter failed. Not define 'frameRate' in ${configPath}")
|
||||
return False
|
||||
|
||||
if not config.has_option('Parameter', 'tspRangeThreshold'):
|
||||
print(f"Initliaze synchronized config parameter failed. Not define 'tspRangeThreshold' in ${configPath}")
|
||||
return False
|
||||
|
||||
frameRate = config.getfloat('Parameter', 'frameRate')
|
||||
if frameRate <= 0 or frameRate >= 1000:
|
||||
print(f"Initliaze synchronized config parameter failed. Invalid frameRate={frameRate}")
|
||||
return False
|
||||
|
||||
tspRangeThreshold = config.getfloat('Parameter', 'tspRangeThreshold')
|
||||
if tspRangeThreshold <= 0:
|
||||
print(f"Initliaze synchronized config parameter failed. Invalid tspRangeThreshold={tspRangeThreshold}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def getDeviceCount():
|
||||
deviceInfoPath = f"{sourceRootPath}/DevicesInfo.txt"
|
||||
if not os.path.exists(deviceInfoPath):
|
||||
print(f"Get device count failed. {deviceInfoPath} not exists")
|
||||
return 0
|
||||
|
||||
with open(deviceInfoPath, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not 'devices' in data:
|
||||
print(f"Get device count failed. {deviceInfoPath} not contain item 'devices'")
|
||||
return 0
|
||||
|
||||
return len(data['devices'])
|
||||
|
||||
def initProfileInfoDict():
|
||||
global streamProfileDict
|
||||
|
||||
streamProfileDict = dict()
|
||||
profilePath = f"{sourceRootPath}/StreamProfileInfo.txt"
|
||||
if not os.path.exists(profilePath):
|
||||
print(f"Initialize profile information dictionary failed. {profilePath} not exists")
|
||||
return False
|
||||
|
||||
with open(profilePath, 'r') as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not 'streamProfiles' in data:
|
||||
print(f"Initialize profile information dictionary failed. {profilePath} not contain item 'streamProfiles'")
|
||||
return False
|
||||
|
||||
for item in data['streamProfiles']:
|
||||
if 'sensorType' in item and 'width' in item and 'height' in item and 'format' in item and 'fps' in item:
|
||||
profileInfo = StreamProfileInfo(item['sensorType'], item['width'], item['height'], item['format'], item['fps'])
|
||||
streamProfileDict.setdefault(item['sensorType'], profileInfo)
|
||||
else:
|
||||
print(f"Initialize profile information dictionary. Error invalid StreamProfile, {item}")
|
||||
|
||||
return len(streamProfileDict.items()) > 0
|
||||
|
||||
def initResultDir():
|
||||
global resultPath
|
||||
timeText = datetime.now().strftime("%Y-%m-%d_%H%M%S")
|
||||
resultPath = f"{sourceRootPath}/results-{timeText}"
|
||||
if not os.path.exists(resultPath):
|
||||
os.makedirs(resultPath)
|
||||
|
||||
def isFrameFile(fileName):
|
||||
fileExt = os.path.splitext(fileName)
|
||||
if len(fileExt) < 2:
|
||||
return False
|
||||
|
||||
frameExts = {".jpeg", ".jpg", ".png", ".raw", "bmp"}
|
||||
return fileExt[1] in frameExts
|
||||
|
||||
def initPictureInfoDictionary(rootFilePath, pictureInfoDict):
|
||||
frameFileCount = 0
|
||||
for dirpath, dirnames, filenames in os.walk(rootFilePath):
|
||||
# print(f"dirpath={dirpath}, dirpath.basename=" + os.path.basename(dirpath))
|
||||
for fileName in filenames:
|
||||
filePath = os.path.join(dirpath, fileName)
|
||||
if not isFrameFile(fileName):
|
||||
continue
|
||||
|
||||
frameFileCount += 1
|
||||
fileNameNoExt = os.path.splitext(fileName)[0]
|
||||
pictureNameList = fileNameNoExt.split('_')
|
||||
deviceId = pictureNameList[2][5:]
|
||||
sensorType = pictureNameList[0].replace('#', '_')
|
||||
syncTimeStamp = int(pictureNameList[3][1:])
|
||||
exp = str(pictureNameList[6][0:])
|
||||
gain = str(pictureNameList[7][0:])
|
||||
|
||||
# 创建结构体
|
||||
pictureInfo = PictureInfo()
|
||||
info = pictureInfo.make_struct(deviceId, sensorType, syncTimeStamp, filePath, os.path.basename(dirpath), os.path.splitext(fileName)[1],exp,gain)
|
||||
|
||||
# 格式化字典一个key对应一个数组
|
||||
pictureInfoDict.setdefault(deviceId, []).append(info)
|
||||
|
||||
if 0 == frameFileCount:
|
||||
print("initialize pictureInfoDict failed. Not found frame file")
|
||||
|
||||
|
||||
def matchFrame(pictureInfoDict):
|
||||
global primaryId, frameRate
|
||||
global streamProfileDict
|
||||
|
||||
# 获取比较图像数组,取第一个设备的Color数组来进行比较
|
||||
compareList = []
|
||||
for key in pictureInfoDict:
|
||||
listTmp = list(pictureInfoDict[key])
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == 'color':
|
||||
compareList.append(info)
|
||||
|
||||
# 根据开流情况动态分析,减少循环次数
|
||||
hasColorProfile = 'OB_SENSOR_COLOR' in streamProfileDict
|
||||
hasDepthProfile = 'OB_SENSOR_DEPTH' in streamProfileDict
|
||||
hasIRProfile = 'OB_SENSOR_IR' in streamProfileDict
|
||||
hasIRLeftProfile = 'OB_SENSOR_IR_LEFT' in streamProfileDict
|
||||
hasIRRightProfile = 'OB_SENSOR_IR_RIGHT' in streamProfileDict
|
||||
|
||||
# 匹配相邻帧
|
||||
resultDict = {}
|
||||
consumedList = []
|
||||
dictIndex = 0
|
||||
frameTspHalfGap = int(1000.0/frameRate/2.0+0.5)
|
||||
for comparePic in compareList:
|
||||
resultDict.setdefault(dictIndex, []).append(comparePic)
|
||||
for key in pictureInfoDict:
|
||||
listTmp = list(pictureInfoDict[key])
|
||||
# 对比彩色
|
||||
if hasColorProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'color' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# 对Depth
|
||||
if hasDepthProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'depth' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# IR
|
||||
if hasIRProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'ir' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# 对左IR
|
||||
if hasIRLeftProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'ir_left' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# 对右IR
|
||||
if hasIRRightProfile:
|
||||
for info in listTmp:
|
||||
if info.deviceId == primaryId and info.sensorType == comparePic.sensorType:
|
||||
continue
|
||||
|
||||
if info in consumedList:
|
||||
continue
|
||||
|
||||
if info.sensorType == 'ir_right' and abs(info.syncTimeStamp - comparePic.syncTimeStamp) < frameTspHalfGap:
|
||||
resultDict.setdefault(dictIndex, []).append(info)
|
||||
consumedList.append(info)
|
||||
break
|
||||
|
||||
# 计算偏差
|
||||
minTsp = min(resultDict[dictIndex], key=lambda x: x.syncTimeStamp)
|
||||
for info in resultDict[dictIndex]:
|
||||
info.tspDiff = info.syncTimeStamp - minTsp.syncTimeStamp
|
||||
|
||||
dictIndex += 1
|
||||
|
||||
return resultDict
|
||||
|
||||
|
||||
def safe_make_dir(path):
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
|
||||
def handleSyncFrames():
|
||||
global resultPath
|
||||
global sourceFramesPath
|
||||
global streamProfileDict
|
||||
|
||||
fp = open(f"{resultPath}/frameMatchLog.txt", "w")
|
||||
|
||||
if not initProfileInfoDict():
|
||||
print("Initialize stream profile dictionary failed. exit")
|
||||
return
|
||||
print(f"Stream profiles: {streamProfileDict}")
|
||||
streamProfileCount = len(streamProfileDict.items())
|
||||
|
||||
pictureInfoDict = {}
|
||||
|
||||
initPictureInfoDictionary(Path(sourceFramesPath), pictureInfoDict)
|
||||
if len(pictureInfoDict) <= 0:
|
||||
print("init pictureInfoDict failed. pictureInfoDict.len = 0")
|
||||
return
|
||||
|
||||
# # Dump pictureInfoDict
|
||||
# for key in pictureInfoDict:
|
||||
# print("===================key:%s" % key)
|
||||
# listTmp = list(pictureInfoDict[key])
|
||||
# for info in listTmp:
|
||||
# print("=====================value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (info.deviceId,info.sensorType,info.syncTimeStamp,info.picturePath))
|
||||
|
||||
deviceCount = getDeviceCount()
|
||||
print("=================device count:%d" % deviceCount)
|
||||
|
||||
resultDict = {}
|
||||
resultDict = matchFrame(pictureInfoDict)
|
||||
|
||||
for key in resultDict:
|
||||
fp.write("===================result key:%s\n" % key)
|
||||
print("===================result key:%s" % key)
|
||||
listTmp = list(resultDict[key])
|
||||
for info in listTmp:
|
||||
fp.write("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s\n" % (
|
||||
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
|
||||
print("=====================result value : deviceId:%s\tsensorType:%s\tsyncTimeStamp:%s\tpicturePath:%s" % (
|
||||
info.deviceId, info.sensorType, info.syncTimeStamp, info.picturePath))
|
||||
|
||||
matchPath = f"{resultPath}/matchFrames/"
|
||||
safe_make_dir(matchPath)
|
||||
notMatchPath = f"{resultPath}/notMatchFrames/"
|
||||
abnormalPath = f"{resultPath}/abnormal/"
|
||||
|
||||
for key in resultDict:
|
||||
listTmp = list(resultDict[key])
|
||||
if (len(listTmp) == (deviceCount * streamProfileCount)):
|
||||
haveAbnormalData = False
|
||||
for info in listTmp:
|
||||
if info.tspDiff >= tspRangeThreshold:
|
||||
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
haveAbnormalData = True
|
||||
else:
|
||||
path = matchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
|
||||
if haveAbnormalData:
|
||||
safe_make_dir(abnormalPath)
|
||||
for info in listTmp:
|
||||
if info.tspDiff >= tspRangeThreshold:
|
||||
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_xxxxxx" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
else:
|
||||
path = abnormalPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt
|
||||
shutil.copy(info.picturePath, path)
|
||||
else:
|
||||
safe_make_dir(notMatchPath)
|
||||
for info in listTmp:
|
||||
newFilePath = notMatchPath + str(key) + "_" + info.sensorType + "_" + info.deviceIdFull + "_" + str(
|
||||
info.syncTimeStamp) + "_[" + str(info.tspDiff) + "]" + "_"+ str(info.exp) + "_"+ str(info.gain) + info.fileExt
|
||||
shutil.copy(info.picturePath, newFilePath)
|
||||
|
||||
def main():
|
||||
if not initPrimaryId():
|
||||
print("Initialize primaryId failed. exit.")
|
||||
return
|
||||
|
||||
if not initSyncConfigParams():
|
||||
print("Initialized Sync config parameter failed. exit.")
|
||||
return
|
||||
|
||||
initResultDir()
|
||||
|
||||
print(f"PrimaryId={primaryId}, frameRate={frameRate}, tspRangeThreshold={tspRangeThreshold}")
|
||||
handleSyncFrames()
|
||||
|
||||
return
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description="Synchronize frames support for 'Orbbec Devices'")
|
||||
parser.add_argument('frames_dir', type=str, help="Frames directory")
|
||||
parser.add_argument('main_script_root_dir', type=str, help="SyncFramesMain.py working directory")
|
||||
args = parser.parse_args()
|
||||
|
||||
sourceRootPath = args.frames_dir
|
||||
sourceFramesPath = f"{args.frames_dir}/TotalModeFrames"
|
||||
mainPythonWorkPath = args.main_script_root_dir
|
||||
print(f"Start of CommonFrameMatch synchronize frame. sourceRootPath={sourceRootPath}")
|
||||
|
||||
main()
|
||||
|
||||
print("Finish of CommonFrameMatch synchronize frame.")
|
||||
@@ -1,42 +0,0 @@
|
||||
{
|
||||
"deviceVid": "0x2BC5",
|
||||
"devicePid": "0x0800",
|
||||
"primarySerialNumber": "CP1E5420006D",
|
||||
"devices": [{
|
||||
"index": 0,
|
||||
"isPrimaryDevice": true,
|
||||
"vid": "0x2BC5",
|
||||
"pid": "0x0800",
|
||||
"name": "Orbbec Gemini 335",
|
||||
"firmwareVersion": "1.3.64",
|
||||
"serialNumber": "CP1E5420006D",
|
||||
"hardwareVersion": "0.1",
|
||||
"ipAddress": "0.0.0.0",
|
||||
"extensionInfo": {
|
||||
"ExtensionInfo": {
|
||||
"IspFwVer": "20240820",
|
||||
"IspNeedVer": "20240820",
|
||||
"HwType": "R1",
|
||||
"HwVer": "V1.1"
|
||||
}
|
||||
}
|
||||
}, {
|
||||
"index": 1,
|
||||
"isPrimaryDevice": false,
|
||||
"vid": "0x2BC5",
|
||||
"pid": "0x0800",
|
||||
"name": "Orbbec Gemini 335",
|
||||
"firmwareVersion": "1.3.64",
|
||||
"serialNumber": "CP1L44P00051",
|
||||
"hardwareVersion": "0.1",
|
||||
"ipAddress": "0.0.0.0",
|
||||
"extensionInfo": {
|
||||
"ExtensionInfo": {
|
||||
"IspFwVer": "20240820",
|
||||
"IspNeedVer": "20240820",
|
||||
"HwType": "R1",
|
||||
"HwVer": "V1.0"
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"D2CAlignMode": "ALIGN_D2C_SW_MODE",
|
||||
"streamProfiles": [{
|
||||
"sensorType": "OB_SENSOR_COLOR",
|
||||
"width": 1280,
|
||||
"height": 720,
|
||||
"fps": 30,
|
||||
"format": "MJPEG"
|
||||
}, {
|
||||
"sensorType": "OB_SENSOR_IR_LEFT",
|
||||
"width": 848,
|
||||
"height": 480,
|
||||
"fps": 30,
|
||||
"format": "Y8"
|
||||
}]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
@@ -1,55 +0,0 @@
|
||||
===================result key:0
|
||||
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543527 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543527_f9_s1739874543627_e5000_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543527 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543527_f9_s1739874543616_e50_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543527 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543527_f10_s1739874543687_e5000_d16_.jpg
|
||||
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543527 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543527_f9_s1739874543603_e50_d16_.jpg
|
||||
===================result key:1
|
||||
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543494 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543494_f8_s1739874543528_e5000_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543494 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543494_f8_s1739874543540_e50_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543494 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543494_f8_s1739874543687_e5000_d16_.jpg
|
||||
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543494 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543494_f8_s1739874543548_e50_d16_.jpg
|
||||
===================result key:2
|
||||
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543461 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543461_f7_s1739874543493_e5000_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543460 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543460_f7_s1739874543505_e50_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543460 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543460_f7_s1739874543685_e5000_d16_.jpg
|
||||
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543461 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543461_f7_s1739874543520_e50_d16_.jpg
|
||||
===================result key:3
|
||||
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543260 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543260_f1_s1739874543310_e5000_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543260 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543260_f1_s1739874543345_e50_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543260 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543260_f1_s1739874543294_e5000_d16_.jpg
|
||||
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543260 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543260_f1_s1739874543351_e50_d16_.jpg
|
||||
===================result key:4
|
||||
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543361 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543361_f4_s1739874543405_e5000_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543360 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543360_f4_s1739874543468_e50_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543360 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543360_f4_s1739874543460_e5000_d16_.jpg
|
||||
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543360 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543360_f4_s1739874543442_e50_d16_.jpg
|
||||
===================result key:5
|
||||
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543327 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543327_f3_s1739874543393_e5000_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543327 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543327_f3_s1739874543458_e50_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543327 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543327_f3_s1739874543460_e5000_d16_.jpg
|
||||
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543327 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543327_f3_s1739874543441_e50_d16_.jpg
|
||||
===================result key:6
|
||||
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543561 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543561_f10_s1739874543628_e5000_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543560 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543560_f10_s1739874543618_e50_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543560 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543560_f11_s1739874543687_e5000_d16_.jpg
|
||||
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543561 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543561_f10_s1739874543648_e50_d16_.jpg
|
||||
===================result key:7
|
||||
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543427 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543427_f6_s1739874543474_e5000_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543427 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543427_f6_s1739874543485_e50_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543427 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543427_f6_s1739874543684_e5000_d16_.jpg
|
||||
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543427 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543427_f6_s1739874543518_e50_d16_.jpg
|
||||
===================result key:8
|
||||
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543294 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543294_f2_s1739874543329_e5000_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543293 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543293_f2_s1739874543446_e50_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543294 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543294_f2_s1739874543319_e5000_d16_.jpg
|
||||
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543294 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543294_f2_s1739874543359_e50_d16_.jpg
|
||||
===================result key:9
|
||||
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543394 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543394_f5_s1739874543473_e5000_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543393 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543393_f5_s1739874543470_e50_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543394 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543394_f5_s1739874543661_e5000_d16_.jpg
|
||||
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543394 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543394_f5_s1739874543506_e50_d16_.jpg
|
||||
===================result key:10
|
||||
=====================result value : deviceId:0 sensorType:ir_left syncTimeStamp:1739874543227 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/ir#left_SNCP1E5420006D_Index0_g1739874543227_f0_s1739874543250_e5000_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:color syncTimeStamp:1739874543227 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/color_SNCP1L44P00051_Index1_g1739874543227_f0_s1739874543304_e50_d16_.jpg
|
||||
=====================result value : deviceId:1 sensorType:ir_left syncTimeStamp:1739874543227 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1L44P00051_Index1/ir#left_SNCP1L44P00051_Index1_g1739874543227_f0_s1739874543250_e5000_d16_.jpg
|
||||
=====================result value : deviceId:0 sensorType:color syncTimeStamp:1739874543227 picturePath:/home/jj/openSDK/opensdk_ros2/src/OrbbecSDK_ROS2/orbbec_camera/examples/multi_camera_synced_verification_tool/multicamera_sync/output/20250218102900/TotalModeFrames/SNCP1E5420006D_Index0/color_SNCP1E5420006D_Index0_g1739874543227_f0_s1739874543327_e50_d16_.jpg
|
||||
|
Before Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 145 KiB |