1515#include < cv_bridge/cv_bridge.h>
1616#endif
1717#include < atomic>
18+ #include < image_transport/image_transport.hpp>
1819#include < iostream>
1920#include < libcamera/base/shared_fd.h>
2021#include < libcamera/base/signal.h>
@@ -97,38 +98,18 @@ class CameraNode : public rclcpp::Node
9798
9899 bool use_node_time;
99100
100- rclcpp::Publisher<sensor_msgs::msg::Image>::SharedPtr pub_image;
101- rclcpp::Publisher<sensor_msgs::msg::CompressedImage>::SharedPtr pub_image_compressed;
102- rclcpp::Publisher<sensor_msgs::msg::CameraInfo>::SharedPtr pub_ci;
101+ image_transport::CameraPublisher pub_camera;
103102
104103 camera_info_manager::CameraInfoManager cim;
105104
106105 ParameterHandler parameter_handler;
107106 std::string frame_id;
108107
109- #ifdef RCLCPP_HAS_PARAM_EXT_CB
110- // use new "post_set" callback to apply parameters
111- PostSetParametersCallbackHandle::SharedPtr param_cb_change;
112- #else
113- OnSetParametersCallbackHandle::SharedPtr param_cb_change;
114- #endif
115-
116- // compression quality parameter
117- std::atomic_uint8_t jpeg_quality;
118-
119108 void
120109 requestComplete (libcamera::Request *const request);
121110
122111 void
123112 process (libcamera::Request *const request);
124-
125- void
126- postParameterChange (const std::vector<rclcpp::Parameter> ¶meters);
127-
128- #ifndef RCLCPP_HAS_PARAM_EXT_CB
129- rcl_interfaces::msg::SetParametersResult
130- onParameterChange (const std::vector<rclcpp::Parameter> ¶meters);
131- #endif
132113};
133114
134115RCLCPP_COMPONENTS_REGISTER_NODE (camera::CameraNode)
@@ -182,42 +163,6 @@ get_sensor_format(const std::string &format_str)
182163 throw std::runtime_error (" Invalid sensor_mode. Expected [width]:[height] but got " + format_str);
183164}
184165
185- // The following function "compressImageMsg" is adapted from "CvImage::toCompressedImageMsg"
186- // (https://github.com/ros-perception/vision_opencv/blob/066793a23e5d06d76c78ca3d69824a501c3554fd/cv_bridge/src/cv_bridge.cpp#L512-L535)
187- // and covered by the BSD-3-Clause licence.
188- void
189- compressImageMsg (const sensor_msgs::msg::Image &source,
190- sensor_msgs::msg::CompressedImage &destination,
191- const std::vector<int > ¶ms = std::vector<int >())
192- {
193- namespace enc = sensor_msgs::image_encodings;
194-
195- std::shared_ptr<CameraNode> tracked_object;
196- cv_bridge::CvImageConstPtr cv_ptr = cv_bridge::toCvShare (source, tracked_object);
197-
198- destination.header = source.header ;
199- cv::Mat image;
200- if (cv_ptr->encoding == enc::BGR8 || cv_ptr->encoding == enc::BGRA8 ||
201- cv_ptr->encoding == enc::MONO8 || cv_ptr->encoding == enc::MONO16)
202- {
203- image = cv_ptr->image ;
204- }
205- else {
206- cv_bridge::CvImagePtr tempThis = std::make_shared<cv_bridge::CvImage>(*cv_ptr);
207- cv_bridge::CvImagePtr temp;
208- if (enc::hasAlpha (cv_ptr->encoding )) {
209- temp = cv_bridge::cvtColor (tempThis, enc::BGRA8);
210- }
211- else {
212- temp = cv_bridge::cvtColor (tempThis, enc::BGR8);
213- }
214- image = temp->image ;
215- }
216-
217- destination.format = " jpg" ;
218- cv::imencode (" .jpg" , image, destination.data , params);
219- }
220-
221166
222167CameraNode::CameraNode (const rclcpp::NodeOptions &options)
223168 : Node(" camera" , options),
@@ -234,14 +179,7 @@ CameraNode::CameraNode(const rclcpp::NodeOptions &options)
234179#else
235180 cim (this ),
236181#endif
237- parameter_handler (this ),
238- param_cb_change(
239- #ifdef RCLCPP_HAS_PARAM_EXT_CB
240- add_post_set_parameters_callback (std::bind(&CameraNode::postParameterChange, this , std::placeholders::_1))
241- #else
242- add_on_set_parameters_callback (std::bind(&CameraNode::onParameterChange, this , std::placeholders::_1))
243- #endif
244- )
182+ parameter_handler (this )
245183{
246184 // pixel format
247185 rcl_interfaces::msg::ParameterDescriptor param_descr_format;
@@ -301,34 +239,14 @@ CameraNode::CameraNode(const rclcpp::NodeOptions &options)
301239 const rclcpp::ParameterValue &camera_id =
302240 declare_parameter (" camera" , rclcpp::ParameterValue {}, param_descr_ro.set__dynamic_typing (true ));
303241
304- // we cannot control the compression rate of the libcamera MJPEG stream
305- // ignore "jpeg_quality" parameter for MJPEG streams
306- if (libcamera::PixelFormat::fromString (format) != libcamera::formats::MJPEG) {
307- rcl_interfaces::msg::ParameterDescriptor jpeg_quality_description;
308- jpeg_quality_description.name = " jpeg_quality" ;
309- jpeg_quality_description.type = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER;
310- jpeg_quality_description.description = " Image quality for JPEG format" ;
311- jpeg_quality_description.read_only = false ;
312- rcl_interfaces::msg::IntegerRange jpeg_range;
313- jpeg_range.from_value = 1 ;
314- jpeg_range.to_value = 100 ;
315- jpeg_range.step = 1 ;
316- jpeg_quality_description.integer_range = {jpeg_range};
317- // default to 95
318- jpeg_quality = declare_parameter<uint8_t >(" jpeg_quality" , 95 , jpeg_quality_description);
319- }
320-
321242 // use_node_time parameter
322243 rcl_interfaces::msg::ParameterDescriptor param_descr_use_node_time;
323244 param_descr_use_node_time.description = " use node time instead of sensor timestamp for image messages" ;
324245 param_descr_use_node_time.read_only = true ;
325246 use_node_time = declare_parameter<bool >(" use_node_time" , false , param_descr_use_node_time);
326247
327- // publisher for raw and compressed image
328- pub_image = this ->create_publisher <sensor_msgs::msg::Image>(" ~/image_raw" , 1 );
329- pub_image_compressed =
330- this ->create_publisher <sensor_msgs::msg::CompressedImage>(" ~/image_raw/compressed" , 1 );
331- pub_ci = this ->create_publisher <sensor_msgs::msg::CameraInfo>(" ~/camera_info" , 1 );
248+ // publisher for images and camera info
249+ pub_camera = image_transport::create_camera_publisher (this , " ~/image_raw" );
332250
333251 // start camera manager and check for cameras
334252 camera_manager.start ();
@@ -658,55 +576,44 @@ CameraNode::process(libcamera::Request *const request)
658576 // prepare image messages
659577 const libcamera::StreamConfiguration &cfg = stream->configuration ();
660578
661- auto msg_img = std::make_unique< sensor_msgs::msg::Image>() ;
662- auto msg_img_compressed = std::make_unique< sensor_msgs::msg::CompressedImage>() ;
579+ sensor_msgs::msg::Image msg_img ;
580+ sensor_msgs::msg::CompressedImage msg_img_compressed ;
663581
664582 if (format_type (cfg.pixelFormat ) == FormatType::RAW) {
665583 // raw uncompressed image
666584 assert (buffer_info[buffer].size == bytesused);
667- msg_img->header = hdr;
668- msg_img->width = cfg.size .width ;
669- msg_img->height = cfg.size .height ;
670- msg_img->step = cfg.stride ;
671- msg_img->encoding = get_ros_encoding (cfg.pixelFormat );
672- msg_img->is_bigendian = (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__);
673- msg_img->data .resize (buffer_info[buffer].size );
674- memcpy (msg_img->data .data (), buffer_info[buffer].data , buffer_info[buffer].size );
675-
676- // compress to jpeg
677- if (pub_image_compressed->get_subscription_count ()) {
678- try {
679- compressImageMsg (*msg_img, *msg_img_compressed,
680- {cv::IMWRITE_JPEG_QUALITY, jpeg_quality});
681- }
682- catch (const cv_bridge::Exception &e) {
683- RCLCPP_ERROR_STREAM (get_logger (), e.what ());
684- }
685- }
585+ msg_img.header = hdr;
586+ msg_img.width = cfg.size .width ;
587+ msg_img.height = cfg.size .height ;
588+ msg_img.step = cfg.stride ;
589+ msg_img.encoding = get_ros_encoding (cfg.pixelFormat );
590+ msg_img.is_bigendian = (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__);
591+ msg_img.data .resize (buffer_info[buffer].size );
592+ memcpy (msg_img.data .data (), buffer_info[buffer].data , buffer_info[buffer].size );
686593 }
687594 else if (format_type (cfg.pixelFormat ) == FormatType::COMPRESSED) {
688595 // compressed image
689596 assert (bytesused < buffer_info[buffer].size );
690- msg_img_compressed-> header = hdr;
691- msg_img_compressed-> format = get_ros_encoding (cfg.pixelFormat );
692- msg_img_compressed-> data .resize (bytesused);
693- memcpy (msg_img_compressed-> data .data (), buffer_info[buffer].data , bytesused);
597+ msg_img_compressed. header = hdr;
598+ msg_img_compressed. format = get_ros_encoding (cfg.pixelFormat );
599+ msg_img_compressed. data .resize (bytesused);
600+ memcpy (msg_img_compressed. data .data (), buffer_info[buffer].data , bytesused);
694601
695602 // decompress into raw rgb8 image
696- if (pub_image-> get_subscription_count () )
697- cv_bridge::toCvCopy (* msg_img_compressed, " rgb8" )->toImageMsg (* msg_img);
603+ if (pub_camera. getNumSubscribers () > 0 )
604+ cv_bridge::toCvCopy (msg_img_compressed, " rgb8" )->toImageMsg (msg_img);
698605 }
699606 else {
700607 throw std::runtime_error (" unsupported pixel format: " +
701608 stream->configuration ().pixelFormat .toString ());
702609 }
703610
704- pub_image->publish (std::move (msg_img));
705- pub_image_compressed->publish (std::move (msg_img_compressed));
706-
611+ // Get camera info
707612 sensor_msgs::msg::CameraInfo ci = cim.getCameraInfo ();
708613 ci.header = hdr;
709- pub_ci->publish (ci);
614+
615+ // Publish image and camera info
616+ pub_camera.publish (msg_img, ci);
710617 }
711618 else if (request->status () == libcamera::Request::RequestCancelled) {
712619 RCLCPP_ERROR_STREAM (get_logger (), " request '" << request->toString () << " ' cancelled" );
@@ -727,27 +634,4 @@ CameraNode::process(libcamera::Request *const request)
727634 }
728635}
729636
730- void
731- CameraNode::postParameterChange (const std::vector<rclcpp::Parameter> ¶meters)
732- {
733- // check non-control parameters
734- for (const rclcpp::Parameter ¶meter : parameters) {
735- if (parameter.get_name () == " jpeg_quality" ) {
736- jpeg_quality = parameter.get_parameter_value ().get <uint8_t >();
737- }
738- }
739- }
740-
741- #ifndef RCLCPP_HAS_PARAM_EXT_CB
742- rcl_interfaces::msg::SetParametersResult
743- CameraNode::onParameterChange (const std::vector<rclcpp::Parameter> ¶meters)
744- {
745- postParameterChange (parameters);
746-
747- rcl_interfaces::msg::SetParametersResult result;
748- result.successful = true ;
749- return result;
750- }
751- #endif
752-
753637} // namespace camera
0 commit comments