From 75b90ac2a397b30e7ac38dba784d0f250bb32459 Mon Sep 17 00:00:00 2001 From: Boitumelo Ruf Date: Tue, 12 Mar 2024 08:14:45 +0100 Subject: [PATCH] Feature: Export GenICam XML (#32) * Added node export_genicam_xml * Updated Readme --- CMakeLists.txt | 8 +- README.md | 26 +++++- src/export_genicam_xml_node.cpp | 142 ++++++++++++++++++++++++++++++++ 3 files changed, 173 insertions(+), 3 deletions(-) create mode 100644 src/export_genicam_xml_node.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 540940e..698d872 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,12 @@ add_executable(cam_aravis target_link_libraries(cam_aravis ${PROJECT_NAME} ${LIBRARIES}) add_dependencies(cam_aravis ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) +add_executable(export_genicam_xml + src/export_genicam_xml_node.cpp +) +target_link_libraries(export_genicam_xml ${PROJECT_NAME} ${LIBRARIES}) +add_dependencies(export_genicam_xml ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS}) + install(DIRECTORY include/${PROJECT_NAME}/ DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} FILES_MATCHING PATTERN "*.h" @@ -109,7 +115,7 @@ install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION} ) -install(TARGETS cam_aravis +install(TARGETS cam_aravis export_genicam_xml RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} ) diff --git a/README.md b/README.md index 333b321..83d588b 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,7 @@ set initial values for the camera by setting ROS parameters in the camera's name In addition to the above features, this driver now supports (almost) every feature of every camera, you just have to know how the feature is specified; each GenICam-based camera contains an XML file onboard, and by viewing this file you can determine which ROS parameters to set -for camera_aravis to write to the camera. You can use arv-tool-0.2 to see the feature list -and the XML file (e.g. "arv-tool-0.2 --name=Basler-21285878 features") +for camera_aravis to write to the camera. Details on how to export the camera-specific XML can be found here: [Extracting Camera-Specific GenICam XML](#extracting-camera-specific-genicam-xml). Note that for this special feature access, the ROS parameter type must match the feature type. For example, a Basler ac640 has a boolean feature called "GammaEnable", an integer feature @@ -240,6 +239,29 @@ time in order for the camera to be properly disconnected. The shutdown delay time in secondes can by configured by the parameter ```shutdown_delay_s```, default: 5 seconds. +## Extracting Camera-Specific GenICam XML + +Each camera model has a specific XML data stored inside the device memory which describes the GenICam interface of the camera. +In this XML data the supported features of the camera are documented and can help to configure the camera. +To extract this XML data and write it into a file, camera_aravis provides the node `export_genicam_xml` which can be invoked as shown below: + +```bash +rosrun camera_aravis export_genicam_xml _guid:= _xml_file:= +``` + +If `_guid` is omitted, the XML data will be read from any of the cameras which are connected and found by camera_aravis. +As a `_xml_file`, either a relative or absolute path can be provided in which the XML data is to be saved. +If omitted, the data is saved into a file in the current working directory with the 'guid' as filename. + + +Alternatively, you can use `aravis-tools` to see the feature list and the XML file: + +``` +sudo apt install aravis-tools +arv-tool-0.6 --name= features +arv-tool-0.6 --name= genicam +``` + ## Known Issues ### Slow read of white balance and black level values diff --git a/src/export_genicam_xml_node.cpp b/src/export_genicam_xml_node.cpp new file mode 100644 index 0000000..43117e5 --- /dev/null +++ b/src/export_genicam_xml_node.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** + * + * camera_aravis + * + * Copyright © 2024 Fraunhofer IOSB and contributors + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + ****************************************************************************/ + +extern "C" { +#include +} + +#include + +#include +#include + +#include + +int main(int argc, char** argv) +{ + ros::init(argc, argv, "export_genicam_xml"); + + /// private node handle + ros::NodeHandle pnh("~"); + + /// GUID of the camera for which the xml is to be exported. + std::string guid = pnh.param("guid", ""); + + /// Path string of output xml file. + std::string xmlFileStr = pnh.param("xml_file", ""); + + //--- Print out some useful info. + ROS_INFO("Attached cameras:"); + arv_update_device_list(); + uint n_interfaces = arv_get_n_interfaces(); + ROS_INFO("# Interfaces: %d", n_interfaces); + + uint n_devices = arv_get_n_devices(); + ROS_INFO("# Devices: %d", n_devices); + for (uint i = 0; i < n_devices; i++) + ROS_INFO("Device%d: %s", i, arv_get_device_id(i)); + + if (n_devices == 0) + { + ROS_ERROR("No cameras detected."); + return -1; + } + + //--- Open the camera. + ArvCamera *p_camera = nullptr; + while (!p_camera) + { + if (guid.empty()) + { + ROS_INFO("Opening: (any)"); + p_camera = arv_camera_new(NULL); + } + else + { + ROS_INFO("Opening: %s", guid.c_str()); + p_camera = arv_camera_new(guid.c_str()); + } + ros::Duration(1.0).sleep(); + } + + //--- get device + ArvDevice *p_device = arv_camera_get_device(p_camera); + const char* vendor_name = arv_camera_get_vendor_name(p_camera); + const char* model_name = arv_camera_get_model_name(p_camera); + const char* device_id = arv_camera_get_device_id(p_camera); + const char* device_sn = arv_device_get_string_feature_value(p_device, "DeviceSerialNumber"); + ROS_INFO("Successfully Opened: %s-%s-%s", vendor_name, model_name, + (device_sn) ? device_sn : device_id); + + //--- if xmlFileStr is empty construct relative path from guid + + /// Path of output xml file. + boost::filesystem::path xmlFilePath; + if(xmlFileStr.empty()) + { + std::string tmpFileName = ""; + if(guid.empty()) + { + tmpFileName += std::string(vendor_name); + tmpFileName += "-" + std::string(model_name); + tmpFileName += "-" + std::string((device_sn) ? device_sn : device_id); + } + else + { + tmpFileName = guid; + } + + xmlFilePath = boost::filesystem::path(tmpFileName + ".xml"); + } + else + { + xmlFilePath = boost::filesystem::path(xmlFileStr); + } + + //--- make path absolute + xmlFilePath = boost::filesystem::absolute(xmlFilePath); + + //--- print warning if file already exists + if(boost::filesystem::exists(xmlFilePath)) + ROS_WARN("Output file already exists and will be overwritten. Path: %s", + boost::filesystem::canonical(xmlFilePath).c_str()); + + //--- make parent directory if not existing + if(!xmlFilePath.parent_path().empty()) + boost::filesystem::create_directories(xmlFilePath.parent_path()); + + //--- extract and save XML + size_t xmlSize = 0; + const char* p_xmldata = arv_device_get_genicam_xml(p_device, &xmlSize); + std::ofstream fout; + fout.open(xmlFilePath.c_str(), std::ios::binary | std::ios::out); + fout.write(p_xmldata, xmlSize); + fout.close(); + + ROS_INFO("Written GenICam XML to file: %s", boost::filesystem::canonical(xmlFilePath).c_str()); + + //--- release camera + g_object_unref(p_camera); + + return 0; +} \ No newline at end of file