Skip to content

Commit

Permalink
Feature: Export GenICam XML (#32)
Browse files Browse the repository at this point in the history
* Added node export_genicam_xml

* Updated Readme
  • Loading branch information
boitumeloruf authored Mar 12, 2024
1 parent 2cabebe commit 75b90ac
Show file tree
Hide file tree
Showing 3 changed files with 173 additions and 3 deletions.
8 changes: 7 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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}
)

Expand Down
26 changes: 24 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:=<camera_guid> _xml_file:=<output_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=<camera_guid> features
arv-tool-0.6 --name=<camera_guid> genicam
```

## Known Issues

### Slow read of white balance and black level values
Expand Down
142 changes: 142 additions & 0 deletions src/export_genicam_xml_node.cpp
Original file line number Diff line number Diff line change
@@ -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 <arv.h>
}

#include <fstream>

#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/path.hpp>

#include <ros/ros.h>

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<std::string>("guid", "");

/// Path string of output xml file.
std::string xmlFileStr = pnh.param<std::string>("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;
}

0 comments on commit 75b90ac

Please sign in to comment.