Main MRPT website > C++ reference for MRPT 1.9.9
CImage.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2018, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #include "img-precomp.h" // Precompiled headers
11 
12 #include <mrpt/img/CImage.h>
15 #include <mrpt/io/CMemoryStream.h>
16 #include <mrpt/io/zip.h>
17 #include <mrpt/math/CMatrix.h>
18 #include <mrpt/math/fourier.h>
19 #include <mrpt/math/utils.h> // for roundup()
20 #include <mrpt/core/round.h> // for round()
21 #include <mrpt/system/CTicTac.h>
23 #include <mrpt/system/memory.h>
24 #include <mrpt/system/filesystem.h>
26 #include <iostream>
27 
28 // Universal include for all versions of OpenCV
29 #include <mrpt/otherlibs/do_opencv_includes.h>
30 
31 #if MRPT_HAS_MATLAB
32 #include <mexplus/mxarray.h>
33 #endif
34 
35 // Prototypes of SSE2/SSE3/SSSE3 optimized functions:
36 #include "CImage_SSEx.h"
37 
38 using namespace mrpt;
39 using namespace mrpt::img;
40 using namespace mrpt::math;
41 using namespace mrpt::system;
42 using namespace std;
43 
44 // This must be added to any CSerializable class implementation file.
45 IMPLEMENTS_MRPT_OBJECT_ALT_NAME(CImage, CSerializable, mrpt::img, "CMRPTImage")
46 
47 bool CImage::DISABLE_ZIP_COMPRESSION = false;
48 bool CImage::DISABLE_JPEG_COMPRESSION = false;
49 int CImage::SERIALIZATION_JPEG_QUALITY = 95;
50 
51 static std::string IMAGES_PATH_BASE(".");
52 
53 const std::string& CImage::getImagesPathBase() { return IMAGES_PATH_BASE; }
55 {
56  IMAGES_PATH_BASE = path;
57 }
58 
59 // Do performance time logging?
60 #define IMAGE_ALLOC_PERFLOG 0
61 
62 #if IMAGE_ALLOC_PERFLOG
63 mrpt::img::CTimeLogger alloc_tims;
64 #endif
65 
66 /*---------------------------------------------------------------
67  Constructor
68 ---------------------------------------------------------------*/
70  unsigned int width, unsigned int height, TImageChannels nChannels,
71  bool originTopLeft)
72  : img(nullptr), m_imgIsReadOnly(false), m_imgIsExternalStorage(false)
73 {
75  changeSize(width, height, nChannels, originTopLeft);
76  MRPT_END
77 }
78 /*---------------------------------------------------------------
79  Default Constructor
80 ---------------------------------------------------------------*/
82  : img(nullptr), m_imgIsReadOnly(false), m_imgIsExternalStorage(false)
83 {
84 #if MRPT_HAS_OPENCV
86  changeSize(1, 1, CH_RGB, true);
87  MRPT_END
88 #endif
89 }
90 
91 /*---------------------------------------------------------------
92  Copy constructor
93 ---------------------------------------------------------------*/
95  : img(nullptr), m_imgIsReadOnly(false), m_imgIsExternalStorage(false)
96 {
98  *this = o;
99  MRPT_END
100 }
101 
102 /*---------------------------------------------------------------
103  Copy operator
104 ---------------------------------------------------------------*/
106 {
107  MRPT_START
108  if (this == &o) return *this;
109  releaseIpl();
111  m_imgIsReadOnly = false;
112 
113  if (!o.m_imgIsExternalStorage)
114  { // A normal image
115 #if MRPT_HAS_OPENCV
116  ASSERTMSG_(
117  o.img != nullptr,
118  "Source image in = operator has nullptr IplImage*");
119  img = cvCloneImage((IplImage*)o.img);
120 #endif
121  }
122  else
123  { // An externally stored image:
125  }
126  return *this;
127  MRPT_END
128 }
129 
130 /*---------------------------------------------------------------
131  swap
132 ---------------------------------------------------------------*/
134 {
135  std::swap(img, o.img);
136  std::swap(m_imgIsReadOnly, o.m_imgIsReadOnly);
138  std::swap(m_externalFile, o.m_externalFile);
139 }
140 
141 /*---------------------------------------------------------------
142  copyFromForceLoad
143 
144 Copies from another image, and, if that one is externally
145 stored, the image file will be actually loaded into memory
146 in "this" object.
147 ---------------------------------------------------------------*/
149 {
150  if (o.isExternallyStored())
151  {
152  // Load from that file:
156  "Error loading externally-stored image from: %s",
158  }
159  else
160  { // It's not external storage.
161  *this = o;
162  }
163 }
164 
165 /*---------------------------------------------------------------
166  copyFastFrom
167 ---------------------------------------------------------------*/
169 {
170  MRPT_START
171  if (this == &o) return;
173  {
174  // Just copy the reference to the ext. file:
175  *this = o;
176  }
177  else
178  { // Normal copy
179 #if MRPT_HAS_OPENCV
180  if (!o.img) THROW_EXCEPTION("Origin image is empty! (o.img==nullptr)");
181 #endif
182  // Erase current image:
183  releaseIpl();
184 
185  // Make the transfer of just the pointer:
186  img = o.img;
190 
191  o.img = nullptr;
192  o.m_imgIsReadOnly = false;
193  o.m_imgIsExternalStorage = false;
194  }
195 
196  MRPT_END
197 }
198 
199 /*---------------------------------------------------------------
200  Constructor from IplImage
201 ---------------------------------------------------------------*/
202 CImage::CImage(void* iplImage)
203  : img(nullptr), m_imgIsReadOnly(false), m_imgIsExternalStorage(false)
204 {
205  MRPT_START
206 
207 #if MRPT_HAS_OPENCV
208  if (!iplImage)
209  changeSize(1, 1, 1, true);
210  else
211  img = cvCloneImage((IplImage*)iplImage);
212 #endif
213  MRPT_END
214 }
215 
216 /*---------------------------------------------------------------
217  Destructor
218 ---------------------------------------------------------------*/
220 /*---------------------------------------------------------------
221  changeSize
222 ---------------------------------------------------------------*/
224  unsigned int width, unsigned int height, TImageChannels nChannels,
225  bool originTopLeft)
226 {
227  MRPT_START
228 
229 #if MRPT_HAS_OPENCV
230  // If we're resizing to exactly the current size, do nothing and avoid
231  // wasting mem allocs/deallocs!
232  if (img)
233  {
234  makeSureImageIsLoaded(); // For delayed loaded images stored externally
235  IplImage* ipl = static_cast<IplImage*>(img);
236  if (static_cast<unsigned int>(ipl->width) == width &&
237  static_cast<unsigned int>(ipl->height) == height &&
238  ipl->nChannels == nChannels &&
239  ipl->origin == (originTopLeft ? 0 : 1))
240  {
241  return; // nothing to do, we're already right with the current
242  // IplImage!
243  }
244  }
245 
246  // Delete current img
247  releaseIpl();
248 
249 #if IMAGE_ALLOC_PERFLOG
250  const std::string sLog = mrpt::format("cvCreateImage %ux%u", width, height);
251  alloc_tims.enter(sLog.c_str());
252 #endif
253 
254  img = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, nChannels);
255  ((IplImage*)img)->origin = originTopLeft ? 0 : 1;
256 
257 #if IMAGE_ALLOC_PERFLOG
258  alloc_tims.leave(sLog.c_str());
259 #endif
260 
261 #else
262  THROW_EXCEPTION("The MRPT has been compiled with MRPT_HAS_OPENCV=0 !");
263 #endif
264 
265  MRPT_END
266 }
267 
268 /*---------------------------------------------------------------
269  loadFromFile
270 ---------------------------------------------------------------*/
271 bool CImage::loadFromFile(const std::string& fileName, int isColor)
272 {
273  MRPT_START
274 
275 #if MRPT_HAS_OPENCV
276  IplImage* newImg = cvLoadImage(fileName.c_str(), isColor);
277  if (newImg != nullptr)
278  {
279  releaseIpl();
280  img = newImg;
281  return true;
282  }
283  else
284  {
285  return false;
286  }
287 #else
288  THROW_EXCEPTION("The MRPT has been compiled with MRPT_HAS_OPENCV=0 !");
289 #endif
290  MRPT_END
291 }
292 
293 /*---------------------------------------------------------------
294  saveToFile
295 ---------------------------------------------------------------*/
296 bool CImage::saveToFile(const std::string& fileName, int jpeg_quality) const
297 {
298  MRPT_START
299 #if MRPT_HAS_OPENCV
300  makeSureImageIsLoaded(); // For delayed loaded images stored externally
301  ASSERT_(img != nullptr);
302 
303 #if MRPT_OPENCV_VERSION_NUM > 0x110
304  int p[3];
305  p[0] = CV_IMWRITE_JPEG_QUALITY;
306  p[1] = jpeg_quality;
307  p[2] = 0;
308  return (0 != cvSaveImage(fileName.c_str(), img, p));
309 #else
310  return (0 != cvSaveImage(fileName.c_str(), img));
311 #endif
312 #else
313  THROW_EXCEPTION("The MRPT has been compiled with MRPT_HAS_OPENCV=0 !");
314 #endif
315  MRPT_END
316 }
317 
318 /*---------------------------------------------------------------
319  loadFromIplImage
320 ---------------------------------------------------------------*/
321 void CImage::loadFromIplImage(void* iplImage)
322 {
323  MRPT_START
324  ASSERT_(iplImage != nullptr);
325  releaseIpl();
326  if (iplImage)
327  {
328 #if MRPT_HAS_OPENCV
329  img = cvCloneImage((IplImage*)iplImage);
330 #else
331  THROW_EXCEPTION("The MRPT has been compiled with MRPT_HAS_OPENCV=0 !");
332 #endif
333  }
334  MRPT_END
335 }
336 
337 /*---------------------------------------------------------------
338  setFromIplImageReadOnly
339 ---------------------------------------------------------------*/
341 {
342  MRPT_START
343  releaseIpl();
344 #if MRPT_HAS_OPENCV
345  ASSERT_(iplImage != nullptr);
346  ASSERTMSG_(iplImage != this->img, "Trying to assign read-only to itself.");
347 
348  img = (IplImage*)iplImage;
349 #else
350  if (iplImage)
351  {
352  THROW_EXCEPTION("The MRPT has been compiled with MRPT_HAS_OPENCV=0 !");
353  }
354 #endif
355  m_imgIsReadOnly = true;
356  m_imgIsExternalStorage = false;
357  MRPT_END
358 }
359 
360 /*---------------------------------------------------------------
361  setFromIplImage
362 ---------------------------------------------------------------*/
363 void CImage::setFromIplImage(void* iplImage)
364 {
365  MRPT_START
366 
367  releaseIpl();
368  if (iplImage)
369  {
370 #if MRPT_HAS_OPENCV
371  img = (IplImage*)iplImage;
372 #else
373  THROW_EXCEPTION("The MRPT has been compiled with MRPT_HAS_OPENCV=0 !");
374 #endif
375  }
376  m_imgIsReadOnly = false;
377  m_imgIsExternalStorage = false;
378 
379  MRPT_END
380 }
381 
382 /*---------------------------------------------------------------
383  loadFromMemoryBuffer
384 ---------------------------------------------------------------*/
386  unsigned int width, unsigned int height, bool color,
387  unsigned char* rawpixels, bool swapRedBlue)
388 {
389  MRPT_START
390 
391 #if MRPT_HAS_OPENCV
392  resize(width, height, color ? 3 : 1, true);
393  m_imgIsReadOnly = false;
394  m_imgIsExternalStorage = false;
395 
396  if (color && swapRedBlue)
397  {
398  // Do copy & swap at once:
399  unsigned char* ptr_src = rawpixels;
400  unsigned char* ptr_dest =
401  reinterpret_cast<unsigned char*>(((IplImage*)img)->imageData);
402  const int bytes_per_row_out = ((IplImage*)img)->widthStep;
403 
404  for (int h = height; h--;)
405  {
406  for (unsigned int i = 0; i < width;
407  i++, ptr_src += 3, ptr_dest += 3)
408  {
409  unsigned char t0 = ptr_src[0], t1 = ptr_src[1], t2 = ptr_src[2];
410  ptr_dest[2] = t0;
411  ptr_dest[1] = t1;
412  ptr_dest[0] = t2;
413  }
414  ptr_dest += bytes_per_row_out - width * 3;
415  }
416  }
417  else
418  {
419  if (((IplImage*)img)->widthStep ==
420  ((IplImage*)img)->width * ((IplImage*)img)->nChannels)
421  {
422  // Copy the image data:
423  memcpy(
424  ((IplImage*)img)->imageData, rawpixels,
425  ((IplImage*)img)->imageSize);
426  }
427  else
428  {
429  // Copy the image row by row:
430  unsigned char* ptr_src = rawpixels;
431  unsigned char* ptr_dest =
432  reinterpret_cast<unsigned char*>(((IplImage*)img)->imageData);
433  int bytes_per_row = width * (color ? 3 : 1);
434  int bytes_per_row_out = ((IplImage*)img)->widthStep;
435  for (unsigned int y = 0; y < height; y++)
436  {
437  memcpy(ptr_dest, ptr_src, bytes_per_row);
438  ptr_src += bytes_per_row;
439  ptr_dest += bytes_per_row_out;
440  }
441  }
442  }
443 #else
444  THROW_EXCEPTION("The MRPT has been compiled with MRPT_HAS_OPENCV=0 !");
445 #endif
446  MRPT_END
447 }
448 
449 /*---------------------------------------------------------------
450  operator()
451 ---------------------------------------------------------------*/
452 unsigned char* CImage::operator()(
453  unsigned int col, unsigned int row, unsigned int channel) const
454 {
455 #if MRPT_HAS_OPENCV
456 
457 #if defined(_DEBUG) || (MRPT_ALWAYS_CHECKS_DEBUG)
458  MRPT_START
459 #endif
460 
461  makeSureImageIsLoaded(); // For delayed loaded images stored externally
462  IplImage* ipl = ((IplImage*)img);
463 
464 #if defined(_DEBUG) || (MRPT_ALWAYS_CHECKS_DEBUG)
465  ASSERT_(ipl);
466  if (row >= (unsigned int)ipl->height || col >= (unsigned int)ipl->width ||
467  channel >= (unsigned int)ipl->nChannels)
468  {
470  format(
471  "Pixel coordinates/channel out of bounds: row=%u/%u col=%u/%u "
472  "chan=%u/%u",
473  row, ipl->height, col, ipl->width, channel, ipl->nChannels));
474  }
475 #endif
476 
477  return (unsigned char*)&ipl
478  ->imageData[row * ipl->widthStep + col * ipl->nChannels + channel];
479 #if defined(_DEBUG) || (MRPT_ALWAYS_CHECKS_DEBUG)
480  MRPT_END
481 #endif
482 
483 #else
484  THROW_EXCEPTION("MRPT was compiled without OpenCV");
485 #endif
486 }
487 
488 /*---------------------------------------------------------------
489  get_unsafe()
490 ---------------------------------------------------------------*/
491 unsigned char* CImage::get_unsafe(
492  unsigned int col, unsigned int row, unsigned int channel) const
493 {
494 #if MRPT_HAS_OPENCV
495  makeSureImageIsLoaded(); // For delayed loaded images stored externally
496  IplImage* ipl = ((IplImage*)img);
497  return (unsigned char*)&ipl
498  ->imageData[row * ipl->widthStep + col * ipl->nChannels + channel];
499 #else
500  return nullptr;
501 #endif
502 }
503 
505 {
506 #if !MRPT_HAS_OPENCV
507  return 100;
508 #else
509  return 8;
510 #endif
511 }
513 {
514 #if !MRPT_HAS_OPENCV
515  out << m_imgIsExternalStorage;
517 // Nothing else to serialize!
518 #else
519  {
520  // Added in version 6: possibility of being stored offline:
521  out << m_imgIsExternalStorage;
522 
524  {
525  out << m_externalFile;
526  }
527  else
528  { // Normal image loaded in memory:
529  ASSERT_(img != nullptr);
530 
531  const bool hasColor = isColor();
532 
533  out << hasColor;
534 
535  // Version >2: Color->JPEG, GrayScale->BYTE's array!
536  if (!hasColor)
537  {
538  // GRAY-SCALE: Raw bytes:
539  // Version 3: ZIP compression!
540  // Version 4: Skip zip if the image size <= 16Kb
541  int32_t width = ((IplImage*)img)->width;
542  int32_t height = ((IplImage*)img)->height;
543  int32_t origin = ((IplImage*)img)->origin;
544  int32_t imageSize = ((IplImage*)img)->imageSize;
545 
546  out << width << height << origin << imageSize;
547 
548  // Version 5: Use CImage::DISABLE_ZIP_COMPRESSION
549  bool imageStoredAsZip =
550  !CImage::DISABLE_ZIP_COMPRESSION && (imageSize > 16 * 1024);
551 
552  out << imageStoredAsZip;
553 
554  // Version 4: Skip zip if the image size <= 16Kb
555  if (imageStoredAsZip)
556  {
557  std::vector<unsigned char> tempBuf;
559  ((IplImage*)img)->imageData, // Data
560  ((IplImage*)img)->imageSize, // Size
561  tempBuf);
562 
563  int32_t zipDataLen = (int32_t)tempBuf.size();
564 
565  out << zipDataLen;
566 
567  out.WriteBuffer(&tempBuf[0], tempBuf.size());
568  tempBuf.clear();
569  }
570  else
571  {
572  out.WriteBuffer(
573  ((IplImage*)img)->imageData,
574  ((IplImage*)img)->imageSize);
575  }
576  }
577  else
578  {
579  // COLOR: High quality JPEG image
580 
581  // v7: If size is 0xN or Nx0, don't call "saveToStreamAsJPEG"!!
582  const int32_t width = ((IplImage*)img)->width;
583  const int32_t height = ((IplImage*)img)->height;
584 
585  // v8: If DISABLE_JPEG_COMPRESSION
587  {
588  // normal behavior: compress images:
589  out << width << height;
590 
591  if (width >= 1 && height >= 1)
592  {
593  // Save to temporary memory stream:
597 
598  const uint32_t nBytes =
599  static_cast<uint32_t>(aux.getTotalBytesCount());
600 
601  out << nBytes;
602  out.WriteBuffer(aux.getRawBufferData(), nBytes);
603  }
604  }
605  else
606  { // (New in v8)
607  // Don't JPEG-compress behavior:
608  // Use negative image sizes to signal this behavior:
609  const int32_t neg_width = -width;
610  const int32_t neg_height = -height;
611 
612  out << neg_width << neg_height;
613 
614  // Dump raw image data:
615  const IplImage* ipl = static_cast<const IplImage*>(img);
616  const size_t bytes_per_row = ipl->width * 3;
617 
618  out.WriteBuffer(
619  &ipl->imageData[0], bytes_per_row * ipl->height);
620  }
621  }
622  } // end m_imgIsExternalStorage=false
623  }
624 #endif
625 }
626 
628 {
629 #if !MRPT_HAS_OPENCV
630  if (version == 100)
631  {
634  in >> m_externalFile;
635  else
636  {
638  "[CImage] Cannot deserialize image since MRPT has been "
639  "compiled without OpenCV");
640  }
641  }
642 #else
643  releaseIpl(); // First, free current image.
644 
645  switch (version)
646  {
647  case 100: // Saved from an MRPT build without OpenCV:
648  {
651  }
652  break;
653  case 0:
654  {
655  uint32_t width, height, nChannels, imgLength;
656  uint8_t originTopLeft;
657 
658  in >> width >> height >> nChannels >> originTopLeft >> imgLength;
659 
660  changeSize(width, height, nChannels, originTopLeft != 0);
661  in.ReadBuffer(((IplImage*)img)->imageData, imgLength);
662  }
663  break;
664  case 1:
665  {
666  // Version 1: High quality JPEG image
668  uint32_t nBytes;
669  in >> nBytes;
670 
671  aux.changeSize(nBytes + 10);
672 
673  in.ReadBuffer(aux.getRawBufferData(), nBytes);
674 
675  aux.Seek(0);
676 
678  }
679  break;
680  case 2:
681  case 3:
682  case 4:
683  case 5:
684  case 6:
685  case 7:
686  case 8:
687  {
688  // Version 6: m_imgIsExternalStorage ??
689  if (version >= 6)
691  else
692  m_imgIsExternalStorage = false;
693 
695  {
696  // Just the file name:
697  in >> m_externalFile;
698  }
699  else
700  { // Normal, the whole image data:
701 
702  // Version 2: Color->JPEG, GrayScale->BYTE's array!
703  uint8_t hasColor;
704  in >> hasColor;
705  if (!hasColor)
706  {
707  // GRAY SCALE:
708  int32_t width, height, origin, imageSize;
709  in >> width >> height >> origin >> imageSize;
710 
711  changeSize(width, height, 1, origin == 0);
712  ASSERT_(imageSize == ((IplImage*)img)->imageSize);
713 
714  if (version == 2)
715  {
716  // RAW BYTES:
717  in.ReadBuffer(((IplImage*)img)->imageData, imageSize);
718  }
719  else
720  {
721  // Version 3: ZIP compression!
722  bool imageIsZIP = true;
723 
724  // Version 4: Skip zip if the image size <= 16Kb
725  // Version 5: Use CImage::DISABLE_ZIP_COMPRESSION
726  if (version == 4 && imageSize <= 16 * 1024)
727  imageIsZIP = false;
728 
729  if (version >= 5)
730  {
731  // It is stored int the stream:
732  in >> imageIsZIP;
733  }
734 
735  if (imageIsZIP)
736  {
737  uint32_t zipDataLen;
738  in >> zipDataLen;
739 
740 #if 0
741  size_t outDataBufferSize = imageSize;
742  size_t outDataActualSize;
744  in, zipDataLen, ((IplImage*)img)->imageData,
745  outDataBufferSize, outDataActualSize);
746  ASSERT_(outDataActualSize == outDataBufferSize);
747 #else
748  MRPT_TODO("Implement deserialization case:");
749  THROW_EXCEPTION("Implement mising method");
750 #endif
751  }
752  else
753  {
754  // Raw bytes:
755  in.ReadBuffer(
756  ((IplImage*)img)->imageData,
757  ((IplImage*)img)->imageSize);
758  }
759  }
760  }
761  else
762  {
763  bool loadJPEG = true;
764 
765  if (version >= 7)
766  {
768  in >> width >> height;
769 
770  if (width >= 1 && height >= 1)
771  {
772  loadJPEG = true;
773  }
774  else
775  {
776  loadJPEG = false;
777 
778  if (width < 0 && height < 0)
779  {
780  // v8: raw image:
781  const int32_t real_w = -width;
782  const int32_t real_h = -height;
783 
784  this->changeSize(real_w, real_h, 3, true);
785 
786  const IplImage* ipl =
787  static_cast<const IplImage*>(img);
788  const size_t bytes_per_row = ipl->width * 3;
789  for (int y = 0; y < ipl->height; y++)
790  {
791  const size_t nRead = in.ReadBuffer(
792  &ipl->imageData[y * ipl->widthStep],
793  bytes_per_row);
794  if (nRead != bytes_per_row)
796  "Error: Truncated data stream "
797  "while parsing raw image?")
798  }
799  }
800  else
801  {
802  // it's a 0xN or Nx0 image: just resize and load
803  // nothing:
804  this->changeSize(width, height, 3, true);
805  }
806  }
807  }
808 
809  // COLOR IMAGE: JPEG
810  if (loadJPEG)
811  {
813  uint32_t nBytes;
814  in >> nBytes;
815  aux.changeSize(nBytes + 10);
816  in.ReadBuffer(aux.getRawBufferData(), nBytes);
817  aux.Seek(0);
819  }
820  }
821  }
822  }
823  break;
824  default:
826  };
827 #endif
828 }
829 
830 /*---------------------------------------------------------------
831 Implements the writing to a mxArray for Matlab
832 ---------------------------------------------------------------*/
833 #if MRPT_HAS_MATLAB
834 // Add to implement mexplus::from template specialization
836 #endif
837 
839 {
840 #if MRPT_HAS_MATLAB
841  cv::Mat cvImg = cv::cvarrToMat(this->getAs<IplImage>());
842  return mexplus::from(cvImg);
843 #else
844  THROW_EXCEPTION("MRPT built without MATLAB/Mex support");
845 #endif
846 }
847 
848 /*---------------------------------------------------------------
849  getSize
850 ---------------------------------------------------------------*/
852 {
853 #if MRPT_HAS_OPENCV
854  makeSureImageIsLoaded(); // For delayed loaded images stored externally
855  ASSERT_(img != nullptr);
856  s.x = ((IplImage*)img)->width;
857  s.y = ((IplImage*)img)->height;
858 #endif
859 }
860 
861 /*---------------------------------------------------------------
862  getWidth
863 ---------------------------------------------------------------*/
864 size_t CImage::getWidth() const
865 {
866 #if MRPT_HAS_OPENCV
867  makeSureImageIsLoaded(); // For delayed loaded images stored externally
868  ASSERT_(img != nullptr);
869  return ((IplImage*)img)->width;
870 #else
871  return 0;
872 #endif
873 }
874 
875 /*---------------------------------------------------------------
876  getRowStride
877 ---------------------------------------------------------------*/
878 size_t CImage::getRowStride() const
879 {
880 #if MRPT_HAS_OPENCV
881  makeSureImageIsLoaded(); // For delayed loaded images stored externally
882  ASSERT_(img != nullptr);
883  return ((IplImage*)img)->widthStep;
884 #else
885  return 0;
886 #endif
887 }
888 
889 /*---------------------------------------------------------------
890  getWidth
891 ---------------------------------------------------------------*/
892 size_t CImage::getHeight() const
893 {
894 #if MRPT_HAS_OPENCV
895  makeSureImageIsLoaded(); // For delayed loaded images stored externally
896  ASSERT_(img != nullptr);
897  return ((IplImage*)img)->height;
898 #else
899  return 0;
900 #endif
901 }
902 
903 /*---------------------------------------------------------------
904  isColor
905 ---------------------------------------------------------------*/
906 bool CImage::isColor() const
907 {
908 #if MRPT_HAS_OPENCV
909  makeSureImageIsLoaded(); // For delayed loaded images stored externally
910  ASSERT_(img != nullptr);
911  return ((IplImage*)img)->nChannels > 1;
912 #else
913  return false;
914 #endif
915 }
916 
917 /*---------------------------------------------------------------
918  getChannelCount
919 ---------------------------------------------------------------*/
921 {
922 #if MRPT_HAS_OPENCV
923  makeSureImageIsLoaded(); // For delayed loaded images stored externally
924  ASSERT_(img != nullptr);
925  return static_cast<unsigned int>(((IplImage*)img)->nChannels);
926 #else
927  return 0;
928 #endif
929 }
930 
931 /*---------------------------------------------------------------
932  isOriginTopLeft
933 ---------------------------------------------------------------*/
935 {
936 #if MRPT_HAS_OPENCV
937  makeSureImageIsLoaded(); // For delayed loaded images stored externally
938  ASSERT_(img != nullptr);
939  return ((IplImage*)img)->origin == 0;
940 #else
941  THROW_EXCEPTION("MRPT compiled without OpenCV");
942 #endif
943 }
944 
945 /*---------------------------------------------------------------
946  getAsFloat
947 ---------------------------------------------------------------*/
949  unsigned int col, unsigned int row, unsigned int channel) const
950 {
951  makeSureImageIsLoaded(); // For delayed loaded images stored externally
952  // [0,255]->[0,1]
953  return (*(*this)(col, row, channel)) / 255.0f;
954 }
955 
956 /*---------------------------------------------------------------
957  getAsFloat
958 ---------------------------------------------------------------*/
959 float CImage::getAsFloat(unsigned int col, unsigned int row) const
960 {
961  // Is a RGB image??
962  if (isColor())
963  {
964  // Luminance: Y = 0.3R + 0.59G + 0.11B
965  unsigned char* pixels = (*this)(col, row, 0);
966  return (pixels[0] * 0.3f + pixels[1] * 0.59f + pixels[2] * 0.11f) /
967  255.0f;
968  }
969  else
970  {
971  // [0,255]->[0,1]
972  return (*(*this)(col, row, 0 /* Channel 0:Gray level */)) / 255.0f;
973  }
974 }
975 
976 /*---------------------------------------------------------------
977  getMaxAsFloat
978 ---------------------------------------------------------------*/
980 {
981  int x, y, cx = getWidth(), cy = getHeight();
982 
983  float maxPixel = 0;
984 
985  for (x = 0; x < cx; x++)
986  for (y = 0; y < cy; y++) maxPixel = max(maxPixel, getAsFloat(x, y));
987 
988  return maxPixel;
989 }
990 
991 /*---------------------------------------------------------------
992  grayscale
993 ---------------------------------------------------------------*/
995 {
996  CImage ret;
997  grayscale(ret);
998  return ret;
999 }
1000 
1001 // Auxiliary function for both ::grayscale() and ::grayscaleInPlace()
1002 #if MRPT_HAS_OPENCV
1003 IplImage* ipl_to_grayscale(const IplImage* img_src)
1004 {
1005  IplImage* img_dest =
1006  cvCreateImage(cvSize(img_src->width, img_src->height), IPL_DEPTH_8U, 1);
1007  img_dest->origin = img_src->origin;
1008 
1009 // If possible, use SSE optimized version:
1010 #if MRPT_HAS_SSE3
1011  if (is_aligned<16>(img_src->imageData) && (img_src->width & 0xF) == 0 &&
1012  img_src->widthStep == img_src->width * img_src->nChannels &&
1013  img_dest->widthStep == img_dest->width * img_dest->nChannels)
1014  {
1015  ASSERT_(is_aligned<16>(img_dest->imageData));
1017  (const uint8_t*)img_src->imageData, (uint8_t*)img_dest->imageData,
1018  img_src->width, img_src->height);
1019  return img_dest;
1020  }
1021 #endif
1022 
1023  // OpenCV Method:
1024  cvCvtColor(img_src, img_dest, CV_BGR2GRAY);
1025  return img_dest;
1026 }
1027 #endif
1028 
1029 /*---------------------------------------------------------------
1030  grayscale
1031 ---------------------------------------------------------------*/
1032 void CImage::grayscale(CImage& ret) const
1033 {
1034 #if MRPT_HAS_OPENCV
1035  // The image is already grayscale??
1036  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1037  const IplImage* ipl = this->getAs<const IplImage>();
1038  ASSERT_(ipl);
1039  if (ipl->nChannels == 1)
1040  {
1041  ret = *this;
1042  return;
1043  }
1044  else
1045  {
1046  // Convert to a single luminance channel image
1048  }
1049 #endif
1050 }
1051 
1052 /*---------------------------------------------------------------
1053  grayscaleInPlace
1054 ---------------------------------------------------------------*/
1056 {
1057 #if MRPT_HAS_OPENCV
1058  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1059  const IplImage* ipl = this->getAs<const IplImage>();
1060  ASSERT_(ipl);
1061  if (ipl->nChannels == 1) return; // Already done.
1062 
1064 #endif
1065 }
1066 
1067 /*---------------------------------------------------------------
1068  scaleHalf
1069 ---------------------------------------------------------------*/
1070 void CImage::scaleHalf(CImage& out) const
1071 {
1072 #if MRPT_HAS_OPENCV
1073  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1074  ASSERT_(img != nullptr);
1075  // Get this image size:
1076  const IplImage* img_src = ((IplImage*)img);
1077  const int w = img_src->width;
1078  const int h = img_src->height;
1079 
1080  // Create target image:
1081  IplImage* img_dest =
1082  cvCreateImage(cvSize(w >> 1, h >> 1), IPL_DEPTH_8U, img_src->nChannels);
1083  img_dest->origin = img_src->origin;
1084  memcpy(img_dest->colorModel, img_src->colorModel, 4);
1085  memcpy(img_dest->channelSeq, img_src->channelSeq, 4);
1086  img_dest->dataOrder = img_src->dataOrder;
1087 
1088 // If possible, use SSE optimized version:
1089 #if MRPT_HAS_SSE3
1090  if (img_src->nChannels == 3 && is_aligned<16>(img_src->imageData) &&
1091  is_aligned<16>(img_dest->imageData) && (w & 0xF) == 0 &&
1092  img_src->widthStep == img_src->width * img_src->nChannels &&
1093  img_dest->widthStep == img_dest->width * img_dest->nChannels)
1094  {
1096  (const uint8_t*)img_src->imageData, (uint8_t*)img_dest->imageData,
1097  w, h);
1098  out.setFromIplImage(img_dest);
1099  return;
1100  }
1101 #endif
1102 
1103 #if MRPT_HAS_SSE2
1104  if (img_src->nChannels == 1 && is_aligned<16>(img_src->imageData) &&
1105  is_aligned<16>(img_dest->imageData) && (w & 0xF) == 0 &&
1106  img_src->widthStep == img_src->width * img_src->nChannels &&
1107  img_dest->widthStep == img_dest->width * img_dest->nChannels)
1108  {
1110  (const uint8_t*)img_src->imageData, (uint8_t*)img_dest->imageData,
1111  w, h);
1112 
1113  out.setFromIplImage(img_dest);
1114  return;
1115  }
1116 #endif
1117 
1118  // Fall back to slow method:
1119  cvResize(img_src, img_dest, IMG_INTERP_NN);
1120  out.setFromIplImage(img_dest);
1121 #endif
1122 }
1123 
1124 /*---------------------------------------------------------------
1125  scaleHalfSmooth
1126 ---------------------------------------------------------------*/
1128 {
1129 #if MRPT_HAS_OPENCV
1130  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1131  ASSERT_(img != nullptr);
1132  // Get this image size:
1133  const IplImage* img_src = ((IplImage*)img);
1134  const int w = img_src->width;
1135  const int h = img_src->height;
1136 
1137  // Create target image:
1138  IplImage* img_dest =
1139  cvCreateImage(cvSize(w >> 1, h >> 1), IPL_DEPTH_8U, img_src->nChannels);
1140  img_dest->origin = img_src->origin;
1141  memcpy(img_dest->colorModel, img_src->colorModel, 4);
1142  memcpy(img_dest->channelSeq, img_src->channelSeq, 4);
1143  img_dest->dataOrder = img_src->dataOrder;
1144 
1145 // If possible, use SSE optimized version:
1146 #if MRPT_HAS_SSE2
1147  if (img_src->nChannels == 1 && is_aligned<16>(img_src->imageData) &&
1148  is_aligned<16>(img_dest->imageData) && (w & 0xF) == 0 &&
1149  img_src->widthStep == img_src->width * img_src->nChannels &&
1150  img_dest->widthStep == img_dest->width * img_dest->nChannels)
1151  {
1153  (const uint8_t*)img_src->imageData, (uint8_t*)img_dest->imageData,
1154  w, h);
1155 
1156  out.setFromIplImage(img_dest);
1157  return;
1158  }
1159 #endif
1160 
1161  // Fall back to slow method:
1162  cvResize(img_src, img_dest, IMG_INTERP_LINEAR);
1163  out.setFromIplImage(img_dest);
1164 #endif
1165 }
1166 
1167 /*---------------------------------------------------------------
1168  scaleDouble
1169 ---------------------------------------------------------------*/
1170 void CImage::scaleDouble(CImage& out) const
1171 {
1172  out = *this;
1173  const TImageSize siz = this->getSize();
1174  out.scaleImage(siz.x * 2, siz.y * 2);
1175 }
1176 
1177 /*---------------------------------------------------------------
1178  getChannelsOrder
1179 ---------------------------------------------------------------*/
1180 const char* CImage::getChannelsOrder() const
1181 {
1182 #if MRPT_HAS_OPENCV
1183  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1184  ASSERT_(img);
1185  return ((IplImage*)img)->channelSeq;
1186 #else
1187  THROW_EXCEPTION("MRPT compiled without OpenCV");
1188 #endif
1189 }
1190 
1191 /*---------------------------------------------------------------
1192  loadFromMemoryBuffer
1193 ---------------------------------------------------------------*/
1195  unsigned int width, unsigned int height, unsigned int bytesPerRow,
1196  unsigned char* red, unsigned char* green, unsigned char* blue)
1197 {
1198 #if MRPT_HAS_OPENCV
1199  MRPT_START
1200 
1201  // Fill in the IPL structure:
1202  changeSize(width, height, 3, true);
1203 
1204  // Copy the image data:
1205  for (unsigned int y = 0; y < height; y++)
1206  {
1207  // The target pixels:
1208  unsigned char* dest = (unsigned char*)((IplImage*)img)->imageData +
1209  ((IplImage*)img)->widthStep * y;
1210 
1211  // Source channels:
1212  unsigned char* srcR = red + bytesPerRow * y;
1213  unsigned char* srcG = green + bytesPerRow * y;
1214  unsigned char* srcB = blue + bytesPerRow * y;
1215 
1216  for (unsigned int x = 0; x < width; x++)
1217  {
1218  *(dest++) = *(srcB++);
1219  *(dest++) = *(srcG++);
1220  *(dest++) = *(srcR++);
1221  } // end of x
1222  } // end of y
1223 
1224  MRPT_END
1225 #endif
1226 }
1227 
1228 /*---------------------------------------------------------------
1229  setOriginTopLeft
1230 ---------------------------------------------------------------*/
1232 {
1233 #if MRPT_HAS_OPENCV
1234  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1235  ASSERT_(img);
1236  ((IplImage*)img)->origin = val ? 0 : 1;
1237 #endif
1238 }
1239 
1240 /*---------------------------------------------------------------
1241  setPixel
1242 ---------------------------------------------------------------*/
1243 void CImage::setPixel(int x, int y, size_t color)
1244 {
1245 #if MRPT_HAS_OPENCV
1246 
1247 #if defined(_DEBUG) || (MRPT_ALWAYS_CHECKS_DEBUG)
1248  MRPT_START
1249 #endif
1250 
1251  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1252 
1253  IplImage* ipl = ((IplImage*)img);
1254 
1255 #if defined(_DEBUG) || (MRPT_ALWAYS_CHECKS_DEBUG)
1256  ASSERT_(ipl);
1257 #endif
1258 
1259  if (x >= 0 && y >= 0 && y < ipl->height && x < ipl->width)
1260  {
1261  // The pixel coordinates is valid:
1262  if (ipl->nChannels == 1)
1263  {
1264  *((unsigned char*)&ipl->imageData[y * ipl->widthStep + x]) =
1265  (unsigned char)color;
1266  }
1267  else
1268  {
1269 #if defined(_DEBUG) || (MRPT_ALWAYS_CHECKS_DEBUG)
1270  ASSERT_(ipl->nChannels == 3);
1271  if (ipl->dataOrder != 0)
1273  "Please, use interleaved images like normal people!!! :-)");
1274 #endif
1275  unsigned char* dest =
1276  (unsigned char*)&ipl->imageData[y * ipl->widthStep + 3 * x];
1277  unsigned char* src = (unsigned char*)&color;
1278 
1279  // Copy the color:
1280  *dest++ = *src++; // R
1281  *dest++ = *src++; // G
1282  *dest++ = *src++; // B
1283  }
1284  }
1285 
1286 #if defined(_DEBUG) || (MRPT_ALWAYS_CHECKS_DEBUG)
1287  MRPT_END
1288 #endif
1289 
1290 #endif
1291 }
1292 
1293 /*---------------------------------------------------------------
1294  line
1295 ---------------------------------------------------------------*/
1297  int x0, int y0, int x1, int y1, const mrpt::img::TColor color,
1298  unsigned int width, TPenStyle penStyle)
1299 {
1300 #if MRPT_HAS_OPENCV
1301  MRPT_UNUSED_PARAM(penStyle);
1302  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1303  IplImage* ipl = ((IplImage*)img);
1304  ASSERT_(ipl);
1305 
1306  cvLine(
1307  ipl, cvPoint(x0, y0), cvPoint(x1, y1),
1308  CV_RGB(color.R, color.G, color.B), width);
1309 #endif
1310 }
1311 
1312 /*---------------------------------------------------------------
1313  drawCircle
1314 ---------------------------------------------------------------*/
1316  int x, int y, int radius, const mrpt::img::TColor& color,
1317  unsigned int width)
1318 {
1319 #if MRPT_HAS_OPENCV
1320  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1321  IplImage* ipl = ((IplImage*)img);
1322  ASSERT_(ipl);
1323 
1324  cvCircle(
1325  ipl, cvPoint(x, y), radius, CV_RGB(color.R, color.G, color.B), width);
1326 #endif
1327 } // end
1328 
1329 /*---------------------------------------------------------------
1330  update_patch
1331 --------------------------------------------------------------*/
1333  const CImage& patch, const unsigned int col_, const unsigned int row_)
1334 {
1335 #if MRPT_HAS_OPENCV
1336  IplImage* ipl_int = ((IplImage*)img);
1337  IplImage* ipl_ext = ((IplImage*)patch.img);
1338  ASSERT_(ipl_int);
1339  ASSERT_(ipl_ext);
1340  // We check that patch do not jut out of the image.
1341  if (row_ + ipl_ext->height > getHeight() ||
1342  col_ + ipl_ext->width > getWidth())
1343  {
1344  THROW_EXCEPTION("Error : Patch jut out of image");
1345  }
1346  for (unsigned int i = 0; i < patch.getHeight(); i++)
1347  {
1348  memcpy(
1349  &ipl_int->imageData[(i + row_) * ipl_int->widthStep +
1350  col_ * ipl_int->nChannels],
1351  &ipl_ext->imageData[i * ipl_ext->widthStep], ipl_ext->widthStep);
1352  }
1353 #endif
1354 }
1355 
1356 /*---------------------------------------------------------------
1357  extract_patch
1358 ---------------------------------------------------------------*/
1360  CImage& patch, const unsigned int col_, const unsigned int row_,
1361  const unsigned int col_num, const unsigned int row_num) const
1362 {
1363 #if MRPT_HAS_OPENCV
1364  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1365 
1366  IplImage* ipl_int = ((IplImage*)img);
1367  ASSERT_(ipl_int);
1368 
1369  if ((ipl_int->width < (int)(col_ + col_num)) ||
1370  (ipl_int->height < (int)(row_ + row_num)))
1371  {
1373  format(
1374  "Trying to extract patch out of image boundaries: Image "
1375  "size=%ix%i, Patch size=%ux%u, extraction location=(%u,%u)",
1376  ipl_int->width, ipl_int->height, col_num, row_num, col_, row_))
1377  }
1378 
1379  patch.resize(col_num, row_num, ((IplImage*)img)->nChannels, true);
1380  IplImage* ipl_ext = ((IplImage*)patch.img);
1381  ASSERT_(ipl_ext);
1382 
1383  for (unsigned int i = 0; i < row_num; i++)
1384  {
1385  memcpy(
1386  &ipl_ext->imageData[i * ipl_ext->widthStep],
1387  &ipl_int->imageData[(i + row_) * ipl_int->widthStep +
1388  col_ * ipl_int->nChannels],
1389  ipl_ext->widthStep);
1390  }
1391 
1392 #endif
1393 }
1394 
1395 /*---------------------------------------------------------------
1396  correlate
1397 ---------------------------------------------------------------*/
1399  const CImage& img2, int width_init, int height_init) const
1400 {
1401 #if MRPT_HAS_OPENCV
1402  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1403 
1404  if ((img2.getWidth() + width_init > getWidth()) |
1405  (img2.getHeight() + height_init > getHeight()))
1406  THROW_EXCEPTION("Correlation Error!, image to correlate out of bounds");
1407 
1408  unsigned int i, j;
1409  float x1, x2;
1410  float syy = 0.0f, sxy = 0.0f, sxx = 0.0f, m1 = 0.0f, m2 = 0.0f,
1411  n = (float)(img2.getHeight() * img2.getWidth());
1412  // IplImage *ipl1 = (*this).img;
1413  // IplImage *ipl2 = img2.img;
1414 
1415  // find the means
1416  for (i = 0; i < img2.getHeight(); i++)
1417  {
1418  for (j = 0; j < img2.getWidth(); j++)
1419  {
1420  m1 += *(*this)(
1421  j + width_init,
1422  i + height_init); //(double)(ipl1->imageData[i*ipl1->widthStep
1423  //+ j ]);
1424  m2 += *img2(
1425  j, i); //(double)(ipl2->imageData[i*ipl2->widthStep + j ]);
1426  } //[ row * ipl->widthStep + col * ipl->nChannels + channel ];
1427  }
1428  m1 /= n;
1429  m2 /= n;
1430 
1431  for (i = 0; i < img2.getHeight(); i++)
1432  {
1433  for (j = 0; j < img2.getWidth(); j++)
1434  {
1435  x1 = *(*this)(j + width_init, i + height_init) -
1436  m1; //(double)(ipl1->imageData[i*ipl1->widthStep + j]) - m1;
1437  x2 = *img2(j, i) -
1438  m2; //(double)(ipl2->imageData[i*ipl2->widthStep + j]) - m2;
1439  sxx += x1 * x1;
1440  syy += x2 * x2;
1441  sxy += x1 * x2;
1442  }
1443  }
1444 
1445  return sxy / sqrt(sxx * syy);
1446 #else
1447  return 0;
1448 #endif
1449 }
1450 
1451 /*---------------------------------------------------------------
1452  cross_correlation
1453 ---------------------------------------------------------------*/
1455  const CImage& patch_img, size_t& x_max, size_t& y_max, double& max_val,
1456  int x_search_ini, int y_search_ini, int x_search_size, int y_search_size,
1457  CImage* out_corr_image) const
1458 {
1459  MRPT_START
1460 
1461  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1462 
1463 #if MRPT_HAS_OPENCV
1464  double mini;
1465  CvPoint min_point, max_point;
1466 
1467  bool entireImg =
1468  (x_search_ini < 0 || y_search_ini < 0 || x_search_size < 0 ||
1469  y_search_size < 0);
1470 
1471  const IplImage *im, *patch_im;
1472 
1473  if (this->isColor() && patch_img.isColor())
1474  {
1475  const IplImage* im_ = this->getAs<IplImage>();
1476  const IplImage* patch_im_ = patch_img.getAs<IplImage>();
1477 
1478  IplImage* aux = cvCreateImage(cvGetSize(im_), 8, 1);
1479  IplImage* aux2 = cvCreateImage(cvGetSize(patch_im_), 8, 1);
1480  cvCvtColor(im_, aux, CV_BGR2GRAY);
1481  cvCvtColor(patch_im_, aux2, CV_BGR2GRAY);
1482  im = aux;
1483  patch_im = aux2;
1484  }
1485  else
1486  {
1487  im = this->getAs<IplImage>();
1488  patch_im = patch_img.getAs<IplImage>();
1489  }
1490 
1491  if (entireImg)
1492  {
1493  x_search_size = im->width - patch_im->width;
1494  y_search_size = im->height - patch_im->height;
1495  }
1496 
1497  // JLBC: Perhaps is better to raise the exception always??
1498  if ((x_search_ini + x_search_size + patch_im->width - 1) > im->width)
1499  x_search_size -=
1500  (x_search_ini + x_search_size + patch_im->width - 1) - im->width;
1501 
1502  if ((y_search_ini + y_search_size + patch_im->height - 1) > im->height)
1503  y_search_size -=
1504  (y_search_ini + y_search_size + patch_im->height - 1) - im->height;
1505 
1506  ASSERT_((x_search_ini + x_search_size + patch_im->width - 1) <= im->width);
1507  ASSERT_(
1508  (y_search_ini + y_search_size + patch_im->height - 1) <= im->height);
1509  IplImage* result = cvCreateImage(
1510  cvSize(x_search_size + 1, y_search_size + 1), IPL_DEPTH_32F, 1);
1511 
1512  const IplImage* ipl_ext;
1513 
1514  if (!entireImg)
1515  {
1516  IplImage* aux = cvCreateImage(
1517  cvSize(
1518  patch_im->width + x_search_size,
1519  patch_im->height + y_search_size),
1520  IPL_DEPTH_8U, 1);
1521  for (unsigned int i = 0; i < (unsigned int)y_search_size; i++)
1522  {
1523  memcpy(
1524  &aux->imageData[i * aux->widthStep],
1525  &im->imageData[(i + y_search_ini) * im->widthStep +
1526  x_search_ini * im->nChannels],
1527  aux->width * aux->nChannels); // widthStep); <-- JLBC:
1528  // widthstep SHOULD NOT be used
1529  // as the length of each row (the
1530  // last one may be shorter!!)
1531  }
1532  ipl_ext = aux;
1533  }
1534  else
1535  {
1536  ipl_ext = im;
1537  }
1538 
1539  // Compute cross correlation:
1540  cvMatchTemplate(ipl_ext, patch_im, result, CV_TM_CCORR_NORMED);
1541  // cvMatchTemplate(ipl_ext,patch_im,result,CV_TM_CCOEFF_NORMED);
1542 
1543  // Find the max point:
1544  cvMinMaxLoc(result, &mini, &max_val, &min_point, &max_point, nullptr);
1545  x_max = max_point.x + x_search_ini + (round(patch_im->width - 1) / 2);
1546  y_max = max_point.y + y_search_ini + (round(patch_im->height - 1) / 2);
1547 
1548  // Free memory:
1549  if (!entireImg)
1550  {
1551  IplImage* aux = const_cast<IplImage*>(ipl_ext);
1552  cvReleaseImage(&aux);
1553  ipl_ext = nullptr;
1554  }
1555 
1556  // Leave output image?
1557  if (out_corr_image)
1558  out_corr_image->setFromIplImage(result);
1559  else
1560  cvReleaseImage(&result);
1561 #else
1562  THROW_EXCEPTION("The MRPT has been compiled with MRPT_HAS_OPENCV=0 !");
1563 #endif
1564 
1565  MRPT_END
1566 }
1567 
1568 /*---------------------------------------------------------------
1569  normalize
1570 ---------------------------------------------------------------*/
1572 {
1573 #if MRPT_HAS_OPENCV
1574  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1575  IplImage* ipl = getAs<IplImage>(); // Source Image
1576  ASSERT_(ipl);
1577  ASSERTMSG_(
1578  ipl->nChannels == 1,
1579  "CImage::normalize() only defined for grayscale images.");
1580 
1581  uint8_t min_ = 255, max_ = 1;
1582  for (int y = 0; y < ipl->height; y++)
1583  {
1584  const uint8_t* ptr = reinterpret_cast<const uint8_t*>(
1585  ipl->imageData + y * ipl->widthStep);
1586  for (int x = 0; x < ipl->width; x++)
1587  {
1588  const uint8_t val = *ptr++;
1589  if (min_ > val) min_ = val;
1590  if (max_ < val) max_ = val;
1591  }
1592  }
1593 
1594  // Compute scale factor & build convert look-up-table:
1595  const double s = 255.0 / ((double)max_ - (double)min_);
1596  uint8_t lut[256];
1597  for (int v = 0; v < 256; v++) lut[v] = static_cast<uint8_t>((v - min_) * s);
1598 
1599  // Apply LUT:
1600  for (int y = 0; y < ipl->height; y++)
1601  {
1602  uint8_t* ptr =
1603  reinterpret_cast<uint8_t*>(ipl->imageData + y * ipl->widthStep);
1604  for (int x = 0; x < ipl->width; x++)
1605  {
1606  *ptr = lut[*ptr];
1607  ptr++;
1608  }
1609  }
1610 #endif
1611 }
1612 
1613 /*---------------------------------------------------------------
1614  getAsMatrix
1615 ---------------------------------------------------------------*/
1617  CMatrixFloat& outMatrix, bool doResize, int x_min, int y_min, int x_max,
1618  int y_max) const
1619 {
1620 #if MRPT_HAS_OPENCV
1621  MRPT_START
1622 
1623  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1624  ASSERT_(img);
1625 
1626  // Set sizes:
1627  if (x_max == -1) x_max = ((IplImage*)img)->width - 1;
1628  if (y_max == -1) y_max = ((IplImage*)img)->height - 1;
1629 
1630  ASSERT_(x_min >= 0 && x_min < ((IplImage*)img)->width && x_min < x_max);
1631  ASSERT_(y_min >= 0 && y_min < ((IplImage*)img)->height && y_min < y_max);
1632 
1633  int lx = (x_max - x_min + 1);
1634  int ly = (y_max - y_min + 1);
1635 
1636  if (doResize || (int)outMatrix.rows() < ly || (int)outMatrix.cols() < lx)
1637  outMatrix.setSize(y_max - y_min + 1, x_max - x_min + 1);
1638 
1639  if (isColor())
1640  {
1641  // Luminance: Y = 0.3R + 0.59G + 0.11B
1642  for (int y = 0; y < ly; y++)
1643  {
1644  unsigned char* pixels = this->get_unsafe(x_min, y_min + y, 0);
1645  float aux;
1646  for (int x = 0; x < lx; x++)
1647  {
1648  aux = *pixels++ * 0.3f * (1.0f / 255);
1649  aux += *pixels++ * 0.59f * (1.0f / 255);
1650  aux += *pixels++ * 0.11f * (1.0f / 255);
1651  outMatrix.set_unsafe(y, x, aux);
1652  }
1653  }
1654  }
1655  else
1656  {
1657  for (int y = 0; y < ly; y++)
1658  {
1659  unsigned char* pixels = this->get_unsafe(x_min, y_min + y, 0);
1660  for (int x = 0; x < lx; x++)
1661  outMatrix.set_unsafe(y, x, (*pixels++) * (1.0f / 255));
1662  }
1663  }
1664 
1665  MRPT_END
1666 #endif
1667 }
1668 
1669 /*---------------------------------------------------------------
1670  getAsRGBMatrices
1671 ---------------------------------------------------------------*/
1673  mrpt::math::CMatrixFloat& outMatrixR, mrpt::math::CMatrixFloat& outMatrixG,
1674  mrpt::math::CMatrixFloat& outMatrixB, bool doResize, int x_min, int y_min,
1675  int x_max, int y_max) const
1676 {
1677 #if MRPT_HAS_OPENCV
1678  MRPT_START
1679 
1680  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1681  ASSERT_(img);
1682 
1683  // Set sizes:
1684  if (x_max == -1) x_max = ((IplImage*)img)->width - 1;
1685  if (y_max == -1) y_max = ((IplImage*)img)->height - 1;
1686 
1687  ASSERT_(x_min >= 0 && x_min < ((IplImage*)img)->width && x_min < x_max);
1688  ASSERT_(y_min >= 0 && y_min < ((IplImage*)img)->height && y_min < y_max);
1689 
1690  int lx = (x_max - x_min + 1);
1691  int ly = (y_max - y_min + 1);
1692 
1693  if (doResize || (int)outMatrixR.rows() < ly || (int)outMatrixR.cols() < lx)
1694  outMatrixR.setSize(y_max - y_min + 1, x_max - x_min + 1);
1695  if (doResize || (int)outMatrixG.rows() < ly || (int)outMatrixG.cols() < lx)
1696  outMatrixG.setSize(y_max - y_min + 1, x_max - x_min + 1);
1697  if (doResize || (int)outMatrixB.rows() < ly || (int)outMatrixB.cols() < lx)
1698  outMatrixB.setSize(y_max - y_min + 1, x_max - x_min + 1);
1699 
1700  if (isColor())
1701  {
1702  for (int y = 0; y < ly; y++)
1703  {
1704  unsigned char* pixels = this->get_unsafe(x_min, y_min + y, 0);
1705  float aux;
1706  for (int x = 0; x < lx; x++)
1707  {
1708  aux = *pixels++ * (1.0f / 255);
1709  outMatrixR.set_unsafe(y, x, aux);
1710  aux = *pixels++ * (1.0f / 255);
1711  outMatrixG.set_unsafe(y, x, aux);
1712  aux = *pixels++ * (1.0f / 255);
1713  outMatrixB.set_unsafe(y, x, aux);
1714  }
1715  }
1716  }
1717  else
1718  {
1719  for (int y = 0; y < ly; y++)
1720  {
1721  unsigned char* pixels = this->get_unsafe(x_min, y_min + y, 0);
1722  for (int x = 0; x < lx; x++)
1723  {
1724  outMatrixR.set_unsafe(y, x, (*pixels) * (1.0f / 255));
1725  outMatrixG.set_unsafe(y, x, (*pixels) * (1.0f / 255));
1726  outMatrixB.set_unsafe(y, x, (*pixels++) * (1.0f / 255));
1727  }
1728  }
1729  }
1730 
1731  MRPT_END
1732 #endif
1733 }
1734 
1735 /*---------------------------------------------------------------
1736  cross_correlation_FFT
1737 ---------------------------------------------------------------*/
1739  const CImage& in_img, CMatrixFloat& out_corr, int u_search_ini,
1740  int v_search_ini, int u_search_size, int v_search_size, float biasThisImg,
1741  float biasInImg) const
1742 {
1743  MRPT_START
1744 
1745  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1746  ASSERT_(img);
1747 
1748  // Set limits:
1749  if (u_search_ini == -1) u_search_ini = 0;
1750  if (v_search_ini == -1) v_search_ini = 0;
1751  if (u_search_size == -1) u_search_size = getWidth();
1752  if (v_search_size == -1) v_search_size = getHeight();
1753 
1754  int u_search_end = u_search_ini + u_search_size - 1;
1755  int v_search_end = v_search_ini + v_search_size - 1;
1756 
1757  ASSERT_(u_search_end < (int)getWidth());
1758  ASSERT_(v_search_end < (int)getHeight());
1759 
1760  // Find smallest valid size:
1761  size_t x, y;
1762  size_t actual_lx = max(u_search_size, (int)in_img.getWidth());
1763  size_t actual_ly = max(v_search_size, (int)in_img.getHeight());
1764  size_t lx = mrpt::round2up(actual_lx);
1765  size_t ly = mrpt::round2up(actual_ly);
1766 
1767  // printf("ly=%u lx=%u\n",ly,lx);
1768 
1769  CMatrix i1(ly, lx), i2(ly, lx);
1770 
1771  // We fill the images with the bias, such as when we substract the bias
1772  // later on,
1773  // those pixels not really occupied by the image really becomes zero:
1774  i1.fill(biasInImg);
1775  i2.fill(biasThisImg);
1776 
1777  // Get as matrixes, padded with zeros up to power-of-two sizes:
1778  getAsMatrix(
1779  i2, false, u_search_ini, v_search_ini, u_search_ini + u_search_size - 1,
1780  v_search_ini + v_search_size - 1);
1781  in_img.getAsMatrix(i1, false);
1782 
1783  // Remove the bias now:
1784  i2.array() -= biasThisImg;
1785  i1.array() -= biasInImg;
1786 
1787  // Fill the "padded zeros" with copies of the images:
1788  // SAVE_MATRIX(i1); SAVE_MATRIX(i2);
1789 
1790  // FFT:
1791  CMatrix I1_R, I1_I, I2_R, I2_I, ZEROS(ly, lx);
1792  math::dft2_complex(i1, ZEROS, I1_R, I1_I);
1793  math::dft2_complex(i2, ZEROS, I2_R, I2_I);
1794 
1795  // SAVE_MATRIX(I1_R); SAVE_MATRIX(I1_I);
1796  // SAVE_MATRIX(I2_R); SAVE_MATRIX(I2_I);
1797 
1798  // Compute the COMPLEX division of I2 by I1:
1799  for (y = 0; y < ly; y++)
1800  for (x = 0; x < lx; x++)
1801  {
1802  float r1 = I1_R.get_unsafe(y, x);
1803  float r2 = I2_R.get_unsafe(y, x);
1804 
1805  float ii1 = I1_I.get_unsafe(y, x);
1806  float ii2 = I2_I.get_unsafe(y, x);
1807 
1808  float den = square(r1) + square(ii1);
1809  I2_R.set_unsafe(y, x, (r1 * r2 + ii1 * ii2) / den);
1810  I2_I.set_unsafe(y, x, (ii2 * r1 - r2 * ii1) / den);
1811  }
1812 
1813  // I2_R.saveToTextFile("DIV_R.txt");
1814  // I2_I.saveToTextFile("DIV_I.txt");
1815 
1816  // IFFT:
1817  CMatrix res_R, res_I;
1818  math::idft2_complex(I2_R, I2_I, res_R, res_I);
1819 
1820  out_corr.setSize(actual_ly, actual_lx);
1821  for (y = 0; y < actual_ly; y++)
1822  for (x = 0; x < actual_lx; x++)
1823  out_corr(y, x) = sqrt(square(res_R(y, x)) + square(res_I(y, x)));
1824 
1825  MRPT_END
1826 }
1827 
1828 /*---------------------------------------------------------------
1829  getAsMatrixTiled
1830 ---------------------------------------------------------------*/
1831 void CImage::getAsMatrixTiled(CMatrix& outMatrix) const
1832 {
1833 #if MRPT_HAS_OPENCV
1834  MRPT_START
1835 
1836  makeSureImageIsLoaded(); // For delayed loaded images stored externally
1837  ASSERT_(img);
1838 
1839  // The size of the matrix:
1840  size_t matrix_lx = outMatrix.cols();
1841  size_t matrix_ly = outMatrix.rows();
1842 
1843  if (isColor())
1844  {
1845  // Luminance: Y = 0.3R + 0.59G + 0.11B
1846  for (unsigned int y = 0; y < matrix_ly; y++)
1847  {
1848  unsigned char* min_pixels =
1849  (*this)(0, y % ((IplImage*)img)->height, 0);
1850  unsigned char* max_pixels =
1851  min_pixels + ((IplImage*)img)->width * 3;
1852  unsigned char* pixels = min_pixels;
1853  float aux;
1854  for (unsigned int x = 0; x < matrix_lx; x++)
1855  {
1856  aux = *pixels++ * 0.30f;
1857  aux += *pixels++ * 0.59f;
1858  aux += *pixels++ * 0.11f;
1859  outMatrix.set_unsafe(y, x, aux);
1860  if (pixels >= max_pixels) pixels = min_pixels;
1861  }
1862  }
1863  }
1864  else
1865  {
1866  for (unsigned int y = 0; y < matrix_ly; y++)
1867  {
1868  unsigned char* min_pixels =
1869  (*this)(0, y % ((IplImage*)img)->height, 0);
1870  unsigned char* max_pixels = min_pixels + ((IplImage*)img)->width;
1871  unsigned char* pixels = min_pixels;
1872  for (unsigned int x = 0; x < matrix_lx; x++)
1873  {
1874  outMatrix.set_unsafe(y, x, *pixels++);
1875  if (pixels >= max_pixels) pixels = min_pixels;
1876  }
1877  }
1878  }
1879 
1880  MRPT_END
1881 #endif
1882 }
1883 
1884 /*---------------------------------------------------------------
1885  setExternalStorage
1886 ---------------------------------------------------------------*/
1887 void CImage::setExternalStorage(const std::string& fileName) noexcept
1888 {
1889  releaseIpl();
1890  m_externalFile = fileName;
1891  m_imgIsExternalStorage = true;
1892 }
1893 
1894 /*---------------------------------------------------------------
1895  unload
1896 ---------------------------------------------------------------*/
1897 void CImage::unload() const noexcept
1898 {
1900  const_cast<CImage*>(this)->releaseIpl(
1901  true); // Do NOT mark the image as NON external
1902 }
1903 
1904 /*---------------------------------------------------------------
1905  releaseIpl
1906 ---------------------------------------------------------------*/
1907 void CImage::releaseIpl(bool thisIsExternalImgUnload) noexcept
1908 {
1909 #if MRPT_HAS_OPENCV
1910  if (img && !m_imgIsReadOnly)
1911  {
1912  IplImage* ptr = (IplImage*)img;
1913  cvReleaseImage(&ptr);
1914  }
1915  img = nullptr;
1916  m_imgIsReadOnly = false;
1917  if (!thisIsExternalImgUnload)
1918  {
1919  m_imgIsExternalStorage = false;
1920  m_externalFile = string();
1921  }
1922 #endif
1923 }
1924 
1925 /*---------------------------------------------------------------
1926  makeSureImageIsLoaded
1927 ---------------------------------------------------------------*/
1929 {
1930  if (img != nullptr) return; // OK, continue
1931 
1933  {
1934  // Load the file:
1935  string wholeFile;
1937 
1938  const std::string tmpFile = m_externalFile;
1939 
1940  bool ret = const_cast<CImage*>(this)->loadFromFile(wholeFile);
1941 
1942  // These are removed by "loadFromFile", and that's good, just fix it
1943  // here and carry on.
1944  m_imgIsExternalStorage = true;
1945  m_externalFile = tmpFile;
1946 
1947  if (!ret)
1950  "Error loading externally-stored image from: %s",
1951  wholeFile.c_str());
1952  }
1953  else
1954  THROW_EXCEPTION("img is nullptr in a non-externally stored image.");
1955 }
1956 
1957 /*---------------------------------------------------------------
1958  getExternalStorageFileAbsolutePath
1959 ---------------------------------------------------------------*/
1961 {
1962  ASSERT_(m_externalFile.size() > 2);
1963 
1964  if (m_externalFile[0] == '/' ||
1965  (m_externalFile[1] == ':' && m_externalFile[2] == '\\'))
1966  {
1967  out_path = m_externalFile;
1968  }
1969  else
1970  {
1971  out_path = IMAGES_PATH_BASE;
1972 
1973  size_t N = IMAGES_PATH_BASE.size() - 1;
1974  if (IMAGES_PATH_BASE[N] != '/' && IMAGES_PATH_BASE[N] != '\\')
1975  out_path += "/";
1976 
1977  out_path += m_externalFile;
1978  }
1979 }
1980 
1981 /*---------------------------------------------------------------
1982  flipVertical
1983 ---------------------------------------------------------------*/
1984 void CImage::flipVertical(bool also_swapRB)
1985 {
1986 #if MRPT_HAS_OPENCV
1987  IplImage* ptr = (IplImage*)img;
1988  int options = CV_CVTIMG_FLIP;
1989  if (also_swapRB) options |= CV_CVTIMG_SWAP_RB;
1990  cvConvertImage(ptr, ptr, options);
1991 #endif
1992 }
1993 
1995 {
1996 #if MRPT_HAS_OPENCV
1997  IplImage* ptr = (IplImage*)img;
1998  cvFlip(ptr, nullptr, 1);
1999 #endif
2000 }
2001 
2002 /*---------------------------------------------------------------
2003  swapRB
2004 ---------------------------------------------------------------*/
2006 {
2007 #if MRPT_HAS_OPENCV
2008  makeSureImageIsLoaded(); // For delayed loaded images stored externally
2009  ASSERT_(img != nullptr);
2010  IplImage* ptr = (IplImage*)img;
2011  cvConvertImage(ptr, ptr, CV_CVTIMG_SWAP_RB);
2012 #endif
2013 }
2014 
2015 /*---------------------------------------------------------------
2016  rectifyImageInPlace
2017 ---------------------------------------------------------------*/
2018 void CImage::rectifyImageInPlace(void* mapX, void* mapY)
2019 {
2020 #if MRPT_HAS_OPENCV
2021  makeSureImageIsLoaded(); // For delayed loaded images stored externally
2022  ASSERT_(img != nullptr);
2023 
2024 #if MRPT_OPENCV_VERSION_NUM < 0x200
2025  THROW_EXCEPTION("This method requires OpenCV 2.0.0 or above.");
2026 #else
2027 
2028  IplImage* srcImg = getAs<IplImage>(); // Source Image
2029  IplImage* outImg =
2030  cvCreateImage(cvGetSize(srcImg), srcImg->depth, srcImg->nChannels);
2031 
2032  cv::Mat *_mapX, *_mapY;
2033  _mapX = static_cast<cv::Mat*>(mapX);
2034  _mapY = static_cast<cv::Mat*>(mapY);
2035 
2036  IplImage _mapXX = *_mapX;
2037  IplImage _mapYY = *_mapY;
2038 
2039  cvRemap(srcImg, outImg, &_mapXX, &_mapYY, CV_INTER_CUBIC);
2040 #endif
2041 
2042  releaseIpl();
2043  img = outImg;
2044 #endif
2045 }
2046 
2047 /*---------------------------------------------------------------
2048  rectifyImageInPlace
2049 ---------------------------------------------------------------*/
2051 {
2052 #if MRPT_HAS_OPENCV
2053  makeSureImageIsLoaded(); // For delayed loaded images stored externally
2054  ASSERT_(img != nullptr);
2055  // MRPT -> OpenCV Input Transformation
2056  IplImage* srcImg = getAs<IplImage>(); // Source Image
2057  IplImage* outImg; // Output Image
2058  outImg = cvCreateImage(cvGetSize(srcImg), srcImg->depth, srcImg->nChannels);
2059 
2060  double aux1[3][3], aux2[1][5];
2061  const CMatrixDouble33& cameraMatrix = cameraParams.intrinsicParams;
2062 
2063  for (int i = 0; i < 3; i++)
2064  for (int j = 0; j < 3; j++) aux1[i][j] = cameraMatrix(i, j);
2065  for (int i = 0; i < 5; i++) aux2[0][i] = cameraParams.dist[i];
2066 
2067  CvMat inMat = cvMat(cameraMatrix.rows(), cameraMatrix.cols(), CV_64F, aux1);
2068  CvMat distM = cvMat(1, 5, CV_64F, aux2);
2069 
2070  // Remove distortion
2071  cvUndistort2(srcImg, outImg, &inMat, &distM);
2072 
2073  // Assign the output image to the IPLImage pointer within the CImage
2074  releaseIpl();
2075  img = outImg;
2076 #endif
2077 }
2078 
2079 /*---------------------------------------------------------------
2080  rectifyImage
2081 ---------------------------------------------------------------*/
2083  CImage& out_img, const mrpt::img::TCamera& cameraParams) const
2084 {
2085 #if MRPT_HAS_OPENCV
2086  makeSureImageIsLoaded(); // For delayed loaded images stored externally
2087  ASSERT_(img != nullptr);
2088  // MRPT -> OpenCV Input Transformation
2089  const IplImage* srcImg = getAs<IplImage>(); // Source Image
2090  IplImage* outImg; // Output Image
2091  outImg = cvCreateImage(cvGetSize(srcImg), srcImg->depth, srcImg->nChannels);
2092 
2093  double aux1[3][3], aux2[1][5];
2094  const CMatrixDouble33& cameraMatrix = cameraParams.intrinsicParams;
2095 
2096  for (int i = 0; i < 3; i++)
2097  for (int j = 0; j < 3; j++) aux1[i][j] = cameraMatrix(i, j);
2098  for (int i = 0; i < 5; i++) aux2[0][i] = cameraParams.dist[i];
2099 
2100  CvMat inMat = cvMat(cameraMatrix.rows(), cameraMatrix.cols(), CV_64F, aux1);
2101  CvMat distM = cvMat(1, 5, CV_64F, aux2);
2102 
2103  // Remove distortion
2104  cvUndistort2(srcImg, outImg, &inMat, &distM);
2105 
2106  // OpenCV -> MRPT Output Transformation
2107  out_img.loadFromIplImage(outImg);
2108 
2109  // Release Output Image
2110  cvReleaseImage(&outImg);
2111 #endif
2112 } // end CImage::rectifyImage
2113 
2114 /*---------------------------------------------------------------
2115  filterMedian
2116 ---------------------------------------------------------------*/
2117 void CImage::filterMedian(CImage& out_img, int W) const
2118 {
2119 #if MRPT_HAS_OPENCV
2120  makeSureImageIsLoaded(); // For delayed loaded images stored externally
2121  ASSERT_(img != nullptr);
2122  // MRPT -> OpenCV Input Transformation
2123  const IplImage* srcImg = getAs<IplImage>(); // Source Image
2124  IplImage* outImg; // Output Image
2125  outImg = cvCreateImage(cvGetSize(srcImg), srcImg->depth, srcImg->nChannels);
2126 
2127  // Filter
2128  cvSmooth(srcImg, outImg, CV_MEDIAN, W);
2129 
2130  outImg->origin = srcImg->origin;
2131 
2132  // OpenCV -> MRPT Output Transformation
2133  out_img.loadFromIplImage(outImg);
2134 
2135  // Release Output Image
2136  cvReleaseImage(&outImg);
2137 #endif
2138 }
2139 
2140 /*---------------------------------------------------------------
2141  filterMedian
2142 ---------------------------------------------------------------*/
2144 {
2145 #if MRPT_HAS_OPENCV
2146  makeSureImageIsLoaded(); // For delayed loaded images stored externally
2147  ASSERT_(img != nullptr);
2148  // MRPT -> OpenCV Input Transformation
2149  IplImage* srcImg = getAs<IplImage>(); // Source Image
2150  IplImage* outImg; // Output Image
2151  outImg = cvCreateImage(cvGetSize(srcImg), srcImg->depth, srcImg->nChannels);
2152 
2153  // Filter
2154  cvSmooth(srcImg, outImg, CV_MEDIAN, W);
2155 
2156  outImg->origin = srcImg->origin;
2157 
2158  // Assign the output image to the IPLImage pointer within the CImage
2159  releaseIpl();
2160  img = outImg;
2161 #endif
2162 }
2163 
2164 /*---------------------------------------------------------------
2165  filterGaussian
2166 ---------------------------------------------------------------*/
2167 void CImage::filterGaussian(CImage& out_img, int W, int H) const
2168 {
2169 #if MRPT_HAS_OPENCV
2170  makeSureImageIsLoaded(); // For delayed loaded images stored externally
2171  ASSERT_(img != nullptr);
2172  // MRPT -> OpenCV Input Transformation
2173  const IplImage* srcImg = getAs<IplImage>(); // Source Image
2174  IplImage* outImg; // Output Image
2175  outImg = cvCreateImage(cvGetSize(srcImg), srcImg->depth, srcImg->nChannels);
2176 
2177  // Filter
2178  cvSmooth(srcImg, outImg, CV_GAUSSIAN, W, H);
2179 
2180  outImg->origin = srcImg->origin;
2181 
2182  // OpenCV -> MRPT Output Transformation
2183  out_img.loadFromIplImage(outImg);
2184 
2185  // Release Output Image
2186  cvReleaseImage(&outImg);
2187 #endif
2188 }
2189 
2190 /*---------------------------------------------------------------
2191  filterGaussianInPlace
2192 ---------------------------------------------------------------*/
2194 {
2195 #if MRPT_HAS_OPENCV
2196  makeSureImageIsLoaded(); // For delayed loaded images stored externally
2197  ASSERT_(img != nullptr);
2198  // MRPT -> OpenCV Input Transformation
2199  IplImage* srcImg = getAs<IplImage>(); // Source Image
2200  IplImage* outImg; // Output Image
2201  outImg = cvCreateImage(cvGetSize(srcImg), srcImg->depth, srcImg->nChannels);
2202 
2203  // Filter
2204  cvSmooth(srcImg, outImg, CV_GAUSSIAN, W, H);
2205 
2206  outImg->origin = srcImg->origin;
2207 
2208  // Assign the output image to the IPLImage pointer within the CImage
2209  releaseIpl();
2210  img = outImg;
2211 #endif
2212 }
2213 
2214 /*---------------------------------------------------------------
2215  scaleImage
2216 ---------------------------------------------------------------*/
2218  unsigned int width, unsigned int height, TInterpolationMethod interp)
2219 {
2220 #if MRPT_HAS_OPENCV
2221  makeSureImageIsLoaded(); // For delayed loaded images stored externally
2222  ASSERT_(img != nullptr);
2223  IplImage* srcImg = getAs<IplImage>(); // Source Image
2224 
2225  if (static_cast<unsigned int>(srcImg->width) == width &&
2226  static_cast<unsigned int>(srcImg->height) == height)
2227  return;
2228 
2229  IplImage* outImg; // Output Image
2230  outImg =
2231  cvCreateImage(cvSize(width, height), srcImg->depth, srcImg->nChannels);
2232 
2233  // Resize:
2234  cvResize(srcImg, outImg, (int)interp);
2235 
2236  outImg->origin = srcImg->origin;
2237 
2238  // Assign the output image to the IPLImage pointer within the CImage
2239  releaseIpl();
2240  img = outImg;
2241 #endif
2242 }
2243 
2244 /*---------------------------------------------------------------
2245  scaleImage
2246 ---------------------------------------------------------------*/
2248  CImage& out_img, unsigned int width, unsigned int height,
2250 {
2251 #if MRPT_HAS_OPENCV
2252  makeSureImageIsLoaded(); // For delayed loaded images stored externally
2253  ASSERT_(img != nullptr);
2254  const IplImage* srcImg = getAs<IplImage>(); // Source Image
2255 
2256  if (static_cast<unsigned int>(srcImg->width) == width &&
2257  static_cast<unsigned int>(srcImg->height) == height)
2258  {
2259  // already at the required size:
2260  out_img = *this;
2261  return;
2262  }
2263 
2264  IplImage* outImg; // Output Image
2265  outImg =
2266  cvCreateImage(cvSize(width, height), srcImg->depth, srcImg->nChannels);
2267 
2268  // Resize:
2269  cvResize(srcImg, outImg, (int)interp);
2270  outImg->origin = srcImg->origin;
2271 
2272  // Assign:
2273  out_img.setFromIplImage(outImg);
2274 #endif
2275 }
2276 
2277 /*---------------------------------------------------------------
2278  rotateImage
2279 ---------------------------------------------------------------*/
2281  double angle_radians, unsigned int center_x, unsigned int center_y,
2282  double scale)
2283 {
2284 #if MRPT_HAS_OPENCV
2285  makeSureImageIsLoaded(); // For delayed loaded images stored externally
2286  ASSERT_(img != nullptr);
2287 
2288  IplImage* srcImg = getAs<IplImage>(); // Source Image
2289  IplImage* outImg; // Output Image
2290  outImg = cvCreateImage(cvGetSize(srcImg), srcImg->depth, srcImg->nChannels);
2291 
2292  // Based on the blog entry:
2293  // http://blog.weisu.org/2007/12/opencv-image-rotate-and-zoom-rotation.html
2294 
2295  // Apply rotation & scale:
2296  float m[6];
2297  CvMat M = cvMat(2, 3, CV_32F, m);
2298 
2299  m[0] = (float)(scale * cos(angle_radians));
2300  m[1] = (float)(scale * sin(angle_radians));
2301  m[3] = -m[1];
2302  m[4] = m[0];
2303  m[2] = center_x;
2304  m[5] = center_y;
2305 
2306  cvGetQuadrangleSubPix(srcImg, outImg, &M);
2307 
2308  outImg->origin = srcImg->origin;
2309 
2310  // Assign the output image to the IPLImage pointer within the CImage
2311  releaseIpl();
2312  img = outImg;
2313 
2314 #endif
2315 }
2316 
2317 /** Draw onto this image the detected corners of a chessboard. The length of
2318  * cornerCoords must be the product of the two check_sizes.
2319  *
2320  * \param cornerCoords [IN] The pixel coordinates of all the corners.
2321  * \param check_size_x [IN] The number of squares, in the X direction
2322  * \param check_size_y [IN] The number of squares, in the Y direction
2323  *
2324  * \return false if the length of cornerCoords is inconsistent (nothing is drawn
2325  * then).
2326  */
2328  std::vector<TPixelCoordf>& cornerCoords, unsigned int check_size_x,
2329  unsigned int check_size_y, unsigned int lines_width, unsigned int r)
2330 {
2331 #if MRPT_HAS_OPENCV
2332 
2333  if (cornerCoords.size() != check_size_x * check_size_y) return false;
2334 
2335  IplImage* ipl = this->getAs<IplImage>();
2336 
2337  unsigned int x, y, i;
2338  CvPoint prev_pt = cvPoint(0, 0);
2339  const int line_max = 8;
2340  CvScalar line_colors[8];
2341 
2342  line_colors[0] = CV_RGB(255, 0, 0);
2343  line_colors[1] = CV_RGB(255, 128, 0);
2344  line_colors[2] = CV_RGB(255, 128, 0);
2345  line_colors[3] = CV_RGB(200, 200, 0);
2346  line_colors[4] = CV_RGB(0, 255, 0);
2347  line_colors[5] = CV_RGB(0, 200, 200);
2348  line_colors[6] = CV_RGB(0, 0, 255);
2349  line_colors[7] = CV_RGB(255, 0, 255);
2350 
2351  CCanvas::selectTextFont("10x20");
2352 
2353  for (y = 0, i = 0; y < check_size_y; y++)
2354  {
2355  CvScalar color = line_colors[y % line_max];
2356  for (x = 0; x < check_size_x; x++, i++)
2357  {
2358  CvPoint pt;
2359  pt.x = cvRound(cornerCoords[i].x);
2360  pt.y = cvRound(cornerCoords[i].y);
2361 
2362  if (i != 0) cvLine(ipl, prev_pt, pt, color, lines_width);
2363 
2364  cvLine(
2365  ipl, cvPoint(pt.x - r, pt.y - r), cvPoint(pt.x + r, pt.y + r),
2366  color, lines_width);
2367  cvLine(
2368  ipl, cvPoint(pt.x - r, pt.y + r), cvPoint(pt.x + r, pt.y - r),
2369  color, lines_width);
2370 
2371  if (r > 0) cvCircle(ipl, pt, r + 1, color);
2372  prev_pt = pt;
2373 
2374  // Text label with the corner index in the first and last corners:
2375  if (i == 0 || i == cornerCoords.size() - 1)
2377  pt.x + 5, pt.y - 5, mrpt::format("%u", i),
2379  }
2380  }
2381 
2382  return true;
2383 #else
2384  return false;
2385 #endif
2386 }
2387 
2388 /** Replaces this grayscale image with a RGB version of it.
2389  * \sa grayscaleInPlace
2390  */
2391 void CImage::colorImage(CImage& ret) const
2392 {
2393 #if MRPT_HAS_OPENCV
2394  if (this->isColor())
2395  {
2396  ret = *this;
2397  return;
2398  }
2399 
2400  const IplImage* srcImg = getAs<IplImage>(); // Source Image
2401  IplImage* outImg = cvCreateImage(cvGetSize(srcImg), srcImg->depth, 3);
2402 
2403  cvCvtColor(srcImg, outImg, CV_GRAY2BGR);
2404 
2405  outImg->origin = srcImg->origin;
2406 
2407  // Assign the output image to the IPLImage pointer within the CImage
2408  ret.setFromIplImage(outImg);
2409 #endif
2410 }
2411 
2412 /** Replaces this grayscale image with a RGB version of it.
2413  * \sa grayscaleInPlace
2414  */
2416 {
2417 #if MRPT_HAS_OPENCV
2418  if (this->isColor()) return;
2419 
2420  IplImage* srcImg = getAs<IplImage>(); // Source Image
2421  IplImage* outImg; // Output Image
2422  outImg = cvCreateImage(cvGetSize(srcImg), srcImg->depth, 3);
2423 
2424  cvCvtColor(srcImg, outImg, CV_GRAY2BGR);
2425 
2426  outImg->origin = srcImg->origin;
2427 
2428  // Assign the output image to the IPLImage pointer within the CImage
2429  releaseIpl();
2430  img = outImg;
2431 #endif
2432 }
2433 
2434 /*---------------------------------------------------------------
2435  joinImagesHorz
2436 ---------------------------------------------------------------*/
2437 void CImage::joinImagesHorz(const CImage& im1, const CImage& im2)
2438 {
2439 #if MRPT_HAS_OPENCV
2440  ASSERT_(im1.getHeight() == im2.getHeight());
2441 
2442  const IplImage* _im1 = im1.getAs<IplImage>();
2443  const IplImage* _im2 = im2.getAs<IplImage>();
2444 
2445  ASSERT_(_im1->depth == _im2->depth && _im1->nChannels == _im2->nChannels);
2446 
2447  IplImage* out = cvCreateImage(
2448  cvSize(_im1->width + _im2->width, _im1->height), _im1->depth,
2449  _im1->nChannels);
2450 
2451  cvSetImageROI(out, cvRect(0, 0, _im1->width, _im1->height));
2452  cvCopy(_im1, out);
2453  cvSetImageROI(out, cvRect(_im1->width, 0, _im2->width, _im2->height));
2454  cvCopy(_im2, out);
2455  cvSetImageROI(out, cvRect(0, 0, out->width, out->height));
2456 
2457  IplImage* out2;
2458  if ((int)_im1->nChannels != (int)this->getChannelCount()) // Convert the
2459  // input to the
2460  // output channel
2461  // format
2462  {
2463  out2 = cvCreateImage(
2464  cvSize(_im1->width + _im2->width, _im1->height), _im1->depth,
2465  this->getChannelCount());
2466  cvCvtColor(out, out2, CV_GRAY2BGR);
2467  this->setFromIplImageReadOnly(out2);
2468  }
2469  else // Assign the output image to the IPLImage pointer within the CImage
2470  this->setFromIplImageReadOnly(out);
2471 
2472 #endif
2473 } // end
2474 
2475 /*---------------------------------------------------------------
2476  equalizeHist
2477 ---------------------------------------------------------------*/
2478 void CImage::equalizeHist(CImage& outImg) const
2479 {
2480 #if MRPT_HAS_OPENCV
2481  // Convert to a single luminance channel image
2482  const IplImage* srcImg = getAs<IplImage>(); // Source Image
2483  ASSERT_(srcImg != nullptr);
2484  outImg.changeSize(srcImg->width, srcImg->height, 1, isOriginTopLeft());
2485 
2486  if (srcImg->nChannels == 1)
2487  { // Grayscale:
2488  cvEqualizeHist(srcImg, outImg.getAs<IplImage>());
2489  }
2490  else
2491  { // Color:
2492  IplImage* hsv = cvCreateImage(cvGetSize(srcImg), 8, 3);
2493  IplImage* h = cvCreateImage(cvGetSize(srcImg), 8, 1);
2494  IplImage* s = cvCreateImage(cvGetSize(srcImg), 8, 1);
2495  IplImage* v = cvCreateImage(cvGetSize(srcImg), 8, 1);
2496 
2497  cvCvtColor(srcImg, hsv, CV_BGR2HSV);
2498  cvSplit(hsv, h, s, v, nullptr);
2499 
2500  cvEqualizeHist(v, v);
2501 
2502  cvMerge(h, s, v, nullptr, hsv);
2503  cvCvtColor(hsv, outImg.getAs<IplImage>(), CV_HSV2BGR);
2504 
2505  cvReleaseImage(&hsv);
2506  cvReleaseImage(&h);
2507  cvReleaseImage(&s);
2508  cvReleaseImage(&v);
2509  }
2510 
2511 #endif
2512 }
2513 
2514 /*---------------------------------------------------------------
2515  equalizeHistInPlace
2516 ---------------------------------------------------------------*/
2518 {
2519 #if MRPT_HAS_OPENCV
2520  // Convert to a single luminance channel image
2521  IplImage* srcImg = getAs<IplImage>(); // Source Image
2522  ASSERT_(srcImg != nullptr);
2523 
2524  IplImage* outImg =
2525  cvCreateImage(cvGetSize(srcImg), srcImg->depth, srcImg->nChannels);
2526  outImg->origin = srcImg->origin;
2527 
2528  if (srcImg->nChannels == 1)
2529  { // Grayscale:
2530  cvEqualizeHist(srcImg, outImg);
2531  }
2532  else
2533  { // Color:
2534  IplImage* hsv = cvCreateImage(cvGetSize(srcImg), 8, 3);
2535  IplImage* h = cvCreateImage(cvGetSize(srcImg), 8, 1);
2536  IplImage* s = cvCreateImage(cvGetSize(srcImg), 8, 1);
2537  IplImage* v = cvCreateImage(cvGetSize(srcImg), 8, 1);
2538 
2539  cvCvtColor(srcImg, hsv, CV_BGR2HSV);
2540  cvSplit(hsv, h, s, v, nullptr);
2541 
2542  cvEqualizeHist(v, v);
2543 
2544  cvMerge(h, s, v, nullptr, hsv);
2545  cvCvtColor(hsv, outImg, CV_HSV2BGR);
2546 
2547  cvReleaseImage(&hsv);
2548  cvReleaseImage(&h);
2549  cvReleaseImage(&s);
2550  cvReleaseImage(&v);
2551  }
2552 
2553  // Assign the output image to the IPLImage pointer within the CImage
2554  releaseIpl();
2555  img = outImg;
2556 
2557 #endif
2558 }
2559 
2560 template <unsigned int HALF_WIN_SIZE>
2562  const uint8_t* in, const int widthStep, int x, int y, int32_t& _gxx,
2563  int32_t& _gyy, int32_t& _gxy)
2564 {
2565  const unsigned int min_x = x - HALF_WIN_SIZE;
2566  const unsigned int min_y = y - HALF_WIN_SIZE;
2567 
2568  int32_t gxx = 0;
2569  int32_t gxy = 0;
2570  int32_t gyy = 0;
2571 
2572  const unsigned int WIN_SIZE = 1 + 2 * HALF_WIN_SIZE;
2573 
2574  unsigned int yy = min_y;
2575  for (unsigned int iy = WIN_SIZE; iy; --iy, ++yy)
2576  {
2577  const uint8_t* ptr = in + widthStep * yy + min_x;
2578  unsigned int xx = min_x;
2579  for (unsigned int ix = WIN_SIZE; ix; --ix, ++xx)
2580  {
2581  const int32_t dx = ptr[+1] - ptr[-1];
2582  const int32_t dy = ptr[+widthStep] - ptr[-widthStep];
2583  gxx += dx * dx;
2584  gxy += dx * dy;
2585  gyy += dy * dy;
2586  }
2587  }
2588  _gxx = gxx;
2589  _gyy = gyy;
2590  _gxy = gxy;
2591 }
2592 
2594  const unsigned int x, const unsigned int y,
2595  const unsigned int half_window_size) const
2596 {
2597 #if MRPT_HAS_OPENCV
2598  const IplImage* srcImg = this->getAs<IplImage>();
2599  ASSERT_(srcImg != nullptr);
2600  ASSERTMSG_(
2601  srcImg->nChannels == 1,
2602  "KLT_response only works with grayscale images.");
2603 
2604  const unsigned int img_w = srcImg->width;
2605  const unsigned int img_h = srcImg->height;
2606  const int widthStep = srcImg->widthStep;
2607 
2608  // If any of those predefined values worked, do the generic way:
2609  const unsigned int min_x = x - half_window_size;
2610  const unsigned int max_x = x + half_window_size;
2611  const unsigned int min_y = y - half_window_size;
2612  const unsigned int max_y = y + half_window_size;
2613 
2614  // Since min_* are "unsigned", checking "<" will detect negative numbers:
2615  ASSERTMSG_(
2616  min_x < img_w && max_x < img_w && min_y < img_h && max_y < img_h,
2617  "Window is out of image bounds");
2618 
2619  // Gradient sums: Use integers since they're much faster than
2620  // doubles/floats!!
2621  int32_t gxx = 0;
2622  int32_t gxy = 0;
2623  int32_t gyy = 0;
2624 
2625  const uint8_t* img_data = reinterpret_cast<const uint8_t*>(
2626  srcImg->imageData); //*VERY IMPORTANT*: Use unsigned
2627  switch (half_window_size)
2628  {
2629  case 2:
2630  image_KLT_response_template<2>(
2631  img_data, widthStep, x, y, gxx, gyy, gxy);
2632  break;
2633  case 3:
2634  image_KLT_response_template<3>(
2635  img_data, widthStep, x, y, gxx, gyy, gxy);
2636  break;
2637  case 4:
2638  image_KLT_response_template<4>(
2639  img_data, widthStep, x, y, gxx, gyy, gxy);
2640  break;
2641  case 5:
2642  image_KLT_response_template<5>(
2643  img_data, widthStep, x, y, gxx, gyy, gxy);
2644  break;
2645  case 6:
2646  image_KLT_response_template<6>(
2647  img_data, widthStep, x, y, gxx, gyy, gxy);
2648  break;
2649  case 7:
2650  image_KLT_response_template<7>(
2651  img_data, widthStep, x, y, gxx, gyy, gxy);
2652  break;
2653  case 8:
2654  image_KLT_response_template<8>(
2655  img_data, widthStep, x, y, gxx, gyy, gxy);
2656  break;
2657  case 9:
2658  image_KLT_response_template<9>(
2659  img_data, widthStep, x, y, gxx, gyy, gxy);
2660  break;
2661  case 10:
2662  image_KLT_response_template<10>(
2663  img_data, widthStep, x, y, gxx, gyy, gxy);
2664  break;
2665  case 11:
2666  image_KLT_response_template<11>(
2667  img_data, widthStep, x, y, gxx, gyy, gxy);
2668  break;
2669  case 12:
2670  image_KLT_response_template<12>(
2671  img_data, widthStep, x, y, gxx, gyy, gxy);
2672  break;
2673  case 13:
2674  image_KLT_response_template<13>(
2675  img_data, widthStep, x, y, gxx, gyy, gxy);
2676  break;
2677  case 14:
2678  image_KLT_response_template<14>(
2679  img_data, widthStep, x, y, gxx, gyy, gxy);
2680  break;
2681  case 15:
2682  image_KLT_response_template<15>(
2683  img_data, widthStep, x, y, gxx, gyy, gxy);
2684  break;
2685  case 16:
2686  image_KLT_response_template<16>(
2687  img_data, widthStep, x, y, gxx, gyy, gxy);
2688  break;
2689  case 32:
2690  image_KLT_response_template<32>(
2691  img_data, widthStep, x, y, gxx, gyy, gxy);
2692  break;
2693 
2694  default:
2695  for (unsigned int yy = min_y; yy <= max_y; yy++)
2696  {
2697  const uint8_t* ptr = img_data + widthStep * yy + min_x;
2698  for (unsigned int xx = min_x; xx <= max_x; xx++)
2699  {
2700  const int32_t dx = ptr[+1] - ptr[-1];
2701  const int32_t dy = ptr[+widthStep] - ptr[-widthStep];
2702  gxx += dx * dx;
2703  gxy += dx * dy;
2704  gyy += dy * dy;
2705  }
2706  }
2707  break;
2708  }
2709  // Convert to float's and normalize in the way:
2710  const float K = 0.5f / ((max_y - min_y + 1) * (max_x - min_x + 1));
2711  const float Gxx = gxx * K;
2712  const float Gxy = gxy * K;
2713  const float Gyy = gyy * K;
2714 
2715  // Return the minimum eigenvalue of:
2716  // ( gxx gxy )
2717  // ( gxy gyy )
2718  // See, for example: mrpt::math::detail::eigenVectorsMatrix_special_2x2():
2719  const float t = Gxx + Gyy; // Trace
2720  const float de = Gxx * Gyy - Gxy * Gxy; // Det
2721  // The smallest eigenvalue is:
2722  return 0.5f * (t - std::sqrt(t * t - 4.0f * de));
2723 #else
2724  return 0;
2725 #endif
2726 }
2727 
2728 /** Marks the channel ordering in a color image (this doesn't actually modify
2729  * the image data, just the format description)
2730  */
2732 {
2733 #if MRPT_HAS_OPENCV
2734  makeSureImageIsLoaded(); // For delayed loaded images stored externally
2735  ASSERT_(img);
2736  strcpy(((IplImage*)img)->channelSeq, "RGB");
2737 #else
2738  THROW_EXCEPTION("MRPT compiled without OpenCV");
2739 #endif
2740 }
2741 
2743 {
2744 #if MRPT_HAS_OPENCV
2745  makeSureImageIsLoaded(); // For delayed loaded images stored externally
2746  ASSERT_(img);
2747  strcpy(((IplImage*)img)->channelSeq, "BGR");
2748 #else
2749  THROW_EXCEPTION("MRPT compiled without OpenCV");
2750 #endif
2751 }
2752 
2753 // Load from TGA files. Used in loadFromFile()
2754 // Contains code from
2755 // https://github.com/tjohnman/Simple-Targa-Library/blob/master/src/simpleTGA.cpp
2756 // (FreeBSD license)
2758  const std::string& fileName, mrpt::img::CImage& out_RGB,
2759  mrpt::img::CImage& out_alpha)
2760 {
2761 #if MRPT_HAS_OPENCV
2762  std::fstream stream;
2763  stream.open(fileName.c_str(), std::fstream::in | std::fstream::binary);
2764  if (!stream.is_open())
2765  {
2766  std::cerr << "[CImage::loadTGA] Couldn't open file '" << fileName
2767  << "'.\n";
2768  return false;
2769  }
2770 
2771  stream.seekg(0, std::ios_base::end);
2772  // long length = stream.tellg();
2773  stream.seekg(0, std::ios_base::beg);
2774 
2775  // Simple uncompressed true-color image
2776  char dumpBuffer[12];
2777  char trueColorHeader[] = "\0\0\2\0\0\0\0\0\0\0\0\0";
2778  stream.read(dumpBuffer, 12);
2779  if (memcmp(dumpBuffer, trueColorHeader, 12) != 0)
2780  {
2781  std::cerr << "[CImage::loadTGA] Unsupported format or invalid file.\n";
2782  return false;
2783  }
2784 
2785  unsigned short width, height;
2786  unsigned char bpp;
2787 
2788  stream.read((char*)&width, 2);
2789  stream.read((char*)&height, 2);
2790  bpp = stream.get();
2791  if (bpp != 32)
2792  {
2793  std::cerr << "[CImage::loadTGA] Only 32 bpp format supported!\n";
2794  return false;
2795  }
2796 
2797  unsigned char desc;
2798  desc = stream.get();
2799  if (desc != 8 && desc != 32)
2800  {
2801  std::cerr << "[CImage::loadTGA] Unsupported format or invalid file.\n";
2802  return false;
2803  }
2804  const bool origin_is_low_corner = (desc == 8);
2805 
2806  // Data section
2807  std::vector<uint8_t> bytes(width * height * 4);
2808  stream.read((char*)&bytes[0], width * height * 4);
2809  stream.close();
2810 
2811  // Move data to images:
2812  out_RGB.resize(width, height, CH_RGB, true);
2813  out_alpha.resize(width, height, CH_GRAY, true);
2814 
2815  size_t idx = 0;
2816  for (unsigned int r = 0; r < height; r++)
2817  {
2818  unsigned int actual_row = origin_is_low_corner ? (height - 1 - r) : r;
2819  IplImage* ipl = ((IplImage*)out_RGB.img);
2820  unsigned char* data =
2821  (unsigned char*)&ipl->imageData[actual_row * ipl->widthStep];
2822 
2823  IplImage* ipl_alpha = ((IplImage*)out_alpha.img);
2824  unsigned char* data_alpha =
2825  (unsigned char*)&ipl->imageData[actual_row * ipl_alpha->widthStep];
2826 
2827  for (unsigned int c = 0; c < width; c++)
2828  {
2829  *data++ = bytes[idx++]; // R
2830  *data++ = bytes[idx++]; // G
2831  *data++ = bytes[idx++]; // B
2832  *data_alpha++ = bytes[idx++]; // A
2833  }
2834  }
2835 
2836  return true;
2837 #else
2838  return false;
2839 #endif // MRPT_HAS_OPENCV
2840 }
2841 
2842 std::ostream& mrpt::img::operator<<(std::ostream& o, const TPixelCoordf& p)
2843 {
2844  o << "(" << p.x << "," << p.y << ")";
2845  return o;
2846 }
2847 std::ostream& mrpt::img::operator<<(std::ostream& o, const TPixelCoord& p)
2848 {
2849  o << "(" << p.x << "," << p.y << ")";
2850  return o;
2851 }
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:1332
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:1315
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei imageSize
Definition: glext.h:3743
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:1296
Used in mrpt::img::CImage.
Definition: img/CImage.h:55
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
const T * getAs() const
Returns a pointer to a const T* containing the image - the idea is to call like "img.getAs<IplImage>()" so we can avoid here including OpenCV&#39;s headers.
Definition: img/CImage.h:599
void changeSize(unsigned int width, unsigned int height, TImageChannels nChannels, bool originTopLeft)
Resize the buffers in "img" to accomodate a new image size and/or format.
Definition: CImage.cpp:223
#define MRPT_START
Definition: exceptions.h:262
GLdouble GLdouble t
Definition: glext.h:3689
void cross_correlation(const CImage &patch_img, size_t &u_max, size_t &v_max, double &max_val, int u_search_ini=-1, int v_search_ini=-1, int u_search_size=-1, int v_search_size=-1, CImage *out_corr_image=nullptr) const
Computes the correlation between this image and another one, encapsulating the openCV function cvMatc...
Definition: CImage.cpp:1454
TPenStyle
Definition of pen styles.
Definition: CCanvas.h:56
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:2327
void rectifyImageInPlace(const mrpt::img::TCamera &cameraParams)
Rectify (un-distort) the image according to a certain camera matrix and vector of distortion coeffici...
Definition: CImage.cpp:2050
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) const
Returns the image as a matrix with pixel grayscale values in the range [0,1].
Definition: CImage.cpp:1616
void resize(unsigned int width, unsigned int height, TImageChannels nChannels, bool originTopLeft)
Changes the size of the image, erasing previous contents (does NOT scale its current content...
Definition: img/CImage.h:261
unsigned char red[10]
This namespace provides a OS-independent interface to many useful functions: filenames manipulation...
Definition: math_frwds.h:25
#define THROW_EXCEPTION(msg)
Definition: exceptions.h:41
uint8_t serializeGetVersion() const override
Must return the current versioning number of the object.
Definition: CImage.cpp:504
GLenum GLenum GLenum GLenum GLenum scale
Definition: glext.h:6502
void copyFromForceLoad(const CImage &o)
Copies from another image, and, if that one is externally stored, the image file will be actually loa...
Definition: CImage.cpp:148
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:1672
void image_SSSE3_bgr_to_gray_8u(const uint8_t *in, uint8_t *out, int w, int h)
Convert a RGB image (3cu8) into a GRAYSCALE (1c8u) image, using Y=77*R+150*G+29*B.
GLenum GLsizei n
Definition: glext.h:5074
float getMaxAsFloat() const
Return the maximum pixel value of the image, as a float value in the range [0,1]. ...
Definition: CImage.cpp:979
CImage & operator=(const CImage &o)
Copy operator (if the image is externally stored, the writen image will be such as well)...
Definition: CImage.cpp:105
void flipHorizontal()
Flips the image horizontally.
Definition: CImage.cpp:1994
TImageChannels getChannelCount() const
Returns the number of channels, typically 1 (GRAY) or 3 (RGB)
Definition: CImage.cpp:920
static constexpr TColor blue()
Definition: TColor.h:64
size_t getHeight() const override
Returns the height of the image in pixels.
Definition: CImage.cpp:892
A pair (x,y) of pixel coordinates (subpixel resolution).
Definition: TPixelCoord.h:20
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:1887
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:1398
void makeSureImageIsLoaded() const
Checks if the image is of type "external storage", and if so and not loaded yet, load it...
Definition: CImage.cpp:1928
STL namespace.
void WriteBuffer(const void *Buffer, size_t Count)
Writes a block of bytes to the stream from Buffer.
Definition: CArchive.cpp:48
void joinImagesHorz(const CImage &im1, const CImage &im2)
Joins two images side-by-side horizontally.
Definition: CImage.cpp:2437
bool isExternallyStored() const noexcept
See setExternalStorage().
Definition: img/CImage.h:793
void grayscaleInPlace()
Replaces the image with a grayscale version of it.
Definition: CImage.cpp:1055
static int SERIALIZATION_JPEG_QUALITY
Unless DISABLE_JPEG_COMPRESSION=true, this sets the JPEG quality (range 1-100) of serialized RGB imag...
Definition: img/CImage.h:245
GLdouble s
Definition: glext.h:3676
GLuint src
Definition: glext.h:7278
void unload() const noexcept
For external storage image objects only, this method unloads the image from memory (or does nothing i...
Definition: CImage.cpp:1897
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:948
GLenum GLsizei width
Definition: glext.h:3531
GLubyte GLubyte GLubyte GLubyte w
Definition: glext.h:4178
void filterGaussian(CImage &out_img, int W=3, int H=3) const
Filter the image with a Gaussian filter with a window size WxH, replacing "this" image by the filtere...
Definition: CImage.cpp:2167
void swap(CImage &o)
Very efficient swap of two images (just swap the internal pointers)
Definition: CImage.cpp:133
int TImageChannels
For use in mrpt::img::CImage.
Definition: img/CImage.h:43
void colorImageInPlace()
Replaces this grayscale image with a RGB version of it.
Definition: CImage.cpp:2415
static std::string IMAGES_PATH_BASE(".")
unsigned char uint8_t
Definition: rptypes.h:41
#define MRPT_THROW_UNKNOWN_SERIALIZATION_VERSION(__V)
For use in CSerializable implementations.
Definition: exceptions.h:90
void image_SSE2_scale_half_smooth_1c8u(const uint8_t *in, uint8_t *out, int w, int h)
Average each 2x2 pixels into 1x1 pixel (arithmetic average)
Definition: CImage_SSE2.cpp:76
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:2005
GLuint color
Definition: glext.h:8300
mrpt::math::CMatrixDouble33 intrinsicParams
Matrix of intrinsic parameters (containing the focal length and principal point coordinates) ...
Definition: TCamera.h:44
void image_SSSE3_scale_half_3c8u(const uint8_t *in, uint8_t *out, int w, int h)
Subsample each 2x2 pixel block into 1x1 pixel, taking the first pixel & ignoring the other 3...
Definition: CImage_SSE3.cpp:40
#define ASSERT_(f)
Defines an assertion mechanism.
Definition: exceptions.h:113
bool m_imgIsReadOnly
Set to true only when using setFromIplImageReadOnly.
Definition: img/CImage.h:1094
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:271
This base provides a set of functions for maths stuff.
size_t getWidth() const override
Returns the width of the image in pixels.
Definition: CImage.cpp:864
void setChannelsOrder_BGR()
Marks the channel ordering in a color image as "BGR" (this doesn&#39;t actually modify the image data...
Definition: CImage.cpp:2742
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:452
virtual void selectTextFont(const std::string &fontName)
Select the current font used when drawing text.
Definition: CCanvas.cpp:229
void * getRawBufferData()
Method for getting a pointer to the raw stored data.
const GLubyte * c
Definition: glext.h:6313
This CStream derived class allow using a memory buffer as a CStream.
GLint GLvoid * img
Definition: glext.h:3763
void image_KLT_response_template(const uint8_t *in, const int widthStep, int x, int y, int32_t &_gxx, int32_t &_gyy, int32_t &_gxy)
Definition: CImage.cpp:2561
IplImage * ipl_to_grayscale(const IplImage *img_src)
Definition: CImage.cpp:1003
void getExternalStorageFileAbsolutePath(std::string &out_path) const
Only if isExternallyStored() returns true.
Definition: CImage.cpp:1960
void saveToStreamAsJPEG(mrpt::io::CStream &out, const int jpeg_quality=95) const
Save image to binary stream as a JPEG (.jpg) compressed format.
CImage scaleHalfSmooth() const
Returns a new image scaled down to half its original size (averaging between every two rows) ...
Definition: img/CImage.h:363
GLuint GLuint end
Definition: glext.h:3528
void rotateImage(double angle_radians, unsigned int center_x, unsigned int center_y, double scale=1.0)
Rotates the image by the given angle around the given center point, with an optional scale factor...
Definition: CImage.cpp:2280
TInterpolationMethod
Interpolation methods for images.
Definition: img/CImage.h:34
void normalize()
Optimize the brightness range of an image without using histogram Only for one channel images...
Definition: CImage.cpp:1571
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...
A pair (x,y) of pixel coordinates (integer resolution).
Definition: TPixelCoord.h:39
T round2up(T val)
Round up to the nearest power of two of a given number.
int val
Definition: mrpt_jpeglib.h:955
CImage scaleHalf() const
Returns a new image scaled down to half its original size.
Definition: img/CImage.h:348
std::string format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
Definition: format.cpp:16
#define IMPLEMENTS_MEXPLUS_FROM(complete_type)
const char * getChannelsOrder() const
Returns a string of the form "BGR","RGB" or "GRAY" indicating the channels ordering.
Definition: CImage.cpp:1180
Structure to hold the parameters of a pinhole camera model.
Definition: TCamera.h:29
#define ASSERTMSG_(f, __ERROR_MSG)
Defines an assertion mechanism.
Definition: exceptions.h:101
void loadFromStreamAsJPEG(mrpt::io::CStream &in)
Reads the image from a binary stream containing a binary jpeg file.
void serializeFrom(mrpt::serialization::CArchive &in, uint8_t serial_version) override
Pure virtual method for reading (deserializing) from an abstract archive.
Definition: CImage.cpp:627
void setOriginTopLeft(bool val)
Changes the property of the image stating if the top-left corner (vs.
Definition: CImage.cpp:1231
TImageSize getSize() const
Return the size of the image.
Definition: img/CImage.h:661
GLsizei const GLchar ** string
Definition: glext.h:4101
GLint GLint GLsizei GLsizei GLsizei GLint GLenum GLenum const GLvoid * pixels
Definition: glext.h:3600
void getAsMatrixTiled(math::CMatrix &outMatrix) const
Returns the image as a matrix, where the image is "tiled" (repeated) the required number of times to ...
Definition: CImage.cpp:1831
void copyFastFrom(CImage &o)
Moves an image from another object, erasing the origin image in the process (this is much faster than...
Definition: CImage.cpp:168
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 itself if it is already a grayscale image.
Definition: CImage.cpp:994
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:1738
void colorImage(CImage &ret) const
Returns a RGB version of the grayscale image, or itself if it is already a RGB image.
Definition: CImage.cpp:2391
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:1337
void image_SSE2_scale_half_1c8u(const uint8_t *in, uint8_t *out, int w, int h)
Subsample each 2x2 pixel block into 1x1 pixel, taking the first pixel & ignoring the other 3...
Definition: CImage_SSE2.cpp:43
std::array< double, 5 > dist
[k1 k2 t1 t2 k3] -> k_i: parameters of radial distortion, t_i: parameters of tangential distortion (d...
Definition: TCamera.h:47
void filterMedianInPlace(int W=3)
Filter the image with a Median filter with a window size WxH, replacing "this" image by the filtered ...
Definition: CImage.cpp:2143
void setPixel(int x, int y, size_t color) override
Changes the value of the pixel (x,y).
Definition: CImage.cpp:1243
__int32 int32_t
Definition: rptypes.h:46
const GLdouble * v
Definition: glext.h:3678
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:2117
CImage()
Default constructor: initialize an 1x1 RGB image.
Definition: CImage.cpp:81
void scaleImage(unsigned int width, unsigned int height, TInterpolationMethod interp=IMG_INTERP_CUBIC)
Scales this image to a new size, interpolating as needed.
Definition: CImage.cpp:2217
Virtual base class for "archives": classes abstracting I/O streams.
Definition: CArchive.h:48
#define MRPT_TODO(x)
Definition: common.h:129
GLdouble GLdouble GLdouble r
Definition: glext.h:3705
#define IMPLEMENTS_MRPT_OBJECT_ALT_NAME(class_name, base, NameSpace, alt_name)
This must be inserted in all CObject classes implementation files.
Definition: CObject.h:217
bool isColor() const
Returns true if the image is RGB, false if it is grayscale.
Definition: CImage.cpp:906
void rectifyImage(CImage &out_img, const mrpt::img::TCamera &cameraParams) const
Rectify (un-distort) the image according to some camera parameters, and returns an output un-distorte...
Definition: CImage.cpp:2082
bool isOriginTopLeft() const
Returns true if the coordinates origin is top-left, or false if it is bottom-left.
Definition: CImage.cpp:934
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:2593
GLclampf green
Definition: glext.h:3525
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:70
bool m_imgIsExternalStorage
Set to true only when using setExternalStorage.
Definition: img/CImage.h:1098
void filterGaussianInPlace(int W=3, int H=3)
Filter the image with a Gaussian filter with a window size WxH, returning the filtered image in out_i...
Definition: CImage.cpp:2193
GLenum GLenum GLvoid * row
Definition: glext.h:3576
CImage scaleDouble() const
Returns a new image scaled up to double its original size.
Definition: img/CImage.h:377
#define MRPT_END
Definition: exceptions.h:266
virtual ~CImage()
Destructor:
Definition: CImage.cpp:219
size_t getRowStride() const
Returns the row stride of the image: this is the number of bytes between two consecutive rows...
Definition: CImage.cpp:878
GLuint in
Definition: glext.h:7274
void changeSize(uint64_t newSize)
Change size.
void serializeTo(mrpt::serialization::CArchive &out) const override
Pure virtual method for writing (serializing) to an abstract archive.
Definition: CImage.cpp:512
void loadFromIplImage(void *iplImage)
Reads the image from a OpenCV IplImage object (making a COPY).
Definition: CImage.cpp:321
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:1359
GLenum GLint GLint y
Definition: glext.h:3538
#define CH_GRAY
Definition: img/CImage.h:44
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:369
void releaseIpl(bool thisIsExternalImgUnload=false) noexcept
Release the internal IPL image, if not nullptr or read-only.
Definition: CImage.cpp:1907
void equalizeHist(CImage &outImg) const
Equalize the image histogram, saving the new image in the given output object.
Definition: CImage.cpp:2478
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:296
GLuint interp
Definition: glext.h:7133
A RGB color - 8bit.
Definition: TColor.h:22
GLclampf GLclampf blue
Definition: glext.h:3525
GLenum GLint x
Definition: glext.h:3538
char * strcpy(char *dest, size_t destSize, const char *source) noexcept
An OS-independent version of strcpy.
Definition: os.cpp:297
This class is a "CSerializable" wrapper for "CMatrixFloat".
Definition: CMatrix.h:24
std::ostream & operator<<(std::ostream &o, const TColor &c)
Definition: TColor.cpp:85
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:3554
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:1232
static bool DISABLE_JPEG_COMPRESSION
By default, when storing images through the CSerializable interface, RGB images are JPEG-compressed t...
Definition: img/CImage.h:240
unsigned __int32 uint32_t
Definition: rptypes.h:47
void equalizeHistInPlace()
Equalize the image histogram, replacing the original image.
Definition: CImage.cpp:2517
std::string getExternalStorageFileAbsolutePath() const
Only if isExternallyStored() returns true.
Definition: img/CImage.h:805
GLsizei GLsizei GLenum GLenum const GLvoid * data
Definition: glext.h:3546
GLfloat GLfloat p
Definition: glext.h:6305
std::string m_externalFile
The file name of a external storage image.
Definition: img/CImage.h:1100
static void setImagesPathBase(const std::string &path)
Definition: CImage.cpp:54
void setFromIplImage(void *iplImage)
Reads the image from a OpenCV IplImage object (WITHOUT making a copy).
Definition: CImage.cpp:363
void flipVertical(bool also_swapRB=false)
Flips the image vertically.
Definition: CImage.cpp:1984
#define THROW_TYPED_EXCEPTION_FMT(exceptionClass, _FORMAT_STRING,...)
Definition: exceptions.h:46
static bool DISABLE_ZIP_COMPRESSION
By default, when storing images through the CSerializable interface, grayscale images will be ZIP com...
Definition: img/CImage.h:233
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:2757
void setChannelsOrder_RGB()
Marks the channel ordering in a color image as "RGB" (this doesn&#39;t actually modify the image data...
Definition: CImage.cpp:2731
void setFromIplImageReadOnly(void *iplImage)
Reads the image from a OpenCV IplImage object (WITHOUT making a copy) and from now on the image canno...
Definition: CImage.cpp:340
A class for storing images as grayscale or RGB bitmaps.
Definition: img/CImage.h:130
unsigned char * get_unsafe(unsigned int col, unsigned int row, unsigned int channel=0) const
Access to pixels without checking boundaries - Use normally the () operator better, which checks the coordinates.
Definition: CImage.cpp:491
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:356
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:385
#define MRPT_UNUSED_PARAM(a)
Determines whether this is an X86 or AMD64 platform.
Definition: common.h:186
void * img
The internal IplImage pointer to the actual image content.
Definition: img/CImage.h:1090
#define CH_RGB
Definition: img/CImage.h:45
int round(const T value)
Returns the closer integer (int) to x.
Definition: round.h:23



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: ad3a9d8ae Tue May 1 23:10:22 2018 -0700 at lun oct 28 00:14:14 CET 2019