Visual Servoing Platform version 3.7.0
Loading...
Searching...
No Matches
catchImageLoadSave.cpp
1/*
2 * ViSP, open source Visual Servoing Platform software.
3 * Copyright (C) 2005 - 2025 by Inria. All rights reserved.
4 *
5 * This software is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 * See the file LICENSE.txt at the root directory of this source
10 * distribution for additional information about the GNU GPL.
11 *
12 * For using ViSP with software that can not be combined with the GNU
13 * GPL, please contact Inria about acquiring a ViSP Professional
14 * Edition License.
15 *
16 * See https://visp.inria.fr for more information.
17 *
18 * This software was developed at:
19 * Inria Rennes - Bretagne Atlantique
20 * Campus Universitaire de Beaulieu
21 * 35042 Rennes Cedex
22 * France
23 *
24 * If you have questions regarding the use of this file, please contact
25 * Inria at visp@inria.fr
26 *
27 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
28 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
29 *
30 * Description:
31 * Check that the different image I/O backends work correctly.
32 */
33
37
38#include <visp3/core/vpConfig.h>
39
40#if defined(VISP_HAVE_CATCH2) && (VISP_HAVE_DATASET_VERSION >= 0x030500) && defined(VISP_HAVE_THREADS)
41
42#include <catch_amalgamated.hpp>
43
44#include <thread>
45#include <visp3/core/vpIoTools.h>
46#include <visp3/io/vpImageIo.h>
47
48#ifdef ENABLE_VISP_NAMESPACE
49using namespace VISP_NAMESPACE_NAME;
50#endif
51
52VP_ATTRIBUTE_NO_DESTROY static const std::string ipath = vpIoTools::getViSPImagesDataPath();
53VP_ATTRIBUTE_NO_DESTROY static const std::string path = ipath + "/Solvay/Solvay_conference_1927_Version2_640x440";
54
55static const double ccThreshPNG = 1.0;
56static const double ccThreshJPG = 0.99;
57
58VP_ATTRIBUTE_NO_DESTROY static const std::vector<vpImageIo::vpImageIoBackendType> backends
59{
60#if defined(VISP_HAVE_JPEG) && defined(VISP_HAVE_PNG)
62#endif
63#if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGCODECS)
65#endif
66#if defined VISP_HAVE_SIMDLIB
68#endif
69#if defined VISP_HAVE_STBIMAGE
71#endif
72};
73
74VP_ATTRIBUTE_NO_DESTROY static const std::vector<std::string> backendNamesJpeg
75{
76#if defined(VISP_HAVE_JPEG)
77 "libjpeg",
78#endif
79#if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGCODECS)
80 "OpenCV",
81#endif
82 "simd", "stb"
83};
84
85VP_ATTRIBUTE_NO_DESTROY static std::vector<std::string> backendNamesPng
86{
87#if defined(VISP_HAVE_PNG)
88 "libpng",
89#endif
90#if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGCODECS)
91 "OpenCV",
92#endif
93 "simd", "stb"
94};
95
96VP_ATTRIBUTE_NO_DESTROY static const std::vector<vpImageIo::vpImageIoBackendType> backendsInMemory
97{
98#if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGCODECS)
100#endif
101#if defined VISP_HAVE_STBIMAGE
103#endif
104};
105
106VP_ATTRIBUTE_NO_DESTROY static std::vector<std::string> backendNamesPngInMemory
107{
108#if defined(VISP_HAVE_OPENCV) && defined(HAVE_OPENCV_IMGCODECS)
109 "OpenCV",
110#endif
111#if defined VISP_HAVE_STBIMAGE
112 "stb"
113#endif
114};
115
116static const unsigned int imgWidth = 640;
117static const unsigned int imgHeight = 440;
118
119namespace
120{
121double computePearsonCC(const vpImage<unsigned char> &I1, const vpImage<unsigned char> &I2)
122{
123 double m1 = I1.getSum() / I1.getSize();
124 double m2 = I2.getSum() / I2.getSize();
125
126 double num = 0, den1 = 0, den2 = 0;
127 for (unsigned int i = 0; i < I1.getSize(); i++) {
128 double x1 = I1.bitmap[i];
129 double x2 = I2.bitmap[i];
130 double x1_m = x1 - m1;
131 double x2_m = x2 - m2;
132 num += x1_m * x2_m;
133 den1 += x1_m * x1_m;
134 den2 += x2_m * x2_m;
135 }
136
137 return num / (std::sqrt(den1) * std::sqrt(den2));
138}
139
140double computePearsonCC(const vpImage<vpRGBa> &I1, const vpImage<vpRGBa> &I2)
141{
142 double m1 = I1.getSum() / (3 * I1.getSize());
143 double m2 = I2.getSum() / (3 * I2.getSize());
144
145 double num = 0, den1 = 0, den2 = 0;
146 for (unsigned int i = 0; i < I1.getSize(); i++) {
147 double x1 = I1.bitmap[i].R + I1.bitmap[i].G + I1.bitmap[i].B;
148 double x2 = I2.bitmap[i].R + I2.bitmap[i].G + I2.bitmap[i].B;
149 double x1_m = x1 - m1;
150 double x2_m = x2 - m2;
151 num += x1_m * x2_m;
152 den1 += x1_m * x1_m;
153 den2 += x2_m * x2_m;
154 }
155
156 return num / (std::sqrt(den1) * std::sqrt(den2));
157}
158} // namespace
159
160TEST_CASE("Test grayscale JPEG image loading", "[image_I/O]")
161{
163 vpImageIo::read(I_ref, path + ".jpg");
164
165 for (size_t j = 0; j < backends.size(); j++) {
167
168 SECTION(backendNamesJpeg[j] + " backend")
169 {
170 CHECK_NOTHROW(vpImageIo::read(I, path + ".jpg", backends[j]));
171 double cc = computePearsonCC(I_ref, I);
172 std::cout << backendNamesJpeg[j] << " backend Pearson correlation coefficient: " << cc << std::endl;
173 CHECK(cc >= Catch::Approx(ccThreshJPG));
174 CHECK(I.getWidth() == imgWidth);
175 CHECK(I.getHeight() == imgHeight);
176 }
177 }
178}
179
180TEST_CASE("Test RGBA JPEG image loading", "[image_I/O]")
181{
182 vpImage<vpRGBa> I_ref;
183 vpImageIo::read(I_ref, path + ".jpg");
184
185 for (size_t j = 0; j < backends.size(); j++) {
187
188 SECTION(backendNamesJpeg[j] + " backend")
189 {
190 CHECK_NOTHROW(vpImageIo::read(I, path + ".jpg", backends[j]));
191 double cc = computePearsonCC(I_ref, I);
192 std::cout << backendNamesJpeg[j] << " backend Pearson correlation coefficient: " << cc << std::endl;
193 CHECK(cc >= Catch::Approx(ccThreshJPG));
194 CHECK(I.getWidth() == imgWidth);
195 CHECK(I.getHeight() == imgHeight);
196 }
197 }
198}
199
200TEST_CASE("Test grayscale PNG image loading", "[image_I/O]")
201{
203 vpImageIo::read(I_ref, path + ".png");
204
205 for (size_t j = 0; j < backends.size(); j++) {
207
208 SECTION(backendNamesPng[j] + " backend")
209 {
210 CHECK_NOTHROW(vpImageIo::read(I, path + ".png", backends[j]));
211 double cc = computePearsonCC(I_ref, I);
212 std::cout << backendNamesPng[j] << " backend Pearson correlation coefficient: " << cc << std::endl;
213 CHECK(cc == Catch::Approx(ccThreshPNG));
214 CHECK(I.getWidth() == imgWidth);
215 CHECK(I.getHeight() == imgHeight);
216 }
217 }
218}
219
220TEST_CASE("Test RGBA PNG image loading", "[image_I/O]")
221{
222 vpImage<vpRGBa> I_ref;
223 vpImageIo::read(I_ref, path + ".png");
224
225 for (size_t j = 0; j < backends.size(); j++) {
227
228 SECTION(backendNamesPng[j] + " backend")
229 {
230 CHECK_NOTHROW(vpImageIo::read(I, path + ".png", backends[j]));
231 double cc = computePearsonCC(I_ref, I);
232 std::cout << backendNamesPng[j] << " backend Pearson correlation coefficient: " << cc << std::endl;
233 CHECK(cc == Catch::Approx(ccThreshPNG));
234 CHECK(I.getWidth() == imgWidth);
235 CHECK(I.getHeight() == imgHeight);
236 }
237 }
238}
239
240TEST_CASE("Test grayscale JPEG image saving", "[image_I/O]")
241{
243 std::string directory_filename_tmp =
244 tmp_dir + "/vpIoTools_perfImageLoadSave_" + vpTime::getDateTime("%Y-%m-%d_%H.%M.%S");
245 vpIoTools::makeDirectory(directory_filename_tmp);
246 REQUIRE(vpIoTools::checkDirectory(directory_filename_tmp));
247
249 vpImageIo::read(I, path + ".png");
250
251 for (size_t j = 0; j < backends.size(); j++) {
252 SECTION(backendNamesJpeg[j] + " backend")
253 {
254 CHECK_NOTHROW(vpImageIo::write(I, directory_filename_tmp + "/ViSP_tmp_perf_write.jpg", backends[j]));
256 vpImageIo::read(I_read, directory_filename_tmp + "/ViSP_tmp_perf_write.jpg");
257 double cc = computePearsonCC(I_read, I);
258 std::cout << backendNamesPng[j] << " backend Pearson correlation coefficient: " << cc << std::endl;
259 CHECK(cc >= Catch::Approx(ccThreshJPG));
260 }
261 }
262
263 REQUIRE(vpIoTools::remove(directory_filename_tmp));
264}
265
266TEST_CASE("Test RGBA JPEG image saving", "[image_I/O]")
267{
269 std::string directory_filename_tmp =
270 tmp_dir + "/vpIoTools_perfImageLoadSave_" + vpTime::getDateTime("%Y-%m-%d_%H.%M.%S");
271 vpIoTools::makeDirectory(directory_filename_tmp);
272 REQUIRE(vpIoTools::checkDirectory(directory_filename_tmp));
273
275 vpImageIo::read(I, path + ".png");
276
277 for (size_t j = 0; j < backends.size(); j++) {
278 SECTION(backendNamesJpeg[j] + " backend")
279 {
280 CHECK_NOTHROW(vpImageIo::write(I, directory_filename_tmp + "/ViSP_tmp_perf_write.jpg", backends[j]));
281 vpImage<vpRGBa> I_read;
282 vpImageIo::read(I_read, directory_filename_tmp + "/ViSP_tmp_perf_write.jpg");
283 double cc = computePearsonCC(I_read, I);
284 std::cout << backendNamesPng[j] << " backend Pearson correlation coefficient: " << cc << std::endl;
285 CHECK(cc >= Catch::Approx(ccThreshJPG));
286 }
287 }
288
289 REQUIRE(vpIoTools::remove(directory_filename_tmp));
290}
291
292TEST_CASE("Test grayscale PNG image saving", "[image_I/O]")
293{
295 std::string directory_filename_tmp =
296 tmp_dir + "/vpIoTools_perfImageLoadSave_" + vpTime::getDateTime("%Y-%m-%d_%H.%M.%S");
297 vpIoTools::makeDirectory(directory_filename_tmp);
298 REQUIRE(vpIoTools::checkDirectory(directory_filename_tmp));
299
301 vpImageIo::read(I, path + ".png");
302
303 for (size_t j = 0; j < backends.size(); j++) {
304 SECTION(backendNamesPng[j] + " backend")
305 {
306 CHECK_NOTHROW(vpImageIo::write(I, directory_filename_tmp + "/ViSP_tmp_perf_write.png", backends[j]));
308 vpImageIo::read(I_read, directory_filename_tmp + "/ViSP_tmp_perf_write.png");
309 double cc = computePearsonCC(I_read, I);
310 std::cout << backendNamesPng[j] << " backend Pearson correlation coefficient: " << cc << std::endl;
311 CHECK(cc == Catch::Approx(ccThreshPNG));
312 }
313 }
314
315 REQUIRE(vpIoTools::remove(directory_filename_tmp));
316}
317
318TEST_CASE("Test RGBA PNG image saving", "[image_I/O]")
319{
321 std::string directory_filename_tmp =
322 tmp_dir + "/vpIoTools_perfImageLoadSave_" + vpTime::getDateTime("%Y-%m-%d_%H.%M.%S");
323 vpIoTools::makeDirectory(directory_filename_tmp);
324 REQUIRE(vpIoTools::checkDirectory(directory_filename_tmp));
325
327 vpImageIo::read(I, path + ".png");
328
329 for (size_t j = 0; j < backends.size(); j++) {
330 SECTION(backendNamesPng[j] + " backend")
331 {
332 CHECK_NOTHROW(vpImageIo::write(I, directory_filename_tmp + "/ViSP_tmp_perf_write.png", backends[j]));
333 vpImage<vpRGBa> I_read;
334 vpImageIo::read(I_read, directory_filename_tmp + "/ViSP_tmp_perf_write.png");
335 double cc = computePearsonCC(I_read, I);
336 std::cout << backendNamesPng[j] << " backend Pearson correlation coefficient: " << cc << std::endl;
337 CHECK(cc == Catch::Approx(ccThreshPNG));
338 }
339 }
340
341 REQUIRE(vpIoTools::remove(directory_filename_tmp));
342}
343
344TEST_CASE("Test grayscale in-memory PNG image encoding/decoding", "[image_I/O]")
345{
347 vpImageIo::read(I_ref, path + ".png");
348 REQUIRE(I_ref.getSize() > 0);
349
350 for (size_t i = 0; i < backendsInMemory.size(); i++) {
351 SECTION(backendNamesPngInMemory[i] + " backend")
352 {
353 std::vector<unsigned char> buffer;
354 vpImageIo::writePNGtoMem(I_ref, buffer, backendsInMemory[i]);
355 REQUIRE(!buffer.empty());
356
357 for (size_t j = 0; j < backendsInMemory.size(); j++) {
358 SECTION(backendNamesPngInMemory[i] + " backend")
359 {
361 vpImageIo::readPNGfromMem(buffer, I, backendsInMemory[j]);
362 CHECK(I_ref == I);
363 }
364 }
365 }
366 }
367}
368
369TEST_CASE("Test color in-memory PNG image encoding/decoding", "[image_I/O]")
370{
371 vpImage<vpRGBa> I_ref;
372 vpImageIo::read(I_ref, path + ".png");
373 REQUIRE(I_ref.getSize() > 0);
374
375 for (size_t i = 0; i < backendsInMemory.size(); i++) {
376 SECTION(backendNamesPngInMemory[i] + " backend")
377 {
378 {
379 std::vector<unsigned char> buffer;
380 const bool saveAlpha = false;
381 vpImageIo::writePNGtoMem(I_ref, buffer, backendsInMemory[i], saveAlpha);
382 REQUIRE(!buffer.empty());
383
384 for (size_t j = 0; j < backendsInMemory.size(); j++) {
385 SECTION(backendNamesPngInMemory[i] + " backend")
386 {
388 vpImageIo::readPNGfromMem(buffer, I, backendsInMemory[j]);
389 CHECK(I_ref == I);
390 }
391 }
392 }
393 {
394 std::vector<unsigned char> buffer;
395 const bool saveAlpha = true;
396 vpImageIo::writePNGtoMem(I_ref, buffer, backendsInMemory[i], saveAlpha);
397 REQUIRE(!buffer.empty());
398
399 for (size_t j = 0; j < backendsInMemory.size(); j++) {
400 SECTION(backendNamesPngInMemory[i] + " backend")
401 {
403 vpImageIo::readPNGfromMem(buffer, I, backendsInMemory[j]);
404 CHECK(I_ref == I);
405 }
406 }
407 }
408 }
409 }
410}
411
412int main(int argc, char *argv[])
413{
414 Catch::Session session; // There must be exactly one instance
415 session.applyCommandLine(argc, argv);
416
417 int numFailed = session.run();
418 return numFailed;
419}
420
421#else
422#include <iostream>
423
424int main() { return EXIT_SUCCESS; }
425#endif
@ IO_STB_IMAGE_BACKEND
Use embedded stb_image library.
Definition vpImageIo.h:134
@ IO_SIMDLIB_BACKEND
Use embedded simd library.
Definition vpImageIo.h:133
@ IO_SYSTEM_LIB_BACKEND
Use system libraries like libpng or libjpeg-turbo.
Definition vpImageIo.h:131
@ IO_OPENCV_BACKEND
Use OpenCV imgcodecs module.
Definition vpImageIo.h:132
static void read(vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
static void readPNGfromMem(const std::vector< unsigned char > &buffer, vpImage< unsigned char > &I, int backend=IO_DEFAULT_BACKEND)
static void writePNGtoMem(const vpImage< unsigned char > &I, std::vector< unsigned char > &buffer, int backend=IO_DEFAULT_BACKEND)
static void write(const vpImage< unsigned char > &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND)
Definition of the vpImage class member functions.
Definition vpImage.h:131
double getSum(const vpImage< bool > *p_mask=nullptr, unsigned int *nbValidPoints=nullptr) const
Compute the sum of image intensities.
unsigned int getSize() const
Definition vpImage.h:221
Type * bitmap
points toward the bitmap
Definition vpImage.h:135
static std::string getViSPImagesDataPath()
static std::string getTempPath()
static bool checkDirectory(const std::string &dirname)
static void makeDirectory(const std::string &dirname)
static bool remove(const std::string &filename)
static std::string makeTempDirectory(const std::string &dirname)
VISP_EXPORT std::string getDateTime(const std::string &format="%Y/%m/%d %H:%M:%S")