MRPT  2.0.4
CImage.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | https://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2020, Individual contributors, see AUTHORS file |
6  | See: https://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See: https://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 //
11 #include "img-precomp.h" // Precompiled headers
12 //
13 
14 #include <mrpt/core/cpu.h>
15 #include <mrpt/core/round.h> // for round()
16 #include <mrpt/img/CImage.h>
19 #include <mrpt/io/CMemoryStream.h>
20 #include <mrpt/math/CMatrixF.h>
21 #include <mrpt/math/fourier.h>
22 #include <mrpt/math/utils.h> // for roundup()
24 #include <mrpt/system/CTicTac.h>
26 #include <mrpt/system/filesystem.h>
27 #include <mrpt/system/memory.h>
28 
29 #include <iostream>
30 
31 // Universal include for all versions of OpenCV
32 #include <mrpt/3rdparty/do_opencv_includes.h>
33 
34 #include "CImage_impl.h"
35 
36 #if MRPT_HAS_MATLAB
37 #include <mexplus/mxarray.h>
38 #endif
39 
40 // Prototypes of SSE2/SSE3/SSSE3 optimized functions:
41 #include "CImage.SSEx.h"
42 
43 using namespace mrpt;
44 using namespace mrpt::img;
45 using namespace mrpt::math;
46 using namespace mrpt::system;
47 using namespace std;
48 
49 // This must be added to any CSerializable class implementation file.
51 
52 static bool DISABLE_JPEG_COMPRESSION_value = true;
54 static std::string IMAGES_PATH_BASE(".");
55 
56 void CImage::DISABLE_JPEG_COMPRESSION(bool val)
57 {
59 }
61 {
63 }
65 {
67 }
69 {
71 }
72 
74  const std::string& s)
75  : std::runtime_error(s)
76 {
77 }
78 
79 const std::string& CImage::getImagesPathBase() { return IMAGES_PATH_BASE; }
80 void CImage::setImagesPathBase(const std::string& path)
81 {
82  IMAGES_PATH_BASE = path;
83 }
84 
85 // Do performance time logging?
86 #define IMAGE_ALLOC_PERFLOG 0
87 
88 #if IMAGE_ALLOC_PERFLOG
89 mrpt::img::CTimeLogger alloc_tims;
90 #endif
91 
92 #if MRPT_HAS_OPENCV
94 {
95  // clang-format off
96  switch (i)
97  {
98  case IMG_INTERP_NN: return cv::INTER_NEAREST;
99  case IMG_INTERP_LINEAR: return cv::INTER_LINEAR;
100  case IMG_INTERP_CUBIC: return cv::INTER_CUBIC;
101  case IMG_INTERP_AREA: return cv::INTER_AREA;
102  };
103  // clang-format on
104  return -1;
105 }
106 
107 template <typename RET = uint32_t>
109 {
110  // clang-format off
111  switch (d)
112  {
113  case PixelDepth::D8U: return static_cast<RET>(CV_8U);
114  case PixelDepth::D8S: return static_cast<RET>(CV_8S);
115  case PixelDepth::D16U: return static_cast<RET>(CV_16U);
116  case PixelDepth::D16S: return static_cast<RET>(CV_16S);
117  case PixelDepth::D32S: return static_cast<RET>(CV_32S);
118  case PixelDepth::D32F: return static_cast<RET>(CV_32F);
119  case PixelDepth::D64F: return static_cast<RET>(CV_64F);
120  }
121  // clang-format on
122  return std::numeric_limits<RET>::max();
123 }
124 template <typename RET = uint32_t>
126 {
127  // clang-format off
128  switch (d)
129  {
130  case PixelDepth::D8U: return static_cast<RET>(IPL_DEPTH_8U);
131  case PixelDepth::D8S: return static_cast<RET>(IPL_DEPTH_8S);
132  case PixelDepth::D16U: return static_cast<RET>(IPL_DEPTH_16U);
133  case PixelDepth::D16S: return static_cast<RET>(IPL_DEPTH_16S);
134  case PixelDepth::D32S: return static_cast<RET>(IPL_DEPTH_32S);
135  case PixelDepth::D32F: return static_cast<RET>(IPL_DEPTH_32F);
136  case PixelDepth::D64F: return static_cast<RET>(IPL_DEPTH_64F);
137  }
138  // clang-format on
139  return std::numeric_limits<RET>::max();
140 }
141 
143 {
144  // clang-format off
145  switch (d)
146  {
147  case CV_8U: return PixelDepth::D8U;
148  case CV_8S: return PixelDepth::D8S;
149  case CV_16U: return PixelDepth::D16U;
150  case CV_16S: return PixelDepth::D16S;
151  case CV_32S: return PixelDepth::D32S;
152  case CV_32F: return PixelDepth::D32F;
153  case CV_64F: return PixelDepth::D64F;
154  }
155  // clang-format on
156  return PixelDepth::D8U;
157 }
158 
159 #endif // MRPT_HAS_OPENCV
160 
161 // Default ctor
163 
164 // Ctor with size
166  unsigned int width, unsigned int height, TImageChannels nChannels)
167  : CImage()
168 {
169  MRPT_START
170  resize(width, height, nChannels);
171  MRPT_END
172 }
173 
175 {
176  std::swap(m_impl, o.m_impl);
178  std::swap(m_externalFile, o.m_externalFile);
179 }
180 
182 {
183  *this = o;
184  forceLoad();
185 }
186 
187 CImage::CImage(const cv::Mat& img, copy_type_t copy_type) : CImage()
188 {
189 #if MRPT_HAS_OPENCV
190  MRPT_START
191  if (copy_type == DEEP_COPY)
192  m_impl->img = img.clone();
193  else
194  m_impl->img = img;
195  MRPT_END
196 #endif
197 }
198 
199 CImage::CImage(const CImage& img, copy_type_t copy_type)
200  :
201 #if MRPT_HAS_OPENCV
202  CImage(img.m_impl->img, copy_type)
203 #else
204  CImage()
205 #endif
206 {
207 }
208 
210 {
211 #if MRPT_HAS_OPENCV
212  CImage ret(*this);
213  ret.m_impl->img = m_impl->img.clone();
214  return ret;
215 #else
216  THROW_EXCEPTION("Operation not supported: build MRPT against OpenCV!");
217 #endif
218 }
219 
220 void CImage::asCvMat(cv::Mat& out_img, copy_type_t copy_type) const
221 {
222 #if MRPT_HAS_OPENCV
223  if (copy_type == DEEP_COPY)
224  out_img = m_impl->img.clone();
225  else
226  out_img = m_impl->img;
227 #endif
228 }
229 
231 {
232 #if MRPT_HAS_OPENCV
234  return m_impl->img;
235 #else
236  THROW_EXCEPTION("Operation not supported: build MRPT against OpenCV!");
237 #endif
238 }
239 
240 const cv::Mat& CImage::asCvMatRef() const
241 {
242 #if MRPT_HAS_OPENCV
244  return m_impl->img;
245 #else
246  THROW_EXCEPTION("Operation not supported: build MRPT against OpenCV!");
247 #endif
248 }
249 
251  std::size_t width, std::size_t height, TImageChannels nChannels,
252  PixelDepth depth)
253 {
254  MRPT_START
255 
256 #if MRPT_HAS_OPENCV
257  // Dont call makeSureImageIsLoaded() here,
258  // since it will throw if resize() is called from a ctor, where it's
259  // legit for the img to be uninitialized.
260 
261  // If we're resizing to exactly the current size, do nothing:
262  if (static_cast<unsigned>(m_impl->img.cols) == width &&
263  static_cast<unsigned>(m_impl->img.rows) == height &&
264  m_impl->img.channels() == nChannels &&
265  m_impl->img.depth() == static_cast<int>(pixelDepth2CvDepth(depth)))
266  {
267  // Nothing to do:
268  return;
269  }
270 
271 #if IMAGE_ALLOC_PERFLOG
272  const std::string sLog = mrpt::format("cvCreateImage %ux%u", width, height);
273  alloc_tims.enter(sLog.c_str());
274 #endif
275 
276  static_assert(
277  pixelDepth2CvDepth<int>(PixelDepth::D8U) + CV_8UC(3) == CV_8UC3);
278 
279  m_impl->img = cv::Mat(
280  static_cast<int>(height), static_cast<int>(width),
281  pixelDepth2CvDepth<int>(depth) + ((nChannels - 1) << CV_CN_SHIFT));
282 
283 #if IMAGE_ALLOC_PERFLOG
284  alloc_tims.leave(sLog.c_str());
285 #endif
286 
287 #else
288  THROW_EXCEPTION("The MRPT has been compiled with MRPT_HAS_OPENCV=0 !");
289 #endif
290  MRPT_END
291 }
292 
294 {
295  MRPT_START
296 #if MRPT_HAS_OPENCV
297  return cvDepth2PixelDepth(m_impl->img.depth());
298 #else
299  THROW_EXCEPTION("The MRPT has been compiled with MRPT_HAS_OPENCV=0 !");
300 #endif
301  MRPT_END
302 }
303 
304 bool CImage::loadFromFile(const std::string& fileName, int isColor)
305 {
306  MRPT_START
307 
308 #if MRPT_HAS_OPENCV
309  m_imgIsExternalStorage = false;
310 #ifdef HAVE_OPENCV_IMGCODECS
311  MRPT_TODO("Port to cv::imdecode()?");
312  MRPT_TODO("add flag to reuse current img buffer");
313 
314  m_impl->img = cv::imread(fileName, static_cast<cv::ImreadModes>(isColor));
315 #else
316  IplImage* newImg = cvLoadImage(fileName.c_str(), isColor);
317  if (!newImg) return false;
318  m_impl->img = cv::cvarrToMat(newImg);
319 #endif
320  if (m_impl->img.empty()) return false;
321 
322  return true;
323 #else
324  THROW_EXCEPTION("The MRPT has been compiled with MRPT_HAS_OPENCV=0 !");
325 #endif
326  MRPT_END
327 }
328 
329 bool CImage::saveToFile(const std::string& fileName, int jpeg_quality) const
330 {
331  MRPT_START
332 #if MRPT_HAS_OPENCV
333  makeSureImageIsLoaded(); // For delayed loaded images stored externally
334  ASSERT_(!m_impl->img.empty());
335 
336 #ifdef HAVE_OPENCV_IMGCODECS
337  const std::vector<int> params = {cv::IMWRITE_JPEG_QUALITY, jpeg_quality};
338  return cv::imwrite(fileName, m_impl->img, params);
339 #else
340  int p[3] = {CV_IMWRITE_JPEG_QUALITY, jpeg_quality, 0};
341  _IplImage ipl = m_impl->img;
342  return (0 != cvSaveImage(fileName.c_str(), &ipl, p));
343 #endif
344 #else
345  THROW_EXCEPTION("The MRPT has been compiled with MRPT_HAS_OPENCV=0 !");
346 #endif
347  MRPT_END
348 }
349 
351 {
352  MRPT_START
353 #if MRPT_HAS_OPENCV
354  ASSERT_(iplImage != nullptr);
355  clear();
356  m_impl->img =
357  cv::cvarrToMat(iplImage, c == DEEP_COPY ? true : false /*copyData*/);
358 #else
359  THROW_EXCEPTION("The MRPT has been compiled with MRPT_HAS_OPENCV=0 !");
360 #endif
361  MRPT_END
362 }
363 
365  unsigned int width, unsigned int height, bool color,
366  unsigned char* rawpixels, bool swapRedBlue)
367 {
368  MRPT_START
369 
370 #if MRPT_HAS_OPENCV
371  resize(width, height, color ? CH_RGB : CH_GRAY);
372  m_imgIsExternalStorage = false;
373 
374  auto* imgData = m_impl->img.data;
375  const auto imgWidthStep = m_impl->img.step[0];
376 
377  if (color && swapRedBlue)
378  {
379  // Do copy & swap at once:
380  unsigned char* ptr_src = rawpixels;
381  auto* ptr_dest = imgData;
382  const int bytes_per_row_out = imgWidthStep;
383 
384  for (int h = height; h--;)
385  {
386  for (unsigned int i = 0; i < width;
387  i++, ptr_src += 3, ptr_dest += 3)
388  {
389  unsigned char t0 = ptr_src[0], t1 = ptr_src[1], t2 = ptr_src[2];
390  ptr_dest[2] = t0;
391  ptr_dest[1] = t1;
392  ptr_dest[0] = t2;
393  }
394  ptr_dest += bytes_per_row_out - width * 3;
395  }
396  }
397  else
398  {
399  if (imgWidthStep ==
400  static_cast<size_t>(m_impl->img.cols * m_impl->img.channels()))
401  {
402  // Copy the image data:
403  memcpy(imgData, rawpixels, m_impl->img.dataend - m_impl->img.data);
404  }
405  else
406  {
407  // Copy the image row by row:
408  unsigned char* ptr_src = rawpixels;
409  auto* ptr_dest = imgData;
410  int bytes_per_row = width * (color ? 3 : 1);
411  int bytes_per_row_out = imgWidthStep;
412  for (unsigned int y = 0; y < height; y++)
413  {
414  memcpy(ptr_dest, ptr_src, bytes_per_row);
415  ptr_src += bytes_per_row;
416  ptr_dest += bytes_per_row_out;
417  }
418  }
419  }
420 #else
421  THROW_EXCEPTION("The MRPT has been compiled with MRPT_HAS_OPENCV=0 !");
422 #endif
423  MRPT_END
424 }
425 
426 unsigned char* CImage::operator()(
427  unsigned int ucol, unsigned int urow, unsigned int uchannel) const
428 {
429 #if MRPT_HAS_OPENCV
430 
431 #if defined(_DEBUG) || (MRPT_ALWAYS_CHECKS_DEBUG)
432  MRPT_START
433 #endif
434 
435  makeSureImageIsLoaded(); // For delayed loaded images stored externally
436  const auto col = static_cast<int>(ucol);
437  const auto row = static_cast<int>(urow);
438  const auto channel = static_cast<int>(uchannel);
439 
440 #if defined(_DEBUG) || (MRPT_ALWAYS_CHECKS_DEBUG)
441  ASSERT_(m_impl && !m_impl->img.empty());
442  if (row >= m_impl->img.rows || col >= m_impl->img.cols ||
443  channel >= m_impl->img.channels())
444  {
446  "Pixel coordinates/channel out of bounds: row=%u/%u col=%u/%u "
447  "chan=%u/%u",
448  row, m_impl->img.rows, col, m_impl->img.cols, channel,
449  m_impl->img.channels()));
450  }
451 #endif
452  auto p =
453  (&m_impl->img.at<uint8_t>(row, m_impl->img.channels() * col)) + channel;
454  return const_cast<unsigned char*>(p);
455 #if defined(_DEBUG) || (MRPT_ALWAYS_CHECKS_DEBUG)
456  MRPT_END
457 #endif
458 
459 #else
460  THROW_EXCEPTION("MRPT was compiled without OpenCV");
461 #endif
462 }
463 
464 uint8_t* CImage::internal_get(int col, int row, uint8_t channel) const
465 {
466 #if MRPT_HAS_OPENCV
467  makeSureImageIsLoaded(); // For delayed loaded images stored externally
468  auto p =
469  (&m_impl->img.at<uint8_t>(row, m_impl->img.channels() * col)) + channel;
470  return const_cast<uint8_t*>(p);
471 #else
472  return nullptr;
473 #endif
474 }
475 
477  unsigned int col, unsigned int row, uint8_t channel) const
478 {
479  return internal_get(col, row, channel);
480 }
481 
483 {
484 #if !MRPT_HAS_OPENCV
485  return 100;
486 #else
487  return 9;
488 #endif
489 }
491 {
492 #if !MRPT_HAS_OPENCV
495 // Nothing else to serialize!
496 #else
497 
498  // Added in version 6: possibility of being stored offline:
500 
502  {
503  out << m_externalFile;
504  return;
505  }
506  // Normal image loaded in memory:
507  ASSERT_(m_impl);
508 
509  const bool hasColor = m_impl->img.empty() ? false : isColor();
510 
511  out << hasColor;
512 
513  // Version >2: Color->JPEG, GrayScale->BYTE's array!
514  const int32_t width = m_impl->img.cols;
515  const int32_t height = m_impl->img.rows;
516  if (!hasColor)
517  {
518  // GRAY-SCALE: Raw bytes:
519  // Version 3: ZIP compression!
520  // Version 4: Skip zip if the image size <= 16Kb
521  int32_t origin = 0; // not used mrpt v1.9.9
522  uint32_t imageSize = height * m_impl->img.step[0];
523  // Version 10: depth
524  int32_t depth = m_impl->img.depth();
525 
526  out << width << height << origin << imageSize
527  << int32_t(cvDepth2PixelDepth(depth));
528 
529  // Version 5: Use CImage::DISABLE_ZIP_COMPRESSION
530  // Dec 2019: Remove this feature since it's not worth.
531  // We still spend 1 byte for this constant bool just not to
532  // bump the serialization number.
533  bool imageStoredAsZip = false;
534 
535  out << imageStoredAsZip;
536 
537  if (imageSize > 0 && m_impl->img.data != nullptr)
538  out.WriteBuffer(m_impl->img.data, imageSize);
539  }
540  else
541  {
542  // COLOR: High quality JPEG image
543 
544  // v7: If size is 0xN or Nx0, don't call
545  // "saveToStreamAsJPEG"!!
546 
547  // v8: If DISABLE_JPEG_COMPRESSION
549  {
550  // normal behavior: compress images:
551  out << width << height;
552 
553  if (width >= 1 && height >= 1)
554  {
555  // Save to temporary memory stream:
558 
559  const auto nBytes =
560  static_cast<uint32_t>(aux.getTotalBytesCount());
561 
562  out << nBytes;
563  out.WriteBuffer(aux.getRawBufferData(), nBytes);
564  }
565  }
566  else
567  { // (New in v8)
568  // Don't JPEG-compress behavior:
569  // Use negative image sizes to signal this behavior:
570  const int32_t neg_width = -width;
571  const int32_t neg_height = -height;
572 
573  out << neg_width << neg_height;
574 
575  // Dump raw image data:
576  const auto bytes_per_row = width * 3;
577 
578  out.WriteBuffer(m_impl->img.data, bytes_per_row * height);
579  }
580  }
581 
582 #endif
583 }
584 
586 {
587 #if !MRPT_HAS_OPENCV
588  if (version == 100)
589  {
592  in >> m_externalFile;
593  else
594  {
596  "[CImage] Cannot deserialize image since MRPT has been "
597  "compiled without OpenCV");
598  }
599  }
600 #else
601  // First, free current image.
602  clear();
603 
604  switch (version)
605  {
606  case 100: // Saved from an MRPT build without OpenCV:
607  {
610  }
611  break;
612  case 0:
613  {
614  uint32_t width, height, nChannels, imgLength;
615  uint8_t originTopLeft;
616 
617  in >> width >> height >> nChannels >> originTopLeft >> imgLength;
618 
619  resize(width, height, static_cast<TImageChannels>(nChannels));
620  in.ReadBuffer(m_impl->img.data, imgLength);
621  }
622  break;
623  case 1:
624  {
625  // Version 1: High quality JPEG image
626  uint32_t nBytes;
627  in >> nBytes;
628  std::vector<uint8_t> buf(nBytes);
629  in.ReadBuffer(buf.data(), nBytes);
630 
632  aux.assignMemoryNotOwn(buf.data(), buf.size());
633  aux.Seek(0);
635  }
636  break;
637  case 2:
638  case 3:
639  case 4:
640  case 5:
641  case 6:
642  case 7:
643  case 8:
644  case 9:
645  {
646  // Version 6: m_imgIsExternalStorage ??
647  if (version >= 6)
649  else
650  m_imgIsExternalStorage = false;
651 
653  {
654  // Just the file name:
655  in >> m_externalFile;
656  }
657  else
658  { // Normal, the whole image data:
659 
660  // Version 2: Color->JPEG, GrayScale->BYTE's array!
661  uint8_t hasColor;
662  in >> hasColor;
663  if (!hasColor)
664  {
665  // GRAY SCALE:
666  int32_t width, height, origin, imageSize;
667  in >> width >> height >> origin >> imageSize;
669  if (version >= 9)
670  {
671  int32_t tempdepth;
672  in >> tempdepth;
673  depth = PixelDepth(tempdepth);
674  }
675  resize(
676  static_cast<uint32_t>(width),
677  static_cast<uint32_t>(height), CH_GRAY, depth);
679  static_cast<uint32_t>(imageSize),
680  static_cast<uint32_t>(height) * m_impl->img.step[0]);
681 
682  if (version == 2)
683  {
684  // RAW BYTES:
685  in.ReadBuffer(m_impl->img.data, imageSize);
686  }
687  else
688  {
689  // Version 3: ZIP compression!
690  bool imageIsZIP = true;
691 
692  // Version 4: Skip zip if the image size <= 16Kb
693  // Version 5: Use CImage::DISABLE_ZIP_COMPRESSION
694  if (version == 4 && imageSize <= 16 * 1024)
695  imageIsZIP = false;
696 
697  if (version >= 5)
698  {
699  // It is stored int the stream:
700  in >> imageIsZIP;
701  }
702 
703  if (imageIsZIP)
704  {
705  uint32_t zipDataLen;
706  in >> zipDataLen;
708  "ZIP image deserialization not supported "
709  "anymore");
710  }
711  else
712  {
713  // Raw bytes:
714  if (imageSize)
715  in.ReadBuffer(m_impl->img.data, imageSize);
716  }
717  }
718  }
719  else
720  {
721  bool loadJPEG = true;
722 
723  if (version >= 7)
724  {
725  int32_t width, height;
726  in >> width >> height;
727 
728  if (width >= 1 && height >= 1)
729  {
730  loadJPEG = true;
731  }
732  else
733  {
734  loadJPEG = false;
735 
736  if (width < 0 && height < 0)
737  {
738  // v8: raw image:
739  const int32_t real_w = -width;
740  const int32_t real_h = -height;
741 
742  resize(real_w, real_h, CH_RGB);
743 
744  auto& img = m_impl->img;
745  const size_t bytes_per_row = img.cols * 3;
746  for (int y = 0; y < img.rows; y++)
747  {
748  const size_t nRead = in.ReadBuffer(
749  img.ptr<void>(y), bytes_per_row);
750  if (nRead != bytes_per_row)
752  "Error: Truncated data stream "
753  "while parsing raw image?");
754  }
755  }
756  else
757  {
758  // it's a 0xN or Nx0 image: just resize and
759  // load nothing:
760  resize(width, height, CH_RGB);
761  }
762  }
763  }
764 
765  // COLOR IMAGE: JPEG
766  if (loadJPEG)
767  {
768  uint32_t nBytes;
769  in >> nBytes;
770 
771  std::vector<uint8_t> buf(nBytes);
772  in.ReadBuffer(buf.data(), nBytes);
773 
775  aux.assignMemoryNotOwn(buf.data(), buf.size());
776  aux.Seek(0);
777 
779  }
780  }
781  }
782  }
783  break;
784  default:
786  };
787 #endif
788 }
789 
790 /*---------------------------------------------------------------
791 Implements the writing to a mxArray for Matlab
792 ---------------------------------------------------------------*/
793 #if MRPT_HAS_MATLAB
794 // Add to implement mexplus::from template specialization
796 #endif
797 
799 {
800 #if MRPT_HAS_MATLAB
801  return mexplus::from(this->asCvMatRef());
802 #else
803  THROW_EXCEPTION("MRPT built without MATLAB/Mex support");
804 #endif
805 }
806 
808 {
809 #if MRPT_HAS_OPENCV
810  makeSureImageIsLoaded(); // For delayed loaded images stored externally
811  s.x = m_impl->img.cols;
812  s.y = m_impl->img.rows;
813 #else
814  THROW_EXCEPTION("MRPT built without OpenCV support");
815 #endif
816 }
817 
818 size_t CImage::getWidth() const
819 {
820 #if MRPT_HAS_OPENCV
822  return m_impl->img.cols;
823 #else
824  return 0;
825 #endif
826 }
827 
828 std::string CImage::getChannelsOrder() const
829 {
830 #if MRPT_HAS_OPENCV
831  makeSureImageIsLoaded(); // For delayed loaded images stored externally
832 
833  // modern opencv versions used these fixed order (see opencv
834  // core/src/array.cpp)
835  const int chCount = m_impl->img.channels();
836  ASSERT_ABOVEEQ_(chCount, 1);
837  ASSERT_BELOWEQ_(chCount, 4);
838  const std::array<const char*, 4> orderNames = {"GRAY", "", "BGR", "BGRA"};
839  return std::string(orderNames.at(chCount - 1));
840 #else
841  THROW_EXCEPTION("MRPT built without OpenCV support");
842 #endif
843 }
844 
845 size_t CImage::getRowStride() const
846 {
847 #if MRPT_HAS_OPENCV
848  makeSureImageIsLoaded(); // For delayed loaded images stored externally
849  return m_impl->img.step[0];
850 #else
851  THROW_EXCEPTION("MRPT built without OpenCV support");
852 #endif
853 }
854 
855 size_t CImage::getHeight() const
856 {
857 #if MRPT_HAS_OPENCV
859  return m_impl->img.rows;
860 #else
861  return 0;
862 #endif
863 }
864 
865 bool CImage::isColor() const
866 {
867 #if MRPT_HAS_OPENCV
868  makeSureImageIsLoaded(); // For delayed loaded images stored externally
869  return m_impl->img.channels() == 3;
870 #else
871  THROW_EXCEPTION("MRPT built without OpenCV support");
872 #endif
873 }
874 
875 bool CImage::isEmpty() const
876 {
877 #if MRPT_HAS_OPENCV
878  return m_imgIsExternalStorage || m_impl->img.empty();
879 #else
880  THROW_EXCEPTION("MRPT built without OpenCV support");
881 #endif
882 }
883 
885 {
886 #if MRPT_HAS_OPENCV
887  makeSureImageIsLoaded(); // For delayed loaded images stored externally
888  return static_cast<TImageChannels>(m_impl->img.channels());
889 #else
890  THROW_EXCEPTION("MRPT built without OpenCV support");
891 #endif
892 }
893 
895 {
896  return true; // As of mrpt v1.9.9
897 }
898 
900  unsigned int col, unsigned int row, unsigned int channel) const
901 {
902  makeSureImageIsLoaded(); // For delayed loaded images stored externally
903  // [0,255]->[0,1]
904  return (*(*this)(col, row, channel)) / 255.0f;
905 }
906 
907 float CImage::getAsFloat(unsigned int col, unsigned int row) const
908 {
909  // Is a RGB image??
910  if (isColor())
911  {
912  // Luminance: Y = 0.3R + 0.59G + 0.11B
913  unsigned char* pixels = (*this)(col, row, 0);
914  return (pixels[0] * 0.3f + pixels[1] * 0.59f + pixels[2] * 0.11f) /
915  255.0f;
916  }
917  else
918  {
919  // [0,255]->[0,1]
920  return (*(*this)(col, row, 0 /* Channel 0:Gray level */)) / 255.0f;
921  }
922 }
923 
924 /*---------------------------------------------------------------
925  getMaxAsFloat
926 ---------------------------------------------------------------*/
928 {
929  int x, y, cx = getWidth(), cy = getHeight();
930 
931  float maxPixel = 0;
932 
933  for (x = 0; x < cx; x++)
934  for (y = 0; y < cy; y++) maxPixel = max(maxPixel, getAsFloat(x, y));
935 
936  return maxPixel;
937 }
938 
940 {
941  CImage ret;
942  grayscale(ret);
943  return ret;
944 }
945 
946 // Auxiliary function for both ::grayscale() and ::grayscaleInPlace()
947 #if MRPT_HAS_OPENCV
948 static bool my_img_to_grayscale(const cv::Mat& src, cv::Mat& dest)
949 {
950  if (dest.size() != src.size() || dest.type() != src.type())
951  dest = cv::Mat(src.rows, src.cols, CV_8UC1);
952 
953  // If possible, use SSE optimized version:
954 #if MRPT_ARCH_INTEL_COMPATIBLE
955  if ((src.step[0] & 0x0f) == 0 && (dest.step[0] & 0x0f) == 0 &&
957  {
959  src.ptr<uint8_t>(), dest.ptr<uint8_t>(), src.cols, src.rows,
960  src.step[0], dest.step[0]);
961  return true;
962  }
963 #endif
964 
965  // OpenCV Method:
966  cv::cvtColor(src, dest, CV_BGR2GRAY);
967  return false;
968 }
969 #endif
970 
971 bool CImage::grayscale(CImage& ret) const
972 {
973 #if MRPT_HAS_OPENCV
974  // The image is already grayscale??
975  makeSureImageIsLoaded(); // For delayed loaded images stored externally
976  if (m_impl->img.channels() == 1)
977  {
978  ret = *this; // shallow copy
979  return true;
980  }
981  else
982  {
983  // Convert to a single luminance channel image
984  cv::Mat src = m_impl->img;
985  // Detect in-place op and make deep copy:
986  if (src.data == ret.m_impl->img.data) src = src.clone();
987 
988  return my_img_to_grayscale(src, ret.m_impl->img);
989  }
990 #else
991  THROW_EXCEPTION("Operation not supported: build MRPT against OpenCV!");
992 #endif
993 }
994 
996 {
997 #if MRPT_HAS_OPENCV
998  makeSureImageIsLoaded(); // For delayed loaded images stored externally
999  // Get this image size:
1000  auto& img = m_impl->img;
1001  const int w = img.cols, h = img.rows;
1002 
1003  // Create target image:
1004  out.resize(w >> 1, h >> 1, getChannelCount());
1005  auto& img_out = out.m_impl->img;
1006 
1007 // If possible, use SSE optimized version:
1008 #if MRPT_ARCH_INTEL_COMPATIBLE
1009  if (img.channels() == 3 && interp == IMG_INTERP_NN &&
1011  {
1013  img.data, img_out.data, w, h, img.step[0], img_out.step[0]);
1014  return true;
1015  }
1016 
1017  if (img.channels() == 1 && mrpt::cpu::supports(mrpt::cpu::feature::SSE2))
1018  {
1019  if (interp == IMG_INTERP_NN)
1020  {
1022  img.data, img_out.data, w, h, img.step[0], img_out.step[0]);
1023  return true;
1024  }
1025  else if (interp == IMG_INTERP_LINEAR)
1026  {
1028  img.data, img_out.data, w, h, img.step[0], img_out.step[0]);
1029  return true;
1030  }
1031  }
1032 #endif
1033 
1034  // Fall back to slow method:
1035  cv::resize(
1036  img, img_out, img_out.size(), 0, 0, interpolationMethod2Cv(interp));
1037  return false;
1038 #else
1039  THROW_EXCEPTION("Operation not supported: build MRPT against OpenCV!");
1040 #endif
1041 }
1042 
1044 {
1045  out = *this;
1046  const TImageSize siz = this->getSize();
1047  out.scaleImage(out, siz.x * 2, siz.y * 2, interp);
1048 }
1049 
1051  unsigned int width, unsigned int height, unsigned int bytesPerRow,
1052  unsigned char* red, unsigned char* green, unsigned char* blue)
1053 {
1054 #if MRPT_HAS_OPENCV
1055  MRPT_START
1056 
1057  resize(width, height, CH_RGB, PixelDepth::D8U);
1058 
1059  // Copy the image data:
1060  for (unsigned int y = 0; y < height; y++)
1061  {
1062  // The target pixels:
1063  auto* dest = m_impl->img.ptr<uint8_t>(y);
1064 
1065  // Source channels:
1066  unsigned char* srcR = red + bytesPerRow * y;
1067  unsigned char* srcG = green + bytesPerRow * y;
1068  unsigned char* srcB = blue + bytesPerRow * y;
1069 
1070  for (unsigned int x = 0; x < width; x++)
1071  {
1072  *(dest++) = *(srcB++);
1073  *(dest++) = *(srcG++);
1074  *(dest++) = *(srcR++);
1075  } // end of x
1076  } // end of y
1077 
1078  MRPT_END
1079 #endif
1080 }
1081 
1082 void CImage::setPixel(int x, int y, size_t color)
1083 {
1084 #if MRPT_HAS_OPENCV
1085 
1086 #if defined(_DEBUG) || (MRPT_ALWAYS_CHECKS_DEBUG)
1087  MRPT_START
1088 #endif
1089 
1090  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1091  auto& img = m_impl->img;
1092 
1094 
1095  if (x >= 0 && y >= 0 && y < img.rows && x < img.cols)
1096  {
1097  // The pixel coordinates is valid:
1098  if (img.channels() == 1)
1099  {
1100  img.ptr<uint8_t>(y)[x] = static_cast<uint8_t>(color);
1101  }
1102  else
1103  {
1104 #if defined(_DEBUG) || (MRPT_ALWAYS_CHECKS_DEBUG)
1105  ASSERT_(img.channels() == 3);
1106 #endif
1107  auto* dest = &img.ptr<uint8_t>(y)[3 * x];
1108  const auto* src = reinterpret_cast<uint8_t*>(&color);
1109  // Copy the color:
1110  *dest++ = *src++; // R
1111  *dest++ = *src++; // G
1112  *dest++ = *src++; // B
1113  }
1114  }
1115 
1116 #if defined(_DEBUG) || (MRPT_ALWAYS_CHECKS_DEBUG)
1117  MRPT_END
1118 #endif
1119 
1120 #endif
1121 }
1122 
1124  int x0, int y0, int x1, int y1, const mrpt::img::TColor color,
1125  unsigned int width, [[maybe_unused]] TPenStyle penStyle)
1126 {
1127 #if MRPT_HAS_OPENCV
1128  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1129 
1130  cv::line(
1131  m_impl->img, cv::Point(x0, y0), cv::Point(x1, y1),
1132  CV_RGB(color.R, color.G, color.B), static_cast<int>(width));
1133 #endif
1134 }
1135 
1137  int x, int y, int radius, const mrpt::img::TColor& color,
1138  unsigned int width)
1139 {
1140 #if MRPT_HAS_OPENCV
1141  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1142  cv::circle(
1143  m_impl->img, cv::Point(x, y), radius, CV_RGB(color.R, color.G, color.B),
1144  static_cast<int>(width));
1145 #endif
1146 }
1147 
1148 void CImage::drawImage(int x, int y, const mrpt::img::CImage& img)
1149 {
1150 #if MRPT_HAS_OPENCV
1152  img.makeSureImageIsLoaded();
1153 
1154  cv::Rect roi(cv::Point(x, y), cv::Size(img.getWidth(), img.getHeight()));
1155  cv::Mat dest = m_impl->img(roi);
1156  img.m_impl->img.copyTo(dest);
1157 #endif
1158 }
1159 
1161  const CImage& patch, const unsigned int col_, const unsigned int row_)
1162 {
1163 #if MRPT_HAS_OPENCV
1164  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1165  const auto& src = m_impl->img;
1166  auto& dest = patch.m_impl->img;
1167 
1168  src(cv::Rect(col_, row_, dest.cols, dest.rows)).copyTo(dest);
1169 #endif
1170 }
1171 
1173  CImage& patch, const unsigned int col_, const unsigned int row_,
1174  const unsigned int col_num, const unsigned int row_num) const
1175 {
1176 #if MRPT_HAS_OPENCV
1177  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1178  const auto& src = m_impl->img;
1179  auto& dest = patch.m_impl->img;
1180 
1181  src(cv::Rect(col_, row_, col_num, row_num)).copyTo(dest);
1182 #endif
1183 }
1184 
1186  const CImage& img2, int width_init, int height_init) const
1187 {
1188 #if MRPT_HAS_OPENCV
1189  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1190 
1191  if ((img2.getWidth() + width_init > getWidth()) |
1192  (img2.getHeight() + height_init > getHeight()))
1193  THROW_EXCEPTION("Correlation Error!, image to correlate out of bounds");
1194 
1195  float x1, x2;
1196  float syy = 0.0f, sxy = 0.0f, sxx = 0.0f, m1 = 0.0f, m2 = 0.0f,
1197  n = (float)(img2.getHeight() * img2.getWidth());
1198 
1199  // find the means
1200  for (size_t i = 0; i < img2.getHeight(); i++)
1201  {
1202  for (size_t j = 0; j < img2.getWidth(); j++)
1203  {
1204  m1 += *(*this)(
1205  j + width_init,
1206  i + height_init); //(double)(ipl1->imageData[i*ipl1->widthStep
1207  //+ j ]);
1208  m2 += *img2(
1209  j, i); //(double)(ipl2->imageData[i*ipl2->widthStep + j ]);
1210  } //[ row * ipl->widthStep + col * ipl->nChannels + channel ];
1211  }
1212  m1 /= n;
1213  m2 /= n;
1214 
1215  for (size_t i = 0; i < img2.getHeight(); i++)
1216  {
1217  for (size_t j = 0; j < img2.getWidth(); j++)
1218  {
1219  x1 = *(*this)(j + width_init, i + height_init) -
1220  m1; //(double)(ipl1->imageData[i*ipl1->widthStep
1221  //+ j]) - m1;
1222  x2 = *img2(j, i) - m2; //(double)(ipl2->imageData[i*ipl2->widthStep
1223  //+ j]) - m2;
1224  sxx += x1 * x1;
1225  syy += x2 * x2;
1226  sxy += x1 * x2;
1227  }
1228  }
1229 
1230  return sxy / sqrt(sxx * syy);
1231 #else
1232  return 0;
1233 #endif
1234 }
1235 
1237 {
1238 #if MRPT_HAS_OPENCV
1239  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1240  cv::normalize(m_impl->img, m_impl->img, 255, 0, cv::NORM_MINMAX);
1241 #endif
1242 }
1243 
1245  CMatrixFloat& outMatrix, bool doResize, int x_min, int y_min, int x_max,
1246  int y_max, bool normalize_01) const
1247 {
1248 #if MRPT_HAS_OPENCV
1249  MRPT_START
1250  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1251 
1252  const auto& img = m_impl->img;
1253 
1254  // Set sizes:
1255  if (x_max == -1) x_max = img.cols - 1;
1256  if (y_max == -1) y_max = img.rows - 1;
1257 
1258  ASSERT_(x_min >= 0 && x_min < img.cols && x_min < x_max);
1259  ASSERT_(y_min >= 0 && y_min < img.rows && y_min < y_max);
1260 
1261  int lx = (x_max - x_min + 1);
1262  int ly = (y_max - y_min + 1);
1263 
1264  if (doResize || outMatrix.rows() < ly || outMatrix.cols() < lx)
1265  outMatrix.setSize(y_max - y_min + 1, x_max - x_min + 1);
1266 
1267  const bool is_color = isColor();
1268 
1269  // Luminance: Y = 0.3R + 0.59G + 0.11B
1270  for (int y = 0; y < ly; y++)
1271  {
1272  const uint8_t* pixels = ptr<uint8_t>(x_min, y_min + y);
1273  for (int x = 0; x < lx; x++)
1274  {
1275  float aux;
1276  if (is_color)
1277  {
1278  aux = *pixels++ * 0.3f;
1279  aux += *pixels++ * 0.59f;
1280  aux += *pixels++ * 0.11f;
1281  }
1282  else
1283  {
1284  aux = (*pixels++);
1285  }
1286  outMatrix.coeffRef(y, x) = aux;
1287  }
1288  }
1289  if (normalize_01) outMatrix *= (1.0f / 255);
1290 
1291  MRPT_END
1292 #endif
1293 }
1294 
1296  CMatrix_u8& outMatrix, bool doResize, int x_min, int y_min, int x_max,
1297  int y_max) const
1298 {
1299 #if MRPT_HAS_OPENCV
1300  MRPT_START
1301  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1302 
1303  const auto& img = m_impl->img;
1304 
1305  // Set sizes:
1306  if (x_max == -1) x_max = img.cols - 1;
1307  if (y_max == -1) y_max = img.rows - 1;
1308 
1309  ASSERT_(x_min >= 0 && x_min < img.cols && x_min < x_max);
1310  ASSERT_(y_min >= 0 && y_min < img.rows && y_min < y_max);
1311 
1312  int lx = (x_max - x_min + 1);
1313  int ly = (y_max - y_min + 1);
1314 
1315  if (doResize || outMatrix.rows() < ly || outMatrix.cols() < lx)
1316  outMatrix.setSize(y_max - y_min + 1, x_max - x_min + 1);
1317 
1318  const bool is_color = isColor();
1319 
1320  // Luminance: Y = 0.3R + 0.59G + 0.11B
1321  for (int y = 0; y < ly; y++)
1322  {
1323  const uint8_t* pixels = ptr<uint8_t>(x_min, y_min + y);
1324  for (int x = 0; x < lx; x++)
1325  {
1326  if (is_color)
1327  {
1328  unsigned int aux = *pixels++ * 3000; // 0.3f;
1329  aux += *pixels++ * 5900; // 0.59f;
1330  aux += *pixels++ * 1100; // 0.11f;
1331  outMatrix.coeffRef(y, x) = static_cast<uint8_t>(aux / 1000);
1332  }
1333  else
1334  {
1335  outMatrix.coeffRef(y, x) = (*pixels++);
1336  }
1337  }
1338  }
1339 
1340  MRPT_END
1341 #endif
1342 }
1345  mrpt::math::CMatrixFloat& B, bool doResize, int x_min, int y_min, int x_max,
1346  int y_max) const
1347 {
1348 #if MRPT_HAS_OPENCV
1349  MRPT_START
1350 
1351  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1352  const auto& img = m_impl->img;
1353 
1354  // Set sizes:
1355  if (x_max == -1) x_max = img.cols - 1;
1356  if (y_max == -1) y_max = img.rows - 1;
1357 
1358  ASSERT_(x_min >= 0 && x_min < img.cols && x_min < x_max);
1359  ASSERT_(y_min >= 0 && y_min < img.rows && y_min < y_max);
1360 
1361  int lx = (x_max - x_min + 1);
1362  int ly = (y_max - y_min + 1);
1363 
1364  if (doResize || R.rows() < ly || R.cols() < lx) R.setSize(ly, lx);
1365  if (doResize || G.rows() < ly || G.cols() < lx) G.setSize(ly, lx);
1366  if (doResize || B.rows() < ly || B.cols() < lx) B.setSize(ly, lx);
1367 
1368  const bool is_color = isColor();
1369  for (int y = 0; y < ly; y++)
1370  {
1371  const uint8_t* pixels = ptr<uint8_t>(x_min, y_min + y);
1372  for (int x = 0; x < lx; x++)
1373  {
1374  if (is_color)
1375  {
1376  R.coeffRef(y, x) = u8tof(*pixels++);
1377  G.coeffRef(y, x) = u8tof(*pixels++);
1378  B.coeffRef(y, x) = u8tof(*pixels++);
1379  }
1380  else
1381  {
1382  R.coeffRef(y, x) = G.coeffRef(y, x) = B.coeffRef(y, x) =
1383  u8tof(*pixels++);
1384  }
1385  }
1386  }
1387 
1388  MRPT_END
1389 #endif
1390 }
1391 
1394  mrpt::math::CMatrix_u8& B, bool doResize, int x_min, int y_min, int x_max,
1395  int y_max) const
1396 {
1397 #if MRPT_HAS_OPENCV
1398  MRPT_START
1399 
1400  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1401  const auto& img = m_impl->img;
1402 
1403  // Set sizes:
1404  if (x_max == -1) x_max = img.cols - 1;
1405  if (y_max == -1) y_max = img.rows - 1;
1406 
1407  ASSERT_(x_min >= 0 && x_min < img.cols && x_min < x_max);
1408  ASSERT_(y_min >= 0 && y_min < img.rows && y_min < y_max);
1409 
1410  int lx = (x_max - x_min + 1);
1411  int ly = (y_max - y_min + 1);
1412 
1413  if (doResize || R.rows() < ly || R.cols() < lx) R.setSize(ly, lx);
1414  if (doResize || G.rows() < ly || G.cols() < lx) G.setSize(ly, lx);
1415  if (doResize || B.rows() < ly || B.cols() < lx) B.setSize(ly, lx);
1416 
1417  const bool is_color = isColor();
1418  for (int y = 0; y < ly; y++)
1419  {
1420  const uint8_t* pixels = ptr<uint8_t>(x_min, y_min + y);
1421  for (int x = 0; x < lx; x++)
1422  {
1423  if (is_color)
1424  {
1425  R.coeffRef(y, x) = *pixels++;
1426  G.coeffRef(y, x) = *pixels++;
1427  B.coeffRef(y, x) = *pixels++;
1428  }
1429  else
1430  {
1431  R.coeffRef(y, x) = G.coeffRef(y, x) = B.coeffRef(y, x) =
1432  *pixels++;
1433  }
1434  }
1435  }
1436 
1437  MRPT_END
1438 #endif
1439 }
1440 
1442  const CImage& in_img, CMatrixFloat& out_corr, int u_search_ini,
1443  int v_search_ini, int u_search_size, int v_search_size, float biasThisImg,
1444  float biasInImg) const
1445 {
1446  MRPT_START
1447 
1448  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1449 
1450  // Set limits:
1451  if (u_search_ini == -1) u_search_ini = 0;
1452  if (v_search_ini == -1) v_search_ini = 0;
1453  if (u_search_size == -1) u_search_size = static_cast<int>(getWidth());
1454  if (v_search_size == -1) v_search_size = static_cast<int>(getHeight());
1455 
1456  int u_search_end = u_search_ini + u_search_size - 1;
1457  int v_search_end = v_search_ini + v_search_size - 1;
1458 
1459  ASSERT_(u_search_end < static_cast<int>(getWidth()));
1460  ASSERT_(v_search_end < static_cast<int>(getHeight()));
1461 
1462  // Find smallest valid size:
1463  size_t x, y;
1464  size_t actual_lx =
1465  std::max(static_cast<size_t>(u_search_size), in_img.getWidth());
1466  size_t actual_ly =
1467  std::max(static_cast<size_t>(v_search_size), in_img.getHeight());
1468  size_t lx = mrpt::round2up<size_t>(actual_lx);
1469  size_t ly = mrpt::round2up<size_t>(actual_ly);
1470 
1471  CMatrixF i1(ly, lx), i2(ly, lx);
1472 
1473  // We fill the images with the bias, such as when we substract the bias
1474  // later on,
1475  // those pixels not really occupied by the image really becomes zero:
1476  i1.fill(biasInImg);
1477  i2.fill(biasThisImg);
1478 
1479  // Get as matrixes, padded with zeros up to power-of-two sizes:
1480  getAsMatrix(
1481  i2, false, u_search_ini, v_search_ini, u_search_ini + u_search_size - 1,
1482  v_search_ini + v_search_size - 1);
1483  in_img.getAsMatrix(i1, false);
1484 
1485  // Remove the bias now:
1486  i2 -= biasThisImg;
1487  i1 -= biasInImg;
1488 
1489  // FFT:
1490  CMatrixF I1_R, I1_I, I2_R, I2_I, ZEROS(ly, lx);
1491  math::dft2_complex(i1, ZEROS, I1_R, I1_I);
1492  math::dft2_complex(i2, ZEROS, I2_R, I2_I);
1493 
1494  // Compute the COMPLEX division of I2 by I1:
1495  for (y = 0; y < ly; y++)
1496  for (x = 0; x < lx; x++)
1497  {
1498  float r1 = I1_R(y, x);
1499  float r2 = I2_R(y, x);
1500 
1501  float ii1 = I1_I(y, x);
1502  float ii2 = I2_I(y, x);
1503 
1504  float den = square(r1) + square(ii1);
1505  I2_R(y, x) = (r1 * r2 + ii1 * ii2) / den;
1506  I2_I(y, x) = (ii2 * r1 - r2 * ii1) / den;
1507  }
1508 
1509  // IFFT:
1510  CMatrixF res_R, res_I;
1511  math::idft2_complex(I2_R, I2_I, res_R, res_I);
1512 
1513  out_corr.setSize(actual_ly, actual_lx);
1514  for (y = 0; y < actual_ly; y++)
1515  for (x = 0; x < actual_lx; x++)
1516  out_corr(y, x) = sqrt(square(res_R(y, x)) + square(res_I(y, x)));
1517 
1518  MRPT_END
1519 }
1520 
1522 {
1523 #if MRPT_HAS_OPENCV
1524  MRPT_START
1525 
1526  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1527  const auto& img = m_impl->img;
1528 
1529  // The size of the matrix:
1530  const auto matrix_lx = outMatrix.cols();
1531  const auto matrix_ly = outMatrix.rows();
1532 
1533  if (isColor())
1534  {
1535  // Luminance: Y = 0.3R + 0.59G + 0.11B
1536  for (CMatrixFloat::Index y = 0; y < matrix_ly; y++)
1537  {
1538  unsigned char* min_pixels = (*this)(0, y % img.rows, 0);
1539  unsigned char* max_pixels = min_pixels + img.cols * 3;
1540  unsigned char* pixels = min_pixels;
1541  float aux;
1542  for (CMatrixFloat::Index x = 0; x < matrix_lx; x++)
1543  {
1544  aux = *pixels++ * 0.30f;
1545  aux += *pixels++ * 0.59f;
1546  aux += *pixels++ * 0.11f;
1547  outMatrix(y, x) = aux;
1548  if (pixels >= max_pixels) pixels = min_pixels;
1549  }
1550  }
1551  }
1552  else
1553  {
1554  for (CMatrixFloat::Index y = 0; y < matrix_ly; y++)
1555  {
1556  unsigned char* min_pixels = (*this)(0, y % img.rows, 0);
1557  unsigned char* max_pixels = min_pixels + img.cols;
1558  unsigned char* pixels = min_pixels;
1559  for (CMatrixFloat::Index x = 0; x < matrix_lx; x++)
1560  {
1561  outMatrix(y, x) = *pixels++;
1562  if (pixels >= max_pixels) pixels = min_pixels;
1563  }
1564  }
1565  }
1566 
1567  MRPT_END
1568 #endif
1569 }
1570 
1572 {
1573  // Reset to defaults:
1574  *this = CImage();
1575 }
1576 
1577 void CImage::setExternalStorage(const std::string& fileName) noexcept
1578 {
1579  clear();
1580  m_externalFile = fileName;
1581  m_imgIsExternalStorage = true;
1582 }
1583 
1584 void CImage::unload() const noexcept
1585 {
1586 #if MRPT_HAS_OPENCV
1587  if (m_imgIsExternalStorage) const_cast<cv::Mat&>(m_impl->img) = cv::Mat();
1588 #endif
1589 }
1590 
1592 {
1593 #if MRPT_HAS_OPENCV
1594  if (!m_impl->img.empty()) return; // OK, continue
1595 #endif
1596 
1598  {
1599  // Load the file:
1600  string wholeFile;
1602 
1603  const std::string tmpFile = m_externalFile;
1604 
1605  bool ret = const_cast<CImage*>(this)->loadFromFile(wholeFile);
1606 
1607  // These are removed by "loadFromFile", and that's good, just fix it
1608  // here and carry on.
1609  m_imgIsExternalStorage = true;
1610  m_externalFile = tmpFile;
1611 
1612  if (!ret)
1615  "Error loading externally-stored image from: %s",
1616  wholeFile.c_str());
1617  }
1618  else
1619  {
1621  "Trying to access uninitialized image in a non "
1622  "externally-stored "
1623  "image.");
1624  }
1625 }
1626 
1627 void CImage::getExternalStorageFileAbsolutePath(std::string& out_path) const
1628 {
1629  ASSERT_(m_externalFile.size() > 2);
1630 
1631  if (m_externalFile[0] == '/' ||
1632  (m_externalFile[1] == ':' &&
1633  (m_externalFile[2] == '\\' || m_externalFile[2] == '/')))
1634  {
1635  out_path = m_externalFile;
1636  }
1637  else
1638  {
1639  out_path = IMAGES_PATH_BASE;
1640 
1641  size_t N = IMAGES_PATH_BASE.size() - 1;
1642  if (IMAGES_PATH_BASE[N] != '/' && IMAGES_PATH_BASE[N] != '\\')
1643  out_path += "/";
1644 
1645  out_path += m_externalFile;
1646  }
1647 }
1648 
1650 {
1651 #if MRPT_HAS_OPENCV
1653  cv::flip(m_impl->img, m_impl->img, 0 /* x-axis */);
1654 #endif
1655 }
1656 
1658 {
1659 #if MRPT_HAS_OPENCV
1661  cv::flip(m_impl->img, m_impl->img, 1 /* y-axis */);
1662 #endif
1663 }
1664 
1666 {
1667 #if MRPT_HAS_OPENCV
1668  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1669  cv::cvtColor(m_impl->img, m_impl->img, cv::COLOR_RGB2BGR);
1670 #endif
1671 }
1672 
1673 void CImage::rectifyImageInPlace(void* mapX, void* mapY)
1674 {
1675 #if MRPT_HAS_OPENCV
1676  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1677 
1678  auto& srcImg = m_impl->img;
1679  cv::Mat outImg(srcImg.rows, srcImg.cols, srcImg.type());
1680 
1681  auto mapXm = static_cast<cv::Mat*>(mapX);
1682  auto mapYm = static_cast<cv::Mat*>(mapX);
1683 
1684  cv::remap(srcImg, outImg, *mapXm, *mapYm, cv::INTER_CUBIC);
1685 
1686  clear();
1687  srcImg = outImg;
1688 #endif
1689 }
1690 
1692  CImage& out_img, const mrpt::img::TCamera& cameraParams) const
1693 {
1694 #if MRPT_HAS_OPENCV
1695  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1696 
1697  ASSERTMSG_(
1698  out_img.m_impl->img.data != m_impl->img.data,
1699  "In-place undistort() not supported");
1700 
1701  auto& srcImg = const_cast<cv::Mat&>(m_impl->img);
1702  // This will avoid re-alloc if size already matches.
1703  out_img.resize(srcImg.cols, srcImg.rows, getChannelCount());
1704 
1705  const auto& intrMat = cameraParams.intrinsicParams;
1706  const auto& dist = cameraParams.dist;
1707 
1708  cv::Mat distM(1, dist.size(), CV_64F, const_cast<double*>(&dist[0]));
1709  cv::Mat inMat(3, 3, CV_64F);
1710 
1711  for (int i = 0; i < 3; i++)
1712  for (int j = 0; j < 3; j++) inMat.at<double>(i, j) = intrMat(i, j);
1713 
1714  cv::undistort(srcImg, out_img.m_impl->img, inMat, distM);
1715 #endif
1716 }
1717 
1718 void CImage::filterMedian(CImage& out_img, int W) const
1719 {
1720 #if MRPT_HAS_OPENCV
1721  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1722 
1723  auto srcImg = const_cast<cv::Mat&>(m_impl->img);
1724  if (this == &out_img)
1725  srcImg = srcImg.clone();
1726  else
1727  out_img.resize(srcImg.cols, srcImg.rows, getChannelCount());
1728 
1729  cv::medianBlur(srcImg, out_img.m_impl->img, W);
1730 #endif
1731 }
1732 
1733 void CImage::filterGaussian(CImage& out_img, int W, int H, double sigma) const
1734 {
1735 #if MRPT_HAS_OPENCV
1736  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1737  auto srcImg = const_cast<cv::Mat&>(m_impl->img);
1738  if (this == &out_img)
1739  srcImg = srcImg.clone();
1740  else
1741  out_img.resize(srcImg.cols, srcImg.rows, getChannelCount());
1742 
1743  cv::GaussianBlur(srcImg, out_img.m_impl->img, cv::Size(W, H), sigma);
1744 #endif
1745 }
1746 
1748  CImage& out_img, unsigned int width, unsigned int height,
1749  TInterpolationMethod interp) const
1750 {
1751 #if MRPT_HAS_OPENCV
1752  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1753 
1754  auto srcImg = m_impl->img;
1755  // Detect in-place operation and make a deep copy if needed:
1756  if (out_img.m_impl->img.data == srcImg.data) srcImg = srcImg.clone();
1757 
1758  // Already done?
1759  if (out_img.getWidth() == width && out_img.getHeight() == height)
1760  {
1761  out_img.m_impl->img = srcImg;
1762  return;
1763  }
1764  out_img.resize(width, height, getChannelCount());
1765 
1766  // Resize:
1767  cv::resize(
1768  srcImg, out_img.m_impl->img, out_img.m_impl->img.size(), 0, 0,
1769  interpolationMethod2Cv(interp));
1770 #endif
1771 }
1772 
1774  CImage& out_img, double ang, unsigned int cx, unsigned int cy,
1775  double scale) const
1776 {
1777 #if MRPT_HAS_OPENCV
1778  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1779 
1780  auto srcImg = m_impl->img;
1781  // Detect in-place operation and make a deep copy if needed:
1782  if (out_img.m_impl->img.data == srcImg.data) srcImg = srcImg.clone();
1783 
1784  out_img.resize(getWidth(), getHeight(), getChannelCount());
1785 
1786  // Based on the blog entry:
1787  // http://blog.weisu.org/2007/12/opencv-image-rotate-and-zoom-rotation.html
1788 
1789  // Apply rotation & scale:
1790  double m[2 * 3] = {scale * cos(ang), -scale * sin(ang), 1.0 * cx,
1791  scale * sin(ang), scale * cos(ang), 1.0 * cy};
1792  cv::Mat M(2, 3, CV_64F, m);
1793 
1794  double dx = (srcImg.cols - 1) * 0.5;
1795  double dy = (srcImg.rows - 1) * 0.5;
1796  m[2] -= m[0] * dx + m[1] * dy;
1797  m[5] -= m[3] * dx + m[4] * dy;
1798 
1799  cv::warpAffine(
1800  srcImg, out_img.m_impl->img, M, out_img.m_impl->img.size(),
1801  cv::INTER_LINEAR + cv::WARP_INVERSE_MAP, cv::BORDER_REPLICATE);
1802 #endif
1803 }
1804 
1806  std::vector<TPixelCoordf>& cornerCoords, unsigned int check_size_x,
1807  unsigned int check_size_y, unsigned int lines_width, unsigned int r)
1808 {
1809 #if MRPT_HAS_OPENCV
1810 
1811  if (cornerCoords.size() != check_size_x * check_size_y) return false;
1812 
1813  auto& img = m_impl->img;
1814 
1815  unsigned int x, y, i;
1816  cv::Point prev_pt = cvPoint(0, 0);
1817  const int line_max = 8;
1818  cv::Scalar line_colors[8];
1819 
1820  line_colors[0] = CV_RGB(255, 0, 0);
1821  line_colors[1] = CV_RGB(255, 128, 0);
1822  line_colors[2] = CV_RGB(255, 128, 0);
1823  line_colors[3] = CV_RGB(200, 200, 0);
1824  line_colors[4] = CV_RGB(0, 255, 0);
1825  line_colors[5] = CV_RGB(0, 200, 200);
1826  line_colors[6] = CV_RGB(0, 0, 255);
1827  line_colors[7] = CV_RGB(255, 0, 255);
1828 
1829  CCanvas::selectTextFont("10x20");
1830 
1831  for (y = 0, i = 0; y < check_size_y; y++)
1832  {
1833  const auto color = line_colors[y % line_max];
1834  for (x = 0; x < check_size_x; x++, i++)
1835  {
1836  cv::Point pt;
1837  pt.x = cvRound(cornerCoords[i].x);
1838  pt.y = cvRound(cornerCoords[i].y);
1839 
1840  if (i != 0) cv::line(img, prev_pt, pt, color, lines_width);
1841 
1842  cv::line(
1843  img, cvPoint(pt.x - r, pt.y - r), cvPoint(pt.x + r, pt.y + r),
1844  color, lines_width);
1845  cv::line(
1846  img, cvPoint(pt.x - r, pt.y + r), cvPoint(pt.x + r, pt.y - r),
1847  color, lines_width);
1848 
1849  if (r > 0) cv::circle(img, pt, r + 1, color);
1850  prev_pt = pt;
1851 
1852  // Text label with the corner index in the first and last
1853  // corners:
1854  if (i == 0 || i == cornerCoords.size() - 1)
1856  pt.x + 5, pt.y - 5, mrpt::format("%u", i),
1858  }
1859  }
1860 
1861  return true;
1862 #else
1863  return false;
1864 #endif
1865 }
1866 
1868 {
1869  CImage ret;
1870  colorImage(ret);
1871  return ret;
1872 }
1873 
1874 void CImage::colorImage(CImage& ret) const
1875 {
1876 #if MRPT_HAS_OPENCV
1877  if (this->isColor())
1878  {
1879  if (&ret != this) ret = *this;
1880  return;
1881  }
1882 
1883  auto srcImg = m_impl->img;
1884  // Detect in-place op. and make deep copy:
1885  if (srcImg.data == ret.m_impl->img.data) srcImg = srcImg.clone();
1886 
1887  ret.resize(getWidth(), getHeight(), CH_RGB);
1888 
1889  cv::cvtColor(srcImg, ret.m_impl->img, cv::COLOR_GRAY2BGR);
1890 #endif
1891 }
1892 
1893 void CImage::joinImagesHorz(const CImage& img1, const CImage& img2)
1894 {
1895 #if MRPT_HAS_OPENCV
1896  ASSERT_(img1.getHeight() == img2.getHeight());
1897 
1898  auto im1 = img1.m_impl->img, im2 = img2.m_impl->img, img = m_impl->img;
1899  ASSERT_(im1.type() == im2.type());
1900 
1901  this->resize(im1.cols + im2.cols, im1.rows, getChannelCount());
1902 
1903  im1.copyTo(img(cv::Rect(0, 0, im1.cols, im1.rows)));
1904  im2.copyTo(img(cv::Rect(im1.cols, 0, im2.cols, im2.rows)));
1905 #endif
1906 } // end
1907 
1908 void CImage::equalizeHist(CImage& out_img) const
1909 {
1910 #if MRPT_HAS_OPENCV
1911  // Convert to a single luminance channel image
1912  auto srcImg = m_impl->img;
1913  if (this != &out_img)
1914  out_img.resize(srcImg.cols, srcImg.rows, getChannelCount());
1915  auto outImg = out_img.m_impl->img;
1916 
1917  if (srcImg.channels() == 1)
1918  cv::equalizeHist(srcImg, outImg);
1919  else
1920  THROW_EXCEPTION("Operation only supported for grayscale images");
1921 #endif
1922 }
1923 
1924 // See: https://github.com/MRPT/mrpt/issues/885
1925 // This seems a bug in GCC?
1926 #if defined(__GNUC__)
1927 #define MRPT_DISABLE_FULL_OPTIMIZATION __attribute__((optimize("O1")))
1928 #else
1929 #define MRPT_DISABLE_FULL_OPTIMIZATION
1930 #endif
1931 
1932 template <unsigned int HALF_WIN_SIZE>
1934  const uint8_t* in, const int widthStep, unsigned int x, unsigned int y,
1935  int32_t& _gxx, int32_t& _gyy, int32_t& _gxy)
1936 {
1937  const auto min_x = x - HALF_WIN_SIZE;
1938  const auto min_y = y - HALF_WIN_SIZE;
1939 
1940  int32_t gxx = 0;
1941  int32_t gxy = 0;
1942  int32_t gyy = 0;
1943 
1944  const unsigned int WIN_SIZE = 1 + 2 * HALF_WIN_SIZE;
1945 
1946  unsigned int yy = min_y;
1947  for (unsigned int iy = WIN_SIZE; iy; --iy, ++yy)
1948  {
1949  const uint8_t* ptr = in + widthStep * yy + min_x;
1950  unsigned int xx = min_x;
1951  for (unsigned int ix = WIN_SIZE; ix; --ix, ++xx, ++ptr)
1952  {
1953  const int32_t dx =
1954  static_cast<int32_t>(ptr[+1]) - static_cast<int32_t>(ptr[-1]);
1955  const int32_t dy = static_cast<int32_t>(ptr[+widthStep]) -
1956  static_cast<int32_t>(ptr[-widthStep]);
1957  gxx += dx * dx;
1958  gxy += dx * dy;
1959  gyy += dy * dy;
1960  }
1961  }
1962  _gxx = gxx;
1963  _gyy = gyy;
1964  _gxy = gxy;
1965 }
1966 
1968  const unsigned int x, const unsigned int y,
1969  const unsigned int half_window_size) const
1970 {
1971 #if MRPT_HAS_OPENCV
1972 
1973  const auto& im1 = m_impl->img;
1974  const auto img_w = static_cast<unsigned int>(im1.cols),
1975  img_h = static_cast<unsigned int>(im1.rows);
1976  const int widthStep = im1.step[0];
1977 
1978  // If any of those predefined values worked, do the generic way:
1979  const unsigned int min_x = x - half_window_size;
1980  const unsigned int max_x = x + half_window_size;
1981  const unsigned int min_y = y - half_window_size;
1982  const unsigned int max_y = y + half_window_size;
1983 
1984  // Since min_* are "unsigned", checking "<" will detect negative
1985  // numbers:
1986  ASSERTMSG_(
1987  min_x < img_w && max_x < img_w && min_y < img_h && max_y < img_h,
1988  "Window is out of image bounds");
1989 
1990  // Gradient sums: Use integers since they're much faster than
1991  // doubles/floats!!
1992  int32_t gxx = 0;
1993  int32_t gxy = 0;
1994  int32_t gyy = 0;
1995 
1996  const auto* img_data = im1.ptr<uint8_t>(0);
1997  switch (half_window_size)
1998  {
1999  case 2:
2000  image_KLT_response_template<2>(
2001  img_data, widthStep, x, y, gxx, gyy, gxy);
2002  break;
2003  case 3:
2004  image_KLT_response_template<3>(
2005  img_data, widthStep, x, y, gxx, gyy, gxy);
2006  break;
2007  case 4:
2008  image_KLT_response_template<4>(
2009  img_data, widthStep, x, y, gxx, gyy, gxy);
2010  break;
2011  case 5:
2012  image_KLT_response_template<5>(
2013  img_data, widthStep, x, y, gxx, gyy, gxy);
2014  break;
2015  case 6:
2016  image_KLT_response_template<6>(
2017  img_data, widthStep, x, y, gxx, gyy, gxy);
2018  break;
2019  case 7:
2020  image_KLT_response_template<7>(
2021  img_data, widthStep, x, y, gxx, gyy, gxy);
2022  break;
2023  case 8:
2024  image_KLT_response_template<8>(
2025  img_data, widthStep, x, y, gxx, gyy, gxy);
2026  break;
2027  case 9:
2028  image_KLT_response_template<9>(
2029  img_data, widthStep, x, y, gxx, gyy, gxy);
2030  break;
2031  case 10:
2032  image_KLT_response_template<10>(
2033  img_data, widthStep, x, y, gxx, gyy, gxy);
2034  break;
2035  case 11:
2036  image_KLT_response_template<11>(
2037  img_data, widthStep, x, y, gxx, gyy, gxy);
2038  break;
2039  case 12:
2040  image_KLT_response_template<12>(
2041  img_data, widthStep, x, y, gxx, gyy, gxy);
2042  break;
2043  case 13:
2044  image_KLT_response_template<13>(
2045  img_data, widthStep, x, y, gxx, gyy, gxy);
2046  break;
2047  case 14:
2048  image_KLT_response_template<14>(
2049  img_data, widthStep, x, y, gxx, gyy, gxy);
2050  break;
2051  case 15:
2052  image_KLT_response_template<15>(
2053  img_data, widthStep, x, y, gxx, gyy, gxy);
2054  break;
2055  case 16:
2056  image_KLT_response_template<16>(
2057  img_data, widthStep, x, y, gxx, gyy, gxy);
2058  break;
2059  case 32:
2060  image_KLT_response_template<32>(
2061  img_data, widthStep, x, y, gxx, gyy, gxy);
2062  break;
2063 
2064  default:
2065  for (unsigned int yy = min_y; yy <= max_y; yy++)
2066  {
2067  const uint8_t* p = img_data + widthStep * yy + min_x;
2068  for (unsigned int xx = min_x; xx <= max_x; xx++)
2069  {
2070  const int32_t dx = p[+1] - p[-1];
2071  const int32_t dy = p[+widthStep] - p[-widthStep];
2072  gxx += dx * dx;
2073  gxy += dx * dy;
2074  gyy += dy * dy;
2075  }
2076  }
2077  break;
2078  }
2079  // Convert to float's and normalize in the way:
2080  const float K = 0.5f / ((max_y - min_y + 1) * (max_x - min_x + 1));
2081  const float Gxx = gxx * K;
2082  const float Gxy = gxy * K;
2083  const float Gyy = gyy * K;
2084 
2085  // Return the minimum eigenvalue of:
2086  // ( gxx gxy )
2087  // ( gxy gyy )
2088  // See, for example:
2089  // mrpt::math::detail::eigenVectorsMatrix_special_2x2():
2090  const float t = Gxx + Gyy; // Trace
2091  const float de = Gxx * Gyy - Gxy * Gxy; // Det
2092  // The smallest eigenvalue is:
2093  return 0.5f * (t - std::sqrt(t * t - 4.0f * de));
2094 #else
2095  return 0;
2096 #endif
2097 }
2098 
2099 // Load from TGA files. Used in loadFromFile()
2100 // Contains code from
2101 // https://github.com/tjohnman/Simple-Targa-Library/blob/master/src/simpleTGA.cpp
2102 // (FreeBSD license)
2104  const std::string& fileName, mrpt::img::CImage& out_RGB,
2105  mrpt::img::CImage& out_alpha)
2106 {
2107 #if MRPT_HAS_OPENCV
2108  std::fstream stream;
2109  stream.open(fileName.c_str(), std::fstream::in | std::fstream::binary);
2110  if (!stream.is_open())
2111  {
2112  std::cerr << "[CImage::loadTGA] Couldn't open file '" << fileName
2113  << "'.\n";
2114  return false;
2115  }
2116 
2117  stream.seekg(0, std::ios_base::end);
2118  // long length = stream.tellg();
2119  stream.seekg(0, std::ios_base::beg);
2120 
2121  // Simple uncompressed true-color image
2122  char dumpBuffer[12];
2123  char trueColorHeader[] = "\0\0\2\0\0\0\0\0\0\0\0\0";
2124  stream.read(dumpBuffer, 12);
2125  if (memcmp(dumpBuffer, trueColorHeader, 12) != 0)
2126  {
2127  std::cerr << "[CImage::loadTGA] Unsupported format or invalid file.\n";
2128  return false;
2129  }
2130 
2131  unsigned short width, height;
2132  unsigned char bpp;
2133 
2134  stream.read((char*)&width, 2);
2135  stream.read((char*)&height, 2);
2136  bpp = stream.get();
2137  if (bpp != 32)
2138  {
2139  std::cerr << "[CImage::loadTGA] Only 32 bpp format supported!\n";
2140  return false;
2141  }
2142 
2143  unsigned char desc;
2144  desc = stream.get();
2145  if (desc != 8 && desc != 32)
2146  {
2147  std::cerr << "[CImage::loadTGA] Unsupported format or invalid file.\n";
2148  return false;
2149  }
2150  const bool origin_is_low_corner = (desc == 8);
2151 
2152  // Data section
2153  std::vector<uint8_t> bytes(width * height * 4);
2154  stream.read((char*)&bytes[0], width * height * 4);
2155  stream.close();
2156 
2157  // Move data to images:
2158  out_RGB.resize(width, height, CH_RGB);
2159  out_alpha.resize(width, height, CH_GRAY);
2160 
2161  size_t idx = 0;
2162  for (int r = 0; r < height; r++)
2163  {
2164  const auto actual_row = origin_is_low_corner ? (height - 1 - r) : r;
2165  auto& img = out_RGB.m_impl->img;
2166  auto data = img.ptr<uint8_t>(actual_row);
2167 
2168  auto& img_alpha = out_alpha.m_impl->img;
2169  auto data_alpha = img_alpha.ptr<uint8_t>(actual_row);
2170 
2171  for (unsigned int c = 0; c < width; c++)
2172  {
2173  *data++ = bytes[idx++]; // R
2174  *data++ = bytes[idx++]; // G
2175  *data++ = bytes[idx++]; // B
2176  *data_alpha++ = bytes[idx++]; // A
2177  }
2178  }
2179 
2180  return true;
2181 #else
2182  return false;
2183 #endif // MRPT_HAS_OPENCV
2184 }
2185 
2187 {
2188 #if MRPT_HAS_OPENCV
2190 
2191 #if MRPT_OPENCV_VERSION_NUM < 0x300
2192  ASSERT_(dest != nullptr);
2193  *dest = cvIplImage(m_impl->img);
2194 #else
2195  THROW_EXCEPTION("Method not supported in OpenCV>=3.0");
2196 #endif
2197 #endif
2198 }
2199 
2200 std::ostream& mrpt::img::operator<<(std::ostream& o, const TPixelCoordf& p)
2201 {
2202  o << "(" << p.x << "," << p.y << ")";
2203  return o;
2204 }
2205 std::ostream& mrpt::img::operator<<(std::ostream& o, const TPixelCoord& p)
2206 {
2207  o << "(" << p.x << "," << p.y << ")";
2208  return o;
2209 }
void update_patch(const CImage &patch, const unsigned int col, const unsigned int row)
Update a part of this image with the "patch" given as argument.
Definition: CImage.cpp:1160
void drawCircle(int x, int y, int radius, const mrpt::img::TColor &color=mrpt::img::TColor(255, 255, 255), unsigned int width=1) override
Draws a circle of a given radius.
Definition: CImage.cpp:1136
void line(int x0, int y0, int x1, int y1, const mrpt::img::TColor color, unsigned int width=1, TPenStyle penStyle=psSolid) override
Draws a line.
Definition: CImage.cpp:1123
Used in mrpt::img::CImage.
Definition: img/CImage.h:82
bool isEmpty() const
Returns true if the object is in the state after default constructor.
Definition: CImage.cpp:875
double Scalar
Definition: KmUtils.h:43
#define MRPT_START
Definition: exceptions.h:241
TPenStyle
Definition of pen styles.
Definition: CCanvas.h:55
bool drawChessboardCorners(std::vector< TPixelCoordf > &cornerCoords, unsigned int check_size_x, unsigned int check_size_y, unsigned int lines_width=1, unsigned int circles_radius=4)
Draw onto this image the detected corners of a chessboard.
Definition: CImage.cpp:1805
void getAsMatrixTiled(mrpt::math::CMatrixFloat &outMatrix) const
Returns the image as a matrix, where the image is "tiled" (repeated) the required number of times to ...
Definition: CImage.cpp:1521
void MRPT_DISABLE_FULL_OPTIMIZATION image_KLT_response_template(const uint8_t *in, const int widthStep, unsigned int x, unsigned int y, int32_t &_gxx, int32_t &_gyy, int32_t &_gxy)
Definition: CImage.cpp:1933
#define THROW_EXCEPTION(msg)
Definition: exceptions.h:67
std::string std::string format(std::string_view fmt, ARGS &&... args)
Definition: format.h:26
CImage scaleHalf(TInterpolationMethod interp) const
Returns a new image scaled down to half its original size.
Definition: img/CImage.h:314
void getAsMatrix(mrpt::math::CMatrixFloat &outMatrix, bool doResize=true, int x_min=0, int y_min=0, int x_max=-1, int y_max=-1, bool normalize_01=true) const
Returns the image as a matrix with pixel grayscale values in the range [0,1].
Definition: CImage.cpp:1244
uint8_t serializeGetVersion() const override
Must return the current versioning number of the object.
Definition: CImage.cpp:482
#define IMPLEMENTS_SERIALIZABLE(class_name, base, NameSpace)
To be added to all CSerializable-classes implementation files.
void copyFromForceLoad(const CImage &o)
Copies from another image (shallow copy), and, if it is externally stored, the image file will be act...
Definition: CImage.cpp:181
const double G
void getAsRGBMatrices(mrpt::math::CMatrixFloat &outMatrixR, mrpt::math::CMatrixFloat &outMatrixG, mrpt::math::CMatrixFloat &outMatrixB, bool doResize=true, int x_min=0, int y_min=0, int x_max=-1, int y_max=-1) const
Returns the image as RGB matrices with pixel values in the range [0,1].
Definition: CImage.cpp:1343
static bool DISABLE_JPEG_COMPRESSION()
Definition: CImage.cpp:60
copy_type_t
Define kind of copies.
Definition: img/CImage.h:72
CExceptionExternalImageNotFound(const std::string &s)
Definition: CImage.cpp:73
float getMaxAsFloat() const
Return the maximum pixel value of the image, as a float value in the range [0,1]. ...
Definition: CImage.cpp:927
void drawImage(int x, int y, const mrpt::img::CImage &img) override
Draws an image as a bitmap at a given position.
Definition: CImage.cpp:1148
cv::Mat & asCvMatRef()
Get a reference to the internal cv::Mat, which can be resized, etc.
Definition: CImage.cpp:230
void flipHorizontal()
Flips the image horizontally.
Definition: CImage.cpp:1657
TImageChannels getChannelCount() const
Returns the number of channels, typically 1 (GRAY) or 3 (RGB)
Definition: CImage.cpp:884
static constexpr TColor blue()
Definition: TColor.h:68
size_t getHeight() const override
Returns the height of the image in pixels.
Definition: CImage.cpp:855
mrpt::vision::TStereoCalibParams params
void scaleImage(CImage &out_img, unsigned int width, unsigned int height, TInterpolationMethod interp=IMG_INTERP_CUBIC) const
Scales this image to a new size, interpolating as needed, saving the new image in a different output ...
Definition: CImage.cpp:1747
std::string getChannelsOrder() const
As of mrpt 2.0.0, this returns either "GRAY" or "BGR".
Definition: CImage.cpp:828
A pair (x,y) of pixel coordinates (subpixel resolution).
Definition: TPixelCoord.h:18
void setExternalStorage(const std::string &fileName) noexcept
By using this method the image is marked as referenced to an external file, which will be loaded only...
Definition: CImage.cpp:1577
float correlate(const CImage &img2int, int width_init=0, int height_init=0) const
Computes the correlation coefficient (returned as val), between two images This function use grayscal...
Definition: CImage.cpp:1185
void makeSureImageIsLoaded() const
Checks if the image is of type "external storage", and if so and not loaded yet, load it...
Definition: CImage.cpp:1591
STL namespace.
uint8_t B
Definition: TColor.h:51
void filterGaussian(CImage &out_img, int W=3, int H=3, double sigma=1.0) const
Filter the image with a Gaussian filter with a window size WxH, replacing "this" image by the filtere...
Definition: CImage.cpp:1733
void joinImagesHorz(const CImage &im1, const CImage &im2)
Joins two images side-by-side horizontally.
Definition: CImage.cpp:1893
uint8_t G
Definition: TColor.h:51
void unload() const noexcept
For external storage image objects only, this method unloads the image from memory (or does nothing i...
Definition: CImage.cpp:1584
float getAsFloat(unsigned int col, unsigned int row, unsigned int channel) const
Returns the contents of a given pixel at the desired channel, in float format: [0,255]->[0,1] The coordinate origin is pixel(0,0)=top-left corner of the image.
Definition: CImage.cpp:899
#define MRPT_DISABLE_FULL_OPTIMIZATION
Definition: CImage.cpp:1929
mrpt::pimpl< Impl > m_impl
Definition: img/CImage.h:1027
bool supports(feature f) noexcept
Returns true if the current CPU (and OS) supports the given CPU feature.
Definition: cpu.h:75
void swap(CImage &o)
Efficiently swap of two images.
Definition: CImage.cpp:174
static std::string IMAGES_PATH_BASE(".")
void asCvMat(cv::Mat &out_img, copy_type_t copy_type) const
Makes a shallow or deep copy of this image into the provided cv::Mat.
Definition: CImage.cpp:220
#define MRPT_THROW_UNKNOWN_SERIALIZATION_VERSION(__V)
For use in CSerializable implementations.
Definition: exceptions.h:97
uint64_t getTotalBytesCount() const override
Returns the total size of the internal buffer.
void swapRB()
Swaps red and blue channels.
Definition: CImage.cpp:1665
CImage colorImage() const
Returns a color (RGB) version of the grayscale image, or a shallow copy of itself if it is already a ...
Definition: CImage.cpp:1867
void image_SSSE3_scale_half_3c8u(const uint8_t *in, uint8_t *out, int w, int h, size_t in_step, size_t out_step)
mrpt::math::CMatrixDouble33 intrinsicParams
Matrix of intrinsic parameters (containing the focal length and principal point coordinates): ...
Definition: TCamera.h:50
#define ASSERT_(f)
Defines an assertion mechanism.
Definition: exceptions.h:120
bool loadFromFile(const std::string &fileName, int isColor=-1)
Load image from a file, whose format is determined from the extension (internally uses OpenCV)...
Definition: CImage.cpp:304
This base provides a set of functions for maths stuff.
static int SERIALIZATION_JPEG_QUALITY_value
Definition: CImage.cpp:53
RET pixelDepth2IPLCvDepth(PixelDepth d)
Definition: CImage.cpp:125
size_t getWidth() const override
Returns the width of the image in pixels.
Definition: CImage.cpp:818
float KLT_response(const unsigned int x, const unsigned int y, const unsigned int half_window_size) const
Compute the KLT response at a given pixel (x,y) - Only for grayscale images (for efficiency it avoids...
Definition: CImage.cpp:1967
void resize(std::size_t width, std::size_t height, TImageChannels nChannels, PixelDepth depth=PixelDepth::D8U)
Changes the size of the image, erasing previous contents (does NOT scale its current content...
Definition: CImage.cpp:250
mrpt::system::CTimeLogger CTimeLogger
unsigned char * operator()(unsigned int col, unsigned int row, unsigned int channel=0) const
Returns a pointer to a given pixel information.
Definition: CImage.cpp:426
void normalize(CONTAINER &c, Scalar valMin, Scalar valMax)
Scales all elements such as the minimum & maximum values are shifted to the given values...
virtual void selectTextFont(const std::string &fontName)
Select the current font used when drawing text.
Definition: CCanvas.cpp:220
#define ASSERT_EQUAL_(__A, __B)
Assert comparing two values, reporting their actual values upon failure.
Definition: exceptions.h:137
void * getRawBufferData()
Method for getting a pointer to the raw stored data.
This CStream derived class allow using a memory buffer as a CStream.
void internal_fromIPL(const IplImage *iplImage, copy_type_t c)
Definition: CImage.cpp:350
void saveToStreamAsJPEG(mrpt::io::CStream &out, const int jpeg_quality=95) const
Save image to binary stream as a JPEG (.jpg) compressed format.
void assignMemoryNotOwn(const void *data, const uint64_t nBytesInData)
Initilize the data in the stream from a block of memory which is NEITHER OWNED NOR COPIED by the obje...
void normalize()
Optimize the brightness range of an image without using histogram Only for one channel images...
Definition: CImage.cpp:1236
uint64_t Seek(int64_t Offset, CStream::TSeekOrigin Origin=sFromBeginning) override
Introduces a pure virtual method for moving to a specified position in the streamed resource...
static PixelDepth cvDepth2PixelDepth(int64_t d)
Definition: CImage.cpp:142
A pair (x,y) of pixel coordinates (integer resolution).
Definition: TPixelCoord.h:40
void flipVertical()
Flips the image vertically.
Definition: CImage.cpp:1649
int val
Definition: mrpt_jpeglib.h:957
uint8_t * get_unsafe(unsigned int col, unsigned int row, uint8_t channel=0) const
Access to pixels without checking boundaries - Use normally the () operator better, which checks the coordinates.
Definition: CImage.cpp:476
#define IMPLEMENTS_MEXPLUS_FROM(complete_type)
Parameters for the Brown-Conrady camera lens distortion model.
Definition: TCamera.h:26
void getAsIplImage(IplImage *dest) const
(DEPRECATED, DO NOT USE - Kept here only to interface opencv 2.4)
Definition: CImage.cpp:2186
#define ASSERTMSG_(f, __ERROR_MSG)
Defines an assertion mechanism.
Definition: exceptions.h:108
void loadFromStreamAsJPEG(mrpt::io::CStream &in)
Reads the image from a binary stream containing a binary jpeg file.
void rectifyImageInPlace(void *mapX, void *mapY)
Rectify an image (undistorts and rectification) from a stereo pair according to a pair of precomputed...
Definition: CImage.cpp:1673
void serializeFrom(mrpt::serialization::CArchive &in, uint8_t serial_version) override
Pure virtual method for reading (deserializing) from an abstract archive.
Definition: CImage.cpp:585
#define ASSERT_ABOVEEQ_(__A, __B)
Definition: exceptions.h:167
TImageSize getSize() const
Return the size of the image.
Definition: img/CImage.h:646
size_type rows() const
Number of rows in the matrix.
size_type cols() const
Number of columns in the matrix.
uint8_t R
Definition: TColor.h:51
std::array< double, 8 > dist
[k1 k2 t1 t2 k3 k4 k5 k6] -> k_i: parameters of radial distortion, t_i: parameters of tangential dist...
Definition: TCamera.h:53
void clear()
Resets the image to the state after a default ctor.
Definition: CImage.cpp:1571
uint8_t * internal_get(int col, int row, uint8_t channel=0) const
Definition: CImage.cpp:464
struct mxArray_tag mxArray
Forward declaration for mxArray (avoid #including as much as possible to speed up compiling) ...
Definition: CSerializable.h:18
CImage grayscale() const
Returns a grayscale version of the image, or a shallow copy of itself if it is already a grayscale im...
Definition: CImage.cpp:939
void cross_correlation_FFT(const CImage &in_img, math::CMatrixFloat &out_corr, int u_search_ini=-1, int v_search_ini=-1, int u_search_size=-1, int v_search_size=-1, float biasThisImg=0, float biasInImg=0) const
Computes the correlation matrix between this image and another one.
Definition: CImage.cpp:1441
void idft2_complex(const CMatrixFloat &in_real, const CMatrixFloat &in_imag, CMatrixFloat &out_real, CMatrixFloat &out_imag)
Compute the 2D inverse Discrete Fourier Transform (DFT).
Definition: fourier.cpp:1332
return_t square(const num_t x)
Inline function for the square of a number.
void setPixel(int x, int y, size_t color) override
Changes the value of the pixel (x,y).
Definition: CImage.cpp:1082
PixelDepth getPixelDepth() const
Definition: CImage.cpp:293
const_iterator end() const
Definition: ts_hash_map.h:246
This class is a "CSerializable" wrapper for "CMatrixFloat".
Definition: CMatrixF.h:22
This is the global namespace for all Mobile Robot Programming Toolkit (MRPT) libraries.
void filterMedian(CImage &out_img, int W=3) const
Filter the image with a Median filter with a window size WxW, returning the filtered image in out_img...
Definition: CImage.cpp:1718
void image_SSSE3_bgr_to_gray_8u(const uint8_t *in, uint8_t *out, int w, int h, size_t in_step, size_t out_step)
CImage()
Default constructor: initialize to empty image.
Definition: CImage.cpp:162
void image_SSE2_scale_half_1c8u(const uint8_t *in, uint8_t *out, int w, int h, size_t in_step, size_t out_step)
Virtual base class for "archives": classes abstracting I/O streams.
Definition: CArchive.h:54
CImage makeDeepCopy() const
Returns a deep copy of this image.
Definition: CImage.cpp:209
bool isColor() const
Returns true if the image is RGB, false if it is grayscale.
Definition: CImage.cpp:865
TInterpolationMethod
Interpolation methods for images.
Definition: img/CImage.h:50
bool isOriginTopLeft() const
Returns true (as of MRPT v2.0.0, it&#39;s fixed)
Definition: CImage.cpp:894
const float R
mrpt::vision::TStereoCalibResults out
struct _IplImage IplImage
Definition: img/CImage.h:22
virtual mxArray * writeToMatlab() const
Introduces a pure virtual method responsible for writing to a mxArray Matlab object, typically a MATLAB struct whose contents are documented in each derived class.
Definition: CSerializable.h:90
bool m_imgIsExternalStorage
Set to true only when using setExternalStorage.
Definition: img/CImage.h:1033
Deep copy: the copied object has a duplicate of all data, becoming independent.
Definition: img/CImage.h:78
void setSize(size_t row, size_t col, bool zeroNewElements=false)
Changes the size of matrix, maintaining the previous contents.
#define MRPT_END
Definition: exceptions.h:245
float u8tof(const uint8_t v)
converts a uint8_t [0,255] into a float [0,1]
size_t getRowStride() const
Returns the row stride of the image: this is the number of bytes between two consecutive rows...
Definition: CImage.cpp:845
#define MRPT_HAS_OPENCV
CImage scaleDouble(TInterpolationMethod interp) const
Returns a new image scaled up to double its original size.
Definition: img/CImage.h:329
void serializeTo(mrpt::serialization::CArchive &out) const override
Pure virtual method for writing (serializing) to an abstract archive.
Definition: CImage.cpp:490
void rotateImage(CImage &out_img, double ang, unsigned int cx, unsigned int cy, double scale=1.0) const
Rotates the image by the given angle around the given center point, with an optional scale factor...
Definition: CImage.cpp:1773
size_t ReadBuffer(void *Buffer, size_t Count)
Reads a block of bytes from the stream into Buffer.
Definition: CArchive.cpp:25
void extract_patch(CImage &patch, const unsigned int col=0, const unsigned int row=0, const unsigned int width=1, const unsigned int height=1) const
Extract a patch from this image, saveing it into "patch" (its previous contents will be overwritten)...
Definition: CImage.cpp:1172
constexpr RET pixelDepth2CvDepth(PixelDepth d)
Definition: CImage.cpp:108
pimpl< T > make_impl(Args &&... args)
Definition: pimpl.h:18
void image_SSE2_scale_half_smooth_1c8u(const uint8_t *in, uint8_t *out, int w, int h, size_t in_step, size_t out_step)
virtual void textOut(int x0, int y0, const std::string &str, const mrpt::img::TColor color)
Renders 2D text using bitmap fonts.
Definition: CCanvas.cpp:374
MRPT_TODO("toPointCloud / calibration")
static int interpolationMethod2Cv(TInterpolationMethod i)
Definition: CImage.cpp:93
bool saveToFile(const std::string &fileName, int jpeg_quality=95) const
Save the image to a file, whose format is determined from the extension (internally uses OpenCV)...
Definition: CImage.cpp:329
TImageChannels
For use in mrpt::img::CImage.
Definition: img/CImage.h:59
#define ASSERT_BELOWEQ_(__A, __B)
Definition: exceptions.h:161
static int SERIALIZATION_JPEG_QUALITY()
Definition: CImage.cpp:68
static bool DISABLE_JPEG_COMPRESSION_value
Definition: CImage.cpp:52
A RGB color - 8bit.
Definition: TColor.h:25
void undistort(CImage &out_img, const mrpt::img::TCamera &cameraParams) const
Undistort the image according to some camera parameters, and returns an output undistorted image...
Definition: CImage.cpp:1691
std::ostream & operator<<(std::ostream &o, const TColor &c)
Definition: TColor.cpp:80
void dft2_complex(const CMatrixFloat &in_real, const CMatrixFloat &in_imag, CMatrixFloat &out_real, CMatrixFloat &out_imag)
Compute the 2D Discrete Fourier Transform (DFT) of a complex matrix, returning the real and imaginary...
Definition: fourier.cpp:1227
std::string getExternalStorageFileAbsolutePath() const
Only if isExternallyStored() returns true.
Definition: img/CImage.h:794
This template class provides the basic functionality for a general 2D any-size, resizable container o...
images resize(NUM_IMGS)
std::string m_externalFile
The file name of a external storage image.
Definition: img/CImage.h:1036
void clear()
Clear the contents of this container.
Definition: ts_hash_map.h:183
static void setImagesPathBase(const std::string &path)
Definition: CImage.cpp:80
void equalizeHist(CImage &out_img) const
Equalize the image histogram, saving the new image in the given output object.
Definition: CImage.cpp:1908
#define THROW_TYPED_EXCEPTION_FMT(exceptionClass, _FORMAT_STRING,...)
Definition: exceptions.h:72
void forceLoad() const
For external storage image objects only, this method makes sure the image is loaded in memory...
Definition: img/CImage.h:806
static bool loadTGA(const std::string &fileName, mrpt::img::CImage &out_RGB, mrpt::img::CImage &out_alpha)
Loads a TGA true-color RGBA image as two CImage objects, one for the RGB channels plus a separate gra...
Definition: CImage.cpp:2103
static bool my_img_to_grayscale(const cv::Mat &src, cv::Mat &dest)
Definition: CImage.cpp:948
A class for storing images as grayscale or RGB bitmaps.
Definition: img/CImage.h:148
void memcpy(void *dest, size_t destSize, const void *src, size_t copyCount) noexcept
An OS and compiler independent version of "memcpy".
void loadFromMemoryBuffer(unsigned int width, unsigned int height, bool color, unsigned char *rawpixels, bool swapRedBlue=false)
Reads the image from raw pixels buffer in memory.
Definition: CImage.cpp:364
Scalar & coeffRef(int r, int c)
static struct FontData data
Definition: gltext.cpp:144
static const std::string & getImagesPathBase()
By default, ".".
Definition: CImage.cpp:79



Page generated by Doxygen 1.8.14 for MRPT 2.0.4 Git: 33de1d0ad Sat Jun 20 11:02:42 2020 +0200 at sáb jun 20 17:35:17 CEST 2020