Robot Raconteur Companion C++ Library
Loading...
Searching...
No Matches
ImageUtil.h
Go to the documentation of this file.
1
23
24#include "com__robotraconteur__image.h"
25
26#include <opencv2/core/version.hpp>
27
28#if !defined(CV_VERSION_EPOCH) && CV_VERSION_MAJOR >= 3
29#include <opencv2/core/mat.hpp>
30#include <opencv2/imgproc.hpp>
31#include <opencv2/imgcodecs.hpp>
32#else
33#include <opencv2/opencv.hpp>
34#endif
35
36#pragma once
37
38#include <boost/predef/other/endian.h>
39
40#if !defined(BOOST_ENDIAN_LITTLE_BYTE_AVAILABLE)
41BOOST_STATIC_ASSERT(false);
42#endif
43
44namespace RobotRaconteur
45{
46namespace Companion
47{
48namespace Util
49{
70static cv::Mat ImageToMat(const com::robotraconteur::image::ImagePtr& image)
71{
72 if (!image)
73 {
74 throw InvalidArgumentException("Image is null");
75 }
76
77 if (!image->image_info)
78 {
79 throw InvalidArgumentException("ImageInfo is null");
80 }
81
82 if (image->data->size() == 0)
83 {
84 throw InvalidArgumentException("Image data is empty");
85 }
86
87 size_t width = image->image_info->width;
88 size_t height = image->image_info->height;
89 size_t step = image->image_info->step;
90
91 if (width <= 0 || height <= 0)
92 {
93 throw InvalidArgumentException("Invalid image dimensions");
94 }
95
96 int32_t mat_type = -1;
97 switch (image->image_info->encoding)
98 {
99 case com::robotraconteur::image::ImageEncoding::rgb888: {
100 if (step <= 0)
101 {
102 step = width * 3;
103 }
104 mat_type = CV_8UC3;
105 break;
106 }
107 case com::robotraconteur::image::ImageEncoding::rgba8888: {
108 if (step <= 0)
109 {
110 step = width * 4;
111 }
112 mat_type = CV_8UC4;
113 break;
114 }
115 case com::robotraconteur::image::ImageEncoding::bgr888: {
116 if (step <= 0)
117 {
118 step = width * 3;
119 }
120 mat_type = CV_8UC3;
121 break;
122 }
123 case com::robotraconteur::image::ImageEncoding::bgra8888: {
124 if (step <= 0)
125 {
126 step = width * 4;
127 }
128 mat_type = CV_8UC4;
129 break;
130 }
131 case com::robotraconteur::image::ImageEncoding::mono8: {
132 if (step <= 0)
133 {
134 step = width;
135 }
136 mat_type = CV_8UC1;
137 break;
138 }
139 case com::robotraconteur::image::ImageEncoding::mono16:
140 case com::robotraconteur::image::ImageEncoding::depth_u16: {
141 if (step <= 0)
142 {
143 step = width * 2;
144 }
145 mat_type = CV_16UC1;
146 break;
147 }
148 case com::robotraconteur::image::ImageEncoding::mono32:
149 case com::robotraconteur::image::ImageEncoding::depth_u32: {
150 if (step <= 0)
151 {
152 step = width * 4;
153 }
154 mat_type = CV_32SC1;
155 break;
156 }
157#if !defined(CV_VERSION_EPOCH) && CV_VERSION_MAJOR >= 4
158 case com::robotraconteur::image::ImageEncoding::mono_f16: {
159 if (step <= 0)
160 {
161 step = width * 2;
162 }
163 mat_type = CV_16FC1;
164 break;
165 }
166#endif
167 case com::robotraconteur::image::ImageEncoding::depth_f32:
168 case com::robotraconteur::image::ImageEncoding::mono_f32: {
169 if (step <= 0)
170 {
171 step = width * 4;
172 }
173 mat_type = CV_32FC1;
174 break;
175 }
176 case com::robotraconteur::image::ImageEncoding::mono_f64:
177 case com::robotraconteur::image::ImageEncoding::depth_f64: {
178 if (step <= 0)
179 {
180 step = width * 8;
181 }
182 mat_type = CV_64FC1;
183 break;
184 }
185 default:
186 throw InvalidArgumentException("Unsupported image encoding");
187 }
188
189 size_t expected_bytes = step * height;
190 if (expected_bytes != image->data->size())
191 {
192 throw InvalidArgumentException("Image data size does not match expected size");
193 }
194
195 cv::Mat mat(boost::numeric_cast<int>(height), boost::numeric_cast<int>(width), mat_type, image->data->data(), step);
196
197 switch (image->image_info->encoding)
198 {
199 case com::robotraconteur::image::ImageEncoding::rgb888:
200 cv::cvtColor(mat, mat, cv::COLOR_RGB2BGR);
201 break;
202 case com::robotraconteur::image::ImageEncoding::rgba8888:
203 cv::cvtColor(mat, mat, cv::COLOR_RGBA2BGRA);
204 break;
205 default:
206 break;
207 }
208
209 return mat.clone();
210}
211
235
236static com::robotraconteur::image::ImagePtr MatToImage(
237 const cv::Mat& mat, com::robotraconteur::image::ImageEncoding::ImageEncoding encoding)
238{
239 if (mat.empty())
240 {
241 throw InvalidArgumentException("Mat is empty");
242 }
243
244 if (mat.channels() <= 0)
245 {
246 throw InvalidArgumentException("Invalid number of channels");
247 }
248
249 if (mat.step[0] <= 0)
250 {
251 throw InvalidArgumentException("Invalid step");
252 }
253
254 com::robotraconteur::image::ImagePtr image(new com::robotraconteur::image::Image());
255 com::robotraconteur::image::ImageInfoPtr image_info(new com::robotraconteur::image::ImageInfo());
256 image->image_info = image_info;
257
258 image_info->width = mat.cols;
259 image_info->height = mat.rows;
260 image_info->step = boost::numeric_cast<uint32_t>((size_t)mat.step[0]);
261 image_info->encoding = encoding;
262
263 int32_t mat_type = -1;
264 size_t element_size = 0;
265
266 switch (encoding)
267 {
268 case com::robotraconteur::image::ImageEncoding::rgb888: {
269 mat_type = CV_8UC3;
270 break;
271 }
272 case com::robotraconteur::image::ImageEncoding::rgba8888: {
273 mat_type = CV_8UC4;
274 break;
275 }
276 case com::robotraconteur::image::ImageEncoding::bgr888: {
277 mat_type = CV_8UC3;
278 break;
279 }
280 case com::robotraconteur::image::ImageEncoding::bgra8888: {
281 mat_type = CV_8UC4;
282 break;
283 }
284 case com::robotraconteur::image::ImageEncoding::mono8: {
285 mat_type = CV_8UC1;
286 break;
287 }
288 case com::robotraconteur::image::ImageEncoding::mono16:
289 case com::robotraconteur::image::ImageEncoding::depth_u16: {
290 mat_type = CV_16UC1;
291 break;
292 }
293 case com::robotraconteur::image::ImageEncoding::mono32:
294 case com::robotraconteur::image::ImageEncoding::depth_u32: {
295 mat_type = CV_32SC1;
296 break;
297 }
298#if !defined(CV_VERSION_EPOCH) && CV_VERSION_MAJOR >= 4
299 case com::robotraconteur::image::ImageEncoding::mono_f16: {
300 mat_type = CV_16FC1;
301 break;
302 }
303#endif
304 case com::robotraconteur::image::ImageEncoding::mono_f32:
305 case com::robotraconteur::image::ImageEncoding::depth_f32: {
306 mat_type = CV_32FC1;
307 break;
308 }
309 case com::robotraconteur::image::ImageEncoding::mono_f64:
310 case com::robotraconteur::image::ImageEncoding::depth_f64: {
311 mat_type = CV_64FC1;
312 break;
313 }
314 default:
315 throw InvalidArgumentException("Unsupported image encoding");
316 }
317
318 if (mat.type() != mat_type)
319 {
320 throw InvalidArgumentException("Invalid image type passed for specified encoding");
321 }
322 RobotRaconteur::RRArrayPtr<uint8_t> rr_data =
323 RobotRaconteur::AllocateRRArray<uint8_t>(mat.total() * mat.elemSize());
324 cv::Mat rr_mat_target(mat.rows, mat.cols, mat_type, rr_data->data(), mat.step[0]);
325 switch (encoding)
326 {
327 case com::robotraconteur::image::ImageEncoding::rgb888:
328 cv::cvtColor(mat, rr_mat_target, cv::COLOR_BGR2RGB);
329 break;
330 case com::robotraconteur::image::ImageEncoding::rgba8888:
331 cv::cvtColor(mat, rr_mat_target, cv::COLOR_BGRA2RGBA);
332 break;
333 default:
334 mat.copyTo(rr_mat_target);
335 break;
336 }
337 image->data = rr_data;
338
339 return image;
340}
341
342#if !defined(CV_VERSION_EPOCH) && CV_VERSION_MAJOR >= 3
343
344// doc
359static com::robotraconteur::image::CompressedImagePtr MatToCompressedImage(
360 const std::string& ext, const cv::Mat& mat, const std::vector<int>& params = std::vector<int>())
361{
362 std::vector<uchar> encoded_buffer;
363 bool success = cv::imencode(ext, mat, encoded_buffer, params);
364 if (!success)
365 {
366 throw InvalidArgumentException("Failed to encode image to compressed format");
367 }
368
369 com::robotraconteur::image::CompressedImagePtr image(new com::robotraconteur::image::CompressedImage());
370 com::robotraconteur::image::ImageInfoPtr image_info(new com::robotraconteur::image::ImageInfo());
371 image->image_info = image_info;
372
373 image_info->width = mat.cols;
374 image_info->height = mat.rows;
375 image_info->step = 0;
376 image_info->encoding = com::robotraconteur::image::ImageEncoding::compressed;
377
378 image->data = RobotRaconteur::AttachRRArrayCopy<uint8_t>((uint8_t*)&encoded_buffer[0], encoded_buffer.size());
379
380 return image;
381}
382
395static cv::Mat CompressedImageToMat(const com::robotraconteur::image::CompressedImagePtr& image)
396{
397 if (!image)
398 {
399 throw InvalidArgumentException("CompressedImage is null");
400 }
401
402 if (!image->image_info)
403 {
404 throw InvalidArgumentException("ImageInfo is null");
405 }
406
407 if (image->data->size() == 0)
408 {
409 throw InvalidArgumentException("CompressedImage data is empty");
410 }
411
412 // Create mat from the encoded data
413 cv::Mat encoded_buffer(1, boost::numeric_cast<int>(image->data->size()), CV_8UC1, (void*)image->data->data());
414 cv::Mat mat = cv::imdecode(encoded_buffer, cv::IMREAD_UNCHANGED);
415 if (mat.empty())
416 {
417 throw InvalidArgumentException("Failed to decode CompressedImage data");
418 }
419
420 if (mat.cols != image->image_info->width || mat.rows != image->image_info->height)
421 {
422 throw InvalidArgumentException("Decoded image dimensions do not match ImageInfo");
423 }
424
425 if (mat.channels() <= 0)
426 {
427 throw InvalidArgumentException("Decoded image has invalid number of channels");
428 }
429
430 if (mat.step[0] <= 0)
431 {
432 throw InvalidArgumentException("Decoded image has invalid step");
433 }
434
435 return mat.clone();
436}
437#endif
438} // namespace Util
439} // namespace Companion
440} // namespace RobotRaconteur
static com::robotraconteur::image::ImagePtr MatToImage(const cv::Mat &mat, com::robotraconteur::image::ImageEncoding::ImageEncoding encoding)
Convert an OpenCV Mat to a Robot Raconteur standard image.
Definition ImageUtil.h:236
static cv::Mat ImageToMat(const com::robotraconteur::image::ImagePtr &image)
Convert a Robot Raconteur standard image to an OpenCV Mat.
Definition ImageUtil.h:70