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



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: 1de0e027c Sat Sep 14 16:15:22 2019 +0200 at sáb sep 14 16:20:14 CEST 2019