codec.cpp
Go to the documentation of this file.
1 /*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 2012 Willow Garage.
5 * Copyright (c) 2016 Google, Inc.
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * * Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
18 * * Neither the name of the Willow Garage nor the names of its
19 * contributors may be used to endorse or promote products derived
20 * from this software without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33 * POSSIBILITY OF SUCH DAMAGE.
34 *********************************************************************/
35 
36 #include <limits>
37 #include <string>
38 #include <vector>
39 
40 #include <opencv2/highgui/highgui.hpp>
41 
42 #include "cv_bridge/cv_bridge.h"
46 #include "ros/ros.h"
47 
48 // If OpenCV3
49 #ifndef CV_VERSION_EPOCH
50 #include <opencv2/imgcodecs.hpp>
51 
52 // If OpenCV4
53 #if CV_VERSION_MAJOR > 3
54 #include <opencv2/imgcodecs/legacy/constants_c.h>
55 #endif
56 #endif
57 
59 using namespace cv;
60 
61 // Encoding and decoding of compressed depth images.
63 {
64 
65 sensor_msgs::Image::Ptr decodeCompressedDepthImage(const sensor_msgs::CompressedImage& message)
66 {
68 
69  // Copy message header
70  cv_ptr->header = message.header;
71 
72  // Assign image encoding
73  const size_t split_pos = message.format.find(';');
74  const std::string image_encoding = message.format.substr(0, split_pos);
75  std::string compression_format;
76  // Older version of compressed_depth_image_transport supports only png.
77  if (split_pos == std::string::npos) {
78  compression_format = "png";
79  } else {
80  std::string format = message.format.substr(split_pos);
81  if (format.find("compressedDepth png") != std::string::npos) {
82  compression_format = "png";
83  } else if (format.find("compressedDepth rvl") != std::string::npos) {
84  compression_format = "rvl";
85  } else if (format.find("compressedDepth") != std::string::npos && format.find("compressedDepth ") == std::string::npos) {
86  compression_format = "png";
87  } else {
88  ROS_ERROR("Unsupported image format: %s", message.format.c_str());
89  return sensor_msgs::Image::Ptr();
90  }
91  }
92 
93  cv_ptr->encoding = image_encoding;
94 
95  // Decode message data
96  if (message.data.size() > sizeof(ConfigHeader))
97  {
98 
99  // Read compression type from stream
100  ConfigHeader compressionConfig;
101  memcpy(&compressionConfig, &message.data[0], sizeof(compressionConfig));
102 
103  // Get compressed image data
104  const std::vector<uint8_t> imageData(message.data.begin() + sizeof(compressionConfig), message.data.end());
105 
106  // Depth map decoding
107  float depthQuantA, depthQuantB;
108 
109  // Read quantization parameters
110  depthQuantA = compressionConfig.depthParam[0];
111  depthQuantB = compressionConfig.depthParam[1];
112 
113  if (enc::bitDepth(image_encoding) == 32)
114  {
115  cv::Mat decompressed;
116  if (compression_format == "png") {
117  try
118  {
119  // Decode image data
120  decompressed = cv::imdecode(imageData, cv::IMREAD_UNCHANGED);
121  }
122  catch (cv::Exception& e)
123  {
124  ROS_ERROR("%s", e.what());
125  return sensor_msgs::Image::Ptr();
126  }
127  } else if (compression_format == "rvl") {
128  const unsigned char *buffer = imageData.data();
129 
130  uint32_t cols, rows;
131  memcpy(&cols, &buffer[0], 4);
132  memcpy(&rows, &buffer[4], 4);
133  if (rows == 0 || cols == 0)
134  {
135  ROS_ERROR_THROTTLE(1.0, "Received malformed RVL-encoded image. Size %ix%i contains zero.", cols, rows);
136  return sensor_msgs::Image::Ptr();
137  }
138 
139  // Sanity check - the best compression ratio is 4x; we leave some buffer, so we check whether the output image would
140  // not be more than 10x larger than the compressed one. If it is, we probably received corrupted data.
141  // The condition should be "numPixels * 2 > compressed.size() * 10" (because each pixel is 2 bytes), but to prevent
142  // overflow, we have canceled out the *2 from both sides of the inequality.
143  const auto numPixels = static_cast<uint64_t>(rows) * cols;
144  if (numPixels > std::numeric_limits<int>::max() || numPixels > static_cast<uint64_t>(imageData.size()) * 5)
145  {
146  ROS_ERROR_THROTTLE(1.0, "Received malformed RVL-encoded image. It reports size %ux%u.", cols, rows);
147  return sensor_msgs::Image::Ptr();
148  }
149 
150  decompressed = Mat(rows, cols, CV_16UC1);
151  RvlCodec rvl;
152  rvl.DecompressRVL(&buffer[8], decompressed.ptr<unsigned short>(), cols * rows);
153  } else {
154  return sensor_msgs::Image::Ptr();
155  }
156 
157  size_t rows = decompressed.rows;
158  size_t cols = decompressed.cols;
159 
160  if ((rows > 0) && (cols > 0))
161  {
162  cv_ptr->image = Mat(rows, cols, CV_32FC1);
163 
164  // Depth conversion
165  MatIterator_<float> itDepthImg = cv_ptr->image.begin<float>(),
166  itDepthImg_end = cv_ptr->image.end<float>();
167  MatConstIterator_<unsigned short> itInvDepthImg = decompressed.begin<unsigned short>(),
168  itInvDepthImg_end = decompressed.end<unsigned short>();
169 
170  for (; (itDepthImg != itDepthImg_end) && (itInvDepthImg != itInvDepthImg_end); ++itDepthImg, ++itInvDepthImg)
171  {
172  // check for NaN & max depth
173  if (*itInvDepthImg)
174  {
175  *itDepthImg = depthQuantA / ((float)*itInvDepthImg - depthQuantB);
176  }
177  else
178  {
179  *itDepthImg = std::numeric_limits<float>::quiet_NaN();
180  }
181  }
182 
183  // Publish message to user callback
184  return cv_ptr->toImageMsg();
185  }
186  }
187  else
188  {
189  // Decode raw image
190  if (compression_format == "png") {
191  try
192  {
193  cv_ptr->image = cv::imdecode(imageData, CV_LOAD_IMAGE_UNCHANGED);
194  }
195  catch (cv::Exception& e)
196  {
197  ROS_ERROR("%s", e.what());
198  return sensor_msgs::Image::Ptr();
199  }
200  } else if (compression_format == "rvl") {
201  const unsigned char *buffer = imageData.data();
202  uint32_t cols, rows;
203  memcpy(&cols, &buffer[0], 4);
204  memcpy(&rows, &buffer[4], 4);
205  cv_ptr->image = Mat(rows, cols, CV_16UC1);
206  RvlCodec rvl;
207  rvl.DecompressRVL(&buffer[8], cv_ptr->image.ptr<unsigned short>(), cols * rows);
208  } else {
209  return sensor_msgs::Image::Ptr();
210  }
211 
212  size_t rows = cv_ptr->image.rows;
213  size_t cols = cv_ptr->image.cols;
214 
215  if ((rows > 0) && (cols > 0))
216  {
217  // Publish message to user callback
218  return cv_ptr->toImageMsg();
219  }
220  }
221  }
222  return sensor_msgs::Image::Ptr();
223 }
224 
225 sensor_msgs::CompressedImage::Ptr encodeCompressedDepthImage(
226  const sensor_msgs::Image& message,
227  const std::string& compression_format,
228  double depth_max, double depth_quantization, int png_level)
229 {
230 
231  // Compressed image message
232  sensor_msgs::CompressedImage::Ptr compressed(new sensor_msgs::CompressedImage());
233  compressed->header = message.header;
234  compressed->format = message.encoding;
235 
236  // Compression settings
237  std::vector<int> params;
238 
239  // Bit depth of image encoding
240  int bitDepth = enc::bitDepth(message.encoding);
241  int numChannels = enc::numChannels(message.encoding);
242 
243  // Image compression configuration
244  ConfigHeader compressionConfig {};
245  compressionConfig.format = INV_DEPTH;
246 
247  // Compressed image data
248  std::vector<uint8_t> compressedImage;
249 
250  // Update ros message format header
251  compressed->format += "; compressedDepth " + compression_format;
252 
253  // Check input format
254  params.reserve(2);
255  params.emplace_back(cv::IMWRITE_PNG_COMPRESSION);
256  params.emplace_back(png_level);
257 
258  if ((bitDepth == 32) && (numChannels == 1))
259  {
260  float depthZ0 = depth_quantization;
261  float depthMax = depth_max;
262 
263  // OpenCV-ROS bridge
264  cv_bridge::CvImagePtr cv_ptr;
265  try
266  {
267  cv_ptr = cv_bridge::toCvCopy(message);
268  }
269  catch (cv_bridge::Exception& e)
270  {
271  ROS_ERROR("%s", e.what());
272  return sensor_msgs::CompressedImage::Ptr();
273  }
274 
275  const Mat& depthImg = cv_ptr->image;
276  size_t rows = depthImg.rows;
277  size_t cols = depthImg.cols;
278 
279  if ((rows > 0) && (cols > 0))
280  {
281  // Allocate matrix for inverse depth (disparity) coding
282  Mat invDepthImg(rows, cols, CV_16UC1);
283 
284  // Inverse depth quantization parameters
285  float depthQuantA = depthZ0 * (depthZ0 + 1.0f);
286  float depthQuantB = 1.0f - depthQuantA / depthMax;
287 
288  // Matrix iterators
289  MatConstIterator_<float> itDepthImg = depthImg.begin<float>(),
290  itDepthImg_end = depthImg.end<float>();
291  MatIterator_<unsigned short> itInvDepthImg = invDepthImg.begin<unsigned short>(),
292  itInvDepthImg_end = invDepthImg.end<unsigned short>();
293 
294  // Quantization
295  for (; (itDepthImg != itDepthImg_end) && (itInvDepthImg != itInvDepthImg_end); ++itDepthImg, ++itInvDepthImg)
296  {
297  // check for NaN & max depth
298  if (*itDepthImg < depthMax)
299  {
300  *itInvDepthImg = depthQuantA / *itDepthImg + depthQuantB;
301  }
302  else
303  {
304  *itInvDepthImg = 0;
305  }
306  }
307 
308  // Add coding parameters to header
309  compressionConfig.depthParam[0] = depthQuantA;
310  compressionConfig.depthParam[1] = depthQuantB;
311 
312  // Compress quantized disparity image
313  if (compression_format == "png") {
314  try
315  {
316  if (cv::imencode(".png", invDepthImg, compressedImage, params))
317  {
318  float cRatio = (float)(cv_ptr->image.rows * cv_ptr->image.cols * cv_ptr->image.elemSize())
319  / (float)compressedImage.size();
320  ROS_DEBUG("Compressed Depth Image Transport - Compression: 1:%.2f (%lu bytes)", cRatio, compressedImage.size());
321  }
322  else
323  {
324  ROS_ERROR("cv::imencode (png) failed on input image");
325  return sensor_msgs::CompressedImage::Ptr();
326  }
327  }
328  catch (cv::Exception& e)
329  {
330  ROS_ERROR("%s", e.msg.c_str());
331  return sensor_msgs::CompressedImage::Ptr();
332  }
333  } else if (compression_format == "rvl") {
334  int numPixels = invDepthImg.rows * invDepthImg.cols;
335  // In the worst case, RVL compression results in ~1.5x larger data.
336  compressedImage.resize(3 * numPixels + 12);
337  uint32_t cols = invDepthImg.cols;
338  uint32_t rows = invDepthImg.rows;
339  memcpy(&compressedImage[0], &cols, 4);
340  memcpy(&compressedImage[4], &rows, 4);
341  RvlCodec rvl;
342  int compressedSize = rvl.CompressRVL(invDepthImg.ptr<unsigned short>(), &compressedImage[8], numPixels);
343  compressedImage.resize(8 + compressedSize);
344  }
345  }
346  }
347  // Raw depth map compression
348  else if ((bitDepth == 16) && (numChannels == 1))
349  {
350  // OpenCV-ROS bridge
351  cv_bridge::CvImagePtr cv_ptr;
352  try
353  {
354  cv_ptr = cv_bridge::toCvCopy(message);
355  }
356  catch (Exception& e)
357  {
358  ROS_ERROR("%s", e.msg.c_str());
359  return sensor_msgs::CompressedImage::Ptr();
360  }
361 
362  const Mat& depthImg = cv_ptr->image;
363  size_t rows = depthImg.rows;
364  size_t cols = depthImg.cols;
365 
366  if ((rows > 0) && (cols > 0))
367  {
368  unsigned short depthMaxUShort = static_cast<unsigned short>(depth_max * 1000.0f);
369 
370  // Matrix iterators
371  MatIterator_<unsigned short> itDepthImg = cv_ptr->image.begin<unsigned short>(),
372  itDepthImg_end = cv_ptr->image.end<unsigned short>();
373 
374  // Max depth filter
375  for (; itDepthImg != itDepthImg_end; ++itDepthImg)
376  {
377  if (*itDepthImg > depthMaxUShort)
378  *itDepthImg = 0;
379  }
380 
381  // Compress raw depth image
382  if (compression_format == "png") {
383  if (cv::imencode(".png", cv_ptr->image, compressedImage, params))
384  {
385  float cRatio = (float)(cv_ptr->image.rows * cv_ptr->image.cols * cv_ptr->image.elemSize())
386  / (float)compressedImage.size();
387  ROS_DEBUG("Compressed Depth Image Transport - Compression: 1:%.2f (%lu bytes)", cRatio, compressedImage.size());
388  }
389  else
390  {
391  ROS_ERROR("cv::imencode (png) failed on input image");
392  return sensor_msgs::CompressedImage::Ptr();
393  }
394  } else if (compression_format == "rvl") {
395  int numPixels = cv_ptr->image.rows * cv_ptr->image.cols;
396  // In the worst case, RVL compression results in ~1.5x larger data.
397  compressedImage.resize(3 * numPixels + 12);
398  uint32_t cols = cv_ptr->image.cols;
399  uint32_t rows = cv_ptr->image.rows;
400  memcpy(&compressedImage[0], &cols, 4);
401  memcpy(&compressedImage[4], &rows, 4);
402  RvlCodec rvl;
403  int compressedSize = rvl.CompressRVL(cv_ptr->image.ptr<unsigned short>(), &compressedImage[8], numPixels);
404  compressedImage.resize(8 + compressedSize);
405  }
406  }
407  }
408  else
409  {
410  ROS_ERROR("Compressed Depth Image Transport - Compression requires single-channel 32bit-floating point or 16bit raw depth images (input format is: %s).", message.encoding.c_str());
411  return sensor_msgs::CompressedImage::Ptr();
412  }
413 
414  if (compressedImage.size() > 0)
415  {
416  // Add configuration to binary output
417  compressed->data.resize(sizeof(ConfigHeader));
418  memcpy(&compressed->data[0], &compressionConfig, sizeof(ConfigHeader));
419 
420  // Add compressed binary data to messages
421  compressed->data.insert(compressed->data.end(), compressedImage.begin(), compressedImage.end());
422 
423  return compressed;
424  }
425 
426  return sensor_msgs::CompressedImage::Ptr();
427 }
428 
429 } // namespace compressed_depth_image_transport
ROS_ERROR_THROTTLE
#define ROS_ERROR_THROTTLE(period,...)
sensor_msgs::image_encodings
boost::shared_ptr
compressed_depth_image_transport::decodeCompressedDepthImage
sensor_msgs::Image::Ptr decodeCompressedDepthImage(const sensor_msgs::CompressedImage &compressed_image)
Definition: codec.cpp:65
ros.h
compressed_depth_image_transport::RvlCodec::CompressRVL
int CompressRVL(const unsigned short *input, unsigned char *output, int numPixels)
Definition: rvl_codec.cpp:43
codec.h
cv_bridge::Exception
compressed_depth_image_transport
Definition: codec.h:43
compressed_depth_image_transport::ConfigHeader::depthParam
float depthParam[2]
Definition: compression_common.h:85
cv_bridge::toCvCopy
CvImagePtr toCvCopy(const sensor_msgs::CompressedImage &source, const std::string &encoding=std::string())
ROS_DEBUG
#define ROS_DEBUG(...)
compressed_depth_image_transport::RvlCodec
Definition: rvl_codec.h:6
compressed_depth_image_transport::RvlCodec::DecompressRVL
void DecompressRVL(const unsigned char *input, unsigned short *output, int numPixels)
Definition: rvl_codec.cpp:70
compressed_depth_image_transport::ConfigHeader
Definition: compression_common.h:80
compressed_depth_image_transport::encodeCompressedDepthImage
sensor_msgs::CompressedImage::Ptr encodeCompressedDepthImage(const sensor_msgs::Image &message, const std::string &compression_format, double depth_max, double depth_quantization, int png_level)
Definition: codec.cpp:225
compressed_depth_image_transport::ConfigHeader::format
compressionFormat format
Definition: compression_common.h:83
cv_bridge.h
ROS_ERROR
#define ROS_ERROR(...)
cv_bridge::CvImage
rvl_codec.h
compressed_depth_image_transport::INV_DEPTH
@ INV_DEPTH
Definition: compression_common.h:108
compression_common.h


compressed_depth_image_transport
Author(s): Julius Kammerl, David Gossow
autogenerated on Sat Jan 27 2024 03:31:04