MRPT  1.9.9
CImage_unittest.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | https://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2019, Individual contributors, see AUTHORS file |
6  | See: https://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See: https://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #include <CTraitsTest.h>
11 #include <gtest/gtest.h>
12 #include <mrpt/img/CImage.h>
13 #include <mrpt/img/TColor.h>
14 #include <mrpt/io/CMemoryStream.h>
16 #include <mrpt/random.h>
18 #include <mrpt/system/filesystem.h>
19 #include <mrpt/system/memory.h>
20 #include <test_mrpt_common.h>
21 
22 // Universal include for all versions of OpenCV
23 #include <mrpt/otherlibs/do_opencv_includes.h>
24 
25 template class mrpt::CTraitsTest<mrpt::img::CImage>;
26 
27 using namespace std::string_literals;
28 const auto tstImgFileColor =
29  mrpt::UNITTEST_BASEDIR + "/samples/img_basic_example/frame_color.jpg"s;
30 
31 // Generate random img:
33 {
36 
37  for (unsigned y = 0; y < img.getHeight(); y++)
38  {
39  for (unsigned x = 0; x < img.getWidth(); x++)
40  {
41  const uint8_t c = static_cast<uint8_t>(rnd.drawUniform32bit());
42  img.at<uint8_t>(x, y) = c;
43  }
44  }
45 }
46 
47 // Expect images to be identical:
48 static bool expect_identical(
49  const mrpt::img::CImage& a, const mrpt::img::CImage& b,
50  const std::string& s = std::string())
51 {
52  EXPECT_EQ(a.getWidth(), b.getWidth());
53  EXPECT_EQ(a.getHeight(), b.getHeight());
54  for (unsigned int y = 0; y < a.getHeight(); y++)
55  for (unsigned int x = 0; x < a.getWidth(); x++)
56  {
57  EXPECT_EQ(a.at<uint8_t>(x, y), b.at<uint8_t>(x, y)) << s;
58  if (a.at<uint8_t>(x, y) != b.at<uint8_t>(x, y)) return false;
59  }
60  return true;
61 }
62 
63 TEST(CImage, CtorDefault)
64 {
66  EXPECT_THROW(img.isColor(), std::exception);
67 }
68 
69 #if MRPT_HAS_OPENCV
70 
71 static void CtorSized_gray(unsigned int w, unsigned int h)
72 {
73  using namespace mrpt::img;
74  CImage img(w, h, CH_GRAY);
75  EXPECT_EQ(img.getWidth(), w);
76  EXPECT_EQ(img.getHeight(), 48U);
77  EXPECT_EQ(img.getChannelCount(), 1U);
78  EXPECT_EQ(img.getPixelDepth(), PixelDepth::D8U);
79  EXPECT_FALSE(img.isColor());
80 }
81 
82 TEST(CImage, CtorSized)
83 {
84  using namespace mrpt::img;
85  {
86  CImage img(64, 48, CH_RGB);
87  EXPECT_EQ(img.getWidth(), 64U);
88  EXPECT_EQ(img.getHeight(), 48U);
89  EXPECT_EQ(img.getChannelCount(), 3U);
90  EXPECT_EQ(img.getPixelDepth(), PixelDepth::D8U);
91  EXPECT_TRUE(img.isColor());
92  }
93  for (unsigned w = 64; w < 70; w++)
94  {
95  CtorSized_gray(w, 48);
96  }
97 }
98 
99 TEST(CImage, GetSetPixel)
100 {
101  using namespace mrpt::img;
102  CImage img(20, 10, CH_GRAY);
103  img.setPixel(10, 2, TColor(0x80, 0x80, 0x80));
104  EXPECT_EQ(img.at<uint8_t>(10, 2), 0x80);
105  EXPECT_EQ(*img(10, 2), 0x80);
106 
107  img.setPixel(11, 2, TColor(0x0, 0x0, 0x0));
108  EXPECT_EQ(img.at<uint8_t>(11, 2), 0x00);
109 
110  img.setPixel(12, 2, TColor(0xff, 0xff, 0xff));
111  EXPECT_EQ(img.at<uint8_t>(12, 2), 0xff);
112 
113  img.at<uint8_t>(13, 2) = 0x70;
114  EXPECT_EQ(img.at<uint8_t>(13, 2), 0x70);
115 
116  auto* line = img.ptrLine<uint8_t>(5);
117  for (uint8_t i = 0; i < 20; i++)
118  {
119  line[i] = i;
120  EXPECT_EQ(img.at<uint8_t>(i, 5), i);
121  }
122 
124  img.getAsMatrix(M, true, 0, 0, -1, -1, false /* dont normalize (0,1) */);
125  for (uint8_t i = 0; i < 20; i++)
126  {
127  EXPECT_NEAR(static_cast<double>(M(5, i)), i, 1e-8);
128  }
129 }
130 
131 TEST(CImage, CopyMoveSwap)
132 {
133  using namespace mrpt::img;
134  {
135  CImage a(20, 10, CH_GRAY);
136  a.at<uint8_t>(1, 2) = 0x80;
137  // Shallow copy:
138  CImage b = a;
139  EXPECT_EQ(b.at<uint8_t>(1, 2), 0x80);
140 
141  a.at<uint8_t>(1, 3) = 0x81;
142  EXPECT_EQ(b.at<uint8_t>(1, 3), 0x81);
143 
144  // Deep copy:
145  CImage c = a.makeDeepCopy();
146  EXPECT_EQ(c.at<uint8_t>(1, 2), 0x80);
147 
148  c.at<uint8_t>(1, 3) = 0x0;
149  a.at<uint8_t>(1, 3) = 0x81;
150  EXPECT_NE(c.at<uint8_t>(1, 3), 0x81);
151  }
152 
153  {
154  CImage a(20, 10, CH_GRAY);
155  a.at<uint8_t>(1, 2) = 0x80;
156  // Shallow copy ctor:
158  EXPECT_EQ(b.at<uint8_t>(1, 2), 0x80);
159 
160  a.at<uint8_t>(1, 3) = 0x81;
161  EXPECT_EQ(b.at<uint8_t>(1, 3), 0x81);
162 
163  // Deep copy ctor:
165  EXPECT_EQ(c.at<uint8_t>(1, 2), 0x80);
166 
167  c.at<uint8_t>(1, 3) = 0x0;
168  a.at<uint8_t>(1, 3) = 0x81;
169  EXPECT_NE(c.at<uint8_t>(1, 3), 0x81);
170  }
171 
172  {
173  CImage a(20, 10, CH_GRAY);
174  a.at<uint8_t>(1, 2) = 0x80;
175  // Deep copy:
176  CImage b = a.makeDeepCopy();
177  EXPECT_EQ(b.at<uint8_t>(1, 2), 0x80);
178 
179  a.clear();
180  a.resize(30, 30, CH_RGB);
181  b.at<uint8_t>(1, 3) = 0x0;
182  a.at<uint8_t>(1, 3) = 0x81;
183  EXPECT_NE(b.at<uint8_t>(1, 3), 0x81);
184  }
185 
186  {
187  CImage a(20, 10, CH_GRAY);
188  a.at<uint8_t>(1, 2) = 0x80;
189  // move:
190  CImage b = std::move(a);
191  EXPECT_EQ(b.getWidth(), 20U);
192  EXPECT_EQ(b.getHeight(), 10U);
193  EXPECT_EQ(b.at<uint8_t>(1, 2), 0x80);
194  }
195 
196  {
197  CImage a(20, 10, CH_GRAY), b;
198  a.at<uint8_t>(1, 2) = 0x80;
199  // swap:
200  a.swap(b);
201  EXPECT_EQ(b.getWidth(), 20U);
202  EXPECT_EQ(b.getHeight(), 10U);
203  EXPECT_EQ(b.at<uint8_t>(1, 2), 0x80);
204  }
205 }
206 
207 TEST(CImage, ExternalImage)
208 {
209  using namespace mrpt::img;
210  {
211  CImage a;
212  a.setExternalStorage(tstImgFileColor);
213  // Test automatic load-on-the-fly:
214  EXPECT_EQ(a.getWidth(), 320U);
215  EXPECT_EQ(a.getHeight(), 240U);
216  }
217 
218  {
219  CImage a;
220  a.setExternalStorage("./foo_61717181.png");
221  // Test exception on not found
222  EXPECT_THROW(a.getWidth(), mrpt::img::CExceptionExternalImageNotFound);
223  }
224 }
225 
226 TEST(CImage, ConvertGray)
227 {
228  using namespace mrpt::img;
229  {
230  CImage a;
231  bool load_ok = a.loadFromFile(tstImgFileColor);
232  EXPECT_TRUE(load_ok);
233 
234  CImage b = a.grayscale();
235  EXPECT_EQ(b.getWidth(), a.getWidth());
236  EXPECT_EQ(b.getHeight(), a.getHeight());
237  EXPECT_FALSE(b.isColor());
238  }
239 }
240 
241 TEST(CImage, CtorRefOrGray)
242 {
243  using namespace mrpt::img;
244  {
245  CImage a;
246  bool load_ok = a.loadFromFile(tstImgFileColor);
247  EXPECT_TRUE(load_ok);
248 
250  EXPECT_EQ(b.getWidth(), a.getWidth());
251  EXPECT_EQ(b.getHeight(), a.getHeight());
252  EXPECT_FALSE(b.isColor());
253  }
254  {
255  CImage a(20, 10, CH_GRAY);
256  a.at<uint8_t>(1, 2) = 0x80;
257 
259  EXPECT_EQ(b.getWidth(), a.getWidth());
260  EXPECT_EQ(b.getHeight(), a.getHeight());
261  EXPECT_FALSE(b.isColor());
262  EXPECT_EQ(b.at<uint8_t>(1, 2), 0x80);
263  }
264 }
265 
266 TEST(CImage, HalfAndDouble)
267 {
268  using namespace mrpt::img;
269 
270  CImage a(32, 10, CH_GRAY);
271  a.at<uint8_t>(0, 0) = 0x80;
272  a.at<uint8_t>(0, 1) = 0x80;
273  a.at<uint8_t>(1, 0) = 0x80;
274  a.at<uint8_t>(1, 1) = 0x80;
275 
276  // Half:
277  {
279  EXPECT_EQ(imgH.getWidth(), a.getWidth() / 2);
280  EXPECT_EQ(imgH.getHeight(), a.getHeight() / 2);
281  EXPECT_EQ(imgH.isColor(), a.isColor());
282  EXPECT_EQ(imgH.at<uint8_t>(0, 0), a.at<uint8_t>(0, 0));
283  }
284  // Double:
285  {
287  EXPECT_EQ(imgD.getWidth(), a.getWidth() * 2);
288  EXPECT_EQ(imgD.getHeight(), a.getHeight() * 2);
289  EXPECT_EQ(imgD.isColor(), a.isColor());
290  }
291 }
292 TEST(CImage, getChannelsOrder)
293 {
294  using namespace mrpt::img;
295  {
296  CImage a;
297  bool load_ok = a.loadFromFile(tstImgFileColor);
298  EXPECT_TRUE(load_ok);
299  EXPECT_EQ(std::string("BGR"), a.getChannelsOrder());
300  }
301  {
302  CImage a(32, 10, CH_GRAY);
303  EXPECT_EQ(std::string("GRAY"), a.getChannelsOrder());
304  }
305 }
306 
307 TEST(CImage, ChangeCvMatCopies)
308 {
309  using namespace mrpt::img;
310 
311  {
312  CImage a(20, 10, CH_GRAY);
313  a.at<uint8_t>(1, 2) = 0x80;
314  // change shallow copy:
315  cv::Mat m = a.asCvMat<cv::Mat>(SHALLOW_COPY);
316  m.at<uint8_t>(2, 1) = 0x70;
317  // Expect change in source:
318  EXPECT_EQ(a.at<uint8_t>(1, 2), 0x70);
319 
320  // size:
321  cv::Mat& m2 = a.asCvMatRef();
322  cv::Mat& m3 = a.asCvMatRef();
323  EXPECT_EQ(&m2, &m3);
324 
325  m2 = cv::Mat(40, 40, CV_8UC1);
326 
327  cv::Mat& m4 = a.asCvMatRef();
328  EXPECT_EQ(&m2, &m4);
329 
330  EXPECT_EQ(a.getWidth(), 40U);
331  EXPECT_EQ(a.getHeight(), 40U);
332  }
333  {
334  CImage a(20, 10, CH_GRAY);
335  a.at<uint8_t>(1, 2) = 0x80;
336  // change deep copy:
337  cv::Mat m = a.asCvMat<cv::Mat>(DEEP_COPY);
338  m.at<uint8_t>(2, 1) = 0x70;
339  // Expect NO change in source:
340  EXPECT_EQ(a.at<uint8_t>(1, 2), 0x80);
341 
342  // size:
343  m = cv::Mat(40, 40, CV_8UC1);
344  EXPECT_EQ(a.getWidth(), 20U);
345  EXPECT_EQ(a.getHeight(), 10U);
346  }
347 }
348 
349 TEST(CImage, ScaleImage)
350 {
351  using namespace mrpt::img;
352  CImage a;
353  bool load_ok = a.loadFromFile(tstImgFileColor);
354  EXPECT_TRUE(load_ok);
355 
356  {
357  CImage b;
358  a.scaleImage(b, 600, 400);
359  EXPECT_EQ(b.getWidth(), 600U);
360  EXPECT_EQ(b.getHeight(), 400U);
361  EXPECT_EQ(a.getWidth(), 320U);
362  EXPECT_EQ(a.getHeight(), 240U);
363  }
364 
365  for (int pass = 0; pass < 2; pass++)
366  {
367  CImage c;
368  if (pass == 0)
369  c = a.makeDeepCopy();
370  else
371  a.scaleImage(c, 311, 211);
372  const auto cw = c.getWidth(), ch = c.getHeight();
373 
374  {
375  CImage b;
376  c.scaleHalf(b, IMG_INTERP_NN);
377  EXPECT_EQ(b.getWidth(), cw / 2);
378  EXPECT_EQ(b.getHeight(), ch / 2);
379  EXPECT_EQ(c.getWidth(), cw);
380  EXPECT_EQ(c.getHeight(), ch);
381  }
382  {
383  CImage ag = c.grayscale();
384  CImage b;
385  ag.scaleHalf(b, IMG_INTERP_LINEAR);
386  EXPECT_EQ(b.getWidth(), cw / 2);
387  EXPECT_EQ(b.getHeight(), ch / 2);
388  EXPECT_EQ(ag.getWidth(), cw);
389  EXPECT_EQ(ag.getHeight(), ch);
390  }
391  {
392  CImage ag = c.grayscale();
393  CImage b;
394  ag.scaleHalf(b, IMG_INTERP_NN);
395  EXPECT_EQ(b.getWidth(), cw / 2);
396  EXPECT_EQ(b.getHeight(), ch / 2);
397  EXPECT_EQ(ag.getWidth(), cw);
398  EXPECT_EQ(ag.getHeight(), ch);
399  }
400  } // two passes
401 
402  {
403  CImage b;
404  a.scaleHalf(b, IMG_INTERP_LINEAR);
405  EXPECT_EQ(b.getWidth(), a.getWidth() / 2);
406  EXPECT_EQ(b.getHeight(), a.getHeight() / 2);
407  EXPECT_EQ(a.getWidth(), 320U);
408  EXPECT_EQ(a.getHeight(), 240U);
409  }
410  {
411  CImage b;
412  a.scaleDouble(b, IMG_INTERP_NN);
413  EXPECT_EQ(b.getWidth(), a.getWidth() * 2);
414  EXPECT_EQ(b.getHeight(), a.getHeight() * 2);
415  EXPECT_EQ(a.getWidth(), 320U);
416  EXPECT_EQ(a.getHeight(), 240U);
417  }
418  {
419  CImage b;
420  a.scaleDouble(b, IMG_INTERP_LINEAR);
421  EXPECT_EQ(b.getWidth(), a.getWidth() * 2);
422  EXPECT_EQ(b.getHeight(), a.getHeight() * 2);
423  EXPECT_EQ(a.getWidth(), 320U);
424  EXPECT_EQ(a.getHeight(), 240U);
425  }
426 }
427 
428 TEST(CImage, Serialize)
429 {
430  using namespace mrpt::img;
431  CImage a;
432  bool load_ok = a.loadFromFile(tstImgFileColor);
433  EXPECT_TRUE(load_ok);
434 
436  a.getAsMatrix(am, true, 0, 0, -1, -1, false /* dont normalize to [0,1] */);
437 
439  auto arch = mrpt::serialization::archiveFrom(buf);
440  arch << a;
441  buf.Seek(0);
442  CImage b;
443  arch >> b;
444 
446  b.getAsMatrix(bm, true, 0, 0, -1, -1, false /* dont normalize to [0,1] */);
447 
448  EXPECT_EQ(am, bm);
449 }
450 
451 TEST(CImage, KLT_response)
452 {
453  using namespace mrpt::img;
454 
455  {
456  CImage a(100, 90, CH_GRAY);
457  a.filledRectangle(0, 0, 99, 99, TColor(0x10));
458  a.filledRectangle(40, 30, 41, 31, TColor(0x20));
459 
460  for (int w = 2; w < 12; w++)
461  {
462  const auto resp = a.KLT_response(40, 30, w);
463  EXPECT_GT(resp, 0.5f);
464  }
465  }
466 }
467 
468 TEST(CImage, LoadAndComparePseudoRnd)
469 {
470  using namespace mrpt::img;
471  using namespace std::string_literals;
472 
473  const auto tstimg =
474  mrpt::UNITTEST_BASEDIR + "/tests/test_pseudorandom_img_seed70.png"s;
475 
476  CImage a;
477  bool load_ok = a.loadFromFile(tstimg);
478  EXPECT_TRUE(load_ok) << "Cannot load: " << tstimg;
479 
480  CImage b(10, 7, CH_GRAY);
482 
483  expect_identical(a, b, "LoadAndComparePseudoRnd"s);
484 }
485 
486 TEST(CImage, LoadAndSave)
487 {
488  using namespace mrpt::img;
489  using namespace std::string_literals;
490 
491  for (unsigned h = 7; h < 20; h += 17)
492  {
493  for (unsigned w = 10; w < 33; w++)
494  {
495  CImage a(w, h, CH_GRAY);
496  fillImagePseudoRandom(w * h, a);
497 
498  const auto f = mrpt::system::getTempFileName() + ".png"s;
499 
500  const auto tstName =
501  mrpt::format("From: LoadAndSave test w=%u h=%u", w, h);
502 
503  bool saved_ok = a.saveToFile(f);
504  EXPECT_TRUE(saved_ok) << tstName;
505 
506  CImage b;
507  bool load_ok = b.loadFromFile(f);
508  EXPECT_TRUE(load_ok) << tstName;
509 
510  if (!expect_identical(a, b, tstName))
511  {
512  GTEST_FAIL() << "a:\n"
513  << a.asCvMatRef() << "\nb:\n"
514  << b.asCvMatRef() << "\n";
515  }
516  }
517  }
518 }
519 
520 TEST(CImage, DifferentAccessMethodsColor)
521 {
522  using namespace mrpt::img;
523  CImage a;
524  bool load_ok = a.loadFromFile(tstImgFileColor);
525  EXPECT_TRUE(load_ok);
526  EXPECT_TRUE(a.isColor());
527 
528  for (unsigned r = 0; r < 3; r++)
529  {
530  for (unsigned c = 0; c < 3; c++)
531  {
532  for (int ch = 0; ch < 3; ch++)
533  {
534  EXPECT_EQ(*a(c, r, ch), a.at<uint8_t>(c, r, ch))
535  << "ch=" << ch << "\n";
536  EXPECT_EQ(*a(c, r, ch), *a.ptr<uint8_t>(c, r, ch))
537  << "ch=" << ch << "\n";
538  EXPECT_EQ(*a(c, r, ch), a.ptrLine<uint8_t>(r)[c * 3 + ch])
539  << "(c,r,ch)=(" << c << "," << r << "," << ch << ")"
540  << "\n a(c, r, ch)=" << static_cast<void*>(a(c, r, ch))
541  << "\n &a.ptrLine<uint8_t>(r)[c * 3 + ch] = "
542  << static_cast<void*>(&a.ptrLine<uint8_t>(r)[c * 3 + ch])
543  << "\n a(0, r, ch)=" << static_cast<void*>(a(0, r, ch))
544  << "\n a.ptrLine<uint8_t>(r) = "
545  << static_cast<void*>(a.ptrLine<uint8_t>(r)) << "\n";
546  }
547  }
548  }
549 }
550 
551 TEST(CImage, DifferentAccessMethodsGray)
552 {
553  using namespace mrpt::img;
554  CImage a;
555  bool load_ok = a.loadFromFile(tstImgFileColor);
556  EXPECT_TRUE(load_ok);
557  a = a.grayscale();
558  EXPECT_FALSE(a.isColor());
559 
560  for (unsigned r = 5; r < 7; r++)
561  {
562  for (unsigned c = 10; c < 12; c++)
563  {
564  EXPECT_EQ(*a(c, r), a.at<uint8_t>(c, r));
565  EXPECT_EQ(*a(c, r), *a.ptr<uint8_t>(c, r));
566  EXPECT_EQ(*a(c, r), a.ptrLine<uint8_t>(r)[c]);
567  }
568  }
569 }
570 
571 #endif // MRPT_HAS_OPENCV
Shallow copy: the copied object is a reference to the original one.
Definition: img/CImage.h:74
Used in mrpt::img::CImage.
Definition: img/CImage.h:81
CImage scaleHalf(TInterpolationMethod interp) const
Returns a new image scaled down to half its original size.
Definition: img/CImage.h:313
static void fillImagePseudoRandom(uint32_t seed, mrpt::img::CImage &img)
TEST(CImage, CtorDefault)
const auto tstImgFileColor
GLdouble s
Definition: glext.h:3682
GLubyte GLubyte GLubyte GLubyte w
Definition: glext.h:4199
unsigned char uint8_t
Definition: rptypes.h:44
CArchiveStreamBase< STREAM > archiveFrom(STREAM &s)
Helper function to create a templatized wrapper CArchive object for a: MRPT&#39;s CStream, std::istream, std::ostream, std::stringstream.
Definition: CArchive.h:586
mrpt::img::CImage CImage
Definition: utils/CImage.h:5
std::string getTempFileName()
Returns the name of a proposed temporary file name.
Definition: filesystem.cpp:283
const GLubyte * c
Definition: glext.h:6406
This CStream derived class allow using a memory buffer as a CStream.
GLint GLvoid * img
Definition: glext.h:3769
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...
GLubyte GLubyte b
Definition: glext.h:6372
void Randomize(const uint32_t seed)
Randomize the generators.
GLsizei const GLchar ** string
Definition: glext.h:4116
CImage grayscale() const
Returns a grayscale version of the image, or a shallow copy of itself if it is already a grayscale im...
Definition: CImage.cpp:953
GLdouble GLdouble GLdouble r
Definition: glext.h:3711
CImage makeDeepCopy() const
Returns a deep copy of this image.
Definition: CImage.cpp:212
static void CtorSized_gray(unsigned int w, unsigned int h)
std::string format(const char *fmt,...) MRPT_printf_format_check(1
A std::string version of C sprintf.
Definition: format.cpp:16
Deep copy: the copied object has a duplicate of all data, becoming independent.
Definition: img/CImage.h:77
static bool expect_identical(const mrpt::img::CImage &a, const mrpt::img::CImage &b, const std::string &s=std::string())
CImage scaleDouble(TInterpolationMethod interp) const
Returns a new image scaled up to double its original size.
Definition: img/CImage.h:328
GLenum GLint GLint y
Definition: glext.h:3542
A RGB color - 8bit.
Definition: TColor.h:20
GLenum GLint x
Definition: glext.h:3542
unsigned __int32 uint32_t
Definition: rptypes.h:50
CRandomGenerator & getRandomGenerator()
A static instance of a CRandomGenerator class, for use in single-thread applications.
This template class provides the basic functionality for a general 2D any-size, resizable container o...
GLubyte GLubyte GLubyte a
Definition: glext.h:6372
A class for storing images as grayscale or RGB bitmaps.
Definition: img/CImage.h:147



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: 8fe78517f Sun Jul 14 19:43:28 2019 +0200 at lun oct 28 02:10:00 CET 2019