From bc2f29dc14a110b749cffe6a1799fe6b41f9ec2e Mon Sep 17 00:00:00 2001 From: Ethan Gao Date: Fri, 13 Apr 2018 15:16:31 +0000 Subject: [PATCH] Add initial porting of ros2 cv_bridge It mainly contains: - C++ interfaces and lib - python interfaces and corresponding C++ backend - all tests - remove boost dependence with C++11 or above instead Signed-off-by: Ethan Gao --- cv_bridge/CMakeLists.txt | 54 ++++++++---- cv_bridge/include/cv_bridge/cv_bridge.h | 105 ++++++++++++------------ cv_bridge/package.xml | 27 +++--- cv_bridge/python/CMakeLists.txt | 10 ++- cv_bridge/python/cv_bridge/core.py | 2 +- cv_bridge/setup.cfg | 4 + cv_bridge/setup.py | 45 ++++++++-- cv_bridge/src/CMakeLists.txt | 29 +++---- cv_bridge/src/cv_bridge.cpp | 55 ++++++------- cv_bridge/src/module.cpp | 4 +- cv_bridge/test/CMakeLists.txt | 30 +++++-- cv_bridge/test/conversions.py | 10 ++- cv_bridge/test/enumerants.py | 19 +++-- cv_bridge/test/python_bindings.py | 10 ++- cv_bridge/test/test_compression.cpp | 6 +- cv_bridge/test/test_endian.cpp | 8 +- cv_bridge/test/utest.cpp | 12 +-- cv_bridge/test/utest2.cpp | 10 +-- 18 files changed, 257 insertions(+), 183 deletions(-) create mode 100644 cv_bridge/setup.cfg diff --git a/cv_bridge/CMakeLists.txt b/cv_bridge/CMakeLists.txt index 997bef3e1..538210775 100644 --- a/cv_bridge/CMakeLists.txt +++ b/cv_bridge/CMakeLists.txt @@ -1,7 +1,16 @@ -cmake_minimum_required(VERSION 2.8) +cmake_minimum_required(VERSION 3.5) project(cv_bridge) -find_package(catkin REQUIRED COMPONENTS rosconsole sensor_msgs) +find_package(ament_cmake_ros REQUIRED) + +# Default to C++14 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 14) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra) +endif() if(NOT ANDROID) find_package(PythonLibs) @@ -11,9 +20,11 @@ if(NOT ANDROID) find_package(Boost REQUIRED python3) endif() else() -find_package(Boost REQUIRED) + find_package(Boost REQUIRED ) endif() -find_package(OpenCV 3 REQUIRED + +find_package(sensor_msgs REQUIRED) +find_package(OpenCV REQUIRED COMPONENTS opencv_core opencv_imgproc @@ -21,28 +32,35 @@ find_package(OpenCV 3 REQUIRED CONFIG ) -catkin_package( - INCLUDE_DIRS include - LIBRARIES ${PROJECT_NAME} - CATKIN_DEPENDS rosconsole sensor_msgs - DEPENDS OpenCV - CFG_EXTRAS cv_bridge-extras.cmake -) - -catkin_python_setup() - -include_directories(include ${Boost_INCLUDE_DIRS} ${OpenCV_INCLUDE_DIRS} ${catkin_INCLUDE_DIRS}) +include_directories(include) if(NOT ANDROID) -add_subdirectory(python) + add_subdirectory(python) endif() + add_subdirectory(src) -if(CATKIN_ENABLE_TESTING) + +# cv_bridge_lib_dir is passed as APPEND_LIBRARY_DIRS for each ament_add_gtest call so +# the project library that they link against is on the library path. +# This is especially important on Windows. +# This is overwritten each loop, but which one it points to doesn't really matter. +set(cv_bridge_lib_dir "$") + +if(BUILD_TESTING) add_subdirectory(test) endif() +ament_export_dependencies(OpenCV) + +ament_export_include_directories(include) +ament_export_libraries(${PROJECT_NAME}) + # install the include folder install( DIRECTORY include/${PROJECT_NAME}/ - DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION} + DESTINATION include/${PROJECT_NAME} +) + +ament_package( + CONFIG_EXTRAS "cmake/cv_bridge-extras.cmake.in" ) diff --git a/cv_bridge/include/cv_bridge/cv_bridge.h b/cv_bridge/include/cv_bridge/cv_bridge.h index 3e7b9aaab..018250f62 100644 --- a/cv_bridge/include/cv_bridge/cv_bridge.h +++ b/cv_bridge/include/cv_bridge/cv_bridge.h @@ -36,15 +36,15 @@ #ifndef CV_BRIDGE_CV_BRIDGE_H #define CV_BRIDGE_CV_BRIDGE_H -#include -#include -#include -#include +#include +#include +#include #include #include #include #include + namespace cv_bridge { class Exception : public std::runtime_error @@ -55,8 +55,8 @@ class Exception : public std::runtime_error class CvImage; -typedef boost::shared_ptr CvImagePtr; -typedef boost::shared_ptr CvImageConstPtr; +typedef std::shared_ptr CvImagePtr; +typedef std::shared_ptr CvImageConstPtr; //from: http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html#Mat imread(const string& filename, int flags) typedef enum { @@ -76,7 +76,7 @@ typedef enum { class CvImage { public: - std_msgs::Header header; //!< ROS header + std_msgs::msg::Header header; //!< ROS header std::string encoding; //!< Image encoding ("mono8", "bgr8", etc.) cv::Mat image; //!< Image data for use with OpenCV @@ -88,18 +88,18 @@ class CvImage /** * \brief Constructor. */ - CvImage(const std_msgs::Header& header, const std::string& encoding, + CvImage(const std_msgs::msg::Header& header, const std::string& encoding, const cv::Mat& image = cv::Mat()) : header(header), encoding(encoding), image(image) { } /** - * \brief Convert this message to a ROS sensor_msgs::Image message. + * \brief Convert this message to a ROS sensor_msgs::msg::Image message. * - * The returned sensor_msgs::Image message contains a copy of the image data. + * The returned sensor_msgs::msg::Image message contains a copy of the image data. */ - sensor_msgs::ImagePtr toImageMsg() const; + sensor_msgs::msg::Image::SharedPtr toImageMsg() const; /** * dst_format is compress the image to desire format. @@ -108,15 +108,15 @@ class CvImage * support this format from opencv: * http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html#Mat imread(const string& filename, int flags) */ - sensor_msgs::CompressedImagePtr toCompressedImageMsg(const Format dst_format = JPG) const; + sensor_msgs::msg::CompressedImage::SharedPtr toCompressedImageMsg(const Format dst_format = JPG) const; /** - * \brief Copy the message data to a ROS sensor_msgs::Image message. + * \brief Copy the message data to a ROS sensor_msgs::msg::Image message. * * This overload is intended mainly for aggregate messages such as stereo_msgs::DisparityImage, - * which contains a sensor_msgs::Image as a data member. + * which contains a sensor_msgs::msg::Image as a data member. */ - void toImageMsg(sensor_msgs::Image& ros_image) const; + void toImageMsg(sensor_msgs::msg::Image& ros_image) const; /** * dst_format is compress the image to desire format. @@ -125,29 +125,29 @@ class CvImage * support this format from opencv: * http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html#Mat imread(const string& filename, int flags) */ - void toCompressedImageMsg(sensor_msgs::CompressedImage& ros_image, const Format dst_format = JPG) const; + void toCompressedImageMsg(sensor_msgs::msg::CompressedImage& ros_image, const Format dst_format = JPG) const; - typedef boost::shared_ptr Ptr; - typedef boost::shared_ptr ConstPtr; + typedef std::shared_ptr Ptr; + typedef std::shared_ptr ConstPtr; protected: - boost::shared_ptr tracked_object_; // for sharing ownership + std::shared_ptr tracked_object_; // for sharing ownership /// @cond DOXYGEN_IGNORE friend - CvImageConstPtr toCvShare(const sensor_msgs::Image& source, - const boost::shared_ptr& tracked_object, + CvImageConstPtr toCvShare(const sensor_msgs::msg::Image& source, + const std::shared_ptr& tracked_object, const std::string& encoding); /// @endcond }; /** - * \brief Convert a sensor_msgs::Image message to an OpenCV-compatible CvImage, copying the + * \brief Convert a sensor_msgs::msg::Image message to an OpenCV-compatible CvImage, copying the * image data. * - * \param source A shared_ptr to a sensor_msgs::Image message + * \param source A shared_ptr to a sensor_msgs::msg::Image message * \param encoding The desired encoding of the image data, one of the following strings: * - \c "mono8" * - \c "bgr8" @@ -159,17 +159,17 @@ class CvImage * If \a encoding is the empty string (the default), the returned CvImage has the same encoding * as \a source. */ -CvImagePtr toCvCopy(const sensor_msgs::ImageConstPtr& source, +CvImagePtr toCvCopy(const sensor_msgs::msg::Image::ConstSharedPtr& source, const std::string& encoding = std::string()); -CvImagePtr toCvCopy(const sensor_msgs::CompressedImageConstPtr& source, +CvImagePtr toCvCopy(const sensor_msgs::msg::CompressedImage::ConstSharedPtr& source, const std::string& encoding = std::string()); /** - * \brief Convert a sensor_msgs::Image message to an OpenCV-compatible CvImage, copying the + * \brief Convert a sensor_msgs::msg::Image message to an OpenCV-compatible CvImage, copying the * image data. * - * \param source A sensor_msgs::Image message + * \param source A sensor_msgs::msg::Image message * \param encoding The desired encoding of the image data, one of the following strings: * - \c "mono8" * - \c "bgr8" @@ -184,21 +184,21 @@ CvImagePtr toCvCopy(const sensor_msgs::CompressedImageConstPtr& source, * 255/65535 respectively). Otherwise, no scaling is applied and the rules from the convertTo OpenCV * function are applied (capping): http://docs.opencv.org/modules/core/doc/basic_structures.html#mat-convertto */ -CvImagePtr toCvCopy(const sensor_msgs::Image& source, +CvImagePtr toCvCopy(const sensor_msgs::msg::Image& source, const std::string& encoding = std::string()); -CvImagePtr toCvCopy(const sensor_msgs::CompressedImage& source, +CvImagePtr toCvCopy(const sensor_msgs::msg::CompressedImage& source, const std::string& encoding = std::string()); /** - * \brief Convert an immutable sensor_msgs::Image message to an OpenCV-compatible CvImage, sharing + * \brief Convert an immutable sensor_msgs::msg::Image message to an OpenCV-compatible CvImage, sharing * the image data if possible. * * If the source encoding and desired encoding are the same, the returned CvImage will share * the image data with \a source without copying it. The returned CvImage cannot be modified, as that * could modify the \a source data. * - * \param source A shared_ptr to a sensor_msgs::Image message + * \param source A shared_ptr to a sensor_msgs::msg::Image message * \param encoding The desired encoding of the image data, one of the following strings: * - \c "mono8" * - \c "bgr8" @@ -210,11 +210,11 @@ CvImagePtr toCvCopy(const sensor_msgs::CompressedImage& source, * If \a encoding is the empty string (the default), the returned CvImage has the same encoding * as \a source. */ -CvImageConstPtr toCvShare(const sensor_msgs::ImageConstPtr& source, +CvImageConstPtr toCvShare(const sensor_msgs::msg::Image::ConstSharedPtr& source, const std::string& encoding = std::string()); /** - * \brief Convert an immutable sensor_msgs::Image message to an OpenCV-compatible CvImage, sharing + * \brief Convert an immutable sensor_msgs::msg::Image message to an OpenCV-compatible CvImage, sharing * the image data if possible. * * If the source encoding and desired encoding are the same, the returned CvImage will share @@ -222,10 +222,10 @@ CvImageConstPtr toCvShare(const sensor_msgs::ImageConstPtr& source, * could modify the \a source data. * * This overload is useful when you have a shared_ptr to a message that contains a - * sensor_msgs::Image, and wish to share ownership with the containing message. + * sensor_msgs::msg::Image, and wish to share ownership with the containing message. * - * \param source The sensor_msgs::Image message - * \param tracked_object A shared_ptr to an object owning the sensor_msgs::Image + * \param source The sensor_msgs::msg::Image message + * \param tracked_object A shared_ptr to an object owning the sensor_msgs::msg::Image * \param encoding The desired encoding of the image data, one of the following strings: * - \c "mono8" * - \c "bgr8" @@ -237,8 +237,8 @@ CvImageConstPtr toCvShare(const sensor_msgs::ImageConstPtr& source, * If \a encoding is the empty string (the default), the returned CvImage has the same encoding * as \a source. */ -CvImageConstPtr toCvShare(const sensor_msgs::Image& source, - const boost::shared_ptr& tracked_object, +CvImageConstPtr toCvShare(const sensor_msgs::msg::Image& source, + const std::shared_ptr& tracked_object, const std::string& encoding = std::string()); /** @@ -263,27 +263,27 @@ struct CvtColorForDisplayOptions { /** - * \brief Converts an immutable sensor_msgs::Image message to another CvImage for display purposes, + * \brief Converts an immutable sensor_msgs::msg::Image message to another CvImage for display purposes, * using practical conversion rules if needed. * * Data will be shared between input and output if possible. * - * Recall: sensor_msgs::image_encodings::isColor and isMono tell whether an image contains R,G,B,A, mono + * Recall: sensor_msgs::msg::image_encodings::isColor and isMono tell whether an image contains R,G,B,A, mono * (or any combination/subset) with 8 or 16 bit depth. * * The following rules apply: * - if the output encoding is empty, the fact that the input image is mono or multiple-channel is * preserved in the ouput image. The bit depth will be 8. it tries to convert to BGR no matter what * encoding image is passed. - * - if the output encoding is not empty, it must have sensor_msgs::image_encodings::isColor and + * - if the output encoding is not empty, it must have sensor_msgs::msg::image_encodings::isColor and * isMono return true. It must also be 8 bit in depth * - if the input encoding is an OpenCV format (e.g. 8UC1), and if we have 1,3 or 4 channels, it is * respectively converted to mono, BGR or BGRA. * - if the input encoding is 32SC1, this estimate that image as label image and will convert it as * bgr image with different colors for each label. * - * \param source A shared_ptr to a sensor_msgs::Image message - * \param encoding Either an encoding string that returns true in sensor_msgs::image_encodings::isColor + * \param source A shared_ptr to a sensor_msgs::msg::Image message + * \param encoding Either an encoding string that returns true in sensor_msgs::msg::image_encodings::isColor * isMono or the empty string as explained above. * \param options (cv_bridge::CvtColorForDisplayOptions) Options to convert the source image with. * - do_dynamic_scaling If true, the image is dynamically scaled between its minimum and maximum value @@ -306,7 +306,7 @@ int getCvType(const std::string& encoding); } // namespace cv_bridge - +#if 0 // CvImage as a first class message type // The rest of this file hooks into the roscpp serialization API to make CvImage @@ -322,26 +322,26 @@ namespace message_traits { template<> struct MD5Sum { - static const char* value() { return MD5Sum::value(); } + static const char* value() { return MD5Sum::value(); } static const char* value(const cv_bridge::CvImage&) { return value(); } - static const uint64_t static_value1 = MD5Sum::static_value1; - static const uint64_t static_value2 = MD5Sum::static_value2; + static const uint64_t static_value1 = MD5Sum::static_value1; + static const uint64_t static_value2 = MD5Sum::static_value2; // If the definition of sensor_msgs/Image changes, we'll get a compile error here. - ROS_STATIC_ASSERT(MD5Sum::static_value1 == 0x060021388200f6f0ULL); - ROS_STATIC_ASSERT(MD5Sum::static_value2 == 0xf447d0fcd9c64743ULL); + ROS_STATIC_ASSERT(MD5Sum::static_value1 == 0x060021388200f6f0ULL); + ROS_STATIC_ASSERT(MD5Sum::static_value2 == 0xf447d0fcd9c64743ULL); }; template<> struct DataType { - static const char* value() { return DataType::value(); } + static const char* value() { return DataType::value(); } static const char* value(const cv_bridge::CvImage&) { return value(); } }; template<> struct Definition { - static const char* value() { return Definition::value(); } + static const char* value() { return Definition::value(); } static const char* value(const cv_bridge::CvImage&) { return value(); } }; @@ -406,7 +406,7 @@ template<> struct Printer template static void stream(Stream&, const std::string&, const cv_bridge::CvImage&) { - /// @todo Replicate printing for sensor_msgs::Image + /// @todo Replicate printing for sensor_msgs::msg::Image } }; @@ -423,6 +423,7 @@ inline std::ostream& operator<<(std::ostream& s, const CvImage& m) } } // namespace cv_bridge +#endif /// @endcond diff --git a/cv_bridge/package.xml b/cv_bridge/package.xml index a93af930c..d319162fb 100644 --- a/cv_bridge/package.xml +++ b/cv_bridge/package.xml @@ -2,36 +2,33 @@ cv_bridge 1.12.7 - This contains CvBridge, which converts between ROS + This contains CvBridge, which converts between ROS2 Image messages and OpenCV images. Patrick Mihelich James Bowman - Vincent Rabaud + Ethan Gao BSD http://www.ros.org/wiki/cv_bridge - https://github.com/ros-perception/vision_opencv + https://github.com/ros-perception/vision_opencv/tree/ros2 https://github.com/ros-perception/vision_opencv/issues + ament_cmake - catkin + ament_cmake_ros + python_cmake_module - boost - opencv3 - python - rosconsole - sensor_msgs + boost + opencv3 + sensor_msgs - boost - opencv3 - python - rosconsole - sensor_msgs + ament_index_python - rostest + ament_cmake_gtest + ament_cmake_pytest python-numpy dvipng diff --git a/cv_bridge/python/CMakeLists.txt b/cv_bridge/python/CMakeLists.txt index 1b677d30b..a1928ca01 100644 --- a/cv_bridge/python/CMakeLists.txt +++ b/cv_bridge/python/CMakeLists.txt @@ -1,8 +1,10 @@ configure_file(__init__.py.plain.in - ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION}/boost/__init__.py + ${CMAKE_CURRENT_BINARY_DIR}/boost/__init__.py @ONLY ) -install(FILES ${CATKIN_DEVEL_PREFIX}/${CATKIN_PACKAGE_PYTHON_DESTINATION}/boost/__init__.py - DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION}/boost/ -) +ament_python_install_package(${PROJECT_NAME}) + +# install(FILES ${CMAKE_CURRENT_BINARY_DIR}/boost/__init__.py +# DESTINATION ${PYTHON_INSTALL_DIR}/${PROJECT_NAME}/boost/ +# ) diff --git a/cv_bridge/python/cv_bridge/core.py b/cv_bridge/python/cv_bridge/core.py index b39298953..a47bbd764 100644 --- a/cv_bridge/python/cv_bridge/core.py +++ b/cv_bridge/python/cv_bridge/core.py @@ -261,6 +261,6 @@ def cv2_to_imgmsg(self, cvim, encoding = "passthrough"): if cvim.dtype.byteorder == '>': img_msg.is_bigendian = True img_msg.data = cvim.tostring() - img_msg.step = len(img_msg.data) / img_msg.height + img_msg.step = len(img_msg.data) // img_msg.height return img_msg diff --git a/cv_bridge/setup.cfg b/cv_bridge/setup.cfg new file mode 100644 index 000000000..5d1a9f923 --- /dev/null +++ b/cv_bridge/setup.cfg @@ -0,0 +1,4 @@ +[develop] +script-dir=$base/lib/cv_bridge +[install] +install-scripts=$base/lib/cv_bridge diff --git a/cv_bridge/setup.py b/cv_bridge/setup.py index e922dfe9c..fbd393c96 100644 --- a/cv_bridge/setup.py +++ b/cv_bridge/setup.py @@ -1,10 +1,39 @@ -#!/usr/bin/env python -from distutils.core import setup -from catkin_pkg.python_setup import generate_distutils_setup +from setuptools import find_packages +from setuptools import setup -d = generate_distutils_setup() +package_name = 'cv_bridge' -d['packages'] = ['cv_bridge'] -d['package_dir'] = {'' : 'python/'} - -setup(**d) +setup( + name=package_name, + version='0.4.0', + packages=find_packages(), + data_files=[ + ('share/ament_index/resource_index/packages', + ['resource/' + package_name]), + ('share/' + package_name, ['package.xml']), + ], + install_requires=['setuptools'], + author='Ethan Gao', + author_email='ethan.gao@linux.intel.com', + maintainer='Ethan Gao', + maintainer_email='ethan.gao@linux.intel.com', + keywords=['ROS'], + classifiers=[ + 'Intended Audience :: Developers', + 'License :: OSI Approved :: Apache Software License', + 'Programming Language :: Python', + 'Topic :: Software Development', + ], + description=( + 'opencv bridge python implementation ' + 'but are now just used for demo purposes.' + ), + license='Apache License, Version 2.0', + tests_require=['pytest'], + entry_points={ + 'console_scripts': [ + 'conversions = test.conversions:main', + 'enumerants = test.enumerants:main', + ], + }, +) diff --git a/cv_bridge/src/CMakeLists.txt b/cv_bridge/src/CMakeLists.txt index 37ba30eea..15428b337 100644 --- a/cv_bridge/src/CMakeLists.txt +++ b/cv_bridge/src/CMakeLists.txt @@ -1,10 +1,12 @@ # add library -include_directories(./) add_library(${PROJECT_NAME} cv_bridge.cpp rgb_colors.cpp) -add_dependencies(${PROJECT_NAME} ${catkin_EXPORTED_TARGETS}) -target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBRARIES} ${catkin_LIBRARIES}) +ament_target_dependencies(${PROJECT_NAME} + "OpenCV" + "sensor_msgs" +) +target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES} ${OpenCV_LIBRARIES}) -install(TARGETS ${PROJECT_NAME} DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}) +install(TARGETS ${PROJECT_NAME} DESTINATION lib) if(NOT ANDROID) # add a Boost Python library @@ -24,27 +26,26 @@ if(NOT PYTHON_NUMPY_INCLUDE_DIR) else(PYTHON_NUMPY_PROCESS EQUAL 0) message(SEND_ERROR "Could not determine the NumPy include directory, verify that NumPy was installed correctly.") endif(PYTHON_NUMPY_PROCESS EQUAL 0) - endif(NOT PYTHON_NUMPY_INCLUDE_DIR) +endif(NOT PYTHON_NUMPY_INCLUDE_DIR) -include_directories(${PYTHON_INCLUDE_PATH} ${Boost_INCLUDE_DIRS} ${PYTHON_NUMPY_INCLUDE_DIR}) +include_directories(${PYTHON_INCLUDE_PATH} ${PYTHON_NUMPY_INCLUDE_DIR}) if (PYTHON_VERSION_MAJOR VERSION_EQUAL 3) add_definitions(-DPYTHON3) endif() if (OpenCV_VERSION_MAJOR VERSION_EQUAL 3) -add_library(${PROJECT_NAME}_boost module.cpp module_opencv3.cpp) + add_library(${PROJECT_NAME}_boost module.cpp module_opencv3.cpp) else() -add_library(${PROJECT_NAME}_boost module.cpp module_opencv2.cpp) + add_library(${PROJECT_NAME}_boost module.cpp module_opencv2.cpp) endif() -target_link_libraries(${PROJECT_NAME}_boost ${Boost_LIBRARIES} - ${catkin_LIBRARIES} - ${PYTHON_LIBRARIES} - ${PROJECT_NAME} +target_link_libraries(${PROJECT_NAME}_boost + ${PYTHON_LIBRARIES} + ${PROJECT_NAME} ) set_target_properties(${PROJECT_NAME}_boost PROPERTIES - LIBRARY_OUTPUT_DIRECTORY ${CATKIN_DEVEL_PREFIX}/${CATKIN_GLOBAL_PYTHON_DESTINATION}/${PROJECT_NAME}/boost/ + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/boost/ PREFIX "" ) if(APPLE) @@ -52,5 +53,5 @@ if(APPLE) SUFFIX ".so") endif() -install(TARGETS ${PROJECT_NAME}_boost DESTINATION ${CATKIN_PACKAGE_PYTHON_DESTINATION}/boost/) +install(TARGETS ${PROJECT_NAME}_boost DESTINATION ${PYTHON_INSTALL_DIR}/${PROJECT_NAME}/boost/) endif() diff --git a/cv_bridge/src/cv_bridge.cpp b/cv_bridge/src/cv_bridge.cpp index 109440e37..b9c5829a8 100644 --- a/cv_bridge/src/cv_bridge.cpp +++ b/cv_bridge/src/cv_bridge.cpp @@ -33,17 +33,16 @@ * POSSIBILITY OF SUCH DAMAGE. *********************************************************************/ -#include "boost/endian/conversion.hpp" +#include #include - -#include -#include +#include +#include #include #include -#include +#include #include #include @@ -97,15 +96,15 @@ int getCvType(const std::string& encoding) if (encoding == enc::YUV422) return CV_8UC2; // Check all the generic content encodings - boost::cmatch m; + std::cmatch m; - if (boost::regex_match(encoding.c_str(), m, - boost::regex("(8U|8S|16U|16S|32S|32F|64F)C([0-9]+)"))) { + if (std::regex_match(encoding.c_str(), m, + std::regex("(8U|8S|16U|16S|32S|32F|64F)C([0-9]+)"))) { return CV_MAKETYPE(depthStrToInt(m[1].str()), atoi(m[2].str().c_str())); } - if (boost::regex_match(encoding.c_str(), m, - boost::regex("(8U|8S|16U|16S|32S|32F|64F)"))) { + if (std::regex_match(encoding.c_str(), m, + std::regex("(8U|8S|16U|16S|32S|32F|64F)"))) { return CV_MAKETYPE(depthStrToInt(m[1].str()), 1); } @@ -246,7 +245,7 @@ const std::vector getConversionCode(std::string src_encoding, std::string d /////////////////////////////////////// Image /////////////////////////////////////////// // Converts a ROS Image to a cv::Mat by sharing the data or changing its endianness if needed -cv::Mat matFromImage(const sensor_msgs::Image& source) +cv::Mat matFromImage(const sensor_msgs::msg::Image& source) { int source_type = getCvType(source.encoding); int byte_depth = enc::bitDepth(source.encoding) / 8; @@ -298,12 +297,12 @@ cv::Mat matFromImage(const sensor_msgs::Image& source) // Internal, used by toCvCopy and cvtColor CvImagePtr toCvCopyImpl(const cv::Mat& source, - const std_msgs::Header& src_header, + const std_msgs::msg::Header& src_header, const std::string& src_encoding, const std::string& dst_encoding) { // Copy metadata - CvImagePtr ptr = boost::make_shared(); + CvImagePtr ptr = std::make_shared(); ptr->header = src_header; // Copy to new buffer if same encoding requested @@ -352,14 +351,14 @@ CvImagePtr toCvCopyImpl(const cv::Mat& source, /// @endcond -sensor_msgs::ImagePtr CvImage::toImageMsg() const +sensor_msgs::msg::Image::SharedPtr CvImage::toImageMsg() const { - sensor_msgs::ImagePtr ptr = boost::make_shared(); + sensor_msgs::msg::Image::SharedPtr ptr = std::make_shared(); toImageMsg(*ptr); return ptr; } -void CvImage::toImageMsg(sensor_msgs::Image& ros_image) const +void CvImage::toImageMsg(sensor_msgs::msg::Image& ros_image) const { ros_image.header = header; ros_image.height = image.rows; @@ -389,13 +388,13 @@ void CvImage::toImageMsg(sensor_msgs::Image& ros_image) const } // Deep copy data, returnee is mutable -CvImagePtr toCvCopy(const sensor_msgs::ImageConstPtr& source, +CvImagePtr toCvCopy(const sensor_msgs::msg::Image::ConstSharedPtr& source, const std::string& encoding) { return toCvCopy(*source, encoding); } -CvImagePtr toCvCopy(const sensor_msgs::Image& source, +CvImagePtr toCvCopy(const sensor_msgs::msg::Image& source, const std::string& encoding) { // Construct matrix pointing to source data @@ -403,14 +402,14 @@ CvImagePtr toCvCopy(const sensor_msgs::Image& source, } // Share const data, returnee is immutable -CvImageConstPtr toCvShare(const sensor_msgs::ImageConstPtr& source, +CvImageConstPtr toCvShare(const sensor_msgs::msg::Image::ConstSharedPtr& source, const std::string& encoding) { return toCvShare(*source, source, encoding); } -CvImageConstPtr toCvShare(const sensor_msgs::Image& source, - const boost::shared_ptr& tracked_object, +CvImageConstPtr toCvShare(const sensor_msgs::msg::Image& source, + const std::shared_ptr& tracked_object, const std::string& encoding) { // If the encoding different or the endianness different, you have to copy @@ -418,7 +417,7 @@ CvImageConstPtr toCvShare(const sensor_msgs::Image& source, (boost::endian::order::native != boost::endian::order::big))) return toCvCopy(source, encoding); - CvImagePtr ptr = boost::make_shared(); + CvImagePtr ptr = std::make_shared(); ptr->header = source.header; ptr->encoding = source.encoding; ptr->tracked_object_ = tracked_object; @@ -434,9 +433,9 @@ CvImagePtr cvtColor(const CvImageConstPtr& source, /////////////////////////////////////// CompressedImage /////////////////////////////////////////// -sensor_msgs::CompressedImagePtr CvImage::toCompressedImageMsg(const Format dst_format) const +sensor_msgs::msg::CompressedImage::SharedPtr CvImage::toCompressedImageMsg(const Format dst_format) const { - sensor_msgs::CompressedImagePtr ptr = boost::make_shared(); + sensor_msgs::msg::CompressedImage::SharedPtr ptr = std::make_shared(); toCompressedImageMsg(*ptr,dst_format); return ptr; } @@ -477,7 +476,7 @@ std::string getFormat(Format format) { throw Exception("Unrecognized image format"); } -void CvImage::toCompressedImageMsg(sensor_msgs::CompressedImage& ros_image, const Format dst_format) const +void CvImage::toCompressedImageMsg(sensor_msgs::msg::CompressedImage& ros_image, const Format dst_format) const { ros_image.header = header; cv::Mat image; @@ -487,7 +486,7 @@ void CvImage::toCompressedImageMsg(sensor_msgs::CompressedImage& ros_image, cons } else { - CvImagePtr tempThis = boost::make_shared(*this); + CvImagePtr tempThis = std::make_shared(*this); CvImagePtr temp; if (enc::hasAlpha(encoding)) { @@ -506,13 +505,13 @@ void CvImage::toCompressedImageMsg(sensor_msgs::CompressedImage& ros_image, cons } // Deep copy data, returnee is mutable -CvImagePtr toCvCopy(const sensor_msgs::CompressedImageConstPtr& source, +CvImagePtr toCvCopy(const sensor_msgs::msg::CompressedImage::ConstSharedPtr& source, const std::string& encoding) { return toCvCopy(*source, encoding); } -CvImagePtr toCvCopy(const sensor_msgs::CompressedImage& source, const std::string& encoding) +CvImagePtr toCvCopy(const sensor_msgs::msg::CompressedImage& source, const std::string& encoding) { // Construct matrix pointing to source data const cv::Mat_ in(1, source.data.size(), const_cast(&source.data[0])); diff --git a/cv_bridge/src/module.cpp b/cv_bridge/src/module.cpp index c12319847..522827f8b 100644 --- a/cv_bridge/src/module.cpp +++ b/cv_bridge/src/module.cpp @@ -43,7 +43,7 @@ cvtColor2Wrap(bp::object obj_in, const std::string & encoding_in, const std::str convert_to_CvMat2(obj_in.ptr(), mat_in); // Call cv_bridge for color conversion - cv_bridge::CvImagePtr cv_image(new cv_bridge::CvImage(std_msgs::Header(), encoding_in, mat_in)); + cv_bridge::CvImagePtr cv_image(new cv_bridge::CvImage(std_msgs::msg::Header(), encoding_in, mat_in)); cv::Mat mat = cv_bridge::cvtColor(cv_image, encoding_out)->image; @@ -61,7 +61,7 @@ cvtColorForDisplayWrap(bp::object obj_in, cv::Mat mat_in; convert_to_CvMat2(obj_in.ptr(), mat_in); - cv_bridge::CvImagePtr cv_image(new cv_bridge::CvImage(std_msgs::Header(), encoding_in, mat_in)); + cv_bridge::CvImagePtr cv_image(new cv_bridge::CvImage(std_msgs::msg::Header(), encoding_in, mat_in)); cv_bridge::CvtColorForDisplayOptions options; options.do_dynamic_scaling = do_dynamic_scaling; diff --git a/cv_bridge/test/CMakeLists.txt b/cv_bridge/test/CMakeLists.txt index 7497d45d7..f7b04c024 100644 --- a/cv_bridge/test/CMakeLists.txt +++ b/cv_bridge/test/CMakeLists.txt @@ -1,15 +1,29 @@ -# add the tests +# Add all the unit tests for cv_bridge -# add boost directories for now -include_directories("../src") +# skip the conversions.py test when necessary, because its +# test_encode_decode_cv2_compressed and test_encode_decode_cv2 +# spends more than 5 minutes generally +if(${SKIP_PYCONVERSION_TEST}) + set(SKIP_TEST "SKIP_TEST") +else() + set(SKIP_TEST "") +endif() -catkin_add_gtest(${PROJECT_NAME}-utest test_endian.cpp test_compression.cpp utest.cpp utest2.cpp test_rgb_colors.cpp) +# enable cv_bridge C++ tests +find_package(ament_cmake_gtest REQUIRED) +ament_add_gtest(${PROJECT_NAME}-utest + test_endian.cpp + test_compression.cpp + utest.cpp utest2.cpp + test_rgb_colors.cpp + APPEND_LIBRARY_DIRS "${cv_bridge_lib_dir}") target_link_libraries(${PROJECT_NAME}-utest ${PROJECT_NAME} ${OpenCV_LIBRARIES} - ${catkin_LIBRARIES} ) -catkin_add_nosetests(enumerants.py) -catkin_add_nosetests(conversions.py) -catkin_add_nosetests(python_bindings.py) +# enable cv_bridge python tests +find_package(ament_cmake_pytest REQUIRED) +ament_add_pytest_test(enumerants.py "enumerants.py") +ament_add_pytest_test(conversions.py "conversions.py" TIMEOUT 600 ${SKIP_TEST}) +ament_add_pytest_test(python_bindings.py "python_bindings.py") diff --git a/cv_bridge/test/conversions.py b/cv_bridge/test/conversions.py index ab20cd7b7..e31127a51 100644 --- a/cv_bridge/test/conversions.py +++ b/cv_bridge/test/conversions.py @@ -1,5 +1,4 @@ -#!/usr/bin/env python -import rostest +# !/usr/bin/env python import unittest import numpy as np @@ -82,4 +81,9 @@ def test_endianness(self): self.assert_((br.imgmsg_to_cv2(msg) == img).all()) if __name__ == '__main__': - rosunit.unitrun('opencv_tests', 'conversions', TestConversions) + suite = unittest.TestSuite() + suite.addTest(TestConversions('test_mono16_cv2')) + suite.addTest(TestConversions('test_encode_decode_cv2')) + suite.addTest(TestConversions('test_encode_decode_cv2_compressed')) + suite.addTest(TestConversions('test_endianness')) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/cv_bridge/test/enumerants.py b/cv_bridge/test/enumerants.py index bdcc7a8ef..b160dbc78 100644 --- a/cv_bridge/test/enumerants.py +++ b/cv_bridge/test/enumerants.py @@ -1,5 +1,4 @@ -#!/usr/bin/env python -import rostest +# !/usr/bin/env python import unittest import numpy as np @@ -17,12 +16,13 @@ def test_enumerants_cv2(self): img_msg.height = 480 img_msg.encoding = "rgba8" img_msg.step = 640*4 - img_msg.data = (640 * 480) * "1234" + img_msg.data = ((640 * 480) * "1234").encode() bridge_ = CvBridge() cvim = bridge_.imgmsg_to_cv2(img_msg, "rgb8") + import sys - self.assertRaises(sys.getrefcount(cvim) == 2) + self.assertTrue(sys.getrefcount(cvim) == 2) # A 3 channel image cannot be sent as an rgba8 self.assertRaises(CvBridgeError, lambda: bridge_.cv2_to_imgmsg(cvim, "rgba8")) @@ -31,9 +31,9 @@ def test_enumerants_cv2(self): bridge_.cv2_to_imgmsg(cvim, "rgb8") bridge_.cv2_to_imgmsg(cvim, "bgr8") - self.assertRaises(getCvType("32FC4") == cv2.CV_8UC4) - self.assertRaises(getCvType("8UC1") == cv2.CV_8UC1) - self.assertRaises(getCvType("8U") == cv2.CV_8UC1) + self.assertFalse(getCvType("32FC4") == cv2.CV_8UC4) + self.assertTrue(getCvType("8UC1") == cv2.CV_8UC1) + self.assertTrue(getCvType("8U") == cv2.CV_8UC1) def test_numpy_types(self): import cv2 @@ -44,4 +44,7 @@ def test_numpy_types(self): self.assertRaises(TypeError, lambda: bridge_.cv2_to_imgmsg(cv2.cv(), "rgba8")) if __name__ == '__main__': - rosunit.unitrun('opencv_tests', 'enumerants', TestEnumerants) + suite = unittest.TestSuite() + suite.addTest(TestEnumerants('test_enumerants_cv2')) + suite.addTest(TestEnumerants('test_numpy_types')) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/cv_bridge/test/python_bindings.py b/cv_bridge/test/python_bindings.py index 6dd320905..8542aa48f 100644 --- a/cv_bridge/test/python_bindings.py +++ b/cv_bridge/test/python_bindings.py @@ -1,3 +1,5 @@ +# !/usr/bin/env python + from nose.tools import assert_equal import numpy as np @@ -10,12 +12,12 @@ def test_cvtColorForDisplay(): height, width = label.shape[:2] label_value = 0 grid_num_y, grid_num_x = 3, 4 - for grid_row in xrange(grid_num_y): - grid_size_y = height / grid_num_y + for grid_row in range(grid_num_y): + grid_size_y = height // grid_num_y min_y = grid_size_y * grid_row max_y = min_y + grid_size_y - for grid_col in xrange(grid_num_x): - grid_size_x = width / grid_num_x + for grid_col in range(grid_num_x): + grid_size_x = width // grid_num_x min_x = grid_size_x * grid_col max_x = min_x + grid_size_x label[min_y:max_y, min_x:max_x] = label_value diff --git a/cv_bridge/test/test_compression.cpp b/cv_bridge/test/test_compression.cpp index 5be403e38..e081d6643 100644 --- a/cv_bridge/test/test_compression.cpp +++ b/cv_bridge/test/test_compression.cpp @@ -5,7 +5,7 @@ TEST(CvBridgeTest, compression) { cv::RNG rng(0); - std_msgs::Header header; + std_msgs::msg::Header header; // Test 3 channel images. for (int i = 0; i < 2; ++i) @@ -14,7 +14,7 @@ TEST(CvBridgeTest, compression) cv::Mat_ in(10, 10); rng.fill(in, cv::RNG::UNIFORM, 0, 256); - sensor_msgs::CompressedImagePtr msg = cv_bridge::CvImage(header, format, in).toCompressedImageMsg(cv_bridge::PNG); + sensor_msgs::msg::CompressedImage::SharedPtr msg = cv_bridge::CvImage(header, format, in).toCompressedImageMsg(cv_bridge::PNG); const cv_bridge::CvImageConstPtr out = cv_bridge::toCvCopy(msg, format); EXPECT_EQ(out->image.channels(), 3); @@ -28,7 +28,7 @@ TEST(CvBridgeTest, compression) cv::Mat_ in(10, 10); rng.fill(in, cv::RNG::UNIFORM, 0, 256); - sensor_msgs::CompressedImagePtr msg = cv_bridge::CvImage(header, format, in).toCompressedImageMsg(cv_bridge::PNG); + sensor_msgs::msg::CompressedImage::SharedPtr msg = cv_bridge::CvImage(header, format, in).toCompressedImageMsg(cv_bridge::PNG); const cv_bridge::CvImageConstPtr out = cv_bridge::toCvCopy(msg, format); EXPECT_EQ(out->image.channels(), 4); EXPECT_EQ(cv::norm(out->image, in), 0); diff --git a/cv_bridge/test/test_endian.cpp b/cv_bridge/test/test_endian.cpp index 58a1f5029..4e17a40eb 100644 --- a/cv_bridge/test/test_endian.cpp +++ b/cv_bridge/test/test_endian.cpp @@ -1,5 +1,5 @@ -#include "boost/endian/conversion.hpp" -#include +#include +#include #include #include @@ -8,7 +8,7 @@ TEST(CvBridgeTest, endianness) using namespace boost::endian; // Create an image of the type opposite to the platform - sensor_msgs::Image msg; + sensor_msgs::msg::Image msg; msg.height = 1; msg.width = 1; msg.encoding = "32SC2"; @@ -30,7 +30,7 @@ TEST(CvBridgeTest, endianness) } // Make sure the values are still the same - cv_bridge::CvImageConstPtr img = cv_bridge::toCvShare(boost::make_shared(msg)); + cv_bridge::CvImageConstPtr img = cv_bridge::toCvShare(std::make_shared(msg)); EXPECT_EQ(img->image.at(0, 0)[0], 1); EXPECT_EQ(img->image.at(0, 0)[1], 2); // Make sure we cannot share data diff --git a/cv_bridge/test/utest.cpp b/cv_bridge/test/utest.cpp index d74be56cc..fe7843340 100644 --- a/cv_bridge/test/utest.cpp +++ b/cv_bridge/test/utest.cpp @@ -1,5 +1,5 @@ #include "cv_bridge/cv_bridge.h" -#include +#include #include @@ -13,7 +13,7 @@ TEST(CvBridgeTest, NonContinuous) cvi.encoding = sensor_msgs::image_encodings::MONO16; cvi.image = partial; - sensor_msgs::ImagePtr msg = cvi.toImageMsg(); + sensor_msgs::msg::Image::SharedPtr msg = cvi.toImageMsg(); EXPECT_EQ(msg->height, 8); EXPECT_EQ(msg->width, 3); EXPECT_EQ(msg->encoding, cvi.encoding); @@ -24,7 +24,7 @@ TEST(CvBridgeTest, ChannelOrder) { cv::Mat_ mat(200, 200); mat.setTo(cv::Scalar(1000,0,0,0)); - sensor_msgs::ImagePtr image(new sensor_msgs::Image()); + sensor_msgs::msg::Image::SharedPtr image(new sensor_msgs::msg::Image()); image = cv_bridge::CvImage(image->header, sensor_msgs::image_encodings::MONO16, mat).toImageMsg(); @@ -46,7 +46,7 @@ TEST(CvBridgeTest, ChannelOrder) TEST(CvBridgeTest, initialization) { - sensor_msgs::Image image; + sensor_msgs::msg::Image image; cv_bridge::CvImagePtr cv_ptr; image.encoding = "bgr8"; @@ -73,7 +73,7 @@ TEST(CvBridgeTest, initialization) TEST(CvBridgeTest, imageMessageStep) { // Test 1: image step is padded - sensor_msgs::Image image; + sensor_msgs::msg::Image image; cv_bridge::CvImagePtr cv_ptr; image.encoding = "mono8"; @@ -107,7 +107,7 @@ TEST(CvBridgeTest, imageMessageStep) TEST(CvBridgeTest, imageMessageConversion) { - sensor_msgs::Image imgmsg; + sensor_msgs::msg::Image imgmsg; cv_bridge::CvImagePtr cv_ptr; imgmsg.height = 220; imgmsg.width = 200; diff --git a/cv_bridge/test/utest2.cpp b/cv_bridge/test/utest2.cpp index d41c976d2..2f6898c53 100644 --- a/cv_bridge/test/utest2.cpp +++ b/cv_bridge/test/utest2.cpp @@ -39,8 +39,8 @@ #include "opencv2/core/core.hpp" #include "cv_bridge/cv_bridge.h" -#include -#include +#include +#include using namespace sensor_msgs::image_encodings; @@ -78,11 +78,11 @@ TEST(OpencvTests, testCase_encode_decode) cv::RNG r(77); r.fill(image_original, cv::RNG::UNIFORM, 0, 127); - sensor_msgs::Image image_message; - cv_bridge::CvImage image_bridge(std_msgs::Header(), src_encoding, image_original); + sensor_msgs::msg::Image image_message; + cv_bridge::CvImage image_bridge(std_msgs::msg::Header(), src_encoding, image_original); // Convert to a sensor_msgs::Image - sensor_msgs::ImagePtr image_msg = image_bridge.toImageMsg(); + sensor_msgs::msg::Image::SharedPtr image_msg = image_bridge.toImageMsg(); for(size_t j=0; j