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



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: cfb2df9d3 Fri Nov 15 06:57:14 2019 +0100 at vie nov 15 07:00:11 CET 2019