From 413f542533080a5dc859607de49ed912ae682046 Mon Sep 17 00:00:00 2001 From: Souriya Trinh Date: Sun, 23 Jan 2022 23:18:03 +0100 Subject: [PATCH 01/12] Benchmark image I/O performance wrt the library backends. --- CMakeLists.txt | 2 + modules/io/CMakeLists.txt | 17 +- modules/io/include/visp3/io/vpImageIo.h | 36 +- .../io/src/image/private/vpImageIoBackend.h | 110 + .../io/src/image/private/vpImageIoLibjpeg.cpp | 355 +++ .../io/src/image/private/vpImageIoLibpng.cpp | 619 +++++ .../io/src/image/private/vpImageIoOpenCV.cpp | 217 ++ .../src/image/private/vpImageIoPortable.cpp | 573 +++++ .../io/src/image/private/vpImageIoSimd.cpp | 91 + modules/io/src/image/private/vpImageIoStb.cpp | 125 + modules/io/src/image/vpImageIo.cpp | 2089 ++--------------- modules/io/test/perfImageLoadSave.cpp | 273 +++ script/PerfVisualize.py | 153 ++ 13 files changed, 2778 insertions(+), 1882 deletions(-) create mode 100644 modules/io/src/image/private/vpImageIoBackend.h create mode 100644 modules/io/src/image/private/vpImageIoLibjpeg.cpp create mode 100644 modules/io/src/image/private/vpImageIoLibpng.cpp create mode 100644 modules/io/src/image/private/vpImageIoOpenCV.cpp create mode 100644 modules/io/src/image/private/vpImageIoPortable.cpp create mode 100644 modules/io/src/image/private/vpImageIoSimd.cpp create mode 100644 modules/io/src/image/private/vpImageIoStb.cpp create mode 100644 modules/io/test/perfImageLoadSave.cpp create mode 100644 script/PerfVisualize.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e570a2f7b..1e62852eef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -701,6 +701,8 @@ if(NOT USE_OPENCV AND (NOT USE_PNG OR NOT USE_JPEG)) else() set(WITH_STBIMAGE OFF) endif() +# TODO: +set(WITH_STBIMAGE ON) VP_OPTION(WITH_CATCH2 "" "" "Use catch2" "" ON IF (VISP_CXX_STANDARD GREATER VISP_CXX_STANDARD_98)) diff --git a/modules/io/CMakeLists.txt b/modules/io/CMakeLists.txt index fc14d066bb..944b426666 100644 --- a/modules/io/CMakeLists.txt +++ b/modules/io/CMakeLists.txt @@ -57,11 +57,21 @@ if(USE_PNG) add_definitions(${PNG_DEFINITIONS}) endif() -if(WITH_STBIMAGE) +# TODO: +#if(WITH_STBIMAGE) # stb_image is private include_directories(${STBIMAGE_INCLUDE_DIRS}) +#endif() + +if(WITH_CATCH2) + # catch2 is private + include_directories(${CATCH2_INCLUDE_DIRS}) endif() +# simdlib is always enabled since it contains fallback code to plain C++ code +# Simd lib is private +include_directories(${SIMDLIB_INCLUDE_DIRS}) + # OpenCV if(USE_OPENCV) # On win32 since OpenCV 2.4.7 and on OSX with OpenCV 2.4.10 we cannot use OpenCV_LIBS to set ViSP 3rd party libraries. @@ -169,11 +179,6 @@ if(USE_OPENCV) endif() endif(USE_OPENCV) -if(WITH_CATCH2) - # catch2 is private - include_directories(${CATCH2_INCLUDE_DIRS}) -endif() - if(ANDROID) vp_add_module(io visp_core) else() diff --git a/modules/io/include/visp3/io/vpImageIo.h b/modules/io/include/visp3/io/vpImageIo.h index d37cad48e3..7edbb765e7 100644 --- a/modules/io/include/visp3/io/vpImageIo.h +++ b/modules/io/include/visp3/io/vpImageIo.h @@ -124,11 +124,21 @@ class VISP_EXPORT vpImageIo static std::string getExtension(const std::string &filename); public: - static void read(vpImage &I, const std::string &filename); - static void read(vpImage &I, const std::string &filename); - - static void write(const vpImage &I, const std::string &filename); - static void write(const vpImage &I, const std::string &filename); + //TODO: + // Image IO backend for only jpeg and png formats + enum vpImageIoBackendType { + IO_DEFAULT_BACKEND, + IO_LIB_BACKEND, + IO_OPENCV_BACKEND, + IO_SIMDLIB_BACKEND, + IO_STB_IMAGE_BACKEND + }; + + static void read(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); + static void read(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); + + static void write(const vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); + static void write(const vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); static void readPFM(vpImage &I, const std::string &filename); @@ -138,11 +148,11 @@ class VISP_EXPORT vpImageIo static void readPPM(vpImage &I, const std::string &filename); static void readPPM(vpImage &I, const std::string &filename); - static void readJPEG(vpImage &I, const std::string &filename); - static void readJPEG(vpImage &I, const std::string &filename); + static void readJPEG(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); + static void readJPEG(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); - static void readPNG(vpImage &I, const std::string &filename); - static void readPNG(vpImage &I, const std::string &filename); + static void readPNG(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); + static void readPNG(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); static void writePFM(const vpImage &I, const std::string &filename); @@ -153,10 +163,10 @@ class VISP_EXPORT vpImageIo static void writePPM(const vpImage &I, const std::string &filename); static void writePPM(const vpImage &I, const std::string &filename); - static void writeJPEG(const vpImage &I, const std::string &filename); - static void writeJPEG(const vpImage &I, const std::string &filename); + static void writeJPEG(const vpImage &I, const std::string &filename, int quality=90, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); + static void writeJPEG(const vpImage &I, const std::string &filename, int quality=90, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); - static void writePNG(const vpImage &I, const std::string &filename); - static void writePNG(const vpImage &I, const std::string &filename); + static void writePNG(const vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); + static void writePNG(const vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); }; #endif diff --git a/modules/io/src/image/private/vpImageIoBackend.h b/modules/io/src/image/private/vpImageIoBackend.h new file mode 100644 index 0000000000..ff7aa80850 --- /dev/null +++ b/modules/io/src/image/private/vpImageIoBackend.h @@ -0,0 +1,110 @@ +/**************************************************************************** + * + * ViSP, open source Visual Servoing Platform software. + * Copyright (C) 2005 - 2019 by Inria. All rights reserved. + * + * This software is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact Inria about acquiring a ViSP Professional + * Edition License. + * + * See http://visp.inria.fr for more information. + * + * This software was developed at: + * Inria Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * + * If you have questions regarding the use of this file, please contact + * Inria at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: +<<<<<<< HEAD + * Read/write images. + * + * Authors: + * Eric Marchand +======= + * Backend functions implementation for image I/O operations. +>>>>>>> 557f1beda01f36ca886ec039d0a1a80a7446ca59 + * + *****************************************************************************/ + +/*! + \file vpImageIoBackend.h + \brief Backend functions implementation for image I/O operations. +*/ + +#ifndef vpIMAGEIOBACKEND_H +#define vpIMAGEIOBACKEND_H + +#include + + +// Portable FloatMap format (PFM) +// Portable Graymap format (PGM) +// Portable Pixmap format (PPM) +void vp_writePFM(const vpImage &I, const std::string &filename); +void vp_writePGM(const vpImage &I, const std::string &filename); +void vp_writePGM(const vpImage &I, const std::string &filename); +void vp_writePGM(const vpImage &I, const std::string &filename); +void vp_readPFM(vpImage &I, const std::string &filename); +void vp_readPGM(vpImage &I, const std::string &filename); +void vp_readPGM(vpImage &I, const std::string &filename); +void vp_readPPM(vpImage &I, const std::string &filename); +void vp_readPPM(vpImage &I, const std::string &filename); +void vp_writePPM(const vpImage &I, const std::string &filename); +void vp_writePPM(const vpImage &I, const std::string &filename); + +// libjpeg +void readJPEGLibjpeg(vpImage &I, const std::string &filename); +void readJPEGLibjpeg(vpImage &I, const std::string &filename); + +void writeJPEGLibjpeg(const vpImage &I, const std::string &filename, int quality); +void writeJPEGLibjpeg(const vpImage &I, const std::string &filename, int quality); + +// libpng +void readPNGLibpng(vpImage &I, const std::string &filename); +void readPNGLibpng(vpImage &I, const std::string &filename); + +void writePNGLibpng(const vpImage &I, const std::string &filename); +void writePNGLibpng(const vpImage &I, const std::string &filename); + +// OpenCV +void readOpenCV(vpImage &I, const std::string &filename); +void readOpenCV(vpImage &I, const std::string &filename); + +void writeOpenCV(const vpImage &I, const std::string &filename, int quality); +void writeOpenCV(const vpImage &I, const std::string &filename, int quality); + +// Simd lib +void readSimdlib(vpImage &I, const std::string &filename); +void readSimdlib(vpImage &I, const std::string &filename); + +void writeJPEGSimdlib(const vpImage &I, const std::string &filename, int quality); +void writeJPEGSimdlib(const vpImage &I, const std::string &filename, int quality); + +void writePNGSimdlib(const vpImage &I, const std::string &filename); +void writePNGSimdlib(const vpImage &I, const std::string &filename); + +// stb lib +void readStb(vpImage &I, const std::string &filename); +void readStb(vpImage &I, const std::string &filename); + +void writeJPEGStb(const vpImage &I, const std::string &filename, int quality); +void writeJPEGStb(const vpImage &I, const std::string &filename, int quality); + +void writePNGStb(const vpImage &I, const std::string &filename); +void writePNGStb(const vpImage &I, const std::string &filename); + +#endif diff --git a/modules/io/src/image/private/vpImageIoLibjpeg.cpp b/modules/io/src/image/private/vpImageIoLibjpeg.cpp new file mode 100644 index 0000000000..c8f51e47fa --- /dev/null +++ b/modules/io/src/image/private/vpImageIoLibjpeg.cpp @@ -0,0 +1,355 @@ +/**************************************************************************** + * + * ViSP, open source Visual Servoing Platform software. + * Copyright (C) 2005 - 2019 by Inria. All rights reserved. + * + * This software is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact Inria about acquiring a ViSP Professional + * Edition License. + * + * See http://visp.inria.fr for more information. + * + * This software was developed at: + * Inria Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * + * If you have questions regarding the use of this file, please contact + * Inria at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: +<<<<<<< HEAD + * Read/write images. + * + * Authors: + * Eric Marchand +======= + * Libjpeg backend for JPEG image I/O operations. +>>>>>>> 557f1beda01f36ca886ec039d0a1a80a7446ca59 + * + *****************************************************************************/ + +/*! + \file vpImageIoLibjpeg.cpp + \brief Libjpeg backend for JPEG image I/O operations. +*/ + +#include "vpImageIoBackend.h" +#include + +//TODO: is it needed? +//#if defined(_WIN32) +//// Include WinSock2.h before windows.h to ensure that winsock.h is not +//// included by windows.h since winsock.h and winsock2.h are incompatible +//#include +//#include +//#endif + +#if defined(VISP_HAVE_JPEG) +#include +#include +#endif + + +//-------------------------------------------------------------------------- +// JPEG +//-------------------------------------------------------------------------- + +#if defined(VISP_HAVE_JPEG) + +/*! + Write the content of the image bitmap in the file which name is given by \e + filename. This function writes a JPEG file. + + \param I : Image to save as a JPEG file. + \param filename : Name of the file containing the image. + \param quality : JPEG quality for compression. +*/ +void writeJPEGLibjpeg(const vpImage &I, const std::string &filename, int quality) +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + FILE *file; + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + + // Test the filename + if (filename.empty()) { + throw(vpImageException(vpImageException::ioError, "Cannot create JPEG file: filename empty")); + } + + file = fopen(filename.c_str(), "wb"); + + if (file == NULL) { + throw(vpImageException(vpImageException::ioError, "Cannot create JPEG file \"%s\"", filename.c_str())); + } + + unsigned int width = I.getWidth(); + unsigned int height = I.getHeight(); + + jpeg_stdio_dest(&cinfo, file); + + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = 1; + cinfo.in_color_space = JCS_GRAYSCALE; + jpeg_set_defaults(&cinfo); + //TODO: + jpeg_set_quality(&cinfo, quality, TRUE); + + jpeg_start_compress(&cinfo, TRUE); + + unsigned char *line; + line = new unsigned char[width]; + unsigned char *input = (unsigned char *)I.bitmap; + while (cinfo.next_scanline < cinfo.image_height) { + for (unsigned int i = 0; i < width; i++) { + line[i] = *(input); + input++; + } + jpeg_write_scanlines(&cinfo, &line, 1); + } + + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + delete[] line; + fclose(file); +} + +/*! + Write the content of the image bitmap in the file which name is given by \e + filename. This function writes a JPEG file. + + \param I : Image to save as a JPEG file. + \param filename : Name of the file containing the image. + \param quality : JPEG quality for compression. +*/ +void writeJPEGLibjpeg(const vpImage &I, const std::string &filename, int quality) +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + FILE *file; + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + + // Test the filename + if (filename.empty()) { + throw(vpImageException(vpImageException::ioError, "Cannot create JPEG file: filename empty")); + } + + file = fopen(filename.c_str(), "wb"); + + if (file == NULL) { + throw(vpImageException(vpImageException::ioError, "Cannot create JPEG file \"%s\"", filename.c_str())); + } + + unsigned int width = I.getWidth(); + unsigned int height = I.getHeight(); + + jpeg_stdio_dest(&cinfo, file); + + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + jpeg_set_defaults(&cinfo); + //TODO: + jpeg_set_quality(&cinfo, quality, TRUE); + + jpeg_start_compress(&cinfo, TRUE); + + unsigned char *line; + line = new unsigned char[3 * width]; + unsigned char *input = (unsigned char *)I.bitmap; + while (cinfo.next_scanline < cinfo.image_height) { + for (unsigned int i = 0; i < width; i++) { + line[i * 3] = *(input); + input++; + line[i * 3 + 1] = *(input); + input++; + line[i * 3 + 2] = *(input); + input++; + input++; + } + jpeg_write_scanlines(&cinfo, &line, 1); + } + + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); + delete[] line; + fclose(file); +} + +/*! + Read the contents of the JPEG file, allocate memory + for the corresponding gray level image, if necessary convert the data in + gray level, and set the bitmap whith the gray level data. That means that + the image \e I is a "black and white" rendering of the original image in \e + filename, as in a black and white photograph. If necessary, the quantization + formula used is \f$0,299 r + 0,587 g + 0,114 b\f$. + + If the image has been already initialized, memory allocation is done + only if the new image size is different, else we re-use the same + memory space. + + \param I : Image to set with the \e filename content. + \param filename : Name of the file containing the image. +*/ +void readJPEGLibjpeg(vpImage &I, const std::string &filename) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + FILE *file; + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + + // Test the filename + if (filename.empty()) { + throw(vpImageException(vpImageException::ioError, "Cannot read JPEG image: filename empty")); + } + + file = fopen(filename.c_str(), "rb"); + + if (file == NULL) { + throw(vpImageException(vpImageException::ioError, "Cannot read JPEG file \"%s\"", filename.c_str())); + } + + jpeg_stdio_src(&cinfo, file); + jpeg_read_header(&cinfo, TRUE); + + unsigned int width = cinfo.image_width; + unsigned int height = cinfo.image_height; + + if ((width != I.getWidth()) || (height != I.getHeight())) + I.resize(height, width); + + jpeg_start_decompress(&cinfo); + + unsigned int rowbytes = cinfo.output_width * (unsigned int)(cinfo.output_components); + JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, rowbytes, 1); + + if (cinfo.out_color_space == JCS_RGB) { + vpImage Ic(height, width); + unsigned char *output = (unsigned char *)Ic.bitmap; + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, buffer, 1); + for (unsigned int i = 0; i < width; i++) { + *(output++) = buffer[0][i * 3]; + *(output++) = buffer[0][i * 3 + 1]; + *(output++) = buffer[0][i * 3 + 2]; + *(output++) = vpRGBa::alpha_default; + } + } + vpImageConvert::convert(Ic, I); + } + + else if (cinfo.out_color_space == JCS_GRAYSCALE) { + while (cinfo.output_scanline < cinfo.output_height) { + unsigned int row = cinfo.output_scanline; + jpeg_read_scanlines(&cinfo, buffer, 1); + memcpy(I[row], buffer[0], rowbytes); + } + } + + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + fclose(file); +} + +/*! + Read a JPEG file and initialize a scalar image. + + Read the contents of the JPEG file, allocate + memory for the corresponding image, and set + the bitmap whith the content of + the file. + + If the image has been already initialized, memory allocation is done + only if the new image size is different, else we re-use the same + memory space. + + If the file corresponds to a grayscaled image, a conversion is done to deal + with \e I which is a color image. + + \param I : Color image to set with the \e filename content. + \param filename : Name of the file containing the image. +*/ +void readJPEGLibjpeg(vpImage &I, const std::string &filename) +{ + struct jpeg_decompress_struct cinfo; + struct jpeg_error_mgr jerr; + FILE *file; + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&cinfo); + + // Test the filename + if (filename.empty()) { + throw(vpImageException(vpImageException::ioError, "Cannot read JPEG image: filename empty")); + } + + file = fopen(filename.c_str(), "rb"); + + if (file == NULL) { + throw(vpImageException(vpImageException::ioError, "Cannot read JPEG file \"%s\"", filename.c_str())); + } + + jpeg_stdio_src(&cinfo, file); + + jpeg_read_header(&cinfo, TRUE); + + unsigned int width = cinfo.image_width; + unsigned int height = cinfo.image_height; + + if ((width != I.getWidth()) || (height != I.getHeight())) + I.resize(height, width); + + jpeg_start_decompress(&cinfo); + + unsigned int rowbytes = cinfo.output_width * (unsigned int)(cinfo.output_components); + JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, rowbytes, 1); + + if (cinfo.out_color_space == JCS_RGB) { + unsigned char *output = (unsigned char *)I.bitmap; + while (cinfo.output_scanline < cinfo.output_height) { + jpeg_read_scanlines(&cinfo, buffer, 1); + for (unsigned int i = 0; i < width; i++) { + *(output++) = buffer[0][i * 3]; + *(output++) = buffer[0][i * 3 + 1]; + *(output++) = buffer[0][i * 3 + 2]; + *(output++) = vpRGBa::alpha_default; + } + } + } + + else if (cinfo.out_color_space == JCS_GRAYSCALE) { + vpImage Ig(height, width); + + while (cinfo.output_scanline < cinfo.output_height) { + unsigned int row = cinfo.output_scanline; + jpeg_read_scanlines(&cinfo, buffer, 1); + memcpy(Ig[row], buffer[0], rowbytes); + } + + vpImageConvert::convert(Ig, I); + } + + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + fclose(file); +} +#endif diff --git a/modules/io/src/image/private/vpImageIoLibpng.cpp b/modules/io/src/image/private/vpImageIoLibpng.cpp new file mode 100644 index 0000000000..8d0ff0d3d5 --- /dev/null +++ b/modules/io/src/image/private/vpImageIoLibpng.cpp @@ -0,0 +1,619 @@ +/**************************************************************************** + * + * ViSP, open source Visual Servoing Platform software. + * Copyright (C) 2005 - 2019 by Inria. All rights reserved. + * + * This software is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact Inria about acquiring a ViSP Professional + * Edition License. + * + * See http://visp.inria.fr for more information. + * + * This software was developed at: + * Inria Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * + * If you have questions regarding the use of this file, please contact + * Inria at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: +<<<<<<< HEAD + * Read/write images. + * + * Authors: + * Eric Marchand +======= + * Libpng backend for PNG image I/O operations. +>>>>>>> 557f1beda01f36ca886ec039d0a1a80a7446ca59 + * + *****************************************************************************/ + +/*! + \file vpImageIoLibpng.cpp + \brief Libpng backend for PNG image I/O operations. +*/ + +#include "vpImageIoBackend.h" +#include + +//TODO: is it needed? +//#if defined(_WIN32) +//// Include WinSock2.h before windows.h to ensure that winsock.h is not +//// included by windows.h since winsock.h and winsock2.h are incompatible +//#include +//#include +//#endif + +#if defined(VISP_HAVE_PNG) +#include +#endif + + +//-------------------------------------------------------------------------- +// PNG +//-------------------------------------------------------------------------- + +#if defined(VISP_HAVE_PNG) + +/*! + Write the content of the image bitmap in the file which name is given by \e + filename. This function writes a PNG file. + + \param I : Image to save as a PNG file. + \param filename : Name of the file containing the image. +*/ +void writePNGLibpng(const vpImage &I, const std::string &filename) +{ + FILE *file; + + // Test the filename + if (filename.empty()) { + throw(vpImageException(vpImageException::ioError, "Cannot create PNG file: filename empty")); + } + + file = fopen(filename.c_str(), "wb"); + + if (file == NULL) { + throw(vpImageException(vpImageException::ioError, "Cannot create PNG file \"%s\"", filename.c_str())); + } + + /* create a png info struct */ + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + fclose(file); + vpERROR_TRACE("Error during png_create_write_struct()\n"); + throw(vpImageException(vpImageException::ioError, "PNG write error")); + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + fclose(file); + png_destroy_write_struct(&png_ptr, NULL); + vpERROR_TRACE("Error during png_create_info_struct()\n"); + throw(vpImageException(vpImageException::ioError, "PNG write error")); + } + + /* initialize the setjmp for returning properly after a libpng error occured + */ + if (setjmp(png_jmpbuf(png_ptr))) { + fclose(file); + png_destroy_write_struct(&png_ptr, &info_ptr); + vpERROR_TRACE("Error during init_io\n"); + throw(vpImageException(vpImageException::ioError, "PNG write error")); + } + + /* setup libpng for using standard C fwrite() function with our FILE pointer + */ + png_init_io(png_ptr, file); + + unsigned int width = I.getWidth(); + unsigned int height = I.getHeight(); + int bit_depth = 8; + int color_type = PNG_COLOR_TYPE_GRAY; + /* set some useful information from header */ + + if (setjmp(png_jmpbuf(png_ptr))) { + fclose(file); + png_destroy_write_struct(&png_ptr, &info_ptr); + vpERROR_TRACE("Error during write header\n"); + throw(vpImageException(vpImageException::ioError, "PNG write error")); + } + + png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_BASE); + + png_write_info(png_ptr, info_ptr); + + png_bytep *row_ptrs = new png_bytep[height]; + for (unsigned int i = 0; i < height; i++) + row_ptrs[i] = new png_byte[width]; + + unsigned char *input = (unsigned char *)I.bitmap; + + for (unsigned int i = 0; i < height; i++) { + png_byte *row = row_ptrs[i]; + for (unsigned int j = 0; j < width; j++) { + row[j] = *(input); + input++; + } + } + + png_write_image(png_ptr, row_ptrs); + + png_write_end(png_ptr, NULL); + + for (unsigned int j = 0; j < height; j++) + delete[] row_ptrs[j]; + + delete[] row_ptrs; + + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(file); +} + +/*! + Write the content of the image bitmap in the file which name is given by \e + filename. This function writes a PNG file. + + \param I : Image to save as a PNG file. + \param filename : Name of the file containing the image. +*/ +void writePNGLibpng(const vpImage &I, const std::string &filename) +{ + FILE *file; + + // Test the filename + if (filename.empty()) { + throw(vpImageException(vpImageException::ioError, "Cannot create PNG file: filename empty")); + } + + file = fopen(filename.c_str(), "wb"); + + if (file == NULL) { + throw(vpImageException(vpImageException::ioError, "Cannot create PNG file \"%s\"", filename.c_str())); + } + + /* create a png info struct */ + png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + fclose(file); + vpERROR_TRACE("Error during png_create_write_struct()\n"); + throw(vpImageException(vpImageException::ioError, "PNG write error")); + } + + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + fclose(file); + png_destroy_write_struct(&png_ptr, NULL); + vpERROR_TRACE("Error during png_create_info_struct()\n"); + throw(vpImageException(vpImageException::ioError, "PNG write error")); + } + + /* initialize the setjmp for returning properly after a libpng error occured + */ + if (setjmp(png_jmpbuf(png_ptr))) { + fclose(file); + png_destroy_write_struct(&png_ptr, &info_ptr); + vpERROR_TRACE("Error during init_io\n"); + throw(vpImageException(vpImageException::ioError, "PNG write error")); + } + + /* setup libpng for using standard C fwrite() function with our FILE pointer + */ + png_init_io(png_ptr, file); + + unsigned int width = I.getWidth(); + unsigned int height = I.getHeight(); + int bit_depth = 8; + int color_type = PNG_COLOR_TYPE_RGB; + /* set some useful information from header */ + + if (setjmp(png_jmpbuf(png_ptr))) { + fclose(file); + png_destroy_write_struct(&png_ptr, &info_ptr); + vpERROR_TRACE("Error during write header\n"); + throw(vpImageException(vpImageException::ioError, "PNG write error")); + } + + png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, + PNG_FILTER_TYPE_BASE); + + png_write_info(png_ptr, info_ptr); + + png_bytep *row_ptrs = new png_bytep[height]; + for (unsigned int i = 0; i < height; i++) + row_ptrs[i] = new png_byte[3 * width]; + + unsigned char *input = (unsigned char *)I.bitmap; + + for (unsigned int i = 0; i < height; i++) { + png_byte *row = row_ptrs[i]; + for (unsigned int j = 0; j < width; j++) { + row[3 * j] = *(input); + input++; + row[3 * j + 1] = *(input); + input++; + row[3 * j + 2] = *(input); + input++; + input++; + } + } + + png_write_image(png_ptr, row_ptrs); + + png_write_end(png_ptr, NULL); + + for (unsigned int j = 0; j < height; j++) + delete[] row_ptrs[j]; + + delete[] row_ptrs; + + png_destroy_write_struct(&png_ptr, &info_ptr); + + fclose(file); +} + +/*! + Read the contents of the PNG file, allocate memory + for the corresponding gray level image, if necessary convert the data in + gray level, and set the bitmap whith the gray level data. That means that + the image \e I is a "black and white" rendering of the original image in \e + filename, as in a black and white photograph. If necessary, the quantization + formula used is \f$0,299 r + 0,587 g + 0,114 b\f$. + + If the image has been already initialized, memory allocation is done + only if the new image size is different, else we re-use the same + memory space. + + \param I : Image to set with the \e filename content. + \param filename : Name of the file containing the image. +*/ +void readPNGLibpng(vpImage &I, const std::string &filename) +{ + FILE *file; + png_byte magic[8]; + // Test the filename + if (filename.empty()) { + throw(vpImageException(vpImageException::ioError, "Cannot read PNG image: filename empty")); + } + + file = fopen(filename.c_str(), "rb"); + + if (file == NULL) { + throw(vpImageException(vpImageException::ioError, "Cannot read file \"%s\"", filename.c_str())); + } + + /* read magic number */ + if (fread(magic, 1, sizeof(magic), file) != sizeof(magic)) { + fclose(file); + throw(vpImageException(vpImageException::ioError, "Cannot read magic number in file \"%s\"", filename.c_str())); + } + + /* check for valid magic number */ + if (png_sig_cmp(magic, 0, sizeof(magic))) { + fclose(file); + throw(vpImageException(vpImageException::ioError, "Cannot read PNG file: \"%s\" is not a valid PNG image", + filename.c_str())); + } + + /* create a png read struct */ + // printf("version %s\n", PNG_LIBPNG_VER_STRING); + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png_ptr == NULL) { + fprintf(stderr, "error: can't create a png read structure!\n"); + fclose(file); + throw(vpImageException(vpImageException::ioError, "error reading png file")); + } + + /* create a png info struct */ + png_infop info_ptr = png_create_info_struct(png_ptr); + if (info_ptr == NULL) { + fprintf(stderr, "error: can't create a png info structure!\n"); + fclose(file); + png_destroy_read_struct(&png_ptr, NULL, NULL); + throw(vpImageException(vpImageException::ioError, "error reading png file")); + } + + /* initialize the setjmp for returning properly after a libpng error occured + */ + if (setjmp(png_jmpbuf(png_ptr))) { + fclose(file); + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + vpERROR_TRACE("Error during init io\n"); + throw(vpImageException(vpImageException::ioError, "PNG read error")); + } + + /* setup libpng for using standard C fread() function with our FILE pointer + */ + png_init_io(png_ptr, file); + + /* tell libpng that we have already read the magic number */ + png_set_sig_bytes(png_ptr, sizeof(magic)); + + /* read png info */ + png_read_info(png_ptr, info_ptr); + + unsigned int width = png_get_image_width(png_ptr, info_ptr); + unsigned int height = png_get_image_height(png_ptr, info_ptr); + + unsigned int bit_depth, channels, color_type; + /* get some useful information from header */ + bit_depth = png_get_bit_depth(png_ptr, info_ptr); + channels = png_get_channels(png_ptr, info_ptr); + color_type = png_get_color_type(png_ptr, info_ptr); + + /* convert index color images to RGB images */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png_ptr); + + /* convert 1-2-4 bits grayscale images to 8 bits grayscale. */ + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand(png_ptr); + + // if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) + // png_set_tRNS_to_alpha (png_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_strip_alpha(png_ptr); + + if (bit_depth == 16) + png_set_strip_16(png_ptr); + else if (bit_depth < 8) + png_set_packing(png_ptr); + + /* update info structure to apply transformations */ + png_read_update_info(png_ptr, info_ptr); + + channels = png_get_channels(png_ptr, info_ptr); + + if ((width != I.getWidth()) || (height != I.getHeight())) + I.resize(height, width); + + png_bytep *rowPtrs = new png_bytep[height]; + + unsigned int stride = png_get_rowbytes(png_ptr, info_ptr); + unsigned char *data = new unsigned char[stride * height]; + + for (unsigned int i = 0; i < height; i++) + rowPtrs[i] = (png_bytep)data + (i * stride); + + png_read_image(png_ptr, rowPtrs); + + vpImage Ic(height, width); + unsigned char *output; + + switch (channels) { + case 1: + output = (unsigned char *)I.bitmap; + for (unsigned int i = 0; i < width * height; i++) { + *(output++) = data[i]; + } + break; + + case 2: + output = (unsigned char *)I.bitmap; + for (unsigned int i = 0; i < width * height; i++) { + *(output++) = data[i * 2]; + } + break; + + case 3: + output = (unsigned char *)Ic.bitmap; + for (unsigned int i = 0; i < width * height; i++) { + *(output++) = data[i * 3]; + *(output++) = data[i * 3 + 1]; + *(output++) = data[i * 3 + 2]; + *(output++) = vpRGBa::alpha_default; + } + vpImageConvert::convert(Ic, I); + break; + + case 4: + output = (unsigned char *)Ic.bitmap; + for (unsigned int i = 0; i < width * height; i++) { + *(output++) = data[i * 4]; + *(output++) = data[i * 4 + 1]; + *(output++) = data[i * 4 + 2]; + *(output++) = data[i * 4 + 3]; + } + vpImageConvert::convert(Ic, I); + break; + } + + delete[](png_bytep) rowPtrs; + delete[] data; + png_read_end(png_ptr, NULL); + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(file); +} + +/*! + Read a PNG file and initialize a scalar image. + + Read the contents of the PNG file, allocate + memory for the corresponding image, and set + the bitmap whith the content of + the file. + + If the image has been already initialized, memory allocation is done + only if the new image size is different, else we re-use the same + memory space. + + If the file corresponds to a grayscaled image, a conversion is done to deal + with \e I which is a color image. + + \param I : Color image to set with the \e filename content. + \param filename : Name of the file containing the image. +*/ +void readPNGLibpng(vpImage &I, const std::string &filename) +{ + FILE *file; + png_byte magic[8]; + + // Test the filename + if (filename.empty()) { + throw(vpImageException(vpImageException::ioError, "Cannot read PNG image: filename empty")); + } + + file = fopen(filename.c_str(), "rb"); + + if (file == NULL) { + throw(vpImageException(vpImageException::ioError, "Cannot read file \"%s\"", filename.c_str())); + } + + /* read magic number */ + if (fread(magic, 1, sizeof(magic), file) != sizeof(magic)) { + fclose(file); + throw(vpImageException(vpImageException::ioError, "Cannot read magic number in file \"%s\"", filename.c_str())); + } + + /* check for valid magic number */ + if (png_sig_cmp(magic, 0, sizeof(magic))) { + fclose(file); + throw(vpImageException(vpImageException::ioError, "Cannot read PNG file: \"%s\" is not a valid PNG image", + filename.c_str())); + } + + /* create a png read struct */ + png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (!png_ptr) { + fclose(file); + vpERROR_TRACE("Error during png_create_read_struct()\n"); + throw(vpImageException(vpImageException::ioError, "PNG read error")); + } + + /* create a png info struct */ + png_infop info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + fclose(file); + png_destroy_read_struct(&png_ptr, NULL, NULL); + vpERROR_TRACE("Error during png_create_info_struct()\n"); + throw(vpImageException(vpImageException::ioError, "PNG read error")); + } + + /* initialize the setjmp for returning properly after a libpng error occured + */ + if (setjmp(png_jmpbuf(png_ptr))) { + fclose(file); + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + vpERROR_TRACE("Error during init io\n"); + throw(vpImageException(vpImageException::ioError, "PNG read error")); + } + + /* setup libpng for using standard C fread() function with our FILE pointer + */ + png_init_io(png_ptr, file); + + /* tell libpng that we have already read the magic number */ + png_set_sig_bytes(png_ptr, sizeof(magic)); + + /* read png info */ + png_read_info(png_ptr, info_ptr); + + unsigned int width = png_get_image_width(png_ptr, info_ptr); + unsigned int height = png_get_image_height(png_ptr, info_ptr); + + unsigned int bit_depth, channels, color_type; + /* get some useful information from header */ + bit_depth = png_get_bit_depth(png_ptr, info_ptr); + channels = png_get_channels(png_ptr, info_ptr); + color_type = png_get_color_type(png_ptr, info_ptr); + + /* convert index color images to RGB images */ + if (color_type == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png_ptr); + + /* convert 1-2-4 bits grayscale images to 8 bits grayscale. */ + if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) + png_set_expand(png_ptr); + + // if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) + // png_set_tRNS_to_alpha (png_ptr); + + if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_strip_alpha(png_ptr); + + if (bit_depth == 16) + png_set_strip_16(png_ptr); + else if (bit_depth < 8) + png_set_packing(png_ptr); + + /* update info structure to apply transformations */ + png_read_update_info(png_ptr, info_ptr); + + channels = png_get_channels(png_ptr, info_ptr); + + if ((width != I.getWidth()) || (height != I.getHeight())) + I.resize(height, width); + + png_bytep *rowPtrs = new png_bytep[height]; + + unsigned int stride = png_get_rowbytes(png_ptr, info_ptr); + unsigned char *data = new unsigned char[stride * height]; + + for (unsigned int i = 0; i < height; i++) + rowPtrs[i] = (png_bytep)data + (i * stride); + + png_read_image(png_ptr, rowPtrs); + + vpImage Ig(height, width); + unsigned char *output; + + switch (channels) { + case 1: + output = (unsigned char *)Ig.bitmap; + for (unsigned int i = 0; i < width * height; i++) { + *(output++) = data[i]; + } + vpImageConvert::convert(Ig, I); + break; + + case 2: + output = (unsigned char *)Ig.bitmap; + for (unsigned int i = 0; i < width * height; i++) { + *(output++) = data[i * 2]; + } + vpImageConvert::convert(Ig, I); + break; + + case 3: + output = (unsigned char *)I.bitmap; + for (unsigned int i = 0; i < width * height; i++) { + *(output++) = data[i * 3]; + *(output++) = data[i * 3 + 1]; + *(output++) = data[i * 3 + 2]; + *(output++) = vpRGBa::alpha_default; + } + break; + + case 4: + output = (unsigned char *)I.bitmap; + for (unsigned int i = 0; i < width * height; i++) { + *(output++) = data[i * 4]; + *(output++) = data[i * 4 + 1]; + *(output++) = data[i * 4 + 2]; + *(output++) = data[i * 4 + 3]; + } + break; + } + + delete[](png_bytep) rowPtrs; + delete[] data; + png_read_end(png_ptr, NULL); + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + fclose(file); +} +#endif diff --git a/modules/io/src/image/private/vpImageIoOpenCV.cpp b/modules/io/src/image/private/vpImageIoOpenCV.cpp new file mode 100644 index 0000000000..cc22229ede --- /dev/null +++ b/modules/io/src/image/private/vpImageIoOpenCV.cpp @@ -0,0 +1,217 @@ +/**************************************************************************** + * + * ViSP, open source Visual Servoing Platform software. + * Copyright (C) 2005 - 2019 by Inria. All rights reserved. + * + * This software is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact Inria about acquiring a ViSP Professional + * Edition License. + * + * See http://visp.inria.fr for more information. + * + * This software was developed at: + * Inria Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * + * If you have questions regarding the use of this file, please contact + * Inria at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: +<<<<<<< HEAD + * Read/write images. + * + * Authors: + * Eric Marchand +======= + * OpenCV backend for image I/O operations. +>>>>>>> 557f1beda01f36ca886ec039d0a1a80a7446ca59 + * + *****************************************************************************/ + +/*! + \file vpImageIoOpenCV.cpp + \brief OpenCV backend for image I/O operations. +*/ + +#include "vpImageIoBackend.h" + +//TODO: +#ifdef VISP_HAVE_OPENCV +#if (VISP_HAVE_OPENCV_VERSION >= 0x030000) // Require opencv >= 3.0.0 +# include +#elif (VISP_HAVE_OPENCV_VERSION >= 0x020408) // Require opencv >= 2.4.8 +# include +# include +# include +#elif (VISP_HAVE_OPENCV_VERSION >= 0x020101) // Require opencv >= 2.1.1 +# include +# include +# include +# include +#else +# include +#endif +#endif + +#include + + +#if defined(VISP_HAVE_OPENCV) + +/*! + Read the contents of the JPEG file, allocate memory + for the corresponding gray level image, if necessary convert the data in + gray level, and set the bitmap whith the gray level data. That means that + the image \e I is a "black and white" rendering of the original image in \e + filename, as in a black and white photograph. If necessary, the quantization + formula used is \f$0,299 r + 0,587 g + 0,114 b\f$. + + If the image has been already initialized, memory allocation is done + only if the new image size is different, else we re-use the same + memory space. + + If EXIF information is embedded in the image file, the EXIF orientation is ignored. + + \param I : Image to set with the \e filename content. + \param filename : Name of the file containing the image. + +*/ +void readOpenCV(vpImage &I, const std::string &filename) +{ +#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 +#if VISP_HAVE_OPENCV_VERSION >= 0x030200 + int flags = cv::IMREAD_GRAYSCALE | cv::IMREAD_IGNORE_ORIENTATION; +#elif VISP_HAVE_OPENCV_VERSION >= 0x030000 + int flags = cv::IMREAD_GRAYSCALE; +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + int flags = CV_LOAD_IMAGE_GRAYSCALE; +#endif + cv::Mat Ip = cv::imread(filename.c_str(), flags); + if (!Ip.empty()) + vpImageConvert::convert(Ip, I); + else + throw(vpImageException(vpImageException::ioError, "Can't read the image")); +#else + IplImage *Ip = NULL; + Ip = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_GRAYSCALE); + if (Ip != NULL) + vpImageConvert::convert(Ip, I); + else + throw(vpImageException(vpImageException::ioError, "Can't read the image")); + cvReleaseImage(&Ip); +#endif +} + +/*! + Read a JPEG file and initialize a scalar image. + + Read the contents of the JPEG file, allocate + memory for the corresponding image, and set + the bitmap whith the content of + the file. + + If the image has been already initialized, memory allocation is done + only if the new image size is different, else we re-use the same + memory space. + + If the file corresponds to a grayscaled image, a conversion is done to deal + with \e I which is a color image. + + If EXIF information is embedded in the image file, the EXIF orientation is ignored. + + \param I : Color image to set with the \e filename content. + \param filename : Name of the file containing the image. +*/ +void readOpenCV(vpImage &I, const std::string &filename) +{ +#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 +#if VISP_HAVE_OPENCV_VERSION >= 0x030200 + int flags = cv::IMREAD_GRAYSCALE | cv::IMREAD_IGNORE_ORIENTATION; +#elif VISP_HAVE_OPENCV_VERSION >= 0x030000 + int flags = cv::IMREAD_GRAYSCALE; +#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 + int flags = CV_LOAD_IMAGE_GRAYSCALE; +#endif + cv::Mat Ip = cv::imread(filename.c_str(), flags); + if (!Ip.empty()) + vpImageConvert::convert(Ip, I); + else + throw(vpImageException(vpImageException::ioError, "Can't read the image")); +#else + IplImage *Ip = NULL; + Ip = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_COLOR); + if (Ip != NULL) + vpImageConvert::convert(Ip, I); + else + throw(vpImageException(vpImageException::ioError, "Can't read the image")); + cvReleaseImage(&Ip); +#endif +} + +/*! + Write the content of the image bitmap in the file which name is given by \e + filename. This function writes a JPEG file. + + \param I : Image to save as a JPEG file. + \param filename : Name of the file containing the image. +*/ +void writeOpenCV(const vpImage &I, const std::string &filename, int quality) +{ +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + cv::Mat Ip; + vpImageConvert::convert(I, Ip); + + std::vector compression_params; + compression_params.push_back(cv::IMWRITE_JPEG_QUALITY); + compression_params.push_back(quality); + cv::imwrite(filename.c_str(), Ip, compression_params); +#else + IplImage *Ip = NULL; + vpImageConvert::convert(I, Ip); + + cvSaveImage(filename.c_str(), Ip); + + cvReleaseImage(&Ip); +#endif +} + +/*! + Write the content of the image bitmap in the file which name is given by \e + filename. This function writes a JPEG file. + + \param I : Image to save as a JPEG file. + \param filename : Name of the file containing the image. +*/ +void writeOpenCV(const vpImage &I, const std::string &filename, int quality) +{ +#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) + cv::Mat Ip; + vpImageConvert::convert(I, Ip); + + std::vector compression_params; + compression_params.push_back(cv::IMWRITE_JPEG_QUALITY); + compression_params.push_back(quality); + cv::imwrite(filename.c_str(), Ip, compression_params); +#else + IplImage *Ip = NULL; + vpImageConvert::convert(I, Ip); + + cvSaveImage(filename.c_str(), Ip); + + cvReleaseImage(&Ip); +#endif +} + +#endif diff --git a/modules/io/src/image/private/vpImageIoPortable.cpp b/modules/io/src/image/private/vpImageIoPortable.cpp new file mode 100644 index 0000000000..8c6df6086b --- /dev/null +++ b/modules/io/src/image/private/vpImageIoPortable.cpp @@ -0,0 +1,573 @@ +/**************************************************************************** + * + * ViSP, open source Visual Servoing Platform software. + * Copyright (C) 2005 - 2019 by Inria. All rights reserved. + * + * This software is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact Inria about acquiring a ViSP Professional + * Edition License. + * + * See http://visp.inria.fr for more information. + * + * This software was developed at: + * Inria Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * + * If you have questions regarding the use of this file, please contact + * Inria at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: +<<<<<<< HEAD + * Read/write images. + * + * Authors: + * Eric Marchand +======= + * Backend for portable image format I/O operations. +>>>>>>> 557f1beda01f36ca886ec039d0a1a80a7446ca59 + * + *****************************************************************************/ + +/*! + \file vpImageIoPortable.cpp + \brief Backend for portable image format I/O operations. +*/ + +#include "vpImageIoBackend.h" +#include +#include + +//TODO: is it needed? +//#if defined(_WIN32) +//// Include WinSock2.h before windows.h to ensure that winsock.h is not +//// included by windows.h since winsock.h and winsock2.h are incompatible +//#include +//#include +//#endif + + +void vp_decodeHeaderPNM(const std::string &filename, std::ifstream &fd, const std::string &magic, unsigned int &w, + unsigned int &h, unsigned int &maxval); + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +/*! + * Decode the PNM image header. + * \param filename[in] : File name. + * \param fd[in] : File desdcriptor. + * \param magic[in] : Magic number for identifying the file type. + * \param w[out] : Image width. + * \param h[out] : Image height. + * \param maxval[out] : Maximum pixel value. + */ +void vp_decodeHeaderPNM(const std::string &filename, std::ifstream &fd, const std::string &magic, unsigned int &w, + unsigned int &h, unsigned int &maxval) +{ + std::string line; + unsigned int nb_elt = 4, cpt_elt = 0; + while (cpt_elt != nb_elt) { + // Skip empty lines or lines starting with # (comment) + while (std::getline(fd, line) && (line.compare(0, 1, "#") == 0 || line.size() == 0)) { + } + + if (fd.eof()) { + fd.close(); + throw(vpImageException(vpImageException::ioError, "Cannot read header of file \"%s\"", filename.c_str())); + } + + std::vector header = vpIoTools::splitChain(line, std::string(" ")); + + if (header.size() == 0) { + fd.close(); + throw(vpImageException(vpImageException::ioError, "Cannot read header of file \"%s\"", filename.c_str())); + } + + if (cpt_elt == 0) { // decode magic + if (header[0].compare(0, magic.size(), magic) != 0) { + fd.close(); + throw(vpImageException(vpImageException::ioError, "\"%s\" is not a PNM file with magic number %s", + filename.c_str(), magic.c_str())); + } + cpt_elt++; + header.erase(header.begin(), + header.begin() + 1); // erase first element that is processed + } + while (header.size()) { + if (cpt_elt == 1) { // decode width + std::istringstream ss(header[0]); + ss >> w; + cpt_elt++; + header.erase(header.begin(), + header.begin() + 1); // erase first element that is processed + } else if (cpt_elt == 2) { // decode height + std::istringstream ss(header[0]); + ss >> h; + cpt_elt++; + header.erase(header.begin(), + header.begin() + 1); // erase first element that is processed + } else if (cpt_elt == 3) { // decode maxval + std::istringstream ss(header[0]); + ss >> maxval; + cpt_elt++; + header.erase(header.begin(), + header.begin() + 1); // erase first element that is processed + } + } + } +} +#endif + +//-------------------------------------------------------------------------- +// PFM +//-------------------------------------------------------------------------- + +/*! + Write the content of the image bitmap in the file which name is given by \e + filename. This function is built like portable gray pixmap (eg PGM P5) file. + but considers float image data. + + \param I : Image to save as a (PFM P8) file. + \param filename : Name of the file containing the image. +*/ +void vp_writePFM(const vpImage &I, const std::string &filename) +{ + FILE *fd; + + // Test the filename + if (filename.empty()) { + throw(vpImageException(vpImageException::ioError, "Cannot write PFM image: filename empty")); + } + + fd = fopen(filename.c_str(), "wb"); + + if (fd == NULL) { + throw(vpImageException(vpImageException::ioError, "Cannot create PFM file \"%s\"", filename.c_str())); + } + + // Write the head + fprintf(fd, "P8\n"); // Magic number + fprintf(fd, "%u %u\n", I.getWidth(), I.getHeight()); // Image size + fprintf(fd, "255\n"); // Max level + + // Write the bitmap + size_t ierr; + size_t nbyte = I.getWidth() * I.getHeight(); + + ierr = fwrite(I.bitmap, sizeof(float), nbyte, fd); + if (ierr != nbyte) { + fclose(fd); + throw(vpImageException(vpImageException::ioError, "Cannot save PFM file \"%s\": only %d bytes over %d saved ", + filename.c_str(), ierr, nbyte)); + } + + fflush(fd); + fclose(fd); +} + +//-------------------------------------------------------------------------- +// PGM +//-------------------------------------------------------------------------- + +/*! + Write the content of the image bitmap in the file which name is given by \e + filename. This function writes a portable gray pixmap (PGM P5) file. + + \param I : Image to save as a (PGM P5) file. + \param filename : Name of the file containing the image. +*/ +void vp_writePGM(const vpImage &I, const std::string &filename) +{ + FILE *fd; + + // Test the filename + if (filename.empty()) { + throw(vpImageException(vpImageException::ioError, "Cannot create PGM file: filename empty")); + } + + fd = fopen(filename.c_str(), "wb"); + + if (fd == NULL) { + throw(vpImageException(vpImageException::ioError, "Cannot create PGM file \"%s\"", filename.c_str())); + } + + // Write the head + fprintf(fd, "P5\n"); // Magic number + fprintf(fd, "%u %u\n", I.getWidth(), I.getHeight()); // Image size + fprintf(fd, "255\n"); // Max level + + // Write the bitmap + size_t ierr; + size_t nbyte = I.getWidth() * I.getHeight(); + + ierr = fwrite(I.bitmap, sizeof(unsigned char), nbyte, fd); + if (ierr != nbyte) { + fclose(fd); + throw(vpImageException(vpImageException::ioError, "Cannot save PGM file \"%s\": only %d over %d bytes saved", + filename.c_str(), ierr, nbyte)); + } + + fflush(fd); + fclose(fd); +} + +/*! + Write the content of the image bitmap in the file which name is given by \e + filename. This function writes a portable gray pixmap (PGM P5) file. + + \param I : Image to save as a (PGM P5) file. + \param filename : Name of the file containing the image. +*/ +void vp_writePGM(const vpImage &I, const std::string &filename) +{ + vpImage Iuc; + unsigned int nrows = I.getHeight(); + unsigned int ncols = I.getWidth(); + + Iuc.resize(nrows, ncols); + + for (unsigned int i = 0; i < nrows * ncols; i++) + Iuc.bitmap[i] = (unsigned char)I.bitmap[i]; + + vp_writePGM(Iuc, filename); +} + +/*! + Write the content of the image bitmap in the file which name is given by \e + filename. This function writes a portable gray pixmap (PGM P5) file. + Color image is converted into a grayscale image. + + \param I : Image to save as a (PGM P5) file. + \param filename : Name of the file containing the image. +*/ +void vp_writePGM(const vpImage &I, const std::string &filename) +{ + + FILE *fd; + + // Test the filename + if (filename.empty()) { + throw(vpImageException(vpImageException::ioError, "Cannot create PGM file: filename empty")); + } + + fd = fopen(filename.c_str(), "wb"); + + if (fd == NULL) { + throw(vpImageException(vpImageException::ioError, "Cannot create PGM file \"%s\"", filename.c_str())); + } + + // Write the head + fprintf(fd, "P5\n"); // Magic number + fprintf(fd, "%u %u\n", I.getWidth(), I.getHeight()); // Image size + fprintf(fd, "255\n"); // Max level + + // Write the bitmap + size_t ierr; + size_t nbyte = I.getWidth() * I.getHeight(); + + vpImage Itmp; + vpImageConvert::convert(I, Itmp); + + ierr = fwrite(Itmp.bitmap, sizeof(unsigned char), nbyte, fd); + if (ierr != nbyte) { + fclose(fd); + throw(vpImageException(vpImageException::ioError, "Cannot save PGM file \"%s\": only %d over %d bytes saved", + filename.c_str(), ierr, nbyte)); + } + + fflush(fd); + fclose(fd); +} + +/*! + Read a PFM P8 file and initialize a float image. + + Read the contents of the portable gray pixmap (PFM P8) filename, allocate + memory for the corresponding image, and set the bitmap whith the content of + the file. + + If the image has been already initialized, memory allocation is done + only if the new image size is different, else we re-use the same + memory space. + + \param I : Image to set with the \e filename content. + \param filename : Name of the file containing the image. +*/ +void vp_readPFM(vpImage &I, const std::string &filename) +{ + unsigned int w = 0, h = 0, maxval = 0; + unsigned int w_max = 100000, h_max = 100000, maxval_max = 255; + std::string magic("P8"); + + std::ifstream fd(filename.c_str(), std::ios::binary); + + // Open the filename + if (!fd.is_open()) { + throw(vpImageException(vpImageException::ioError, "Cannot open file \"%s\"", filename.c_str())); + } + + vp_decodeHeaderPNM(filename, fd, magic, w, h, maxval); + + if (w > w_max || h > h_max) { + fd.close(); + throw(vpException(vpException::badValue, "Bad image size in \"%s\"", filename.c_str())); + } + if (maxval > maxval_max) { + fd.close(); + throw(vpImageException(vpImageException::ioError, "Bad maxval in \"%s\"", filename.c_str())); + } + + if ((h != I.getHeight()) || (w != I.getWidth())) { + I.resize(h, w); + } + + unsigned int nbyte = I.getHeight() * I.getWidth(); + fd.read((char *)I.bitmap, sizeof(float) * nbyte); + if (!fd) { + fd.close(); + throw(vpImageException(vpImageException::ioError, "Read only %d of %d bytes in file \"%s\"", fd.gcount(), nbyte, + filename.c_str())); + } + + fd.close(); +} + +/*! + Read a PGM P5 file and initialize a scalar image. + + Read the contents of the portable gray pixmap (PGM P5) filename, allocate + memory for the corresponding image, and set the bitmap whith the content of + the file. + + If the image has been already initialized, memory allocation is done + only if the new image size is different, else we re-use the same + memory space. + + \param I : Image to set with the \e filename content. + \param filename : Name of the file containing the image. +*/ +void vp_readPGM(vpImage &I, const std::string &filename) +{ + unsigned int w = 0, h = 0, maxval = 0; + unsigned int w_max = 100000, h_max = 100000, maxval_max = 255; + std::string magic("P5"); + + std::ifstream fd(filename.c_str(), std::ios::binary); + + // Open the filename + if (!fd.is_open()) { + throw(vpImageException(vpImageException::ioError, "Cannot open file \"%s\"", filename.c_str())); + } + + vp_decodeHeaderPNM(filename, fd, magic, w, h, maxval); + + if (w > w_max || h > h_max) { + fd.close(); + throw(vpException(vpException::badValue, "Bad image size in \"%s\"", filename.c_str())); + } + if (maxval > maxval_max) { + fd.close(); + throw(vpImageException(vpImageException::ioError, "Bad maxval in \"%s\"", filename.c_str())); + } + + if ((h != I.getHeight()) || (w != I.getWidth())) { + I.resize(h, w); + } + + unsigned int nbyte = I.getHeight() * I.getWidth(); + fd.read((char *)I.bitmap, nbyte); + if (!fd) { + fd.close(); + throw(vpImageException(vpImageException::ioError, "Read only %d of %d bytes in file \"%s\"", fd.gcount(), nbyte, + filename.c_str())); + } + + fd.close(); +} + +/*! + Read a PGM P5 file and initialize a scalar image. + + Read the contents of the portable gray pixmap (PGM P5) filename, allocate + memory for the corresponding image, and set the bitmap whith the content of + the file. + + If the image has been already initialized, memory allocation is done + only if the new image size is different, else we re-use the same + memory space. + + The gray level image contained in the \e filename is converted in a + color image in \e I. + + \param I : Color image to set with the \e filename content. + \param filename : Name of the file containing the image. +*/ +void vp_readPGM(vpImage &I, const std::string &filename) +{ + vpImage Itmp; + + vp_readPGM(Itmp, filename); + + vpImageConvert::convert(Itmp, I); +} + +//-------------------------------------------------------------------------- +// PPM +//-------------------------------------------------------------------------- + +/*! + Read the contents of the portable pixmap (PPM P6) filename, allocate memory + for the corresponding gray level image, convert the data in gray level, and + set the bitmap whith the gray level data. That means that the image \e I is + a "black and white" rendering of the original image in \e filename, as in a + black and white photograph. The quantization formula used is \f$0,299 r + + 0,587 g + 0,114 b\f$. + + If the image has been already initialized, memory allocation is done + only if the new image size is different, else we re-use the same + memory space. + + \param I : Image to set with the \e filename content. + \param filename : Name of the file containing the image. +*/ +void vp_readPPM(vpImage &I, const std::string &filename) +{ + vpImage Itmp; + + vp_readPPM(Itmp, filename); + + vpImageConvert::convert(Itmp, I); +} + +/*! + Read the contents of the portable pixmap (PPM P6) filename, + allocate memory for the corresponding vpRGBa image. + + If the image has been already initialized, memory allocation is done + only if the new image size is different, else we re-use the same + memory space. + + \param I : Image to set with the \e filename content. + \param filename : Name of the file containing the image. +*/ +void vp_readPPM(vpImage &I, const std::string &filename) +{ + unsigned int w = 0, h = 0, maxval = 0; + unsigned int w_max = 100000, h_max = 100000, maxval_max = 255; + std::string magic("P6"); + + std::ifstream fd(filename.c_str(), std::ios::binary); + + // Open the filename + if (!fd.is_open()) { + throw(vpImageException(vpImageException::ioError, "Cannot open file \"%s\"", filename.c_str())); + } + + vp_decodeHeaderPNM(filename, fd, magic, w, h, maxval); + + if (w > w_max || h > h_max) { + fd.close(); + throw(vpException(vpException::badValue, "Bad image size in \"%s\"", filename.c_str())); + } + if (maxval > maxval_max) { + fd.close(); + throw(vpImageException(vpImageException::ioError, "Bad maxval in \"%s\"", filename.c_str())); + } + + if ((h != I.getHeight()) || (w != I.getWidth())) { + I.resize(h, w); + } + + for (unsigned int i = 0; i < I.getHeight(); i++) { + for (unsigned int j = 0; j < I.getWidth(); j++) { + unsigned char rgb[3]; + fd.read((char *)&rgb, 3); + + if (!fd) { + fd.close(); + throw(vpImageException(vpImageException::ioError, "Read only %d of %d bytes in file \"%s\"", + (i * I.getWidth() + j) * 3 + fd.gcount(), I.getSize() * 3, filename.c_str())); + } + + I[i][j].R = rgb[0]; + I[i][j].G = rgb[1]; + I[i][j].B = rgb[2]; + I[i][j].A = vpRGBa::alpha_default; + } + } + + fd.close(); +} + +/*! + Write the content of the bitmap in the file which name is given by \e + filename. This function writes a portable gray pixmap (PPM P6) file. + grayscale image is converted into a color image vpRGBa. + + \param I : Image to save as a (PPM P6) file. + \param filename : Name of the file containing the image. +*/ +void vp_writePPM(const vpImage &I, const std::string &filename) +{ + vpImage Itmp; + + vpImageConvert::convert(I, Itmp); + + vp_writePPM(Itmp, filename); +} + +/*! + Write the content of the bitmap in the file which name is given by \e + filename. This function writes a portable gray pixmap (PPM P6) file. + + \param I : Image to save as a (PPM P6) file. + \param filename : Name of the file containing the image. +*/ +void vp_writePPM(const vpImage &I, const std::string &filename) +{ + FILE *f; + + // Test the filename + if (filename.empty()) { + throw(vpImageException(vpImageException::ioError, "Cannot create PPM file: filename empty")); + } + + f = fopen(filename.c_str(), "wb"); + + if (f == NULL) { + throw(vpImageException(vpImageException::ioError, "Cannot create PPM file \"%s\"", filename.c_str())); + } + + fprintf(f, "P6\n"); // Magic number + fprintf(f, "%u %u\n", I.getWidth(), I.getHeight()); // Image size + fprintf(f, "%d\n", 255); // Max level + + for (unsigned int i = 0; i < I.getHeight(); i++) { + for (unsigned int j = 0; j < I.getWidth(); j++) { + vpRGBa v = I[i][j]; + unsigned char rgb[3]; + rgb[0] = v.R; + rgb[1] = v.G; + rgb[2] = v.B; + + size_t res = fwrite(&rgb, 1, 3, f); + if (res != 3) { + fclose(f); + throw(vpImageException(vpImageException::ioError, "cannot write file \"%s\"", filename.c_str())); + } + } + } + + fflush(f); + fclose(f); +} diff --git a/modules/io/src/image/private/vpImageIoSimd.cpp b/modules/io/src/image/private/vpImageIoSimd.cpp new file mode 100644 index 0000000000..9ed1f25d37 --- /dev/null +++ b/modules/io/src/image/private/vpImageIoSimd.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** + * + * ViSP, open source Visual Servoing Platform software. + * Copyright (C) 2005 - 2019 by Inria. All rights reserved. + * + * This software is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact Inria about acquiring a ViSP Professional + * Edition License. + * + * See http://visp.inria.fr for more information. + * + * This software was developed at: + * Inria Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * + * If you have questions regarding the use of this file, please contact + * Inria at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: +<<<<<<< HEAD + * Read/write images. + * + * Authors: + * Eric Marchand +======= + * Simd backend for JPEG and PNG image I/O operations. +>>>>>>> 557f1beda01f36ca886ec039d0a1a80a7446ca59 + * + *****************************************************************************/ + +/*! + \file vpImageIo.cpp + \brief Simd backend for JPEG and PNG image I/O operations. +*/ + +#include "vpImageIoBackend.h" +#include + + +//TODO: +void readSimdlib(vpImage &I, const std::string &filename) +{ + size_t stride = 0, width = 0, height = 0; + SimdPixelFormatType format = SimdPixelFormatGray8; + uint8_t* data = SimdImageLoadFromFile(filename.c_str(), &stride, &width, &height, &format); + const bool copyData = true; + I.init(data, (unsigned int)height, (unsigned int)width, copyData); + SimdFree(data); +} + +void readSimdlib(vpImage &I, const std::string &filename) +{ + size_t stride = 0, width = 0, height = 0; + SimdPixelFormatType format = SimdPixelFormatRgba32; + uint8_t* data = SimdImageLoadFromFile(filename.c_str(), &stride, &width, &height, &format); + const bool copyData = true; + I.init((vpRGBa *)data, (unsigned int)height, (unsigned int)width, copyData); + SimdFree(data); +} + +void writeJPEGSimdlib(const vpImage &I, const std::string &filename, int quality) +{ + SimdImageSaveToFile((const uint8_t *)I.bitmap, I.getWidth()*4, I.getWidth(), I.getHeight(), SimdPixelFormatGray8, SimdImageFileJpeg, quality, filename.c_str()); +} + +void writeJPEGSimdlib(const vpImage &I, const std::string &filename, int quality) +{ + SimdImageSaveToFile((const uint8_t *)I.bitmap, I.getWidth()*4, I.getWidth(), I.getHeight(), SimdPixelFormatRgba32, SimdImageFileJpeg, quality, filename.c_str()); +} + +void writePNGSimdlib(const vpImage &I, const std::string &filename) +{ + SimdImageSaveToFile((const uint8_t *)I.bitmap, I.getWidth()*4, I.getWidth(), I.getHeight(), SimdPixelFormatGray8, SimdImageFilePng, 90, filename.c_str()); +} + +void writePNGSimdlib(const vpImage &I, const std::string &filename) +{ + SimdImageSaveToFile((const uint8_t *)I.bitmap, I.getWidth()*4, I.getWidth(), I.getHeight(), SimdPixelFormatRgba32, SimdImageFilePng, 90, filename.c_str()); +} diff --git a/modules/io/src/image/private/vpImageIoStb.cpp b/modules/io/src/image/private/vpImageIoStb.cpp new file mode 100644 index 0000000000..dfe7945717 --- /dev/null +++ b/modules/io/src/image/private/vpImageIoStb.cpp @@ -0,0 +1,125 @@ +/**************************************************************************** + * + * ViSP, open source Visual Servoing Platform software. + * Copyright (C) 2005 - 2019 by Inria. All rights reserved. + * + * This software is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact Inria about acquiring a ViSP Professional + * Edition License. + * + * See http://visp.inria.fr for more information. + * + * This software was developed at: + * Inria Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * + * If you have questions regarding the use of this file, please contact + * Inria at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: +<<<<<<< HEAD + * Read/write images. + * + * Authors: + * Eric Marchand +======= + * stb backend for JPEG and PNG image I/O operations. +>>>>>>> 557f1beda01f36ca886ec039d0a1a80a7446ca59 + * + *****************************************************************************/ + +/*! + \file vpImageIo.cpp + \brief stb backend for JPEG and PNG image I/O operations. +*/ + +#include "vpImageIoBackend.h" + +//TODO: +#if defined __SSE2__ || defined _M_X64 || (defined _M_IX86_FP && _M_IX86_FP >= 2) +# define VISP_HAVE_SSE2 1 +#endif + +#ifndef VISP_HAVE_SSE2 +# define STBI_NO_SIMD +#endif + +#define STB_IMAGE_IMPLEMENTATION +#include + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include + + +//TODO: +void readStb(vpImage &I, const std::string &filename) +{ + int width = 0, height = 0, channels = 0; + unsigned char *image = stbi_load(filename.c_str(), &width, &height, &channels, STBI_grey); + if (image == NULL) { + throw(vpImageException(vpImageException::ioError, "Can't read the image: %s", filename.c_str())); + } + I.init(image, static_cast(height), static_cast(width), true); + stbi_image_free(image); +} + +void readStb(vpImage &I, const std::string &filename) +{ + int width = 0, height = 0, channels = 0; + unsigned char *image = stbi_load(filename.c_str(), &width, &height, &channels, STBI_rgb_alpha); + if (image == NULL) { + throw(vpImageException(vpImageException::ioError, "Can't read the image: %s", filename.c_str())); + } + I.init(reinterpret_cast(image), static_cast(height), static_cast(width), true); + stbi_image_free(image); +} + +void writeJPEGStb(const vpImage &I, const std::string &filename, int quality) +{ + int res = stbi_write_jpg(filename.c_str(), static_cast(I.getWidth()), static_cast(I.getHeight()), STBI_grey, + reinterpret_cast(I.bitmap), quality); + if (res == 0) { + throw(vpImageException(vpImageException::ioError, "JEPG write error")); + } +} + +void writeJPEGStb(const vpImage &I, const std::string &filename, int quality) +{ + int res = stbi_write_jpg(filename.c_str(), static_cast(I.getWidth()), static_cast(I.getHeight()), STBI_rgb_alpha, + reinterpret_cast(I.bitmap), quality); + if (res == 0) { + throw(vpImageException(vpImageException::ioError, "JEPG write error")); + } +} + +void writePNGStb(const vpImage &I, const std::string &filename) +{ + const int stride_in_bytes = static_cast(I.getWidth()); + int res = stbi_write_png(filename.c_str(), static_cast(I.getWidth()), static_cast(I.getHeight()), STBI_grey, + reinterpret_cast(I.bitmap), stride_in_bytes); + if (res == 0) { + throw(vpImageException(vpImageException::ioError, "PNG write error: %s", filename.c_str())); + } +} + +void writePNGStb(const vpImage &I, const std::string &filename) +{ + const int stride_in_bytes = static_cast(4 * I.getWidth()); + int res = stbi_write_png(filename.c_str(), static_cast(I.getWidth()), static_cast(I.getHeight()), STBI_rgb_alpha, + reinterpret_cast(I.bitmap), stride_in_bytes); + if (res == 0) { + throw(vpImageException(vpImageException::ioError, "PNG write error: %s", filename.c_str())); + } +} diff --git a/modules/io/src/image/vpImageIo.cpp b/modules/io/src/image/vpImageIo.cpp index 633503389c..fe09d9e23d 100644 --- a/modules/io/src/image/vpImageIo.cpp +++ b/modules/io/src/image/vpImageIo.cpp @@ -41,115 +41,18 @@ \brief Read/write images */ -#include -#include //image conversion #include #include -#if defined(_WIN32) -// Include WinSock2.h before windows.h to ensure that winsock.h is not -// included by windows.h since winsock.h and winsock2.h are incompatible -#include -#include -#endif - -#if defined(VISP_HAVE_JPEG) -#include -#include -#endif - -#if defined(VISP_HAVE_PNG) -#include -#endif - -#if !defined(VISP_HAVE_OPENCV) -#if !defined(VISP_HAVE_JPEG) || !defined(VISP_HAVE_PNG) - -#if defined __SSE2__ || defined _M_X64 || (defined _M_IX86_FP && _M_IX86_FP >= 2) -# define VISP_HAVE_SSE2 1 -#endif - -#ifndef VISP_HAVE_SSE2 -# define STBI_NO_SIMD -#endif - -#define STB_IMAGE_IMPLEMENTATION -#include - -#define STB_IMAGE_WRITE_IMPLEMENTATION -#include -#endif -#endif +//TODO: +#include "private/vpImageIoBackend.h" -void vp_decodeHeaderPNM(const std::string &filename, std::ifstream &fd, const std::string &magic, unsigned int &w, - unsigned int &h, unsigned int &maxval); - -#ifndef DOXYGEN_SHOULD_SKIP_THIS -/*! - * Decode the PNM image header. - * \param filename[in] : File name. - * \param fd[in] : File desdcriptor. - * \param magic[in] : Magic number for identifying the file type. - * \param w[out] : Image width. - * \param h[out] : Image height. - * \param maxval[out] : Maximum pixel value. - */ -void vp_decodeHeaderPNM(const std::string &filename, std::ifstream &fd, const std::string &magic, unsigned int &w, - unsigned int &h, unsigned int &maxval) -{ - std::string line; - unsigned int nb_elt = 4, cpt_elt = 0; - while (cpt_elt != nb_elt) { - // Skip empty lines or lines starting with # (comment) - while (std::getline(fd, line) && (line.compare(0, 1, "#") == 0 || line.size() == 0)) { - }; - - if (fd.eof()) { - fd.close(); - throw(vpImageException(vpImageException::ioError, "Cannot read header of file \"%s\"", filename.c_str())); - } - - std::vector header = vpIoTools::splitChain(line, std::string(" ")); - - if (header.size() == 0) { - fd.close(); - throw(vpImageException(vpImageException::ioError, "Cannot read header of file \"%s\"", filename.c_str())); - } - - if (cpt_elt == 0) { // decode magic - if (header[0].compare(0, magic.size(), magic) != 0) { - fd.close(); - throw(vpImageException(vpImageException::ioError, "\"%s\" is not a PNM file with magic number %s", - filename.c_str(), magic.c_str())); - } - cpt_elt++; - header.erase(header.begin(), - header.begin() + 1); // erase first element that is processed - } - while (header.size()) { - if (cpt_elt == 1) { // decode width - std::istringstream ss(header[0]); - ss >> w; - cpt_elt++; - header.erase(header.begin(), - header.begin() + 1); // erase first element that is processed - } else if (cpt_elt == 2) { // decode height - std::istringstream ss(header[0]); - ss >> h; - cpt_elt++; - header.erase(header.begin(), - header.begin() + 1); // erase first element that is processed - } else if (cpt_elt == 3) { // decode maxval - std::istringstream ss(header[0]); - ss >> maxval; - cpt_elt++; - header.erase(header.begin(), - header.begin() + 1); // erase first element that is processed - } - } - } -} -#endif +//TODO: +// priority order for backend selection is: +// - libjpeg / libpng if available +// - OpenCV if available +// - stb backend for image reading / Simd backend for image writing +// - Simd backend for image reading / stb backend for image writing vpImageIo::vpImageFormatType vpImageIo::getFormat(const std::string &filename) { @@ -241,7 +144,7 @@ std::string vpImageIo::getExtension(const std::string &filename) \param I : Image to set with the \e filename content. \param filename : Name of the file containing the image. */ -void vpImageIo::read(vpImage &I, const std::string &filename) +void vpImageIo::read(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) { bool exist = vpIoTools::checkFilename(filename); if (!exist) { @@ -262,18 +165,10 @@ void vpImageIo::read(vpImage &I, const std::string &filename) readPPM(I, final_filename); break; case FORMAT_JPEG: -#ifdef VISP_HAVE_JPEG - readJPEG(I, final_filename); -#else - try_opencv_reader = true; -#endif + readJPEG(I, final_filename, backend); break; case FORMAT_PNG: -#if defined(VISP_HAVE_PNG) - readPNG(I, final_filename); -#else - try_opencv_reader = true; -#endif + readPNG(I, final_filename, backend); break; case FORMAT_TIFF: case FORMAT_BMP: @@ -288,39 +183,10 @@ void vpImageIo::read(vpImage &I, const std::string &filename) if (try_opencv_reader) { #if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 -#if VISP_HAVE_OPENCV_VERSION >= 0x030200 - int flags = cv::IMREAD_GRAYSCALE | cv::IMREAD_IGNORE_ORIENTATION; -#elif VISP_HAVE_OPENCV_VERSION >= 0x030000 - int flags = cv::IMREAD_GRAYSCALE; -#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 - int flags = CV_LOAD_IMAGE_GRAYSCALE; -#endif - // std::cout << "Use opencv to read the image" << std::endl; - cv::Mat cvI = cv::imread(final_filename, flags); - if (cvI.cols == 0 && cvI.rows == 0) { - std::string message = "Cannot read file \"" + std::string(final_filename) + "\": Image format not supported"; - throw(vpImageException(vpImageException::ioError, message)); - } - vpImageConvert::convert(cvI, I); + readOpenCV(I, filename); #else - switch (getFormat(final_filename)) { - case FORMAT_JPEG: - readJPEG(I, final_filename); - break; - case FORMAT_PNG: - readPNG(I, final_filename); - break; - case FORMAT_BMP: - case FORMAT_TIFF: - case FORMAT_DIB: - case FORMAT_PBM: - case FORMAT_RASTER: - case FORMAT_JPEG2000: - case FORMAT_UNKNOWN: - default: - std::string message = "Cannot read file \"" + std::string(final_filename) + "\": Image format not supported"; - throw(vpImageException(vpImageException::ioError, message)); - } + std::string message = "Cannot read file \"" + filename + "\": No backend able to support this image format"; + throw(vpImageException(vpImageException::ioError, message)); #endif } } @@ -345,7 +211,7 @@ void vpImageIo::read(vpImage &I, const std::string &filename) \param I : Image to set with the \e filename content. \param filename : Name of the file containing the image. */ -void vpImageIo::read(vpImage &I, const std::string &filename) +void vpImageIo::read(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) { bool exist = vpIoTools::checkFilename(filename); if (!exist) { @@ -365,18 +231,10 @@ void vpImageIo::read(vpImage &I, const std::string &filename) readPPM(I, final_filename); break; case FORMAT_JPEG: -#ifdef VISP_HAVE_JPEG - readJPEG(I, final_filename); -#else - try_opencv_reader = true; -#endif + readJPEG(I, final_filename, backend); break; case FORMAT_PNG: -#if defined(VISP_HAVE_PNG) - readPNG(I, final_filename); -#else - try_opencv_reader = true; -#endif + readPNG(I, final_filename, backend); break; case FORMAT_TIFF: case FORMAT_BMP: @@ -391,39 +249,10 @@ void vpImageIo::read(vpImage &I, const std::string &filename) if (try_opencv_reader) { #if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 -#if VISP_HAVE_OPENCV_VERSION >= 0x030200 - int flags = cv::IMREAD_COLOR | cv::IMREAD_IGNORE_ORIENTATION; -#elif VISP_HAVE_OPENCV_VERSION >= 0x030000 - int flags = cv::IMREAD_COLOR; -#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 - int flags = CV_LOAD_IMAGE_COLOR; -#endif - // std::cout << "Use opencv to read the image" << std::endl; - cv::Mat cvI = cv::imread(final_filename, flags); - if (cvI.cols == 0 && cvI.rows == 0) { - std::string message = "Cannot read file \"" + std::string(final_filename) + "\": Image format not supported"; - throw(vpImageException(vpImageException::ioError, message)); - } - vpImageConvert::convert(cvI, I); + readOpenCV(I, filename); #else - switch (getFormat(final_filename)) { - case FORMAT_JPEG: - readJPEG(I, final_filename); - break; - case FORMAT_PNG: - readPNG(I, final_filename); - break; - case FORMAT_BMP: - case FORMAT_TIFF: - case FORMAT_DIB: - case FORMAT_PBM: - case FORMAT_RASTER: - case FORMAT_JPEG2000: - case FORMAT_UNKNOWN: - default: - std::string message = "Cannot read file \"" + std::string(final_filename) + "\": Image format not supported"; - throw(vpImageException(vpImageException::ioError, message)); - } + std::string message = "Cannot read file \"" + filename + "\": No backend able to support this image format"; + throw(vpImageException(vpImageException::ioError, message)); #endif } } @@ -442,7 +271,7 @@ void vpImageIo::read(vpImage &I, const std::string &filename) \param I : Image to write. \param filename : Name of the file containing the image. */ -void vpImageIo::write(const vpImage &I, const std::string &filename) +void vpImageIo::write(const vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) { bool try_opencv_writer = false; @@ -454,18 +283,10 @@ void vpImageIo::write(const vpImage &I, const std::string &filena writePPM(I, filename); break; case FORMAT_JPEG: -#ifdef VISP_HAVE_JPEG - writeJPEG(I, filename); -#else - try_opencv_writer = true; -#endif + writeJPEG(I, filename, backend); break; case FORMAT_PNG: -#ifdef VISP_HAVE_PNG - writePNG(I, filename); -#else - try_opencv_writer = true; -#endif + writePNG(I, filename, backend); break; case FORMAT_TIFF: case FORMAT_BMP: @@ -479,30 +300,11 @@ void vpImageIo::write(const vpImage &I, const std::string &filena } if (try_opencv_writer) { -#if VISP_HAVE_OPENCV_VERSION >= 0x020100 - // std::cout << "Use opencv to write the image" << std::endl; - cv::Mat cvI; - vpImageConvert::convert(I, cvI); - cv::imwrite(filename, cvI); +#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 + writeOpenCV(I, filename, 90); #else - switch (getFormat(filename)) { - case FORMAT_JPEG: - writeJPEG(I, filename); - break; - case FORMAT_PNG: - writePNG(I, filename); - break; - case FORMAT_BMP: - case FORMAT_TIFF: - case FORMAT_DIB: - case FORMAT_PBM: - case FORMAT_RASTER: - case FORMAT_JPEG2000: - case FORMAT_UNKNOWN: - default: - vpCERROR << "Cannot write file: Image format not supported..." << std::endl; - throw(vpImageException(vpImageException::ioError, "Cannot write file: Image format not supported")); - } + std::string message = "Cannot write file \"" + filename + "\": No backend able to support this image format"; + throw(vpImageException(vpImageException::ioError, message)); #endif } } @@ -521,7 +323,7 @@ void vpImageIo::write(const vpImage &I, const std::string &filena \param I : Image to write. \param filename : Name of the file containing the image. */ -void vpImageIo::write(const vpImage &I, const std::string &filename) +void vpImageIo::write(const vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) { bool try_opencv_writer = false; @@ -533,18 +335,10 @@ void vpImageIo::write(const vpImage &I, const std::string &filename) writePPM(I, filename); break; case FORMAT_JPEG: -#ifdef VISP_HAVE_JPEG - writeJPEG(I, filename); -#else - try_opencv_writer = true; -#endif + writeJPEG(I, filename, backend); break; case FORMAT_PNG: -#ifdef VISP_HAVE_PNG - writePNG(I, filename); -#else - try_opencv_writer = true; -#endif + writePNG(I, filename, backend); break; case FORMAT_TIFF: case FORMAT_BMP: @@ -558,1681 +352,250 @@ void vpImageIo::write(const vpImage &I, const std::string &filename) } if (try_opencv_writer) { -#if VISP_HAVE_OPENCV_VERSION >= 0x020100 - // std::cout << "Use opencv to write the image" << std::endl; - cv::Mat cvI; - vpImageConvert::convert(I, cvI); - cv::imwrite(filename, cvI); +#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 + writeOpenCV(I, filename, 90); #else - switch (getFormat(filename)) { - case FORMAT_JPEG: - writeJPEG(I, filename); - break; - case FORMAT_PNG: - writePNG(I, filename); - break; - case FORMAT_BMP: - case FORMAT_TIFF: - case FORMAT_DIB: - case FORMAT_PBM: - case FORMAT_RASTER: - case FORMAT_JPEG2000: - case FORMAT_UNKNOWN: - default: - vpCERROR << "Cannot write file: Image format not supported..." << std::endl; - throw(vpImageException(vpImageException::ioError, "Cannot write file: Image format not supported")); - } + std::string message = "Cannot write file \"" + filename + "\": No backend able to support this image format"; + throw(vpImageException(vpImageException::ioError, message)); #endif } } -//-------------------------------------------------------------------------- -// PFM -//-------------------------------------------------------------------------- - -/*! - Write the content of the image bitmap in the file which name is given by \e - filename. This function is built like portable gray pixmap (eg PGM P5) file. - but considers float image data. - - \param I : Image to save as a (PFM P8) file. - \param filename : Name of the file containing the image. -*/ - -void vpImageIo::writePFM(const vpImage &I, const std::string &filename) +void vpImageIo::readJPEG(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) { - FILE *fd; - - // Test the filename - if (filename.empty()) { - throw(vpImageException(vpImageException::ioError, "Cannot write PFM image: filename empty")); + if (backend == IO_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_JPEG) + readJPEGLibjpeg(I, filename); +#else + std::string message = "Cannot read file \"" + filename + "\": Libjpeg backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); +#endif + } else if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 + readOpenCV(I, filename); +#else + std::string message = "Cannot read file \"" + filename + "\": OpenCV backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); +#endif + } else if (backend == IO_STB_IMAGE_BACKEND || backend == IO_DEFAULT_BACKEND) { + readStb(I, filename); + } else if (backend == IO_SIMDLIB_BACKEND) { + readSimdlib(I, filename); } +} - fd = fopen(filename.c_str(), "wb"); - - if (fd == NULL) { - throw(vpImageException(vpImageException::ioError, "Cannot create PFM file \"%s\"", filename.c_str())); +void vpImageIo::readJPEG(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) +{ + if (backend == IO_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_JPEG) + readJPEGLibjpeg(I, filename); +#else + std::string message = "Cannot read file \"" + filename + "\": Libjpeg backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); +#endif + } else if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 + readOpenCV(I, filename); +#else + std::string message = "Cannot read file \"" + filename + "\": OpenCV backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); +#endif + } else if (backend == IO_STB_IMAGE_BACKEND || backend == IO_DEFAULT_BACKEND) { + readStb(I, filename); + } else if (backend == IO_SIMDLIB_BACKEND) { + readSimdlib(I, filename); } +} - // Write the head - fprintf(fd, "P8\n"); // Magic number - fprintf(fd, "%u %u\n", I.getWidth(), I.getHeight()); // Image size - fprintf(fd, "255\n"); // Max level - - // Write the bitmap - size_t ierr; - size_t nbyte = I.getWidth() * I.getHeight(); - - ierr = fwrite(I.bitmap, sizeof(float), nbyte, fd); - if (ierr != nbyte) { - fclose(fd); - throw(vpImageException(vpImageException::ioError, "Cannot save PFM file \"%s\": only %d bytes over %d saved ", - filename.c_str(), ierr, nbyte)); +void vpImageIo::readPNG(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) +{ + if (backend == IO_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_PNG) + readPNGLibpng(I, filename); +#else + std::string message = "Cannot read file \"" + filename + "\": Libpng backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); +#endif + } else if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 + readOpenCV(I, filename); +#else + std::string message = "Cannot read file \"" + filename + "\": OpenCV backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); +#endif + } else if (backend == IO_STB_IMAGE_BACKEND || backend == IO_DEFAULT_BACKEND) { + readStb(I, filename); + } else if (backend == IO_SIMDLIB_BACKEND) { + readSimdlib(I, filename); } - - fflush(fd); - fclose(fd); } -//-------------------------------------------------------------------------- -// PGM -//-------------------------------------------------------------------------- - -/*! - Write the content of the image bitmap in the file which name is given by \e - filename. This function writes a portable gray pixmap (PGM P5) file. - \param I : Image to save as a (PGM P5) file. - \param filename : Name of the file containing the image. -*/ - -void vpImageIo::writePGM(const vpImage &I, const std::string &filename) +void vpImageIo::readPNG(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) { - - FILE *fd; - - // Test the filename - if (filename.empty()) { - throw(vpImageException(vpImageException::ioError, "Cannot create PGM file: filename empty")); + if (backend == IO_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_PNG) + readPNGLibpng(I, filename); +#else + std::string message = "Cannot read file \"" + filename + "\": Libpng backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); +#endif + } else if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 + readOpenCV(I, filename); +#else + std::string message = "Cannot read file \"" + filename + "\": OpenCV backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); +#endif + } else if (backend == IO_STB_IMAGE_BACKEND || backend == IO_DEFAULT_BACKEND) { + readStb(I, filename); + } else if (backend == IO_SIMDLIB_BACKEND) { + readSimdlib(I, filename); } +} - fd = fopen(filename.c_str(), "wb"); - - if (fd == NULL) { - throw(vpImageException(vpImageException::ioError, "Cannot create PGM file \"%s\"", filename.c_str())); +void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename, int quality, const vpImageIoBackendType& backend) +{ + if (backend == IO_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_JPEG) + writeJPEGLibjpeg(I, filename, quality); +#else + std::string message = "Cannot write file \"" + filename + "\": Libjpeg backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); +#endif + } else if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 + writeOpenCV(I, filename, quality); +#else + std::string message = "Cannot write file \"" + filename + "\": OpenCV backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); +#endif + } else if (backend == IO_SIMDLIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + writeJPEGSimdlib(I, filename, quality); + } else if (backend == IO_STB_IMAGE_BACKEND) { + writeJPEGStb(I, filename, quality); } +} - // Write the head - fprintf(fd, "P5\n"); // Magic number - fprintf(fd, "%u %u\n", I.getWidth(), I.getHeight()); // Image size - fprintf(fd, "255\n"); // Max level +void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename, int quality, const vpImageIoBackendType& backend) +{ + if (backend == IO_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_JPEG) + writeJPEGLibjpeg(I, filename, quality); +#else + std::string message = "Cannot write file \"" + filename + "\": Libjpeg backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); +#endif + } else if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 + writeOpenCV(I, filename, quality); +#else + std::string message = "Cannot write file \"" + filename + "\": OpenCV backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); +#endif + } else if (backend == IO_SIMDLIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + writeJPEGSimdlib(I, filename, quality); + } else if (backend == IO_STB_IMAGE_BACKEND) { + writeJPEGStb(I, filename, quality); + } +} - // Write the bitmap - size_t ierr; - size_t nbyte = I.getWidth() * I.getHeight(); +void vpImageIo::writePNG(const vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) +{ + if (backend == IO_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_PNG) + writePNGLibpng(I, filename); +#else + std::string message = "Cannot write file \"" + filename + "\": Libpng backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); +#endif + } else if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 + writeOpenCV(I, filename, 90); +#else + std::string message = "Cannot write file \"" + filename + "\": OpenCV backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); +#endif + } else if (backend == IO_SIMDLIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + writePNGSimdlib(I, filename); + } else if (backend == IO_STB_IMAGE_BACKEND) { + writePNGStb(I, filename); + } +} - ierr = fwrite(I.bitmap, sizeof(unsigned char), nbyte, fd); - if (ierr != nbyte) { - fclose(fd); - throw(vpImageException(vpImageException::ioError, "Cannot save PGM file \"%s\": only %d over %d bytes saved", - filename.c_str(), ierr, nbyte)); +void vpImageIo::writePNG(const vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) +{ + if (backend == IO_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_PNG) + writePNGLibpng(I, filename); +#else + std::string message = "Cannot write file \"" + filename + "\": Libpng backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); +#endif + } else if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 + writeOpenCV(I, filename, 90); +#else + std::string message = "Cannot write file \"" + filename + "\": OpenCV backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); +#endif + } else if (backend == IO_SIMDLIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + writePNGSimdlib(I, filename); + } else if (backend == IO_STB_IMAGE_BACKEND) { + writePNGStb(I, filename); } +} - fflush(fd); - fclose(fd); +void vpImageIo::writePFM(const vpImage &I, const std::string &filename) +{ + vp_writePFM(I, filename); } -/*! - Write the content of the image bitmap in the file which name is given by \e - filename. This function writes a portable gray pixmap (PGM P5) file. +void vpImageIo::writePGM(const vpImage &I, const std::string &filename) +{ + vp_writePGM(I, filename); +} - \param I : Image to save as a (PGM P5) file. - \param filename : Name of the file containing the image. -*/ void vpImageIo::writePGM(const vpImage &I, const std::string &filename) { - vpImage Iuc; - unsigned int nrows = I.getHeight(); - unsigned int ncols = I.getWidth(); - - Iuc.resize(nrows, ncols); - - for (unsigned int i = 0; i < nrows * ncols; i++) - Iuc.bitmap[i] = (unsigned char)I.bitmap[i]; - - vpImageIo::writePGM(Iuc, filename); + vp_writePGM(I, filename); } -/*! - Write the content of the image bitmap in the file which name is given by \e - filename. This function writes a portable gray pixmap (PGM P5) file. - Color image is converted into a grayscale image. - - \param I : Image to save as a (PGM P5) file. - \param filename : Name of the file containing the image. -*/ void vpImageIo::writePGM(const vpImage &I, const std::string &filename) { - - FILE *fd; - - // Test the filename - if (filename.empty()) { - throw(vpImageException(vpImageException::ioError, "Cannot create PGM file: filename empty")); - } - - fd = fopen(filename.c_str(), "wb"); - - if (fd == NULL) { - throw(vpImageException(vpImageException::ioError, "Cannot create PGM file \"%s\"", filename.c_str())); - } - - // Write the head - fprintf(fd, "P5\n"); // Magic number - fprintf(fd, "%u %u\n", I.getWidth(), I.getHeight()); // Image size - fprintf(fd, "255\n"); // Max level - - // Write the bitmap - size_t ierr; - size_t nbyte = I.getWidth() * I.getHeight(); - - vpImage Itmp; - vpImageConvert::convert(I, Itmp); - - ierr = fwrite(Itmp.bitmap, sizeof(unsigned char), nbyte, fd); - if (ierr != nbyte) { - fclose(fd); - throw(vpImageException(vpImageException::ioError, "Cannot save PGM file \"%s\": only %d over %d bytes saved", - filename.c_str(), ierr, nbyte)); - } - - fflush(fd); - fclose(fd); + vp_writePGM(I, filename); } -/*! - Read a PFM P8 file and initialize a float image. - - Read the contents of the portable gray pixmap (PFM P8) filename, allocate - memory for the corresponding image, and set the bitmap whith the content of - the file. - - If the image has been already initialized, memory allocation is done - only if the new image size is different, else we re-use the same - memory space. - - \param I : Image to set with the \e filename content. - \param filename : Name of the file containing the image. - -*/ - void vpImageIo::readPFM(vpImage &I, const std::string &filename) { - unsigned int w = 0, h = 0, maxval = 0; - unsigned int w_max = 100000, h_max = 100000, maxval_max = 255; - std::string magic("P8"); - - std::ifstream fd(filename.c_str(), std::ios::binary); - - // Open the filename - if (!fd.is_open()) { - throw(vpImageException(vpImageException::ioError, "Cannot open file \"%s\"", filename.c_str())); - } - - vp_decodeHeaderPNM(filename, fd, magic, w, h, maxval); - - if (w > w_max || h > h_max) { - fd.close(); - throw(vpException(vpException::badValue, "Bad image size in \"%s\"", filename.c_str())); - } - if (maxval > maxval_max) { - fd.close(); - throw(vpImageException(vpImageException::ioError, "Bad maxval in \"%s\"", filename.c_str())); - } - - if ((h != I.getHeight()) || (w != I.getWidth())) { - I.resize(h, w); - } - - unsigned int nbyte = I.getHeight() * I.getWidth(); - fd.read((char *)I.bitmap, sizeof(float) * nbyte); - if (!fd) { - fd.close(); - throw(vpImageException(vpImageException::ioError, "Read only %d of %d bytes in file \"%s\"", fd.gcount(), nbyte, - filename.c_str())); - } - - fd.close(); + vp_readPFM(I, filename); } -/*! - Read a PGM P5 file and initialize a scalar image. - - Read the contents of the portable gray pixmap (PGM P5) filename, allocate - memory for the corresponding image, and set the bitmap whith the content of - the file. - - If the image has been already initialized, memory allocation is done - only if the new image size is different, else we re-use the same - memory space. - - \param I : Image to set with the \e filename content. - \param filename : Name of the file containing the image. -*/ - void vpImageIo::readPGM(vpImage &I, const std::string &filename) { - unsigned int w = 0, h = 0, maxval = 0; - unsigned int w_max = 100000, h_max = 100000, maxval_max = 255; - std::string magic("P5"); - - std::ifstream fd(filename.c_str(), std::ios::binary); - - // Open the filename - if (!fd.is_open()) { - throw(vpImageException(vpImageException::ioError, "Cannot open file \"%s\"", filename.c_str())); - } - - vp_decodeHeaderPNM(filename, fd, magic, w, h, maxval); - - if (w > w_max || h > h_max) { - fd.close(); - throw(vpException(vpException::badValue, "Bad image size in \"%s\"", filename.c_str())); - } - if (maxval > maxval_max) { - fd.close(); - throw(vpImageException(vpImageException::ioError, "Bad maxval in \"%s\"", filename.c_str())); - } - - if ((h != I.getHeight()) || (w != I.getWidth())) { - I.resize(h, w); - } - - unsigned int nbyte = I.getHeight() * I.getWidth(); - fd.read((char *)I.bitmap, nbyte); - if (!fd) { - fd.close(); - throw(vpImageException(vpImageException::ioError, "Read only %d of %d bytes in file \"%s\"", fd.gcount(), nbyte, - filename.c_str())); - } - - fd.close(); + vp_readPGM(I, filename); } -/*! - Read a PGM P5 file and initialize a scalar image. - - Read the contents of the portable gray pixmap (PGM P5) filename, allocate - memory for the corresponding image, and set the bitmap whith the content of - the file. - - If the image has been already initialized, memory allocation is done - only if the new image size is different, else we re-use the same - memory space. - - The gray level image contained in the \e filename is converted in a - color image in \e I. - - \param I : Color image to set with the \e filename content. - \param filename : Name of the file containing the image. -*/ - void vpImageIo::readPGM(vpImage &I, const std::string &filename) { - vpImage Itmp; + vp_readPGM(I, filename); +} - vpImageIo::readPGM(Itmp, filename); +void vpImageIo::readPPM(vpImage &I, const std::string &filename) +{ + vp_readPPM(I, filename); +} - vpImageConvert::convert(Itmp, I); +void vpImageIo::readPPM(vpImage &I, const std::string &filename) +{ + vp_readPPM(I, filename); } -//-------------------------------------------------------------------------- -// PPM -//-------------------------------------------------------------------------- +void vpImageIo::writePPM(const vpImage &I, const std::string &filename) +{ + vp_writePPM(I, filename); +} -/*! - Read the contents of the portable pixmap (PPM P6) filename, allocate memory - for the corresponding gray level image, convert the data in gray level, and - set the bitmap whith the gray level data. That means that the image \e I is - a "black and white" rendering of the original image in \e filename, as in a - black and white photograph. The quantization formula used is \f$0,299 r + - 0,587 g + 0,114 b\f$. - - If the image has been already initialized, memory allocation is done - only if the new image size is different, else we re-use the same - memory space. - - \param I : Image to set with the \e filename content. - \param filename : Name of the file containing the image. - -*/ -void vpImageIo::readPPM(vpImage &I, const std::string &filename) -{ - vpImage Itmp; - - vpImageIo::readPPM(Itmp, filename); - - vpImageConvert::convert(Itmp, I); -} - -/*! - Read the contents of the portable pixmap (PPM P6) filename, - allocate memory for the corresponding vpRGBa image. - - If the image has been already initialized, memory allocation is done - only if the new image size is different, else we re-use the same - memory space. - - \param I : Image to set with the \e filename content. - \param filename : Name of the file containing the image. -*/ -void vpImageIo::readPPM(vpImage &I, const std::string &filename) -{ - unsigned int w = 0, h = 0, maxval = 0; - unsigned int w_max = 100000, h_max = 100000, maxval_max = 255; - std::string magic("P6"); - - std::ifstream fd(filename.c_str(), std::ios::binary); - - // Open the filename - if (!fd.is_open()) { - throw(vpImageException(vpImageException::ioError, "Cannot open file \"%s\"", filename.c_str())); - } - - vp_decodeHeaderPNM(filename, fd, magic, w, h, maxval); - - if (w > w_max || h > h_max) { - fd.close(); - throw(vpException(vpException::badValue, "Bad image size in \"%s\"", filename.c_str())); - } - if (maxval > maxval_max) { - fd.close(); - throw(vpImageException(vpImageException::ioError, "Bad maxval in \"%s\"", filename.c_str())); - } - - if ((h != I.getHeight()) || (w != I.getWidth())) { - I.resize(h, w); - } - - for (unsigned int i = 0; i < I.getHeight(); i++) { - for (unsigned int j = 0; j < I.getWidth(); j++) { - unsigned char rgb[3]; - fd.read((char *)&rgb, 3); - - if (!fd) { - fd.close(); - throw(vpImageException(vpImageException::ioError, "Read only %d of %d bytes in file \"%s\"", - (i * I.getWidth() + j) * 3 + fd.gcount(), I.getSize() * 3, filename.c_str())); - } - - I[i][j].R = rgb[0]; - I[i][j].G = rgb[1]; - I[i][j].B = rgb[2]; - I[i][j].A = vpRGBa::alpha_default; - } - } - - fd.close(); -} - -/*! - Write the content of the bitmap in the file which name is given by \e - filename. This function writes a portable gray pixmap (PPM P6) file. - grayscale image is converted into a color image vpRGBa. - - \param I : Image to save as a (PPM P6) file. - \param filename : Name of the file containing the image. - -*/ - -void vpImageIo::writePPM(const vpImage &I, const std::string &filename) -{ - vpImage Itmp; - - vpImageConvert::convert(I, Itmp); - - vpImageIo::writePPM(Itmp, filename); -} - -/*! - Write the content of the bitmap in the file which name is given by \e - filename. This function writes a portable gray pixmap (PPM P6) file. - - \param I : Image to save as a (PPM P6) file. - \param filename : Name of the file containing the image. -*/ void vpImageIo::writePPM(const vpImage &I, const std::string &filename) { - FILE *f; - - // Test the filename - if (filename.empty()) { - throw(vpImageException(vpImageException::ioError, "Cannot create PPM file: filename empty")); - } - - f = fopen(filename.c_str(), "wb"); - - if (f == NULL) { - throw(vpImageException(vpImageException::ioError, "Cannot create PPM file \"%s\"", filename.c_str())); - } - - fprintf(f, "P6\n"); // Magic number - fprintf(f, "%u %u\n", I.getWidth(), I.getHeight()); // Image size - fprintf(f, "%d\n", 255); // Max level - - for (unsigned int i = 0; i < I.getHeight(); i++) { - for (unsigned int j = 0; j < I.getWidth(); j++) { - vpRGBa v = I[i][j]; - unsigned char rgb[3]; - rgb[0] = v.R; - rgb[1] = v.G; - rgb[2] = v.B; - - size_t res = fwrite(&rgb, 1, 3, f); - if (res != 3) { - fclose(f); - throw(vpImageException(vpImageException::ioError, "cannot write file \"%s\"", filename.c_str())); - } - } - } - - fflush(f); - fclose(f); -} - -//-------------------------------------------------------------------------- -// JPEG -//-------------------------------------------------------------------------- - -#if defined(VISP_HAVE_JPEG) - -/*! - Write the content of the image bitmap in the file which name is given by \e - filename. This function writes a JPEG file. - - \param I : Image to save as a JPEG file. - \param filename : Name of the file containing the image. -*/ -void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename) -{ - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - FILE *file; - - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cinfo); - - // Test the filename - if (filename.empty()) { - throw(vpImageException(vpImageException::ioError, "Cannot create JPEG file: filename empty")); - } - - file = fopen(filename.c_str(), "wb"); - - if (file == NULL) { - throw(vpImageException(vpImageException::ioError, "Cannot create JPEG file \"%s\"", filename.c_str())); - } - - unsigned int width = I.getWidth(); - unsigned int height = I.getHeight(); - - jpeg_stdio_dest(&cinfo, file); - - cinfo.image_width = width; - cinfo.image_height = height; - cinfo.input_components = 1; - cinfo.in_color_space = JCS_GRAYSCALE; - jpeg_set_defaults(&cinfo); - - jpeg_start_compress(&cinfo, TRUE); - - unsigned char *line; - line = new unsigned char[width]; - unsigned char *input = (unsigned char *)I.bitmap; - while (cinfo.next_scanline < cinfo.image_height) { - for (unsigned int i = 0; i < width; i++) { - line[i] = *(input); - input++; - } - jpeg_write_scanlines(&cinfo, &line, 1); - } - - jpeg_finish_compress(&cinfo); - jpeg_destroy_compress(&cinfo); - delete[] line; - fclose(file); -} - -/*! - Write the content of the image bitmap in the file which name is given by \e - filename. This function writes a JPEG file. - - \param I : Image to save as a JPEG file. - \param filename : Name of the file containing the image. -*/ -void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename) -{ - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - FILE *file; - - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cinfo); - - // Test the filename - if (filename.empty()) { - throw(vpImageException(vpImageException::ioError, "Cannot create JPEG file: filename empty")); - } - - file = fopen(filename.c_str(), "wb"); - - if (file == NULL) { - throw(vpImageException(vpImageException::ioError, "Cannot create JPEG file \"%s\"", filename.c_str())); - } - - unsigned int width = I.getWidth(); - unsigned int height = I.getHeight(); - - jpeg_stdio_dest(&cinfo, file); - - cinfo.image_width = width; - cinfo.image_height = height; - cinfo.input_components = 3; - cinfo.in_color_space = JCS_RGB; - jpeg_set_defaults(&cinfo); - - jpeg_start_compress(&cinfo, TRUE); - - unsigned char *line; - line = new unsigned char[3 * width]; - unsigned char *input = (unsigned char *)I.bitmap; - while (cinfo.next_scanline < cinfo.image_height) { - for (unsigned int i = 0; i < width; i++) { - line[i * 3] = *(input); - input++; - line[i * 3 + 1] = *(input); - input++; - line[i * 3 + 2] = *(input); - input++; - input++; - } - jpeg_write_scanlines(&cinfo, &line, 1); - } - - jpeg_finish_compress(&cinfo); - jpeg_destroy_compress(&cinfo); - delete[] line; - fclose(file); + vp_writePPM(I, filename); } - -/*! - Read the contents of the JPEG file, allocate memory - for the corresponding gray level image, if necessary convert the data in - gray level, and set the bitmap whith the gray level data. That means that - the image \e I is a "black and white" rendering of the original image in \e - filename, as in a black and white photograph. If necessary, the quantization - formula used is \f$0,299 r + 0,587 g + 0,114 b\f$. - - If the image has been already initialized, memory allocation is done - only if the new image size is different, else we re-use the same - memory space. - - \param I : Image to set with the \e filename content. - \param filename : Name of the file containing the image. - -*/ -void vpImageIo::readJPEG(vpImage &I, const std::string &filename) -{ - struct jpeg_decompress_struct cinfo; - struct jpeg_error_mgr jerr; - FILE *file; - - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_decompress(&cinfo); - - // Test the filename - if (filename.empty()) { - throw(vpImageException(vpImageException::ioError, "Cannot read JPEG image: filename empty")); - } - - file = fopen(filename.c_str(), "rb"); - - if (file == NULL) { - throw(vpImageException(vpImageException::ioError, "Cannot read JPEG file \"%s\"", filename.c_str())); - } - - jpeg_stdio_src(&cinfo, file); - jpeg_read_header(&cinfo, TRUE); - - unsigned int width = cinfo.image_width; - unsigned int height = cinfo.image_height; - - if ((width != I.getWidth()) || (height != I.getHeight())) - I.resize(height, width); - - jpeg_start_decompress(&cinfo); - - unsigned int rowbytes = cinfo.output_width * (unsigned int)(cinfo.output_components); - JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, rowbytes, 1); - - if (cinfo.out_color_space == JCS_RGB) { - vpImage Ic(height, width); - unsigned char *output = (unsigned char *)Ic.bitmap; - while (cinfo.output_scanline < cinfo.output_height) { - jpeg_read_scanlines(&cinfo, buffer, 1); - for (unsigned int i = 0; i < width; i++) { - *(output++) = buffer[0][i * 3]; - *(output++) = buffer[0][i * 3 + 1]; - *(output++) = buffer[0][i * 3 + 2]; - *(output++) = vpRGBa::alpha_default; - } - } - vpImageConvert::convert(Ic, I); - } - - else if (cinfo.out_color_space == JCS_GRAYSCALE) { - while (cinfo.output_scanline < cinfo.output_height) { - unsigned int row = cinfo.output_scanline; - jpeg_read_scanlines(&cinfo, buffer, 1); - memcpy(I[row], buffer[0], rowbytes); - } - } - - jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); - fclose(file); -} - -/*! - Read a JPEG file and initialize a scalar image. - - Read the contents of the JPEG file, allocate - memory for the corresponding image, and set - the bitmap whith the content of - the file. - - If the image has been already initialized, memory allocation is done - only if the new image size is different, else we re-use the same - memory space. - - If the file corresponds to a grayscaled image, a conversion is done to deal - with \e I which is a color image. - - \param I : Color image to set with the \e filename content. - \param filename : Name of the file containing the image. -*/ -void vpImageIo::readJPEG(vpImage &I, const std::string &filename) -{ - struct jpeg_decompress_struct cinfo; - struct jpeg_error_mgr jerr; - FILE *file; - - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_decompress(&cinfo); - - // Test the filename - if (filename.empty()) { - throw(vpImageException(vpImageException::ioError, "Cannot read JPEG image: filename empty")); - } - - file = fopen(filename.c_str(), "rb"); - - if (file == NULL) { - throw(vpImageException(vpImageException::ioError, "Cannot read JPEG file \"%s\"", filename.c_str())); - } - - jpeg_stdio_src(&cinfo, file); - - jpeg_read_header(&cinfo, TRUE); - - unsigned int width = cinfo.image_width; - unsigned int height = cinfo.image_height; - - if ((width != I.getWidth()) || (height != I.getHeight())) - I.resize(height, width); - - jpeg_start_decompress(&cinfo); - - unsigned int rowbytes = cinfo.output_width * (unsigned int)(cinfo.output_components); - JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, rowbytes, 1); - - if (cinfo.out_color_space == JCS_RGB) { - unsigned char *output = (unsigned char *)I.bitmap; - while (cinfo.output_scanline < cinfo.output_height) { - jpeg_read_scanlines(&cinfo, buffer, 1); - for (unsigned int i = 0; i < width; i++) { - *(output++) = buffer[0][i * 3]; - *(output++) = buffer[0][i * 3 + 1]; - *(output++) = buffer[0][i * 3 + 2]; - *(output++) = vpRGBa::alpha_default; - } - } - } - - else if (cinfo.out_color_space == JCS_GRAYSCALE) { - vpImage Ig(height, width); - - while (cinfo.output_scanline < cinfo.output_height) { - unsigned int row = cinfo.output_scanline; - jpeg_read_scanlines(&cinfo, buffer, 1); - memcpy(Ig[row], buffer[0], rowbytes); - } - - vpImageConvert::convert(Ig, I); - } - - jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); - fclose(file); -} - -#elif defined(VISP_HAVE_OPENCV) - -/*! - Write the content of the image bitmap in the file which name is given by \e - filename. This function writes a JPEG file. - - \param I : Image to save as a JPEG file. - \param filename : Name of the file containing the image. -*/ -void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename) -{ -#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) - cv::Mat Ip; - vpImageConvert::convert(I, Ip); - cv::imwrite(filename.c_str(), Ip); -#else - IplImage *Ip = NULL; - vpImageConvert::convert(I, Ip); - - cvSaveImage(filename.c_str(), Ip); - - cvReleaseImage(&Ip); -#endif -} - -/*! - Write the content of the image bitmap in the file which name is given by \e - filename. This function writes a JPEG file. - - \param I : Image to save as a JPEG file. - \param filename : Name of the file containing the image. -*/ -void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename) -{ -#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) - cv::Mat Ip; - vpImageConvert::convert(I, Ip); - cv::imwrite(filename.c_str(), Ip); -#else - IplImage *Ip = NULL; - vpImageConvert::convert(I, Ip); - - cvSaveImage(filename.c_str(), Ip); - - cvReleaseImage(&Ip); -#endif -} - -/*! - Read the contents of the JPEG file, allocate memory - for the corresponding gray level image, if necessary convert the data in - gray level, and set the bitmap whith the gray level data. That means that - the image \e I is a "black and white" rendering of the original image in \e - filename, as in a black and white photograph. If necessary, the quantization - formula used is \f$0,299 r + 0,587 g + 0,114 b\f$. - - If the image has been already initialized, memory allocation is done - only if the new image size is different, else we re-use the same - memory space. - - If EXIF information is embedded in the image file, the EXIF orientation is ignored. - - \param I : Image to set with the \e filename content. - \param filename : Name of the file containing the image. - -*/ -void vpImageIo::readJPEG(vpImage &I, const std::string &filename) -{ -#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 -#if VISP_HAVE_OPENCV_VERSION >= 0x030200 - int flags = cv::IMREAD_GRAYSCALE | cv::IMREAD_IGNORE_ORIENTATION; -#elif VISP_HAVE_OPENCV_VERSION >= 0x030000 - int flags = cv::IMREAD_GRAYSCALE; -#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 - int flags = CV_LOAD_IMAGE_GRAYSCALE; -#endif - cv::Mat Ip = cv::imread(filename.c_str(), flags); - if (!Ip.empty()) - vpImageConvert::convert(Ip, I); - else - throw(vpImageException(vpImageException::ioError, "Can't read the image")); -#else - IplImage *Ip = NULL; - Ip = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_GRAYSCALE); - if (Ip != NULL) - vpImageConvert::convert(Ip, I); - else - throw(vpImageException(vpImageException::ioError, "Can't read the image")); - cvReleaseImage(&Ip); -#endif -} - -/*! - Read a JPEG file and initialize a scalar image. - - Read the contents of the JPEG file, allocate - memory for the corresponding image, and set - the bitmap whith the content of - the file. - - If the image has been already initialized, memory allocation is done - only if the new image size is different, else we re-use the same - memory space. - - If the file corresponds to a grayscaled image, a conversion is done to deal - with \e I which is a color image. - - If EXIF information is embedded in the image file, the EXIF orientation is ignored. - - \param I : Color image to set with the \e filename content. - \param filename : Name of the file containing the image. -*/ -void vpImageIo::readJPEG(vpImage &I, const std::string &filename) -{ -#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 -#if VISP_HAVE_OPENCV_VERSION >= 0x030200 - int flags = cv::IMREAD_GRAYSCALE | cv::IMREAD_IGNORE_ORIENTATION; -#elif VISP_HAVE_OPENCV_VERSION >= 0x030000 - int flags = cv::IMREAD_GRAYSCALE; -#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 - int flags = CV_LOAD_IMAGE_GRAYSCALE; -#endif - cv::Mat Ip = cv::imread(filename.c_str(), flags); - if (!Ip.empty()) - vpImageConvert::convert(Ip, I); - else - throw(vpImageException(vpImageException::ioError, "Can't read the image")); -#else - IplImage *Ip = NULL; - Ip = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_COLOR); - if (Ip != NULL) - vpImageConvert::convert(Ip, I); - else - throw(vpImageException(vpImageException::ioError, "Can't read the image")); - cvReleaseImage(&Ip); -#endif -} -#else -void vpImageIo::readJPEG(vpImage &I, const std::string &filename) -{ - int width = 0, height = 0, channels = 0; - unsigned char *image = stbi_load(filename.c_str(), &width, &height, &channels, STBI_grey); - if (image == NULL) { - throw(vpImageException(vpImageException::ioError, "Can't read the image: %s", filename.c_str())); - } - I.init(image, static_cast(height), static_cast(width), true); - stbi_image_free(image); -} -void vpImageIo::readJPEG(vpImage &I, const std::string &filename) -{ - int width = 0, height = 0, channels = 0; - unsigned char *image = stbi_load(filename.c_str(), &width, &height, &channels, STBI_rgb_alpha); - if (image == NULL) { - throw(vpImageException(vpImageException::ioError, "Can't read the image: %s", filename.c_str())); - } - I.init(reinterpret_cast(image), static_cast(height), static_cast(width), true); - stbi_image_free(image); -} -void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename) -{ - int res = stbi_write_jpg(filename.c_str(), static_cast(I.getWidth()), static_cast(I.getHeight()), STBI_grey, - reinterpret_cast(I.bitmap), 90); - if (res == 0) { - throw(vpImageException(vpImageException::ioError, "JPEG write error")); - } -} -void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename) -{ - int res = stbi_write_jpg(filename.c_str(), static_cast(I.getWidth()), static_cast(I.getHeight()), STBI_rgb_alpha, - reinterpret_cast(I.bitmap), 90); - if (res == 0) { - throw(vpImageException(vpImageException::ioError, "JEPG write error")); - } -} -#endif - -//-------------------------------------------------------------------------- -// PNG -//-------------------------------------------------------------------------- - -#if defined(VISP_HAVE_PNG) - -/*! - Write the content of the image bitmap in the file which name is given by \e - filename. This function writes a PNG file. - - \param I : Image to save as a PNG file. - \param filename : Name of the file containing the image. -*/ -void vpImageIo::writePNG(const vpImage &I, const std::string &filename) -{ - FILE *file; - - // Test the filename - if (filename.empty()) { - throw(vpImageException(vpImageException::ioError, "Cannot create PNG file: filename empty")); - } - - file = fopen(filename.c_str(), "wb"); - - if (file == NULL) { - throw(vpImageException(vpImageException::ioError, "Cannot create PNG file \"%s\"", filename.c_str())); - } - - /* create a png info struct */ - png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) { - fclose(file); - vpERROR_TRACE("Error during png_create_write_struct()\n"); - throw(vpImageException(vpImageException::ioError, "PNG write error")); - } - - png_infop info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - fclose(file); - png_destroy_write_struct(&png_ptr, NULL); - vpERROR_TRACE("Error during png_create_info_struct()\n"); - throw(vpImageException(vpImageException::ioError, "PNG write error")); - } - - /* initialize the setjmp for returning properly after a libpng error occured - */ - if (setjmp(png_jmpbuf(png_ptr))) { - fclose(file); - png_destroy_write_struct(&png_ptr, &info_ptr); - vpERROR_TRACE("Error during init_io\n"); - throw(vpImageException(vpImageException::ioError, "PNG write error")); - } - - /* setup libpng for using standard C fwrite() function with our FILE pointer - */ - png_init_io(png_ptr, file); - - unsigned int width = I.getWidth(); - unsigned int height = I.getHeight(); - int bit_depth = 8; - int color_type = PNG_COLOR_TYPE_GRAY; - /* set some useful information from header */ - - if (setjmp(png_jmpbuf(png_ptr))) { - fclose(file); - png_destroy_write_struct(&png_ptr, &info_ptr); - vpERROR_TRACE("Error during write header\n"); - throw(vpImageException(vpImageException::ioError, "PNG write error")); - } - - png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, - PNG_FILTER_TYPE_BASE); - - png_write_info(png_ptr, info_ptr); - - png_bytep *row_ptrs = new png_bytep[height]; - for (unsigned int i = 0; i < height; i++) - row_ptrs[i] = new png_byte[width]; - - unsigned char *input = (unsigned char *)I.bitmap; - - for (unsigned int i = 0; i < height; i++) { - png_byte *row = row_ptrs[i]; - for (unsigned int j = 0; j < width; j++) { - row[j] = *(input); - input++; - } - } - - png_write_image(png_ptr, row_ptrs); - - png_write_end(png_ptr, NULL); - - for (unsigned int j = 0; j < height; j++) - delete[] row_ptrs[j]; - - delete[] row_ptrs; - - png_destroy_write_struct(&png_ptr, &info_ptr); - - fclose(file); -} - -/*! - Write the content of the image bitmap in the file which name is given by \e - filename. This function writes a PNG file. - - \param I : Image to save as a PNG file. - \param filename : Name of the file containing the image. -*/ -void vpImageIo::writePNG(const vpImage &I, const std::string &filename) -{ - FILE *file; - - // Test the filename - if (filename.empty()) { - throw(vpImageException(vpImageException::ioError, "Cannot create PNG file: filename empty")); - } - - file = fopen(filename.c_str(), "wb"); - - if (file == NULL) { - throw(vpImageException(vpImageException::ioError, "Cannot create PNG file \"%s\"", filename.c_str())); - } - - /* create a png info struct */ - png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) { - fclose(file); - vpERROR_TRACE("Error during png_create_write_struct()\n"); - throw(vpImageException(vpImageException::ioError, "PNG write error")); - } - - png_infop info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - fclose(file); - png_destroy_write_struct(&png_ptr, NULL); - vpERROR_TRACE("Error during png_create_info_struct()\n"); - throw(vpImageException(vpImageException::ioError, "PNG write error")); - } - - /* initialize the setjmp for returning properly after a libpng error occured - */ - if (setjmp(png_jmpbuf(png_ptr))) { - fclose(file); - png_destroy_write_struct(&png_ptr, &info_ptr); - vpERROR_TRACE("Error during init_io\n"); - throw(vpImageException(vpImageException::ioError, "PNG write error")); - } - - /* setup libpng for using standard C fwrite() function with our FILE pointer - */ - png_init_io(png_ptr, file); - - unsigned int width = I.getWidth(); - unsigned int height = I.getHeight(); - int bit_depth = 8; - int color_type = PNG_COLOR_TYPE_RGB; - /* set some useful information from header */ - - if (setjmp(png_jmpbuf(png_ptr))) { - fclose(file); - png_destroy_write_struct(&png_ptr, &info_ptr); - vpERROR_TRACE("Error during write header\n"); - throw(vpImageException(vpImageException::ioError, "PNG write error")); - } - - png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, - PNG_FILTER_TYPE_BASE); - - png_write_info(png_ptr, info_ptr); - - png_bytep *row_ptrs = new png_bytep[height]; - for (unsigned int i = 0; i < height; i++) - row_ptrs[i] = new png_byte[3 * width]; - - unsigned char *input = (unsigned char *)I.bitmap; - ; - - for (unsigned int i = 0; i < height; i++) { - png_byte *row = row_ptrs[i]; - for (unsigned int j = 0; j < width; j++) { - row[3 * j] = *(input); - input++; - row[3 * j + 1] = *(input); - input++; - row[3 * j + 2] = *(input); - input++; - input++; - } - } - - png_write_image(png_ptr, row_ptrs); - - png_write_end(png_ptr, NULL); - - for (unsigned int j = 0; j < height; j++) - delete[] row_ptrs[j]; - - delete[] row_ptrs; - - png_destroy_write_struct(&png_ptr, &info_ptr); - - fclose(file); -} - -/*! - Read the contents of the PNG file, allocate memory - for the corresponding gray level image, if necessary convert the data in - gray level, and set the bitmap whith the gray level data. That means that - the image \e I is a "black and white" rendering of the original image in \e - filename, as in a black and white photograph. If necessary, the quantization - formula used is \f$0,299 r + 0,587 g + 0,114 b\f$. - - If the image has been already initialized, memory allocation is done - only if the new image size is different, else we re-use the same - memory space. - - \param I : Image to set with the \e filename content. - \param filename : Name of the file containing the image. - -*/ -void vpImageIo::readPNG(vpImage &I, const std::string &filename) -{ - FILE *file; - png_byte magic[8]; - // Test the filename - if (filename.empty()) { - throw(vpImageException(vpImageException::ioError, "Cannot read PNG image: filename empty")); - } - - file = fopen(filename.c_str(), "rb"); - - if (file == NULL) { - throw(vpImageException(vpImageException::ioError, "Cannot read file \"%s\"", filename.c_str())); - } - - /* read magic number */ - if (fread(magic, 1, sizeof(magic), file) != sizeof(magic)) { - fclose(file); - throw(vpImageException(vpImageException::ioError, "Cannot read magic number in file \"%s\"", filename.c_str())); - } - - /* check for valid magic number */ - if (png_sig_cmp(magic, 0, sizeof(magic))) { - fclose(file); - throw(vpImageException(vpImageException::ioError, "Cannot read PNG file: \"%s\" is not a valid PNG image", - filename.c_str())); - } - - /* create a png read struct */ - // printf("version %s\n", PNG_LIBPNG_VER_STRING); - png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (png_ptr == NULL) { - fprintf(stderr, "error: can't create a png read structure!\n"); - fclose(file); - throw(vpImageException(vpImageException::ioError, "error reading png file")); - } - - /* create a png info struct */ - png_infop info_ptr = png_create_info_struct(png_ptr); - if (info_ptr == NULL) { - fprintf(stderr, "error: can't create a png info structure!\n"); - fclose(file); - png_destroy_read_struct(&png_ptr, NULL, NULL); - throw(vpImageException(vpImageException::ioError, "error reading png file")); - } - - /* initialize the setjmp for returning properly after a libpng error occured - */ - if (setjmp(png_jmpbuf(png_ptr))) { - fclose(file); - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - vpERROR_TRACE("Error during init io\n"); - throw(vpImageException(vpImageException::ioError, "PNG read error")); - } - - /* setup libpng for using standard C fread() function with our FILE pointer - */ - png_init_io(png_ptr, file); - - /* tell libpng that we have already read the magic number */ - png_set_sig_bytes(png_ptr, sizeof(magic)); - - /* read png info */ - png_read_info(png_ptr, info_ptr); - - unsigned int width = png_get_image_width(png_ptr, info_ptr); - unsigned int height = png_get_image_height(png_ptr, info_ptr); - - unsigned int bit_depth, channels, color_type; - /* get some useful information from header */ - bit_depth = png_get_bit_depth(png_ptr, info_ptr); - channels = png_get_channels(png_ptr, info_ptr); - color_type = png_get_color_type(png_ptr, info_ptr); - - /* convert index color images to RGB images */ - if (color_type == PNG_COLOR_TYPE_PALETTE) - png_set_palette_to_rgb(png_ptr); - - /* convert 1-2-4 bits grayscale images to 8 bits grayscale. */ - if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) - png_set_expand(png_ptr); - - // if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) - // png_set_tRNS_to_alpha (png_ptr); - - if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - png_set_strip_alpha(png_ptr); - - if (bit_depth == 16) - png_set_strip_16(png_ptr); - else if (bit_depth < 8) - png_set_packing(png_ptr); - - /* update info structure to apply transformations */ - png_read_update_info(png_ptr, info_ptr); - - channels = png_get_channels(png_ptr, info_ptr); - - if ((width != I.getWidth()) || (height != I.getHeight())) - I.resize(height, width); - - png_bytep *rowPtrs = new png_bytep[height]; - - unsigned int stride = png_get_rowbytes(png_ptr, info_ptr); - unsigned char *data = new unsigned char[stride * height]; - - for (unsigned int i = 0; i < height; i++) - rowPtrs[i] = (png_bytep)data + (i * stride); - - png_read_image(png_ptr, rowPtrs); - - vpImage Ic(height, width); - unsigned char *output; - - switch (channels) { - case 1: - output = (unsigned char *)I.bitmap; - for (unsigned int i = 0; i < width * height; i++) { - *(output++) = data[i]; - } - break; - - case 2: - output = (unsigned char *)I.bitmap; - for (unsigned int i = 0; i < width * height; i++) { - *(output++) = data[i * 2]; - } - break; - - case 3: - output = (unsigned char *)Ic.bitmap; - for (unsigned int i = 0; i < width * height; i++) { - *(output++) = data[i * 3]; - *(output++) = data[i * 3 + 1]; - *(output++) = data[i * 3 + 2]; - *(output++) = vpRGBa::alpha_default; - } - vpImageConvert::convert(Ic, I); - break; - - case 4: - output = (unsigned char *)Ic.bitmap; - for (unsigned int i = 0; i < width * height; i++) { - *(output++) = data[i * 4]; - *(output++) = data[i * 4 + 1]; - *(output++) = data[i * 4 + 2]; - *(output++) = data[i * 4 + 3]; - } - vpImageConvert::convert(Ic, I); - break; - } - - delete[](png_bytep) rowPtrs; - delete[] data; - png_read_end(png_ptr, NULL); - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - fclose(file); -} - -/*! - Read a PNG file and initialize a scalar image. - - Read the contents of the PNG file, allocate - memory for the corresponding image, and set - the bitmap whith the content of - the file. - - If the image has been already initialized, memory allocation is done - only if the new image size is different, else we re-use the same - memory space. - - If the file corresponds to a grayscaled image, a conversion is done to deal - with \e I which is a color image. - - \param I : Color image to set with the \e filename content. - \param filename : Name of the file containing the image. -*/ -void vpImageIo::readPNG(vpImage &I, const std::string &filename) -{ - FILE *file; - png_byte magic[8]; - - // Test the filename - if (filename.empty()) { - throw(vpImageException(vpImageException::ioError, "Cannot read PNG image: filename empty")); - } - - file = fopen(filename.c_str(), "rb"); - - if (file == NULL) { - throw(vpImageException(vpImageException::ioError, "Cannot read file \"%s\"", filename.c_str())); - } - - /* read magic number */ - if (fread(magic, 1, sizeof(magic), file) != sizeof(magic)) { - fclose(file); - throw(vpImageException(vpImageException::ioError, "Cannot read magic number in file \"%s\"", filename.c_str())); - } - - /* check for valid magic number */ - if (png_sig_cmp(magic, 0, sizeof(magic))) { - fclose(file); - throw(vpImageException(vpImageException::ioError, "Cannot read PNG file: \"%s\" is not a valid PNG image", - filename.c_str())); - } - - /* create a png read struct */ - png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png_ptr) { - fclose(file); - vpERROR_TRACE("Error during png_create_read_struct()\n"); - throw(vpImageException(vpImageException::ioError, "PNG read error")); - } - - /* create a png info struct */ - png_infop info_ptr = png_create_info_struct(png_ptr); - if (!info_ptr) { - fclose(file); - png_destroy_read_struct(&png_ptr, NULL, NULL); - vpERROR_TRACE("Error during png_create_info_struct()\n"); - throw(vpImageException(vpImageException::ioError, "PNG read error")); - } - - /* initialize the setjmp for returning properly after a libpng error occured - */ - if (setjmp(png_jmpbuf(png_ptr))) { - fclose(file); - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - vpERROR_TRACE("Error during init io\n"); - throw(vpImageException(vpImageException::ioError, "PNG read error")); - } - - /* setup libpng for using standard C fread() function with our FILE pointer - */ - png_init_io(png_ptr, file); - - /* tell libpng that we have already read the magic number */ - png_set_sig_bytes(png_ptr, sizeof(magic)); - - /* read png info */ - png_read_info(png_ptr, info_ptr); - - unsigned int width = png_get_image_width(png_ptr, info_ptr); - unsigned int height = png_get_image_height(png_ptr, info_ptr); - - unsigned int bit_depth, channels, color_type; - /* get some useful information from header */ - bit_depth = png_get_bit_depth(png_ptr, info_ptr); - channels = png_get_channels(png_ptr, info_ptr); - color_type = png_get_color_type(png_ptr, info_ptr); - - /* convert index color images to RGB images */ - if (color_type == PNG_COLOR_TYPE_PALETTE) - png_set_palette_to_rgb(png_ptr); - - /* convert 1-2-4 bits grayscale images to 8 bits grayscale. */ - if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) - png_set_expand(png_ptr); - - // if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) - // png_set_tRNS_to_alpha (png_ptr); - - if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) - png_set_strip_alpha(png_ptr); - - if (bit_depth == 16) - png_set_strip_16(png_ptr); - else if (bit_depth < 8) - png_set_packing(png_ptr); - - /* update info structure to apply transformations */ - png_read_update_info(png_ptr, info_ptr); - - channels = png_get_channels(png_ptr, info_ptr); - - if ((width != I.getWidth()) || (height != I.getHeight())) - I.resize(height, width); - - png_bytep *rowPtrs = new png_bytep[height]; - - unsigned int stride = png_get_rowbytes(png_ptr, info_ptr); - unsigned char *data = new unsigned char[stride * height]; - - for (unsigned int i = 0; i < height; i++) - rowPtrs[i] = (png_bytep)data + (i * stride); - - png_read_image(png_ptr, rowPtrs); - - vpImage Ig(height, width); - unsigned char *output; - - switch (channels) { - case 1: - output = (unsigned char *)Ig.bitmap; - for (unsigned int i = 0; i < width * height; i++) { - *(output++) = data[i]; - } - vpImageConvert::convert(Ig, I); - break; - - case 2: - output = (unsigned char *)Ig.bitmap; - for (unsigned int i = 0; i < width * height; i++) { - *(output++) = data[i * 2]; - } - vpImageConvert::convert(Ig, I); - break; - - case 3: - output = (unsigned char *)I.bitmap; - for (unsigned int i = 0; i < width * height; i++) { - *(output++) = data[i * 3]; - *(output++) = data[i * 3 + 1]; - *(output++) = data[i * 3 + 2]; - *(output++) = vpRGBa::alpha_default; - } - break; - - case 4: - output = (unsigned char *)I.bitmap; - for (unsigned int i = 0; i < width * height; i++) { - *(output++) = data[i * 4]; - *(output++) = data[i * 4 + 1]; - *(output++) = data[i * 4 + 2]; - *(output++) = data[i * 4 + 3]; - } - break; - } - - delete[](png_bytep) rowPtrs; - delete[] data; - png_read_end(png_ptr, NULL); - png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - fclose(file); -} - -#elif defined(VISP_HAVE_OPENCV) - -/*! - Write the content of the image bitmap in the file which name is given by \e - filename. This function writes a PNG file. - - \param I : Image to save as a PNG file. - \param filename : Name of the file containing the image. -*/ -void vpImageIo::writePNG(const vpImage &I, const std::string &filename) -{ -#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) - cv::Mat Ip; - vpImageConvert::convert(I, Ip); - cv::imwrite(filename.c_str(), Ip); -#else - IplImage *Ip = NULL; - vpImageConvert::convert(I, Ip); - - cvSaveImage(filename.c_str(), Ip); - - cvReleaseImage(&Ip); -#endif -} - -/*! - Write the content of the image bitmap in the file which name is given by \e - filename. This function writes a PNG file. - - \param I : Image to save as a PNG file. - \param filename : Name of the file containing the image. -*/ -void vpImageIo::writePNG(const vpImage &I, const std::string &filename) -{ -#if (VISP_HAVE_OPENCV_VERSION >= 0x020408) - cv::Mat Ip; - vpImageConvert::convert(I, Ip); - cv::imwrite(filename.c_str(), Ip); -#else - IplImage *Ip = NULL; - vpImageConvert::convert(I, Ip); - - cvSaveImage(filename.c_str(), Ip); - - cvReleaseImage(&Ip); -#endif -} - -/*! - Read the contents of the PNG file, allocate memory - for the corresponding gray level image, if necessary convert the data in - gray level, and set the bitmap whith the gray level data. That means that - the image \e I is a "black and white" rendering of the original image in \e - filename, as in a black and white photograph. If necessary, the quantization - formula used is \f$0,299 r + 0,587 g + 0,114 b\f$. - - If the image has been already initialized, memory allocation is done - only if the new image size is different, else we re-use the same - memory space. - - If EXIF information is embedded in the image file, the EXIF orientation is ignored. - - \param I : Image to set with the \e filename content. - \param filename : Name of the file containing the image. - -*/ -void vpImageIo::readPNG(vpImage &I, const std::string &filename) -{ -#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 -#if VISP_HAVE_OPENCV_VERSION >= 0x030200 - int flags = cv::IMREAD_GRAYSCALE | cv::IMREAD_IGNORE_ORIENTATION; -#elif VISP_HAVE_OPENCV_VERSION >= 0x030000 - int flags = cv::IMREAD_GRAYSCALE; -#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 - int flags = CV_LOAD_IMAGE_GRAYSCALE; -#endif - cv::Mat Ip = cv::imread(filename.c_str(), flags); - if (!Ip.empty()) - vpImageConvert::convert(Ip, I); - else - throw(vpImageException(vpImageException::ioError, "Can't read the image")); -#else - IplImage *Ip = NULL; - Ip = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_GRAYSCALE); - if (Ip != NULL) - vpImageConvert::convert(Ip, I); - else - throw(vpImageException(vpImageException::ioError, "Can't read the image")); - cvReleaseImage(&Ip); -#endif -} - -/*! - Read a PNG file and initialize a scalar image. - - Read the contents of the PNG file, allocate - memory for the corresponding image, and set - the bitmap whith the content of - the file. - - If the image has been already initialized, memory allocation is done - only if the new image size is different, else we re-use the same - memory space. - - If the file corresponds to a grayscaled image, a conversion is done to deal - with \e I which is a color image. - - If EXIF information is embedded in the image file, the EXIF orientation is ignored. - - \param I : Color image to set with the \e filename content. - \param filename : Name of the file containing the image. -*/ -void vpImageIo::readPNG(vpImage &I, const std::string &filename) -{ -#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 -#if VISP_HAVE_OPENCV_VERSION >= 0x030200 - int flags = cv::IMREAD_COLOR | cv::IMREAD_IGNORE_ORIENTATION; -#elif VISP_HAVE_OPENCV_VERSION >= 0x030000 - int flags = cv::IMREAD_COLOR; -#elif VISP_HAVE_OPENCV_VERSION >= 0x020100 - int flags = CV_LOAD_IMAGE_COLOR; -#endif - cv::Mat Ip = cv::imread(filename.c_str(), flags); - if (!Ip.empty()) - vpImageConvert::convert(Ip, I); - else - throw(vpImageException(vpImageException::ioError, "Can't read the image")); -#else - IplImage *Ip = NULL; - Ip = cvLoadImage(filename.c_str(), CV_LOAD_IMAGE_COLOR); - if (Ip != NULL) - vpImageConvert::convert(Ip, I); - else - throw(vpImageException(vpImageException::ioError, "Can't read the image")); - cvReleaseImage(&Ip); -#endif -} -#else -void vpImageIo::readPNG(vpImage &I, const std::string &filename) -{ - int width = 0, height = 0, channels = 0; - unsigned char *image = stbi_load(filename.c_str(), &width, &height, &channels, STBI_grey); - if (image == NULL) { - throw(vpImageException(vpImageException::ioError, "Can't read the image: %s", filename.c_str())); - } - I.init(image, static_cast(height), static_cast(width), true); - stbi_image_free(image); -} -void vpImageIo::readPNG(vpImage &I, const std::string &filename) -{ - int width = 0, height = 0, channels = 0; - unsigned char *image = stbi_load(filename.c_str(), &width, &height, &channels, STBI_rgb_alpha); - if (image == NULL) { - throw(vpImageException(vpImageException::ioError, "Can't read the image: %s", filename.c_str())); - } - I.init(reinterpret_cast(image), static_cast(height), static_cast(width), true); - stbi_image_free(image); -} -void vpImageIo::writePNG(const vpImage &I, const std::string &filename) -{ - const int stride_in_bytes = static_cast(I.getWidth()); - int res = stbi_write_png(filename.c_str(), static_cast(I.getWidth()), static_cast(I.getHeight()), STBI_grey, - reinterpret_cast(I.bitmap), stride_in_bytes); - if (res == 0) { - throw(vpImageException(vpImageException::ioError, "PNG write error: %s", filename.c_str())); - } -} -void vpImageIo::writePNG(const vpImage &I, const std::string &filename) -{ - const int stride_in_bytes = static_cast(4 * I.getWidth()); - int res = stbi_write_png(filename.c_str(), static_cast(I.getWidth()), static_cast(I.getHeight()), STBI_rgb_alpha, - reinterpret_cast(I.bitmap), stride_in_bytes); - if (res == 0) { - throw(vpImageException(vpImageException::ioError, "PNG write error: %s", filename.c_str())); - } -} -#endif diff --git a/modules/io/test/perfImageLoadSave.cpp b/modules/io/test/perfImageLoadSave.cpp new file mode 100644 index 0000000000..b4a1bd97dd --- /dev/null +++ b/modules/io/test/perfImageLoadSave.cpp @@ -0,0 +1,273 @@ +/**************************************************************************** + * + * ViSP, open source Visual Servoing Platform software. + * Copyright (C) 2005 - 2019 by Inria. All rights reserved. + * + * This software is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * See the file LICENSE.txt at the root directory of this source + * distribution for additional information about the GNU GPL. + * + * For using ViSP with software that can not be combined with the GNU + * GPL, please contact Inria about acquiring a ViSP Professional + * Edition License. + * + * See http://visp.inria.fr for more information. + * + * This software was developed at: + * Inria Rennes - Bretagne Atlantique + * Campus Universitaire de Beaulieu + * 35042 Rennes Cedex + * France + * + * If you have questions regarding the use of this file, please contact + * Inria at visp@inria.fr + * + * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE + * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * Description: + * Benchmark color image conversion. + * + *****************************************************************************/ + +#include + +#ifdef VISP_HAVE_CATCH2 +#define CATCH_CONFIG_ENABLE_BENCHMARKING +#define CATCH_CONFIG_RUNNER +#include + +#include +#include +#include + +static std::string ipath = vpIoTools::getViSPImagesDataPath(); +static std::vector paths { + ipath + "/Solvay/Solvay_conference_1927_Version2_640x440", + ipath + "/Solvay/Solvay_conference_1927_Version2_1024x705", + ipath + "/Solvay/Solvay_conference_1927_Version2_1280x881", + ipath + "/Solvay/Solvay_conference_1927_Version2_2126x1463", +}; +static std::vector names { + "Solvay (640x440)", "Solvay (1024x705)", "Solvay (1280x881)", "Solvay (2126x1463)" +}; +static std::vector backends { +#if defined(VISP_HAVE_JPEG) && defined(VISP_HAVE_PNG) + vpImageIo::IO_LIB_BACKEND, +#endif +#if defined(VISP_HAVE_OPENCV) + vpImageIo::IO_OPENCV_BACKEND, +#endif + vpImageIo::IO_SIMDLIB_BACKEND, + vpImageIo::IO_STB_IMAGE_BACKEND +}; +static std::vector backendNamesJpeg { + "libjpeg", "OpenCV", "simd", "stb" +}; +static std::vector backendNamesPng { + "libpng", "OpenCV", "simd", "stb" +}; +static int nThreads = 0; + +TEST_CASE("Benchmark JPEG image loading", "[benchmark]") { + SECTION("Grayscale") { + for (size_t i = 0; i < paths.size(); i++) { + SECTION(names[i]) { + for (size_t j = 0; j < backends.size(); j++) { + vpImage I; + + BENCHMARK(backendNamesJpeg[j] + " backend") { + vpImageIo::read(I, paths[i] + ".jpg", backends[j]); + return I; + }; + } + } + } + } + + SECTION("vpRGBa") { + for (size_t i = 0; i < paths.size(); i++) { + SECTION(names[i]) { + for (size_t j = 0; j < backends.size(); j++) { + vpImage I; + + BENCHMARK(backendNamesJpeg[j] + " backend") { + vpImageIo::read(I, paths[i] + ".jpg", backends[j]); + return I; + }; + } + } + } + } +} + +TEST_CASE("Benchmark PNG image loading", "[benchmark]") { + SECTION("Grayscale") { + for (size_t i = 0; i < paths.size(); i++) { + SECTION(names[i]) { + for (size_t j = 0; j < backends.size(); j++) { + vpImage I; + + BENCHMARK(backendNamesPng[j] + " backend") { + vpImageIo::read(I, paths[i] + ".png", backends[j]); + return I; + }; + } + } + } + } + + SECTION("vpRGBa") { + for (size_t i = 0; i < paths.size(); i++) { + SECTION(names[i]) { + for (size_t j = 0; j < backends.size(); j++) { + vpImage I; + + BENCHMARK(backendNamesPng[j] + " backend") { + vpImageIo::read(I, paths[i] + ".png", backends[j]); + return I; + }; + } + } + } + } +} + +std::string username, directory_filename_tmp; + +#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX +std::string tmp_dir = "/tmp/"; +#else +std::string tmp_dir = "C:/Temp/"; +#endif + +TEST_CASE("Benchmark JPEG image saving", "[benchmark]") { + vpIoTools::getUserName(username); + vpIoTools::makeDirectory(tmp_dir + username); + directory_filename_tmp = tmp_dir + username + "/vpIoTools_perfImageLoadSave_" + vpTime::getDateTime("%Y-%m-%d_%H.%M.%S"); + vpIoTools::makeDirectory(directory_filename_tmp); + REQUIRE(vpIoTools::checkDirectory(directory_filename_tmp)); + + SECTION("Grayscale") { + for (size_t i = 0; i < paths.size(); i++) { + vpImage I; + vpImageIo::read(I, paths[i] + ".png"); + + SECTION(names[i]) { + for (size_t j = 0; j < backends.size(); j++) { + BENCHMARK(backendNamesJpeg[j] + " backend") { + vpImageIo::write(I, directory_filename_tmp + "/ViSP_tmp_perf_write.jpg", backends[j]); + return I; + }; + } + } + } + } + + SECTION("vpRGBa") { + for (size_t i = 0; i < paths.size(); i++) { + vpImage I; + vpImageIo::read(I, paths[i] + ".png"); + + SECTION(names[i]) { + for (size_t j = 0; j < backends.size(); j++) { + BENCHMARK(backendNamesJpeg[j] + " backend") { + vpImageIo::write(I, directory_filename_tmp + "/ViSP_tmp_perf_write.jpg", backends[j]); + return I; + }; + } + } + } + } + + REQUIRE(vpIoTools::remove(directory_filename_tmp)); +} + +TEST_CASE("Benchmark PNG image saving", "[benchmark]") { + vpIoTools::getUserName(username); + vpIoTools::makeDirectory(tmp_dir + username); + directory_filename_tmp = tmp_dir + username + "/vpIoTools_perfImageLoadSave_" + vpTime::getDateTime("%Y-%m-%d_%H.%M.%S"); + vpIoTools::makeDirectory(directory_filename_tmp); + REQUIRE(vpIoTools::checkDirectory(directory_filename_tmp)); + + SECTION("Grayscale") { + for (size_t i = 0; i < paths.size(); i++) { + vpImage I; + vpImageIo::read(I, paths[i] + ".png"); + + SECTION(names[i]) { + for (size_t j = 0; j < backends.size(); j++) { + BENCHMARK(backendNamesPng[j] + " backend") { + vpImageIo::write(I, directory_filename_tmp + "/ViSP_tmp_perf_write.png", backends[j]); + return I; + }; + } + } + } + } + + SECTION("vpRGBa") { + for (size_t i = 0; i < paths.size(); i++) { + vpImage I; + vpImageIo::read(I, paths[i] + ".png"); + + SECTION(names[i]) { + for (size_t j = 0; j < backends.size(); j++) { + BENCHMARK(backendNamesPng[j] + " backend") { + vpImageIo::write(I, directory_filename_tmp + "/ViSP_tmp_perf_write.png", backends[j]); + return I; + }; + } + } + } + } + + REQUIRE(vpIoTools::remove(directory_filename_tmp)); +} + +int main(int argc, char *argv[]) +{ + Catch::Session session; // There must be exactly one instance + + bool runBenchmark = false; + // Build a new parser on top of Catch's + using namespace Catch::clara; + auto cli = session.cli() // Get Catch's composite command line parser + | Opt(runBenchmark) // bind variable to a new option, with a hint string + ["--benchmark"] // the option names it will respond to + ("run benchmark?") // description string for the help output + ("Path to gray image") + | Opt(nThreads, "nThreads") + ["--nThreads"] + ("Number of threads"); + + // Now pass the new composite back to Catch so it uses that + session.cli(cli); + + // Let Catch (using Clara) parse the command line + session.applyCommandLine(argc, argv); + + if (runBenchmark) { + std::cout << "nThreads: " << nThreads << " / available threads: " << std::thread::hardware_concurrency() << std::endl; + + int numFailed = session.run(); + + // numFailed is clamped to 255 as some unices only use the lower 8 bits. + // This clamping has already been applied, so just return it here + // You can also do any post run clean-up here + return numFailed; + } + + return EXIT_SUCCESS; +} +#else +#include + +int main() +{ + return 0; +} +#endif diff --git a/script/PerfVisualize.py b/script/PerfVisualize.py new file mode 100644 index 0000000000..f278ba62e6 --- /dev/null +++ b/script/PerfVisualize.py @@ -0,0 +1,153 @@ +from __future__ import print_function +from __future__ import division + +import argparse +from enum import Enum +import xml.etree.ElementTree as ET +from collections import OrderedDict + +class BenchmarkResult: + """BenchmarkResult class to hold perf numbers""" + + def __init__(self, name, mean_value, mean_lower_bound, mean_upper_bound, \ + std_val, std_lower_bound, std_upper_bound): + self.name = name + self.mean_value = mean_value + self.mean_lower_bound = mean_lower_bound + self.mean_upper_bound = mean_upper_bound + self.std_val = std_val + self.std_lower_bound = std_lower_bound + self.std_upper_bound = std_upper_bound + + def __str__(self): + return "BenchmarkResults %s\nmean value=%f, lowerBound=%f, upperBound=%f\nstd:%f, lowerBound=%f, upperBound=%f" \ + % (self.name, self.mean_value, self.mean_lower_bound, self.mean_upper_bound, \ + self.std_val, self.std_lower_bound, self.std_upper_bound) + +class TestCase: + """TestCase class to hold the list of benchmark results""" + + def __init__(self, name): + self.name = name + self.results = OrderedDict() + + def __str__(self): + return "TestCase %s\nBenchmark result:\n%s" % (self.name, self.results) + +class Metric(Enum): + mean = 'mean' + lowMean = 'low_mean' + highMean = 'high_mean' + +class Unit(Enum): + nano = 'nano' + micro = 'micro' + milli = 'milli' + sec = 'sec' + +def displayUnit(unit): + if unit == Unit.sec: + return "s" + elif unit == Unit.milli: + return "ms" + elif unit == Unit.micro: + return "us" + else: + return "ns" + +def nanoToMicro(nano): + return nano / 1000 + +def nanoToMilli(nano): + # return nano / (1000*1000) + return nano + +def nanoToSec(nano): + return nano / (1000*1000*1000) + +def convertUnit(nano, unit): + if unit == Unit.sec: + return nanoToSec(nano) + elif unit == Unit.milli: + return nanoToMilli(nano) + elif unit == Unit.micro: + return nanoToMicro(nano) + else: + return nano + +parser = argparse.ArgumentParser() +parser.add_argument("--xml-file", help="Path to XML perf log.", required=True) +parser.add_argument("--label", help="Label for before column.", default='Before') +parser.add_argument("--metric", help="Benchmark metric (mean, low_mean, high_mean).", type=Metric, choices=list(Metric), default=Metric.mean) +parser.add_argument("--unit", help="Benchmark unit (nano, micro, milli, sec).", type=Unit, choices=list(Unit), default=Unit.micro) + +args = parser.parse_args() + +xml_file = args.xml_file +unit = args.unit +print('Path to XML log file for perf comparison:', xml_file) +print('Time unit:', unit) +print() + +tree_perf_data = ET.parse(xml_file) + +root_perf_data = tree_perf_data.getroot() + +def loadResults(root): + results = OrderedDict() + + for test_case in root.iter('TestCase'): + test_name = test_case.attrib['name'] + + current_test = TestCase(test_name) + + for bench_res in test_case.iter('BenchmarkResults'): + bench_name = bench_res.attrib['name'] + + mean_node = bench_res.find('mean') + mean = float(mean_node.attrib['value']) + mean_lower = float(mean_node.attrib['lowerBound']) + mean_upper = float(mean_node.attrib['upperBound']) + + std_node = bench_res.find('standardDeviation') + std = float(std_node.attrib['value']) + std_lower = float(std_node.attrib['lowerBound']) + std_upper = float(std_node.attrib['upperBound']) + + current_test.results[bench_name] = BenchmarkResult(bench_name, mean, mean_lower, mean_upper, std, std_lower, std_upper) + + results[test_name] = current_test + + return results + +results_perf_data = loadResults(root_perf_data) + +print("results_perf_data:\n", results_perf_data) + +# for r_before_name, r_before in results_before.items(): +# title = '{} - time unit: {}'.format(r_before_name, displayUnit(unit)) +# print('| {} | {} | {} | Speed-up |'.format(title, args.before_label, args.after_label)) +# print('| --- | --- | --- | --- |') +# if r_before_name in results_after: +# r_after = results_after[r_before_name] + +# for b_before_name, b_before in r_before.results.items(): +# if b_before_name in r_after.results: +# b_after = r_after.results[b_before_name] + +# if args.metric == Metric.lowMean: +# metric_before = b_before.mean_lower_bound +# metric_after = b_after.mean_lower_bound +# elif args.metric == Metric.highMean: +# metric_before = b_before.mean_upper_bound +# metric_after = b_after.mean_upper_bound +# else: +# metric_before = b_before.mean_value +# metric_after = b_after.mean_value + +# metric_before = convertUnit(metric_before, unit) +# metric_after = convertUnit(metric_after, unit) + +# speed_up = metric_before / metric_after +# print('| {} | {:.2f} | {:.2f} | {:.2f} |'.format(b_before_name, metric_before, metric_after, speed_up)) +# print() From 440892bc012e0c5d9288297a474ba84c8d806cfc Mon Sep 17 00:00:00 2001 From: Souriya Trinh Date: Tue, 25 Jan 2022 00:18:13 +0100 Subject: [PATCH 02/12] Add possibility to choose image I/O backend for JPEG and PNG image reading/writing. --- CMakeLists.txt | 12 +-- cmake/VISP3rdParty.cmake | 11 ++- modules/io/CMakeLists.txt | 7 +- modules/io/include/visp3/io/vpImageIo.h | 27 +++--- .../io/src/image/private/vpImageIoLibjpeg.cpp | 17 ---- .../io/src/image/private/vpImageIoLibpng.cpp | 15 ---- .../io/src/image/private/vpImageIoOpenCV.cpp | 8 -- .../src/image/private/vpImageIoPortable.cpp | 15 ---- .../io/src/image/private/vpImageIoSimd.cpp | 8 -- modules/io/src/image/private/vpImageIoStb.cpp | 9 -- modules/io/src/image/vpImageIo.cpp | 87 +++++++++---------- 11 files changed, 63 insertions(+), 153 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e62852eef..b5769480ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -694,17 +694,7 @@ VP_OPTION(WITH_CLIPPER "" "" "Build clipper as built-in library" VP_OPTION(WITH_LAPACK "" "" "Build lapack as built-in library" "" ON IF NOT USE_LAPACK) VP_OPTION(WITH_QBDEVICE "" "" "Build qbdevice-api as built-in library" "" ON IF (VISP_CXX_STANDARD GREATER VISP_CXX_STANDARD_98) AND (NOT WINRT) AND (NOT IOS)) VP_OPTION(WITH_TAKKTILE2 "" "" "Build Right Hand takktile2 driver as built-in library" "" ON IF (VISP_CXX_STANDARD GREATER VISP_CXX_STANDARD_98) AND (NOT WIN32) AND (NOT WINRT) AND (NOT IOS) AND (NOT ANDROID)) - -# Use stb_image as built-in library only when OpenCV, libjpeg and libpng not available -if(NOT USE_OPENCV AND (NOT USE_PNG OR NOT USE_JPEG)) - set(WITH_STBIMAGE ON) -else() - set(WITH_STBIMAGE OFF) -endif() -# TODO: -set(WITH_STBIMAGE ON) - -VP_OPTION(WITH_CATCH2 "" "" "Use catch2" "" ON IF (VISP_CXX_STANDARD GREATER VISP_CXX_STANDARD_98)) +VP_OPTION(WITH_CATCH2 "" "" "Use catch2" "" ON IF (VISP_CXX_STANDARD GREATER VISP_CXX_STANDARD_98)) # ---------------------------------------------------------------------------- # Check for specific functions. Should be after cxx standard detection in VISPDetectCXXStandard.cmake and potential modification depending on pcl, realsense2, libfranka diff --git a/cmake/VISP3rdParty.cmake b/cmake/VISP3rdParty.cmake index f774959d76..ae4a93f1d0 100644 --- a/cmake/VISP3rdParty.cmake +++ b/cmake/VISP3rdParty.cmake @@ -70,12 +70,11 @@ add_subdirectory("${VISP_SOURCE_DIR}/3rdparty/simdlib") set(SIMDLIB_INCLUDE_DIRS "${VISP_SOURCE_DIR}/3rdparty/simdlib") set(SIMDLIB_LIBRARIES ${SIMD_LIBRARY}) -if(WITH_STBIMAGE) - set(STBIMAGE_LIBRARY visp_stbimage) - add_subdirectory("${VISP_SOURCE_DIR}/3rdparty/stb_image") - set(STBIMAGE_INCLUDE_DIRS "${VISP_SOURCE_DIR}/3rdparty/stb_image") - set(STBIMAGE_VERSION ${STBIMAGE_MAJOR_VERSION}.${STBIMAGE_MINOR_VERSION}.${STBIMAGE_PATCH_VERSION}) -endif() +# stb is always enabled +set(STBIMAGE_LIBRARY visp_stbimage) +add_subdirectory("${VISP_SOURCE_DIR}/3rdparty/stb_image") +set(STBIMAGE_INCLUDE_DIRS "${VISP_SOURCE_DIR}/3rdparty/stb_image") +set(STBIMAGE_VERSION ${STBIMAGE_MAJOR_VERSION}.${STBIMAGE_MINOR_VERSION}.${STBIMAGE_PATCH_VERSION}) if(WITH_CATCH2) set(CATCH2_LIBRARY visp_catch2) diff --git a/modules/io/CMakeLists.txt b/modules/io/CMakeLists.txt index 944b426666..298327ac08 100644 --- a/modules/io/CMakeLists.txt +++ b/modules/io/CMakeLists.txt @@ -57,11 +57,8 @@ if(USE_PNG) add_definitions(${PNG_DEFINITIONS}) endif() -# TODO: -#if(WITH_STBIMAGE) - # stb_image is private - include_directories(${STBIMAGE_INCLUDE_DIRS}) -#endif() +# stb_image is private +include_directories(${STBIMAGE_INCLUDE_DIRS}) if(WITH_CATCH2) # catch2 is private diff --git a/modules/io/include/visp3/io/vpImageIo.h b/modules/io/include/visp3/io/vpImageIo.h index 7edbb765e7..0dea6a558b 100644 --- a/modules/io/include/visp3/io/vpImageIo.h +++ b/modules/io/include/visp3/io/vpImageIo.h @@ -124,21 +124,20 @@ class VISP_EXPORT vpImageIo static std::string getExtension(const std::string &filename); public: - //TODO: // Image IO backend for only jpeg and png formats enum vpImageIoBackendType { IO_DEFAULT_BACKEND, - IO_LIB_BACKEND, + IO_SYSTEM_LIB_BACKEND, IO_OPENCV_BACKEND, IO_SIMDLIB_BACKEND, IO_STB_IMAGE_BACKEND }; - static void read(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); - static void read(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); + static void read(vpImage &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND); + static void read(vpImage &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND); - static void write(const vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); - static void write(const vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); + static void write(const vpImage &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND); + static void write(const vpImage &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND); static void readPFM(vpImage &I, const std::string &filename); @@ -148,11 +147,11 @@ class VISP_EXPORT vpImageIo static void readPPM(vpImage &I, const std::string &filename); static void readPPM(vpImage &I, const std::string &filename); - static void readJPEG(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); - static void readJPEG(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); + static void readJPEG(vpImage &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND); + static void readJPEG(vpImage &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND); - static void readPNG(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); - static void readPNG(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); + static void readPNG(vpImage &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND); + static void readPNG(vpImage &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND); static void writePFM(const vpImage &I, const std::string &filename); @@ -163,10 +162,10 @@ class VISP_EXPORT vpImageIo static void writePPM(const vpImage &I, const std::string &filename); static void writePPM(const vpImage &I, const std::string &filename); - static void writeJPEG(const vpImage &I, const std::string &filename, int quality=90, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); - static void writeJPEG(const vpImage &I, const std::string &filename, int quality=90, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); + static void writeJPEG(const vpImage &I, const std::string &filename, int quality=90, int backend=IO_DEFAULT_BACKEND); + static void writeJPEG(const vpImage &I, const std::string &filename, int quality=90, int backend=IO_DEFAULT_BACKEND); - static void writePNG(const vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); - static void writePNG(const vpImage &I, const std::string &filename, const vpImageIoBackendType& backend=IO_DEFAULT_BACKEND); + static void writePNG(const vpImage &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND); + static void writePNG(const vpImage &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND); }; #endif diff --git a/modules/io/src/image/private/vpImageIoLibjpeg.cpp b/modules/io/src/image/private/vpImageIoLibjpeg.cpp index c8f51e47fa..559c07f2ec 100644 --- a/modules/io/src/image/private/vpImageIoLibjpeg.cpp +++ b/modules/io/src/image/private/vpImageIoLibjpeg.cpp @@ -29,14 +29,7 @@ * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Description: -<<<<<<< HEAD - * Read/write images. - * - * Authors: - * Eric Marchand -======= * Libjpeg backend for JPEG image I/O operations. ->>>>>>> 557f1beda01f36ca886ec039d0a1a80a7446ca59 * *****************************************************************************/ @@ -48,14 +41,6 @@ #include "vpImageIoBackend.h" #include -//TODO: is it needed? -//#if defined(_WIN32) -//// Include WinSock2.h before windows.h to ensure that winsock.h is not -//// included by windows.h since winsock.h and winsock2.h are incompatible -//#include -//#include -//#endif - #if defined(VISP_HAVE_JPEG) #include #include @@ -106,7 +91,6 @@ void writeJPEGLibjpeg(const vpImage &I, const std::string &filena cinfo.input_components = 1; cinfo.in_color_space = JCS_GRAYSCALE; jpeg_set_defaults(&cinfo); - //TODO: jpeg_set_quality(&cinfo, quality, TRUE); jpeg_start_compress(&cinfo, TRUE); @@ -166,7 +150,6 @@ void writeJPEGLibjpeg(const vpImage &I, const std::string &filename, int cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); - //TODO: jpeg_set_quality(&cinfo, quality, TRUE); jpeg_start_compress(&cinfo, TRUE); diff --git a/modules/io/src/image/private/vpImageIoLibpng.cpp b/modules/io/src/image/private/vpImageIoLibpng.cpp index 8d0ff0d3d5..1c69d86431 100644 --- a/modules/io/src/image/private/vpImageIoLibpng.cpp +++ b/modules/io/src/image/private/vpImageIoLibpng.cpp @@ -29,14 +29,7 @@ * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Description: -<<<<<<< HEAD - * Read/write images. - * - * Authors: - * Eric Marchand -======= * Libpng backend for PNG image I/O operations. ->>>>>>> 557f1beda01f36ca886ec039d0a1a80a7446ca59 * *****************************************************************************/ @@ -48,14 +41,6 @@ #include "vpImageIoBackend.h" #include -//TODO: is it needed? -//#if defined(_WIN32) -//// Include WinSock2.h before windows.h to ensure that winsock.h is not -//// included by windows.h since winsock.h and winsock2.h are incompatible -//#include -//#include -//#endif - #if defined(VISP_HAVE_PNG) #include #endif diff --git a/modules/io/src/image/private/vpImageIoOpenCV.cpp b/modules/io/src/image/private/vpImageIoOpenCV.cpp index cc22229ede..52fbd89402 100644 --- a/modules/io/src/image/private/vpImageIoOpenCV.cpp +++ b/modules/io/src/image/private/vpImageIoOpenCV.cpp @@ -29,14 +29,7 @@ * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Description: -<<<<<<< HEAD - * Read/write images. - * - * Authors: - * Eric Marchand -======= * OpenCV backend for image I/O operations. ->>>>>>> 557f1beda01f36ca886ec039d0a1a80a7446ca59 * *****************************************************************************/ @@ -47,7 +40,6 @@ #include "vpImageIoBackend.h" -//TODO: #ifdef VISP_HAVE_OPENCV #if (VISP_HAVE_OPENCV_VERSION >= 0x030000) // Require opencv >= 3.0.0 # include diff --git a/modules/io/src/image/private/vpImageIoPortable.cpp b/modules/io/src/image/private/vpImageIoPortable.cpp index 8c6df6086b..80d465097c 100644 --- a/modules/io/src/image/private/vpImageIoPortable.cpp +++ b/modules/io/src/image/private/vpImageIoPortable.cpp @@ -29,14 +29,7 @@ * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Description: -<<<<<<< HEAD - * Read/write images. - * - * Authors: - * Eric Marchand -======= * Backend for portable image format I/O operations. ->>>>>>> 557f1beda01f36ca886ec039d0a1a80a7446ca59 * *****************************************************************************/ @@ -49,14 +42,6 @@ #include #include -//TODO: is it needed? -//#if defined(_WIN32) -//// Include WinSock2.h before windows.h to ensure that winsock.h is not -//// included by windows.h since winsock.h and winsock2.h are incompatible -//#include -//#include -//#endif - void vp_decodeHeaderPNM(const std::string &filename, std::ifstream &fd, const std::string &magic, unsigned int &w, unsigned int &h, unsigned int &maxval); diff --git a/modules/io/src/image/private/vpImageIoSimd.cpp b/modules/io/src/image/private/vpImageIoSimd.cpp index 9ed1f25d37..d872692001 100644 --- a/modules/io/src/image/private/vpImageIoSimd.cpp +++ b/modules/io/src/image/private/vpImageIoSimd.cpp @@ -29,14 +29,7 @@ * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Description: -<<<<<<< HEAD - * Read/write images. - * - * Authors: - * Eric Marchand -======= * Simd backend for JPEG and PNG image I/O operations. ->>>>>>> 557f1beda01f36ca886ec039d0a1a80a7446ca59 * *****************************************************************************/ @@ -49,7 +42,6 @@ #include -//TODO: void readSimdlib(vpImage &I, const std::string &filename) { size_t stride = 0, width = 0, height = 0; diff --git a/modules/io/src/image/private/vpImageIoStb.cpp b/modules/io/src/image/private/vpImageIoStb.cpp index dfe7945717..9a86544554 100644 --- a/modules/io/src/image/private/vpImageIoStb.cpp +++ b/modules/io/src/image/private/vpImageIoStb.cpp @@ -29,14 +29,7 @@ * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Description: -<<<<<<< HEAD - * Read/write images. - * - * Authors: - * Eric Marchand -======= * stb backend for JPEG and PNG image I/O operations. ->>>>>>> 557f1beda01f36ca886ec039d0a1a80a7446ca59 * *****************************************************************************/ @@ -47,7 +40,6 @@ #include "vpImageIoBackend.h" -//TODO: #if defined __SSE2__ || defined _M_X64 || (defined _M_IX86_FP && _M_IX86_FP >= 2) # define VISP_HAVE_SSE2 1 #endif @@ -63,7 +55,6 @@ #include -//TODO: void readStb(vpImage &I, const std::string &filename) { int width = 0, height = 0, channels = 0; diff --git a/modules/io/src/image/vpImageIo.cpp b/modules/io/src/image/vpImageIo.cpp index fe09d9e23d..d0df9a39de 100644 --- a/modules/io/src/image/vpImageIo.cpp +++ b/modules/io/src/image/vpImageIo.cpp @@ -44,15 +44,8 @@ #include #include -//TODO: #include "private/vpImageIoBackend.h" -//TODO: -// priority order for backend selection is: -// - libjpeg / libpng if available -// - OpenCV if available -// - stb backend for image reading / Simd backend for image writing -// - Simd backend for image reading / stb backend for image writing vpImageIo::vpImageFormatType vpImageIo::getFormat(const std::string &filename) { @@ -143,8 +136,9 @@ std::string vpImageIo::getExtension(const std::string &filename) \param I : Image to set with the \e filename content. \param filename : Name of the file containing the image. + \param backend : Library backend type (see vpImageIoBackendType) for image reading (only for JPEG and PNG). */ -void vpImageIo::read(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) +void vpImageIo::read(vpImage &I, const std::string &filename, int backend) { bool exist = vpIoTools::checkFilename(filename); if (!exist) { @@ -210,8 +204,9 @@ void vpImageIo::read(vpImage &I, const std::string &filename, con \param I : Image to set with the \e filename content. \param filename : Name of the file containing the image. + \param backend : Library backend type (see vpImageIoBackendType) for image reading (only for JPEG and PNG). */ -void vpImageIo::read(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) +void vpImageIo::read(vpImage &I, const std::string &filename, int backend) { bool exist = vpIoTools::checkFilename(filename); if (!exist) { @@ -270,8 +265,9 @@ void vpImageIo::read(vpImage &I, const std::string &filename, const vpIm \param I : Image to write. \param filename : Name of the file containing the image. + \param backend : Library backend type (see vpImageIoBackendType) for image writing (only for JPEG and PNG). */ -void vpImageIo::write(const vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) +void vpImageIo::write(const vpImage &I, const std::string &filename, int backend) { bool try_opencv_writer = false; @@ -322,8 +318,9 @@ void vpImageIo::write(const vpImage &I, const std::string &filena \param I : Image to write. \param filename : Name of the file containing the image. + \param backend : Library backend type (see vpImageIoBackendType) for image writing (only for JPEG and PNG). */ -void vpImageIo::write(const vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) +void vpImageIo::write(const vpImage &I, const std::string &filename, int backend) { bool try_opencv_writer = false; @@ -361,9 +358,9 @@ void vpImageIo::write(const vpImage &I, const std::string &filename, con } } -void vpImageIo::readJPEG(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) +void vpImageIo::readJPEG(vpImage &I, const std::string &filename, int backend) { - if (backend == IO_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + if (backend == IO_SYSTEM_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { #if defined(VISP_HAVE_JPEG) readJPEGLibjpeg(I, filename); #else @@ -384,9 +381,9 @@ void vpImageIo::readJPEG(vpImage &I, const std::string &filename, } } -void vpImageIo::readJPEG(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) +void vpImageIo::readJPEG(vpImage &I, const std::string &filename, int backend) { - if (backend == IO_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + if (backend == IO_SYSTEM_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { #if defined(VISP_HAVE_JPEG) readJPEGLibjpeg(I, filename); #else @@ -407,9 +404,9 @@ void vpImageIo::readJPEG(vpImage &I, const std::string &filename, const } } -void vpImageIo::readPNG(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) +void vpImageIo::readPNG(vpImage &I, const std::string &filename, int backend) { - if (backend == IO_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + if (backend == IO_SYSTEM_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { #if defined(VISP_HAVE_PNG) readPNGLibpng(I, filename); #else @@ -430,9 +427,9 @@ void vpImageIo::readPNG(vpImage &I, const std::string &filename, } } -void vpImageIo::readPNG(vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) +void vpImageIo::readPNG(vpImage &I, const std::string &filename, int backend) { - if (backend == IO_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + if (backend == IO_SYSTEM_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { #if defined(VISP_HAVE_PNG) readPNGLibpng(I, filename); #else @@ -453,9 +450,9 @@ void vpImageIo::readPNG(vpImage &I, const std::string &filename, const v } } -void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename, int quality, const vpImageIoBackendType& backend) +void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename, int quality, int backend) { - if (backend == IO_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + if (backend == IO_SYSTEM_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { #if defined(VISP_HAVE_JPEG) writeJPEGLibjpeg(I, filename, quality); #else @@ -476,9 +473,9 @@ void vpImageIo::writeJPEG(const vpImage &I, const std::string &fi } } -void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename, int quality, const vpImageIoBackendType& backend) +void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename, int quality, int backend) { - if (backend == IO_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + if (backend == IO_SYSTEM_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { #if defined(VISP_HAVE_JPEG) writeJPEGLibjpeg(I, filename, quality); #else @@ -499,49 +496,49 @@ void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename, } } -void vpImageIo::writePNG(const vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) +void vpImageIo::writePNG(const vpImage &I, const std::string &filename, int backend) { - if (backend == IO_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { -#if defined(VISP_HAVE_PNG) - writePNGLibpng(I, filename); -#else - std::string message = "Cannot write file \"" + filename + "\": Libpng backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); -#endif - } else if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { + if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { #if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 - writeOpenCV(I, filename, 90); + writeOpenCV(I, filename, 90); #else - std::string message = "Cannot write file \"" + filename + "\": OpenCV backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); + std::string message = "Cannot write file \"" + filename + "\": OpenCV backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); #endif } else if (backend == IO_SIMDLIB_BACKEND || backend == IO_DEFAULT_BACKEND) { writePNGSimdlib(I, filename); } else if (backend == IO_STB_IMAGE_BACKEND) { writePNGStb(I, filename); - } -} - -void vpImageIo::writePNG(const vpImage &I, const std::string &filename, const vpImageIoBackendType& backend) -{ - if (backend == IO_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + } else if (backend == IO_SYSTEM_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { #if defined(VISP_HAVE_PNG) writePNGLibpng(I, filename); #else std::string message = "Cannot write file \"" + filename + "\": Libpng backend is not available"; throw(vpImageException(vpImageException::ioError, message)); #endif - } else if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { + } +} + +void vpImageIo::writePNG(const vpImage &I, const std::string &filename, int backend) +{ + if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { #if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 - writeOpenCV(I, filename, 90); + writeOpenCV(I, filename, 90); #else - std::string message = "Cannot write file \"" + filename + "\": OpenCV backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); + std::string message = "Cannot write file \"" + filename + "\": OpenCV backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); #endif } else if (backend == IO_SIMDLIB_BACKEND || backend == IO_DEFAULT_BACKEND) { writePNGSimdlib(I, filename); } else if (backend == IO_STB_IMAGE_BACKEND) { writePNGStb(I, filename); + } else if (backend == IO_SYSTEM_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_PNG) + writePNGLibpng(I, filename); +#else + std::string message = "Cannot write file \"" + filename + "\": Libpng backend is not available"; + throw(vpImageException(vpImageException::ioError, message)); +#endif } } From 03f61c751418e396677067eabbb33105176c9ec1 Mon Sep 17 00:00:00 2001 From: Souriya Trinh Date: Tue, 25 Jan 2022 00:21:22 +0100 Subject: [PATCH 03/12] Update perfImageLoadSave.cpp and PerfVisualize.py script to be able to plot image I/O performance numbers. --- .../io/src/image/private/vpImageIoBackend.h | 7 - modules/io/test/perfImageLoadSave.cpp | 202 +++++++++--------- script/PerfVisualize.py | 118 ++++++---- 3 files changed, 177 insertions(+), 150 deletions(-) diff --git a/modules/io/src/image/private/vpImageIoBackend.h b/modules/io/src/image/private/vpImageIoBackend.h index ff7aa80850..75a33d1793 100644 --- a/modules/io/src/image/private/vpImageIoBackend.h +++ b/modules/io/src/image/private/vpImageIoBackend.h @@ -29,14 +29,7 @@ * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Description: -<<<<<<< HEAD - * Read/write images. - * - * Authors: - * Eric Marchand -======= * Backend functions implementation for image I/O operations. ->>>>>>> 557f1beda01f36ca886ec039d0a1a80a7446ca59 * *****************************************************************************/ diff --git a/modules/io/test/perfImageLoadSave.cpp b/modules/io/test/perfImageLoadSave.cpp index b4a1bd97dd..a283cae064 100644 --- a/modules/io/test/perfImageLoadSave.cpp +++ b/modules/io/test/perfImageLoadSave.cpp @@ -56,7 +56,7 @@ static std::vector names { }; static std::vector backends { #if defined(VISP_HAVE_JPEG) && defined(VISP_HAVE_PNG) - vpImageIo::IO_LIB_BACKEND, + vpImageIo::IO_SYSTEM_LIB_BACKEND, #endif #if defined(VISP_HAVE_OPENCV) vpImageIo::IO_OPENCV_BACKEND, @@ -72,65 +72,61 @@ static std::vector backendNamesPng { }; static int nThreads = 0; -TEST_CASE("Benchmark JPEG image loading", "[benchmark]") { - SECTION("Grayscale") { - for (size_t i = 0; i < paths.size(); i++) { - SECTION(names[i]) { - for (size_t j = 0; j < backends.size(); j++) { - vpImage I; - - BENCHMARK(backendNamesJpeg[j] + " backend") { - vpImageIo::read(I, paths[i] + ".jpg", backends[j]); - return I; - }; - } +TEST_CASE("Benchmark grayscale JPEG image loading", "[benchmark]") { + for (size_t i = 0; i < paths.size(); i++) { + SECTION(names[i]) { + for (size_t j = 0; j < backends.size(); j++) { + vpImage I; + + BENCHMARK(backendNamesJpeg[j] + " backend") { + vpImageIo::read(I, paths[i] + ".jpg", backends[j]); + return I; + }; } } } +} - SECTION("vpRGBa") { - for (size_t i = 0; i < paths.size(); i++) { - SECTION(names[i]) { - for (size_t j = 0; j < backends.size(); j++) { - vpImage I; - - BENCHMARK(backendNamesJpeg[j] + " backend") { - vpImageIo::read(I, paths[i] + ".jpg", backends[j]); - return I; - }; - } +TEST_CASE("Benchmark RGBA JPEG image loading", "[benchmark]") { + for (size_t i = 0; i < paths.size(); i++) { + SECTION(names[i]) { + for (size_t j = 0; j < backends.size(); j++) { + vpImage I; + + BENCHMARK(backendNamesJpeg[j] + " backend") { + vpImageIo::read(I, paths[i] + ".jpg", backends[j]); + return I; + }; } } } } -TEST_CASE("Benchmark PNG image loading", "[benchmark]") { - SECTION("Grayscale") { - for (size_t i = 0; i < paths.size(); i++) { - SECTION(names[i]) { - for (size_t j = 0; j < backends.size(); j++) { - vpImage I; - - BENCHMARK(backendNamesPng[j] + " backend") { - vpImageIo::read(I, paths[i] + ".png", backends[j]); - return I; - }; - } +TEST_CASE("Benchmark grayscale PNG image loading", "[benchmark]") { + for (size_t i = 0; i < paths.size(); i++) { + SECTION(names[i]) { + for (size_t j = 0; j < backends.size(); j++) { + vpImage I; + + BENCHMARK(backendNamesPng[j] + " backend") { + vpImageIo::read(I, paths[i] + ".png", backends[j]); + return I; + }; } } } +} + +TEST_CASE("Benchmark RGBA PNG image loading", "[benchmark]") { + for (size_t i = 0; i < paths.size(); i++) { + SECTION(names[i]) { + for (size_t j = 0; j < backends.size(); j++) { + vpImage I; - SECTION("vpRGBa") { - for (size_t i = 0; i < paths.size(); i++) { - SECTION(names[i]) { - for (size_t j = 0; j < backends.size(); j++) { - vpImage I; - - BENCHMARK(backendNamesPng[j] + " backend") { - vpImageIo::read(I, paths[i] + ".png", backends[j]); - return I; - }; - } + BENCHMARK(backendNamesPng[j] + " backend") { + vpImageIo::read(I, paths[i] + ".png", backends[j]); + return I; + }; } } } @@ -144,41 +140,47 @@ std::string tmp_dir = "/tmp/"; std::string tmp_dir = "C:/Temp/"; #endif -TEST_CASE("Benchmark JPEG image saving", "[benchmark]") { +TEST_CASE("Benchmark grayscale JPEG image saving", "[benchmark]") { vpIoTools::getUserName(username); vpIoTools::makeDirectory(tmp_dir + username); directory_filename_tmp = tmp_dir + username + "/vpIoTools_perfImageLoadSave_" + vpTime::getDateTime("%Y-%m-%d_%H.%M.%S"); vpIoTools::makeDirectory(directory_filename_tmp); REQUIRE(vpIoTools::checkDirectory(directory_filename_tmp)); - SECTION("Grayscale") { - for (size_t i = 0; i < paths.size(); i++) { - vpImage I; - vpImageIo::read(I, paths[i] + ".png"); - - SECTION(names[i]) { - for (size_t j = 0; j < backends.size(); j++) { - BENCHMARK(backendNamesJpeg[j] + " backend") { - vpImageIo::write(I, directory_filename_tmp + "/ViSP_tmp_perf_write.jpg", backends[j]); - return I; - }; - } + for (size_t i = 0; i < paths.size(); i++) { + vpImage I; + vpImageIo::read(I, paths[i] + ".png"); + + SECTION(names[i]) { + for (size_t j = 0; j < backends.size(); j++) { + BENCHMARK(backendNamesJpeg[j] + " backend") { + vpImageIo::write(I, directory_filename_tmp + "/ViSP_tmp_perf_write.jpg", backends[j]); + return I; + }; } } } - SECTION("vpRGBa") { - for (size_t i = 0; i < paths.size(); i++) { - vpImage I; - vpImageIo::read(I, paths[i] + ".png"); - - SECTION(names[i]) { - for (size_t j = 0; j < backends.size(); j++) { - BENCHMARK(backendNamesJpeg[j] + " backend") { - vpImageIo::write(I, directory_filename_tmp + "/ViSP_tmp_perf_write.jpg", backends[j]); - return I; - }; - } + REQUIRE(vpIoTools::remove(directory_filename_tmp)); +} + +TEST_CASE("Benchmark RGBA JPEG image saving", "[benchmark]") { + vpIoTools::getUserName(username); + vpIoTools::makeDirectory(tmp_dir + username); + directory_filename_tmp = tmp_dir + username + "/vpIoTools_perfImageLoadSave_" + vpTime::getDateTime("%Y-%m-%d_%H.%M.%S"); + vpIoTools::makeDirectory(directory_filename_tmp); + REQUIRE(vpIoTools::checkDirectory(directory_filename_tmp)); + + for (size_t i = 0; i < paths.size(); i++) { + vpImage I; + vpImageIo::read(I, paths[i] + ".png"); + + SECTION(names[i]) { + for (size_t j = 0; j < backends.size(); j++) { + BENCHMARK(backendNamesJpeg[j] + " backend") { + vpImageIo::write(I, directory_filename_tmp + "/ViSP_tmp_perf_write.jpg", backends[j]); + return I; + }; } } } @@ -186,41 +188,47 @@ TEST_CASE("Benchmark JPEG image saving", "[benchmark]") { REQUIRE(vpIoTools::remove(directory_filename_tmp)); } -TEST_CASE("Benchmark PNG image saving", "[benchmark]") { +TEST_CASE("Benchmark grayscale PNG image saving", "[benchmark]") { vpIoTools::getUserName(username); vpIoTools::makeDirectory(tmp_dir + username); directory_filename_tmp = tmp_dir + username + "/vpIoTools_perfImageLoadSave_" + vpTime::getDateTime("%Y-%m-%d_%H.%M.%S"); vpIoTools::makeDirectory(directory_filename_tmp); REQUIRE(vpIoTools::checkDirectory(directory_filename_tmp)); - SECTION("Grayscale") { - for (size_t i = 0; i < paths.size(); i++) { - vpImage I; - vpImageIo::read(I, paths[i] + ".png"); - - SECTION(names[i]) { - for (size_t j = 0; j < backends.size(); j++) { - BENCHMARK(backendNamesPng[j] + " backend") { - vpImageIo::write(I, directory_filename_tmp + "/ViSP_tmp_perf_write.png", backends[j]); - return I; - }; - } + for (size_t i = 0; i < paths.size(); i++) { + vpImage I; + vpImageIo::read(I, paths[i] + ".png"); + + SECTION(names[i]) { + for (size_t j = 0; j < backends.size(); j++) { + BENCHMARK(backendNamesPng[j] + " backend") { + vpImageIo::write(I, directory_filename_tmp + "/ViSP_tmp_perf_write.png", backends[j]); + return I; + }; } } } - SECTION("vpRGBa") { - for (size_t i = 0; i < paths.size(); i++) { - vpImage I; - vpImageIo::read(I, paths[i] + ".png"); - - SECTION(names[i]) { - for (size_t j = 0; j < backends.size(); j++) { - BENCHMARK(backendNamesPng[j] + " backend") { - vpImageIo::write(I, directory_filename_tmp + "/ViSP_tmp_perf_write.png", backends[j]); - return I; - }; - } + REQUIRE(vpIoTools::remove(directory_filename_tmp)); +} + +TEST_CASE("Benchmark RGBA PNG image saving", "[benchmark]") { + vpIoTools::getUserName(username); + vpIoTools::makeDirectory(tmp_dir + username); + directory_filename_tmp = tmp_dir + username + "/vpIoTools_perfImageLoadSave_" + vpTime::getDateTime("%Y-%m-%d_%H.%M.%S"); + vpIoTools::makeDirectory(directory_filename_tmp); + REQUIRE(vpIoTools::checkDirectory(directory_filename_tmp)); + + for (size_t i = 0; i < paths.size(); i++) { + vpImage I; + vpImageIo::read(I, paths[i] + ".png"); + + SECTION(names[i]) { + for (size_t j = 0; j < backends.size(); j++) { + BENCHMARK(backendNamesPng[j] + " backend") { + vpImageIo::write(I, directory_filename_tmp + "/ViSP_tmp_perf_write.png", backends[j]); + return I; + }; } } } diff --git a/script/PerfVisualize.py b/script/PerfVisualize.py index f278ba62e6..804666be11 100644 --- a/script/PerfVisualize.py +++ b/script/PerfVisualize.py @@ -5,6 +5,8 @@ from enum import Enum import xml.etree.ElementTree as ET from collections import OrderedDict +import matplotlib +import matplotlib.pyplot as plt class BenchmarkResult: """BenchmarkResult class to hold perf numbers""" @@ -34,6 +36,16 @@ def __init__(self, name): def __str__(self): return "TestCase %s\nBenchmark result:\n%s" % (self.name, self.results) +class SectionCase: + """SectionCase class to hold the section list of benchmark results""" + + def __init__(self, name): + self.name = name + self.results = OrderedDict() + + def __str__(self): + return "SectionCase %s\nBenchmark result:\n%s" % (self.name, self.results) + class Metric(Enum): mean = 'mean' lowMean = 'low_mean' @@ -59,8 +71,8 @@ def nanoToMicro(nano): return nano / 1000 def nanoToMilli(nano): - # return nano / (1000*1000) - return nano + return nano / (1000*1000) + # return nano def nanoToSec(nano): return nano / (1000*1000*1000) @@ -79,14 +91,18 @@ def convertUnit(nano, unit): parser.add_argument("--xml-file", help="Path to XML perf log.", required=True) parser.add_argument("--label", help="Label for before column.", default='Before') parser.add_argument("--metric", help="Benchmark metric (mean, low_mean, high_mean).", type=Metric, choices=list(Metric), default=Metric.mean) -parser.add_argument("--unit", help="Benchmark unit (nano, micro, milli, sec).", type=Unit, choices=list(Unit), default=Unit.micro) +parser.add_argument("--unit", help="Benchmark unit (nano, micro, milli, sec).", type=Unit, choices=list(Unit), default=Unit.milli) args = parser.parse_args() xml_file = args.xml_file -unit = args.unit +metric = args.metric +time_unit = args.unit + +print("Matplotlib: {}".format(matplotlib.__version__)) print('Path to XML log file for perf comparison:', xml_file) -print('Time unit:', unit) +print('Perf metric:', metric) +print('Time unit:', time_unit) print() tree_perf_data = ET.parse(xml_file) @@ -98,23 +114,28 @@ def loadResults(root): for test_case in root.iter('TestCase'): test_name = test_case.attrib['name'] - current_test = TestCase(test_name) - for bench_res in test_case.iter('BenchmarkResults'): - bench_name = bench_res.attrib['name'] + for section_case in test_case.iter('Section'): + case_name = section_case.attrib['name'] + current_section = SectionCase(case_name) + + for bench_res in section_case.iter('BenchmarkResults'): + bench_name = bench_res.attrib['name'] + + mean_node = bench_res.find('mean') + mean = float(mean_node.attrib['value']) + mean_lower = float(mean_node.attrib['lowerBound']) + mean_upper = float(mean_node.attrib['upperBound']) - mean_node = bench_res.find('mean') - mean = float(mean_node.attrib['value']) - mean_lower = float(mean_node.attrib['lowerBound']) - mean_upper = float(mean_node.attrib['upperBound']) + std_node = bench_res.find('standardDeviation') + std = float(std_node.attrib['value']) + std_lower = float(std_node.attrib['lowerBound']) + std_upper = float(std_node.attrib['upperBound']) - std_node = bench_res.find('standardDeviation') - std = float(std_node.attrib['value']) - std_lower = float(std_node.attrib['lowerBound']) - std_upper = float(std_node.attrib['upperBound']) + current_section.results[bench_name] = BenchmarkResult(bench_name, mean, mean_lower, mean_upper, std, std_lower, std_upper) - current_test.results[bench_name] = BenchmarkResult(bench_name, mean, mean_lower, mean_upper, std, std_lower, std_upper) + current_test.results[case_name] = current_section results[test_name] = current_test @@ -122,32 +143,37 @@ def loadResults(root): results_perf_data = loadResults(root_perf_data) -print("results_perf_data:\n", results_perf_data) - -# for r_before_name, r_before in results_before.items(): -# title = '{} - time unit: {}'.format(r_before_name, displayUnit(unit)) -# print('| {} | {} | {} | Speed-up |'.format(title, args.before_label, args.after_label)) -# print('| --- | --- | --- | --- |') -# if r_before_name in results_after: -# r_after = results_after[r_before_name] - -# for b_before_name, b_before in r_before.results.items(): -# if b_before_name in r_after.results: -# b_after = r_after.results[b_before_name] - -# if args.metric == Metric.lowMean: -# metric_before = b_before.mean_lower_bound -# metric_after = b_after.mean_lower_bound -# elif args.metric == Metric.highMean: -# metric_before = b_before.mean_upper_bound -# metric_after = b_after.mean_upper_bound -# else: -# metric_before = b_before.mean_value -# metric_after = b_after.mean_value - -# metric_before = convertUnit(metric_before, unit) -# metric_after = convertUnit(metric_after, unit) - -# speed_up = metric_before / metric_after -# print('| {} | {:.2f} | {:.2f} | {:.2f} |'.format(b_before_name, metric_before, metric_after, speed_up)) -# print() +for r_name, r_test in results_perf_data.items(): + fig, axs = plt.subplots(ncols=len(r_test.results.items())) + idx1 = 0 + + for r_section_name, r_section in r_test.results.items(): + x_list = [] + y_list = [] + std_list = [] + idx2 = 0 + backend_list = [] + + for r_result_name, r_result in r_section.results.items(): + x_list.append(idx2) + if metric == Metric.lowMean: + y_list.append(convertUnit(r_result.mean_lower_bound, time_unit)) + elif metric == Metric.highMean: + y_list.append(convertUnit(r_result.mean_higher_bound, time_unit)) + else: + y_list.append(convertUnit(r_result.mean_value, time_unit)) + std_list.append(convertUnit(r_result.std_val, time_unit)) + backend_list.append(r_result_name.replace(" backend", "")) + idx2 += 1 + + axs[idx1].bar(x_list, y_list, yerr=std_list, align='center', capsize=10, tick_label=backend_list) + axs[idx1].grid(True) + axs[idx1].xaxis.set_tick_params(labelsize=14) + axs[idx1].set_xlabel(r_section_name, fontsize=16) + axs[0].set_ylabel("Computation time (ms)", fontsize=16) + + plt.setp(axs[idx1].get_xticklabels(), rotation=45) + idx1 += 1 + + plt.suptitle(r_name, fontsize=24) + plt.show() From a03743d48b0692660852d440c7ff0f38e6ebdb98 Mon Sep 17 00:00:00 2001 From: Fabien Spindler Date: Tue, 25 Jan 2022 08:55:55 +0100 Subject: [PATCH 04/12] Turn warning off due to stb_image 3rd party (fedora 35) --- modules/io/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/io/CMakeLists.txt b/modules/io/CMakeLists.txt index 298327ac08..deba785b32 100644 --- a/modules/io/CMakeLists.txt +++ b/modules/io/CMakeLists.txt @@ -189,3 +189,4 @@ vp_add_tests() vp_set_source_file_compile_flag(src/tools/vpParseArgv.cpp -Wno-strict-overflow) vp_set_source_file_compile_flag(src/image/vpImageIo.cpp -Wno-missing-field-initializers) # since stb_image_write.h update from v1.13 to v1.16 +vp_set_source_file_compile_flag(src/image/private/vpImageIoStb.cpp -Wno-missing-field-initializers) From 5ca38354e35e844dba029d99fd6d89da397e3fd5 Mon Sep 17 00:00:00 2001 From: Fabien Spindler Date: Tue, 25 Jan 2022 08:56:23 +0100 Subject: [PATCH 05/12] Update copyright date --- modules/io/src/image/private/vpImageIoBackend.h | 2 +- modules/io/src/image/private/vpImageIoLibjpeg.cpp | 2 +- modules/io/src/image/private/vpImageIoLibpng.cpp | 2 +- modules/io/src/image/private/vpImageIoOpenCV.cpp | 2 +- modules/io/src/image/private/vpImageIoPortable.cpp | 2 +- modules/io/src/image/private/vpImageIoSimd.cpp | 2 +- modules/io/src/image/private/vpImageIoStb.cpp | 2 +- modules/io/test/perfImageLoadSave.cpp | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/io/src/image/private/vpImageIoBackend.h b/modules/io/src/image/private/vpImageIoBackend.h index 75a33d1793..f655c25435 100644 --- a/modules/io/src/image/private/vpImageIoBackend.h +++ b/modules/io/src/image/private/vpImageIoBackend.h @@ -1,7 +1,7 @@ /**************************************************************************** * * ViSP, open source Visual Servoing Platform software. - * Copyright (C) 2005 - 2019 by Inria. All rights reserved. + * Copyright (C) 2005 - 2022 by Inria. All rights reserved. * * This software is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/modules/io/src/image/private/vpImageIoLibjpeg.cpp b/modules/io/src/image/private/vpImageIoLibjpeg.cpp index 559c07f2ec..0040b8f9c0 100644 --- a/modules/io/src/image/private/vpImageIoLibjpeg.cpp +++ b/modules/io/src/image/private/vpImageIoLibjpeg.cpp @@ -1,7 +1,7 @@ /**************************************************************************** * * ViSP, open source Visual Servoing Platform software. - * Copyright (C) 2005 - 2019 by Inria. All rights reserved. + * Copyright (C) 2005 - 2022 by Inria. All rights reserved. * * This software is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/modules/io/src/image/private/vpImageIoLibpng.cpp b/modules/io/src/image/private/vpImageIoLibpng.cpp index 1c69d86431..0ccc2ebfe3 100644 --- a/modules/io/src/image/private/vpImageIoLibpng.cpp +++ b/modules/io/src/image/private/vpImageIoLibpng.cpp @@ -1,7 +1,7 @@ /**************************************************************************** * * ViSP, open source Visual Servoing Platform software. - * Copyright (C) 2005 - 2019 by Inria. All rights reserved. + * Copyright (C) 2005 - 2022 by Inria. All rights reserved. * * This software is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/modules/io/src/image/private/vpImageIoOpenCV.cpp b/modules/io/src/image/private/vpImageIoOpenCV.cpp index 52fbd89402..15ca100032 100644 --- a/modules/io/src/image/private/vpImageIoOpenCV.cpp +++ b/modules/io/src/image/private/vpImageIoOpenCV.cpp @@ -1,7 +1,7 @@ /**************************************************************************** * * ViSP, open source Visual Servoing Platform software. - * Copyright (C) 2005 - 2019 by Inria. All rights reserved. + * Copyright (C) 2005 - 2022 by Inria. All rights reserved. * * This software is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/modules/io/src/image/private/vpImageIoPortable.cpp b/modules/io/src/image/private/vpImageIoPortable.cpp index 80d465097c..d33e5f0745 100644 --- a/modules/io/src/image/private/vpImageIoPortable.cpp +++ b/modules/io/src/image/private/vpImageIoPortable.cpp @@ -1,7 +1,7 @@ /**************************************************************************** * * ViSP, open source Visual Servoing Platform software. - * Copyright (C) 2005 - 2019 by Inria. All rights reserved. + * Copyright (C) 2005 - 2022 by Inria. All rights reserved. * * This software is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/modules/io/src/image/private/vpImageIoSimd.cpp b/modules/io/src/image/private/vpImageIoSimd.cpp index d872692001..dc3431b782 100644 --- a/modules/io/src/image/private/vpImageIoSimd.cpp +++ b/modules/io/src/image/private/vpImageIoSimd.cpp @@ -1,7 +1,7 @@ /**************************************************************************** * * ViSP, open source Visual Servoing Platform software. - * Copyright (C) 2005 - 2019 by Inria. All rights reserved. + * Copyright (C) 2005 - 2022 by Inria. All rights reserved. * * This software is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/modules/io/src/image/private/vpImageIoStb.cpp b/modules/io/src/image/private/vpImageIoStb.cpp index 9a86544554..0d60011727 100644 --- a/modules/io/src/image/private/vpImageIoStb.cpp +++ b/modules/io/src/image/private/vpImageIoStb.cpp @@ -1,7 +1,7 @@ /**************************************************************************** * * ViSP, open source Visual Servoing Platform software. - * Copyright (C) 2005 - 2019 by Inria. All rights reserved. + * Copyright (C) 2005 - 2022 by Inria. All rights reserved. * * This software is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/modules/io/test/perfImageLoadSave.cpp b/modules/io/test/perfImageLoadSave.cpp index a283cae064..b7c3379ecb 100644 --- a/modules/io/test/perfImageLoadSave.cpp +++ b/modules/io/test/perfImageLoadSave.cpp @@ -1,7 +1,7 @@ /**************************************************************************** * * ViSP, open source Visual Servoing Platform software. - * Copyright (C) 2005 - 2019 by Inria. All rights reserved. + * Copyright (C) 2005 - 2022 by Inria. All rights reserved. * * This software is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by From 7c813775e247347cd0bdff05aadf9a1fa53e7972 Mon Sep 17 00:00:00 2001 From: Fabien Spindler Date: Tue, 25 Jan 2022 10:22:10 +0100 Subject: [PATCH 06/12] Use temp directories --- modules/io/test/perfImageLoadSave.cpp | 28 ++++++++------------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/modules/io/test/perfImageLoadSave.cpp b/modules/io/test/perfImageLoadSave.cpp index b7c3379ecb..e0bfda7450 100644 --- a/modules/io/test/perfImageLoadSave.cpp +++ b/modules/io/test/perfImageLoadSave.cpp @@ -132,18 +132,9 @@ TEST_CASE("Benchmark RGBA PNG image loading", "[benchmark]") { } } -std::string username, directory_filename_tmp; - -#if !defined(_WIN32) && (defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))) // UNIX -std::string tmp_dir = "/tmp/"; -#else -std::string tmp_dir = "C:/Temp/"; -#endif - TEST_CASE("Benchmark grayscale JPEG image saving", "[benchmark]") { - vpIoTools::getUserName(username); - vpIoTools::makeDirectory(tmp_dir + username); - directory_filename_tmp = tmp_dir + username + "/vpIoTools_perfImageLoadSave_" + vpTime::getDateTime("%Y-%m-%d_%H.%M.%S"); + std::string tmp_dir = vpIoTools::makeTempDirectory( vpIoTools::getTempPath() ); + std::string directory_filename_tmp = tmp_dir + "/vpIoTools_perfImageLoadSave_" + vpTime::getDateTime("%Y-%m-%d_%H.%M.%S"); vpIoTools::makeDirectory(directory_filename_tmp); REQUIRE(vpIoTools::checkDirectory(directory_filename_tmp)); @@ -165,9 +156,8 @@ TEST_CASE("Benchmark grayscale JPEG image saving", "[benchmark]") { } TEST_CASE("Benchmark RGBA JPEG image saving", "[benchmark]") { - vpIoTools::getUserName(username); - vpIoTools::makeDirectory(tmp_dir + username); - directory_filename_tmp = tmp_dir + username + "/vpIoTools_perfImageLoadSave_" + vpTime::getDateTime("%Y-%m-%d_%H.%M.%S"); + std::string tmp_dir = vpIoTools::makeTempDirectory( vpIoTools::getTempPath() ); + std::string directory_filename_tmp = tmp_dir + "/vpIoTools_perfImageLoadSave_" + vpTime::getDateTime("%Y-%m-%d_%H.%M.%S"); vpIoTools::makeDirectory(directory_filename_tmp); REQUIRE(vpIoTools::checkDirectory(directory_filename_tmp)); @@ -189,9 +179,8 @@ TEST_CASE("Benchmark RGBA JPEG image saving", "[benchmark]") { } TEST_CASE("Benchmark grayscale PNG image saving", "[benchmark]") { - vpIoTools::getUserName(username); - vpIoTools::makeDirectory(tmp_dir + username); - directory_filename_tmp = tmp_dir + username + "/vpIoTools_perfImageLoadSave_" + vpTime::getDateTime("%Y-%m-%d_%H.%M.%S"); + std::string tmp_dir = vpIoTools::makeTempDirectory( vpIoTools::getTempPath() ); + std::string directory_filename_tmp = tmp_dir + "/vpIoTools_perfImageLoadSave_" + vpTime::getDateTime("%Y-%m-%d_%H.%M.%S"); vpIoTools::makeDirectory(directory_filename_tmp); REQUIRE(vpIoTools::checkDirectory(directory_filename_tmp)); @@ -213,9 +202,8 @@ TEST_CASE("Benchmark grayscale PNG image saving", "[benchmark]") { } TEST_CASE("Benchmark RGBA PNG image saving", "[benchmark]") { - vpIoTools::getUserName(username); - vpIoTools::makeDirectory(tmp_dir + username); - directory_filename_tmp = tmp_dir + username + "/vpIoTools_perfImageLoadSave_" + vpTime::getDateTime("%Y-%m-%d_%H.%M.%S"); + std::string tmp_dir = vpIoTools::makeTempDirectory( vpIoTools::getTempPath() ); + std::string directory_filename_tmp = tmp_dir + "/vpIoTools_perfImageLoadSave_" + vpTime::getDateTime("%Y-%m-%d_%H.%M.%S"); vpIoTools::makeDirectory(directory_filename_tmp); REQUIRE(vpIoTools::checkDirectory(directory_filename_tmp)); From 9180a98f06041d5c6602668475400d1016ed96fa Mon Sep 17 00:00:00 2001 From: Fabien Spindler Date: Tue, 25 Jan 2022 10:30:01 +0100 Subject: [PATCH 07/12] In writeJPEG() move quality parameter to the last position to align with writePNG() parameters --- modules/io/include/visp3/io/vpImageIo.h | 4 ++-- modules/io/src/image/vpImageIo.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/io/include/visp3/io/vpImageIo.h b/modules/io/include/visp3/io/vpImageIo.h index 0dea6a558b..1d2fcf5cf3 100644 --- a/modules/io/include/visp3/io/vpImageIo.h +++ b/modules/io/include/visp3/io/vpImageIo.h @@ -162,8 +162,8 @@ class VISP_EXPORT vpImageIo static void writePPM(const vpImage &I, const std::string &filename); static void writePPM(const vpImage &I, const std::string &filename); - static void writeJPEG(const vpImage &I, const std::string &filename, int quality=90, int backend=IO_DEFAULT_BACKEND); - static void writeJPEG(const vpImage &I, const std::string &filename, int quality=90, int backend=IO_DEFAULT_BACKEND); + static void writeJPEG(const vpImage &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND, int quality=90); + static void writeJPEG(const vpImage &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND, int quality=90); static void writePNG(const vpImage &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND); static void writePNG(const vpImage &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND); diff --git a/modules/io/src/image/vpImageIo.cpp b/modules/io/src/image/vpImageIo.cpp index d0df9a39de..41c2d55a9b 100644 --- a/modules/io/src/image/vpImageIo.cpp +++ b/modules/io/src/image/vpImageIo.cpp @@ -450,7 +450,7 @@ void vpImageIo::readPNG(vpImage &I, const std::string &filename, int bac } } -void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename, int quality, int backend) +void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename, int backend, int quality) { if (backend == IO_SYSTEM_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { #if defined(VISP_HAVE_JPEG) @@ -473,7 +473,7 @@ void vpImageIo::writeJPEG(const vpImage &I, const std::string &fi } } -void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename, int quality, int backend) +void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename, int backend, int quality) { if (backend == IO_SYSTEM_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { #if defined(VISP_HAVE_JPEG) From 6144bf471de47f6e792e9ed43bce66c8687130d9 Mon Sep 17 00:00:00 2001 From: Fabien Spindler Date: Tue, 25 Jan 2022 11:54:15 +0100 Subject: [PATCH 08/12] Fix perf helper message --- modules/io/test/perfImageLoadSave.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/io/test/perfImageLoadSave.cpp b/modules/io/test/perfImageLoadSave.cpp index e0bfda7450..92bc73fb48 100644 --- a/modules/io/test/perfImageLoadSave.cpp +++ b/modules/io/test/perfImageLoadSave.cpp @@ -234,8 +234,7 @@ int main(int argc, char *argv[]) auto cli = session.cli() // Get Catch's composite command line parser | Opt(runBenchmark) // bind variable to a new option, with a hint string ["--benchmark"] // the option names it will respond to - ("run benchmark?") // description string for the help output - ("Path to gray image") + ("Run benchmark") | Opt(nThreads, "nThreads") ["--nThreads"] ("Number of threads"); From dfb27ee519d9721c287df5c3ca0d328059de47fb Mon Sep 17 00:00:00 2001 From: Fabien Spindler Date: Tue, 25 Jan 2022 11:56:12 +0100 Subject: [PATCH 09/12] Switch to default backend when system or opencv backend not available --- modules/io/src/image/vpImageIo.cpp | 282 +++++++++++++++++++++-------- 1 file changed, 210 insertions(+), 72 deletions(-) diff --git a/modules/io/src/image/vpImageIo.cpp b/modules/io/src/image/vpImageIo.cpp index 41c2d55a9b..da2ef71f1c 100644 --- a/modules/io/src/image/vpImageIo.cpp +++ b/modules/io/src/image/vpImageIo.cpp @@ -358,186 +358,324 @@ void vpImageIo::write(const vpImage &I, const std::string &filename, int } } +// Strategy based on benchmark: see https://github.com/lagadic/visp/pull/1004 +// Default: 1. opencv, 2. system, 3. stb_image void vpImageIo::readJPEG(vpImage &I, const std::string &filename, int backend) { - if (backend == IO_SYSTEM_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + if (backend == IO_SYSTEM_LIB_BACKEND) { +#if !defined(VISP_HAVE_JPEG) + std::string message = "Libjpeg backend is not available to read file \"" + filename + "\": switch to stb_image backend"; + backend = IO_STB_IMAGE_BACKEND; +#endif + } + else if (backend == IO_OPENCV_BACKEND) { +#if !(defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100) + std::string message = "OpenCV backend is not available to read file \"" + filename + "\": switch to stb_image backend"; + backend = IO_STB_IMAGE_BACKEND; +#endif + } + else if (backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 + backend = IO_OPENCV_BACKEND; +#elif defined(VISP_HAVE_JPEG) + backend = IO_SYSTEM_LIB_BACKEND; +#else + backend = IO_STB_IMAGE_BACKEND; +#endif + } + + if (backend == IO_SYSTEM_LIB_BACKEND) { #if defined(VISP_HAVE_JPEG) readJPEGLibjpeg(I, filename); -#else - std::string message = "Cannot read file \"" + filename + "\": Libjpeg backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); #endif - } else if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { + } else if (backend == IO_OPENCV_BACKEND) { #if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 readOpenCV(I, filename); -#else - std::string message = "Cannot read file \"" + filename + "\": OpenCV backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); #endif - } else if (backend == IO_STB_IMAGE_BACKEND || backend == IO_DEFAULT_BACKEND) { + } else if (backend == IO_STB_IMAGE_BACKEND) { readStb(I, filename); } else if (backend == IO_SIMDLIB_BACKEND) { readSimdlib(I, filename); } } +// Strategy based on benchmark: see https://github.com/lagadic/visp/pull/1004 +// Default: 1. opencv, 2. system, 3. stb_image void vpImageIo::readJPEG(vpImage &I, const std::string &filename, int backend) { - if (backend == IO_SYSTEM_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + if (backend == IO_SYSTEM_LIB_BACKEND) { +#if !defined(VISP_HAVE_JPEG) + std::string message = "Libjpeg backend is not available to read file \"" + filename + "\": switch to stb_image backend"; + backend = IO_STB_IMAGE_BACKEND; +#endif + } + else if (backend == IO_OPENCV_BACKEND) { +#if !(defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100) + std::string message = "OpenCV backend is not available to read file \"" + filename + "\": switch to stb_image backend"; + backend = IO_STB_IMAGE_BACKEND; +#endif + } + else if (backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 + backend = IO_OPENCV_BACKEND; +#elif defined(VISP_HAVE_JPEG) + backend = IO_SYSTEM_LIB_BACKEND; +#else + backend = IO_STB_IMAGE_BACKEND; +#endif + } + + if (backend == IO_SYSTEM_LIB_BACKEND) { #if defined(VISP_HAVE_JPEG) readJPEGLibjpeg(I, filename); -#else - std::string message = "Cannot read file \"" + filename + "\": Libjpeg backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); #endif - } else if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { + } else if (backend == IO_OPENCV_BACKEND) { #if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 readOpenCV(I, filename); -#else - std::string message = "Cannot read file \"" + filename + "\": OpenCV backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); #endif - } else if (backend == IO_STB_IMAGE_BACKEND || backend == IO_DEFAULT_BACKEND) { + } else if (backend == IO_STB_IMAGE_BACKEND) { readStb(I, filename); } else if (backend == IO_SIMDLIB_BACKEND) { readSimdlib(I, filename); } } +// Strategy based on benchmark: see https://github.com/lagadic/visp/pull/1004 +// Default: 1. system, 2. opencv, 3. stb_image void vpImageIo::readPNG(vpImage &I, const std::string &filename, int backend) { - if (backend == IO_SYSTEM_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + if (backend == IO_SYSTEM_LIB_BACKEND) { +#if !defined(VISP_HAVE_PNG) + std::string message = "Libpng backend is not available to read file \"" + filename + "\": switch to stb_image backend"; + backend = IO_STB_IMAGE_BACKEND; +#endif + } + else if (backend == IO_OPENCV_BACKEND) { +#if !(defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100) + std::string message = "OpenCV backend is not available to read file \"" + filename + "\": switch to stb_image backend"; + backend = IO_STB_IMAGE_BACKEND; +#endif + } + else if (backend == IO_DEFAULT_BACKEND) { #if defined(VISP_HAVE_PNG) - readPNGLibpng(I, filename); + backend = IO_SYSTEM_LIB_BACKEND; +#elif defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 + backend = IO_OPENCV_BACKEND; #else - std::string message = "Cannot read file \"" + filename + "\": Libpng backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); + backend = IO_STB_IMAGE_BACKEND; +#endif + } + + if (backend == IO_SYSTEM_LIB_BACKEND) { +#if defined(VISP_HAVE_PNG) + readPNGLibpng(I, filename); #endif - } else if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { + } else if (backend == IO_OPENCV_BACKEND) { #if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 readOpenCV(I, filename); -#else - std::string message = "Cannot read file \"" + filename + "\": OpenCV backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); #endif - } else if (backend == IO_STB_IMAGE_BACKEND || backend == IO_DEFAULT_BACKEND) { + } else if (backend == IO_STB_IMAGE_BACKEND) { readStb(I, filename); } else if (backend == IO_SIMDLIB_BACKEND) { readSimdlib(I, filename); } } +// Strategy based on benchmark: see https://github.com/lagadic/visp/pull/1004 +// Default: 1. opencv, 2. stb_image void vpImageIo::readPNG(vpImage &I, const std::string &filename, int backend) { - if (backend == IO_SYSTEM_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + if (backend == IO_SYSTEM_LIB_BACKEND) { +#if !defined(VISP_HAVE_PNG) + std::string message = "Libpng backend is not available to read file \"" + filename + "\": switch to stb_image backend"; + backend = IO_STB_IMAGE_BACKEND; +#endif + } + else if (backend == IO_OPENCV_BACKEND) { +#if !(defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100) + std::string message = "OpenCV backend is not available to read file \"" + filename + "\": switch to stb_image backend"; + backend = IO_STB_IMAGE_BACKEND; +#endif + } + else if (backend == IO_DEFAULT_BACKEND) { +#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 + backend = IO_OPENCV_BACKEND; +#else + backend = IO_STB_IMAGE_BACKEND; +#endif + } + + if (backend == IO_SYSTEM_LIB_BACKEND) { #if defined(VISP_HAVE_PNG) readPNGLibpng(I, filename); -#else - std::string message = "Cannot read file \"" + filename + "\": Libpng backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); #endif - } else if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { + } else if (backend == IO_OPENCV_BACKEND) { #if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 readOpenCV(I, filename); -#else - std::string message = "Cannot read file \"" + filename + "\": OpenCV backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); #endif - } else if (backend == IO_STB_IMAGE_BACKEND || backend == IO_DEFAULT_BACKEND) { + } else if (backend == IO_STB_IMAGE_BACKEND) { readStb(I, filename); } else if (backend == IO_SIMDLIB_BACKEND) { readSimdlib(I, filename); } } +// Strategy based on benchmark: see https://github.com/lagadic/visp/pull/1004 +// Default: 1. system, 2. opencv, 3. simd void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename, int backend, int quality) { - if (backend == IO_SYSTEM_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + if (backend == IO_SYSTEM_LIB_BACKEND) { +#if !defined(VISP_HAVE_JPEG) + std::string message = "Libjpeg backend is not available to save file \"" + filename + "\": switch to simd backend"; + backend = IO_SIMDLIB_BACKEND; +#endif + } + else if (backend == IO_OPENCV_BACKEND) { +#if !(defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100) + std::string message = "OpenCV backend is not available to save file \"" + filename + "\": switch to simd backend"; + backend = IO_SIMDLIB_BACKEND; +#endif + } + else if (backend == IO_DEFAULT_BACKEND) { #if defined(VISP_HAVE_JPEG) - writeJPEGLibjpeg(I, filename, quality); + backend = IO_SYSTEM_LIB_BACKEND; +#elif defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 + backend = IO_OPENCV_BACKEND; #else - std::string message = "Cannot write file \"" + filename + "\": Libjpeg backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); + backend = IO_SIMDLIB_BACKEND; #endif - } else if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { + } + + if (backend == IO_SYSTEM_LIB_BACKEND) { +#if defined(VISP_HAVE_JPEG) + writeJPEGLibjpeg(I, filename, quality); +#endif + } else if (backend == IO_OPENCV_BACKEND) { #if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 writeOpenCV(I, filename, quality); -#else - std::string message = "Cannot write file \"" + filename + "\": OpenCV backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); #endif - } else if (backend == IO_SIMDLIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + } else if (backend == IO_SIMDLIB_BACKEND) { writeJPEGSimdlib(I, filename, quality); } else if (backend == IO_STB_IMAGE_BACKEND) { writeJPEGStb(I, filename, quality); } } +// Strategy based on benchmark: see https://github.com/lagadic/visp/pull/1004 +// Default: 1. system, 2. opencv, , 3. simd void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename, int backend, int quality) { - if (backend == IO_SYSTEM_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + if (backend == IO_SYSTEM_LIB_BACKEND) { +#if !defined(VISP_HAVE_JPEG) + std::string message = "Libjpeg backend is not available to save file \"" + filename + "\": switch to simd backend"; + backend = IO_SIMDLIB_BACKEND; +#endif + } + else if (backend == IO_OPENCV_BACKEND) { +#if !(defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100) + std::string message = "OpenCV backend is not available to save file \"" + filename + "\": switch to simd backend"; + backend = IO_SIMDLIB_BACKEND; +#endif + } + else if (backend == IO_DEFAULT_BACKEND) { #if defined(VISP_HAVE_JPEG) - writeJPEGLibjpeg(I, filename, quality); + backend = IO_SYSTEM_LIB_BACKEND; +#elif defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 + backend = IO_OPENCV_BACKEND; #else - std::string message = "Cannot write file \"" + filename + "\": Libjpeg backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); + backend = IO_SIMDLIB_BACKEND; #endif - } else if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { + } + + if (backend == IO_SYSTEM_LIB_BACKEND) { +#if defined(VISP_HAVE_JPEG) + writeJPEGLibjpeg(I, filename, quality); +#endif + } else if (backend == IO_OPENCV_BACKEND) { #if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 writeOpenCV(I, filename, quality); -#else - std::string message = "Cannot write file \"" + filename + "\": OpenCV backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); #endif - } else if (backend == IO_SIMDLIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + } else if (backend == IO_SIMDLIB_BACKEND) { writeJPEGSimdlib(I, filename, quality); } else if (backend == IO_STB_IMAGE_BACKEND) { writeJPEGStb(I, filename, quality); } } +// Strategy based on benchmark: see https://github.com/lagadic/visp/pull/1004 +// Default: 1. opencv, 2. simd void vpImageIo::writePNG(const vpImage &I, const std::string &filename, int backend) { - if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { + if (backend == IO_SYSTEM_LIB_BACKEND) { +#if !defined(VISP_HAVE_PNG) + std::string message = "Libpng backend is not available to save file \"" + filename + "\": switch to simd backend"; + backend = IO_SIMDLIB_BACKEND; +#endif + } + else if (backend == IO_OPENCV_BACKEND) { +#if !(defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100) + std::string message = "OpenCV backend is not available to save file \"" + filename + "\": switch to simd backend"; + backend = IO_SIMDLIB_BACKEND; +#endif + } + else if (backend == IO_DEFAULT_BACKEND) { #if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 - writeOpenCV(I, filename, 90); + backend = IO_OPENCV_BACKEND; #else - std::string message = "Cannot write file \"" + filename + "\": OpenCV backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); + backend = IO_SIMDLIB_BACKEND; +#endif + } + + if (backend == IO_OPENCV_BACKEND) { +#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 + writeOpenCV(I, filename, 90); #endif - } else if (backend == IO_SIMDLIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + } else if (backend == IO_SIMDLIB_BACKEND) { writePNGSimdlib(I, filename); } else if (backend == IO_STB_IMAGE_BACKEND) { writePNGStb(I, filename); - } else if (backend == IO_SYSTEM_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + } else if (backend == IO_SYSTEM_LIB_BACKEND) { #if defined(VISP_HAVE_PNG) writePNGLibpng(I, filename); -#else - std::string message = "Cannot write file \"" + filename + "\": Libpng backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); #endif } } +// Strategy based on benchmark: see https://github.com/lagadic/visp/pull/1004 +// Default: 1. opencv, 2. system, 3. simd void vpImageIo::writePNG(const vpImage &I, const std::string &filename, int backend) { - if (backend == IO_OPENCV_BACKEND || backend == IO_DEFAULT_BACKEND) { + if (backend == IO_SYSTEM_LIB_BACKEND) { +#if !defined(VISP_HAVE_PNG) + std::string message = "Libpng backend is not available to save file \"" + filename + "\": switch to simd backend"; + backend = IO_SIMDLIB_BACKEND; +#endif + } + else if (backend == IO_OPENCV_BACKEND) { +#if !(defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100) + std::string message = "OpenCV backend is not available to save file \"" + filename + "\": switch to simd backend"; + backend = IO_SIMDLIB_BACKEND; +#endif + } + else if (backend == IO_DEFAULT_BACKEND) { #if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 - writeOpenCV(I, filename, 90); + backend = IO_OPENCV_BACKEND; #else - std::string message = "Cannot write file \"" + filename + "\": OpenCV backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); + backend = IO_SIMDLIB_BACKEND; #endif - } else if (backend == IO_SIMDLIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + } + + if (backend == IO_OPENCV_BACKEND) { +#if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 + writeOpenCV(I, filename, 90); +#endif + } else if (backend == IO_SIMDLIB_BACKEND) { writePNGSimdlib(I, filename); } else if (backend == IO_STB_IMAGE_BACKEND) { writePNGStb(I, filename); - } else if (backend == IO_SYSTEM_LIB_BACKEND || backend == IO_DEFAULT_BACKEND) { + } else if (backend == IO_SYSTEM_LIB_BACKEND) { #if defined(VISP_HAVE_PNG) writePNGLibpng(I, filename); -#else - std::string message = "Cannot write file \"" + filename + "\": Libpng backend is not available"; - throw(vpImageException(vpImageException::ioError, message)); #endif } } From d600d893dffff5957f6b726859543c7288a201da Mon Sep 17 00:00:00 2001 From: Fabien Spindler Date: Tue, 25 Jan 2022 14:06:09 +0100 Subject: [PATCH 10/12] Improve vpImageIo doc --- modules/io/include/visp3/io/vpImageIo.h | 13 +- modules/io/src/image/vpImageIo.cpp | 217 ++++++++++++++++++++---- 2 files changed, 190 insertions(+), 40 deletions(-) diff --git a/modules/io/include/visp3/io/vpImageIo.h b/modules/io/include/visp3/io/vpImageIo.h index 1d2fcf5cf3..0b5807998d 100644 --- a/modules/io/include/visp3/io/vpImageIo.h +++ b/modules/io/include/visp3/io/vpImageIo.h @@ -103,7 +103,6 @@ int main() class VISP_EXPORT vpImageIo { - private: typedef enum { FORMAT_PGM, @@ -124,13 +123,13 @@ class VISP_EXPORT vpImageIo static std::string getExtension(const std::string &filename); public: - // Image IO backend for only jpeg and png formats + //! Image IO backend for only jpeg and png formats image loading and saving enum vpImageIoBackendType { - IO_DEFAULT_BACKEND, - IO_SYSTEM_LIB_BACKEND, - IO_OPENCV_BACKEND, - IO_SIMDLIB_BACKEND, - IO_STB_IMAGE_BACKEND + IO_DEFAULT_BACKEND, //!< Default backend + IO_SYSTEM_LIB_BACKEND, //!< Use system libraries like libpng or libjpeg + IO_OPENCV_BACKEND, //!< Use OpenCV + IO_SIMDLIB_BACKEND, //!< Use embedded simd library + IO_STB_IMAGE_BACKEND //!< Use embedded stb_image library }; static void read(vpImage &I, const std::string &filename, int backend=IO_DEFAULT_BACKEND); diff --git a/modules/io/src/image/vpImageIo.cpp b/modules/io/src/image/vpImageIo.cpp index da2ef71f1c..55598a5453 100644 --- a/modules/io/src/image/vpImageIo.cpp +++ b/modules/io/src/image/vpImageIo.cpp @@ -126,17 +126,25 @@ std::string vpImageIo::getExtension(const std::string &filename) only if the new image size is different, else we re-use the same memory space. - Always supported formats are `*.pgm` and `*.ppm`. - JPEG and PNG formats are supported through the stb_image public domain image loader. - - If libjpeg 3rd party is used, we support also `*.jpg` and `*.jpeg` files. - - If libpng 3rd party is used, we support also `*.png` files. - - If OpenCV 3rd party is used, we support `*.jpg`, `*.jpeg`, `*.jp2`, `*.rs`, `*.ras`, - `*.tiff`, `*.tif`, `*.png`, `*.bmp`, `*.pbm` files. - - If EXIF information is embedded in the image file, the EXIF orientation is ignored. + Supported formats are: + - portable gray map: `*.pgm` file + - portable pix map: `*.ppm` file + - portable float map: `*.pfm` file + - jpeg: `*.jpg`, `*.jpeg` files + - png: `*.png` file + + If ViSP is build with OpenCV support, additional formats are considered: + - `*.jp2`, `*.rs`, `*.ras`, `*.tiff`, `*.tif`, `*.png`, `*.bmp`, `*.pbm` files. + + If EXIF information is embedded in the image file, the EXIF orientation is ignored. \param I : Image to set with the \e filename content. \param filename : Name of the file containing the image. - \param backend : Library backend type (see vpImageIoBackendType) for image reading (only for JPEG and PNG). + \param backend : Library backend type (see vpImageIo::vpImageIoBackendType) for image reading. + This parameter is only used when the image need to be loaded in jpeg or png format. To know + which is the default backend see respectively + void vpImageIo::readJPEG(const vpImage &, const std::string &, int) and + void vpImageIo::readPNG(const vpImage &, const std::string &, int). */ void vpImageIo::read(vpImage &I, const std::string &filename, int backend) { @@ -187,24 +195,32 @@ void vpImageIo::read(vpImage &I, const std::string &filename, int /*! Read the contents of the image filename, allocate memory for the - corresponding color image, update its content, and return a reference to the - image. + corresponding greyscale image, update its content, and return a reference to + the image. If the image has been already initialized, memory allocation is done only if the new image size is different, else we re-use the same memory space. - Always supported formats are `*.pgm` and `*.ppm`. - JPEG and PNG formats are supported through the stb_image public domain image loader. - - If libjpeg 3rd party is used, we support also `*.jpg` and `*.jpeg` files. - - If libpng 3rd party is used, we support also `*.png` files. - - If OpenCV 3rd party is used, we support `*.jpg`, `*.jpeg`, `*.jp2`, `*.rs`, `*.ras`, - `*.tiff`, `*.tif`, `*.png`, `*.bmp`, `*.pbm` files. - - If EXIF information is embedded in the image file, the EXIF orientation is ignored. + Supported formats are: + - portable gray map: `*.pgm` file + - portable pix map: `*.ppm` file + - portable float map: `*.pfm` file + - jpeg: `*.jpg`, `*.jpeg` files + - png: `*.png` file + + If ViSP is build with OpenCV support, additional formats are considered: + - `*.jp2`, `*.rs`, `*.ras`, `*.tiff`, `*.tif`, `*.png`, `*.bmp`, `*.pbm` files. + + If EXIF information is embedded in the image file, the EXIF orientation is ignored. \param I : Image to set with the \e filename content. \param filename : Name of the file containing the image. - \param backend : Library backend type (see vpImageIoBackendType) for image reading (only for JPEG and PNG). + \param backend : Library backend type (see vpImageIo::vpImageIoBackendType) for image reading. + This parameter is only used when the image need to be loaded in jpeg or png format. To know + which is the default backend see respectively + void vpImageIo::readJPEG(const vpImage &, const std::string &, int) and + void vpImageIo::readPNG(const vpImage &, const std::string &, int). */ void vpImageIo::read(vpImage &I, const std::string &filename, int backend) { @@ -256,17 +272,24 @@ void vpImageIo::read(vpImage &I, const std::string &filename, int backen Write the content of the image in the file which name is given by \e filename. - Always supported formats are *.pgm and *.ppm. - JPEG and PNG formats are supported through the stb_image_write public domain image writer. - If \c libjpeg 3rd party is used, we support also *.jpg and *.jpeg files. - If \c libpng 3rd party is used, we support also *.png files. - If OpenCV 3rd party is used, we support *.jpg, *.jpeg, *.jp2, *.rs, *.ras, - *.tiff, *.tif, *.png, *.bmp, *.pbm files. + Supported formats are: + - portable gray map: `*.pgm` file + - portable pix map: `*.ppm` file + - portable float map: `*.pfm` file + - jpeg: `*.jpg`, `*.jpeg` files + - png: `*.png` file + + If ViSP is build with OpenCV support, additional formats are considered: + - `*.jp2`, `*.rs`, `*.ras`, `*.tiff`, `*.tif`, `*.png`, `*.bmp`, `*.pbm` files. \param I : Image to write. \param filename : Name of the file containing the image. - \param backend : Library backend type (see vpImageIoBackendType) for image writing (only for JPEG and PNG). - */ + \param backend : Library backend type (see vpImageIo::vpImageIoBackendType) for image writing. + This parameter is only used when the image need to be saved in jpeg or png format. To know + which is the default backend see respectively + void vpImageIo::writeJPEG(const vpImage &, const std::string &, int, int) and + void vpImageIo::writePNG(const vpImage &, const std::string &, int). +*/ void vpImageIo::write(const vpImage &I, const std::string &filename, int backend) { bool try_opencv_writer = false; @@ -309,16 +332,23 @@ void vpImageIo::write(const vpImage &I, const std::string &filena Write the content of the image in the file which name is given by \e filename. - Always supported formats are *.pgm and *.ppm. - JPEG and PNG formats are supported through the stb_image_write public domain image writer. - If \c libjpeg 3rd party is used, we support also *.jpg and *.jpeg files. - If \c libpng 3rd party is used, we support also *.png files. - If OpenCV 3rd party is used, we support *.jpg, *.jpeg, *.jp2, *.rs, *.ras, - *.tiff, *.tif, *.png, *.bmp, *.pbm files. + Supported formats are: + - portable gray map: `*.pgm` file + - portable pix map: `*.ppm` file + - portable float map: `*.pfm` file + - jpeg: `*.jpg`, `*.jpeg` files + - png: `*.png` file + + If ViSP is build with OpenCV support, additional formats are considered: + - `*.jp2`, `*.rs`, `*.ras`, `*.tiff`, `*.tif`, `*.png`, `*.bmp`, `*.pbm` files. \param I : Image to write. \param filename : Name of the file containing the image. - \param backend : Library backend type (see vpImageIoBackendType) for image writing (only for JPEG and PNG). + \param backend : Library backend type (see vpImageIo::vpImageIoBackendType) for image writing. + This parameter is only used when the image need to be saved in jpeg or png format. To know + which is the default backend see respectively + void vpImageIo::writeJPEG(const vpImage &, const std::string &, int, int) and + void vpImageIo::writePNG(const vpImage &, const std::string &, int). */ void vpImageIo::write(const vpImage &I, const std::string &filename, int backend) { @@ -358,6 +388,14 @@ void vpImageIo::write(const vpImage &I, const std::string &filename, int } } +/*! + Load a jpeg image. If it is a color image it is converted in gray. + \param[out] I : Gray level image. + \param[in] filename : Image location. + \param[in] backend : Supported backends are described in vpImageIo::vpImageIoBackendType. + Depending on its availability, the default backend vpImageIo::IO_DEFAULT_BACKEND is chosen in the following order: + vpImageIo::IO_OPENCV_BACKEND, vpImageIo::IO_SYSTEM_LIB_BACKEND, vpImageIo::IO_STB_IMAGE_BACKEND. + */ // Strategy based on benchmark: see https://github.com/lagadic/visp/pull/1004 // Default: 1. opencv, 2. system, 3. stb_image void vpImageIo::readJPEG(vpImage &I, const std::string &filename, int backend) @@ -399,6 +437,14 @@ void vpImageIo::readJPEG(vpImage &I, const std::string &filename, } } +/*! + Load a jpeg image. If it is a gray image it is converted in color. + \param[out] I : Color image. + \param[in] filename : Image location. + \param[in] backend : Supported backends are described in vpImageIo::vpImageIoBackendType. + Depending on its availability, the default backend vpImageIo::IO_DEFAULT_BACKEND is chosen in the following order: + vpImageIo::IO_OPENCV_BACKEND, vpImageIo::IO_SYSTEM_LIB_BACKEND, vpImageIo::IO_STB_IMAGE_BACKEND. + */ // Strategy based on benchmark: see https://github.com/lagadic/visp/pull/1004 // Default: 1. opencv, 2. system, 3. stb_image void vpImageIo::readJPEG(vpImage &I, const std::string &filename, int backend) @@ -440,6 +486,14 @@ void vpImageIo::readJPEG(vpImage &I, const std::string &filename, int ba } } +/*! + Load an image in png format. If it is a color image it is converted in gray. + \param[out] I : Gray level image. + \param[in] filename : Image location. + \param[in] backend : Supported backends are described in vpImageIo::vpImageIoBackendType. + Depending on its availability, the default backend vpImageIo::IO_DEFAULT_BACKEND is chosen in the following order: + vpImageIo::IO_SYSTEM_LIB_BACKEND, vpImageIo::IO_OPENCV_BACKEND, vpImageIo::IO_STB_IMAGE_BACKEND. + */ // Strategy based on benchmark: see https://github.com/lagadic/visp/pull/1004 // Default: 1. system, 2. opencv, 3. stb_image void vpImageIo::readPNG(vpImage &I, const std::string &filename, int backend) @@ -481,6 +535,14 @@ void vpImageIo::readPNG(vpImage &I, const std::string &filename, } } +/*! + Load an image in png format. If it is a gray level image it is converted in color. + \param[out] I : Color image. + \param[in] filename : Image location. + \param[in] backend : Supported backends are described in vpImageIo::vpImageIoBackendType. + Depending on its availability, the default backend vpImageIo::IO_DEFAULT_BACKEND is chosen in the following order: + vpImageIo::IO_OPENCV_BACKEND, vpImageIo::IO_STB_IMAGE_BACKEND. + */ // Strategy based on benchmark: see https://github.com/lagadic/visp/pull/1004 // Default: 1. opencv, 2. stb_image void vpImageIo::readPNG(vpImage &I, const std::string &filename, int backend) @@ -520,6 +582,15 @@ void vpImageIo::readPNG(vpImage &I, const std::string &filename, int bac } } +/*! + Save an image in jpeg format. + \param[in] I : Gray level image. + \param[in] filename : Image location. + \param[in] backend : Supported backends are described in vpImageIo::vpImageIoBackendType. + Depending on its availability, the default backend vpImageIo::IO_DEFAULT_BACKEND is chosen in the following order: + vpImageIo::IO_SYSTEM_LIB_BACKEND, vpImageIo::IO_OPENCV_BACKEND, vpImageIo::IO_SIMDLIB_BACKEND. + \param[in] quality : Image quality percentage in range 0-100. + */ // Strategy based on benchmark: see https://github.com/lagadic/visp/pull/1004 // Default: 1. system, 2. opencv, 3. simd void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename, int backend, int quality) @@ -561,6 +632,15 @@ void vpImageIo::writeJPEG(const vpImage &I, const std::string &fi } } +/*! + Save an image in jpeg format. + \param[in] I : Color image. + \param[in] filename : Image location. + \param[in] backend : Supported backends are described in vpImageIo::vpImageIoBackendType. + Depending on its availability, the default backend vpImageIo::IO_DEFAULT_BACKEND is chosen in the following order: + vpImageIo::IO_SYSTEM_LIB_BACKEND, vpImageIo::IO_OPENCV_BACKEND, vpImageIo::IO_SIMDLIB_BACKEND. + \param[in] quality : Image quality percentage in range 0-100. + */ // Strategy based on benchmark: see https://github.com/lagadic/visp/pull/1004 // Default: 1. system, 2. opencv, , 3. simd void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename, int backend, int quality) @@ -602,6 +682,14 @@ void vpImageIo::writeJPEG(const vpImage &I, const std::string &filename, } } +/*! + Save an image in png format. + \param[in] I : Gray level image. + \param[in] filename : Image location. + \param[in] backend : Supported backends are described in vpImageIo::vpImageIoBackendType. + Depending on its availability, the default backend vpImageIo::IO_DEFAULT_BACKEND is chosen in the following order: + vpImageIo::IO_OPENCV_BACKEND, vpImageIo::IO_SIMDLIB_BACKEND. + */ // Strategy based on benchmark: see https://github.com/lagadic/visp/pull/1004 // Default: 1. opencv, 2. simd void vpImageIo::writePNG(const vpImage &I, const std::string &filename, int backend) @@ -641,6 +729,14 @@ void vpImageIo::writePNG(const vpImage &I, const std::string &fil } } +/*! + Save an image in png format. + \param[in] I : Color image. + \param[in] filename : Image location. + \param[in] backend : Supported backends are described in vpImageIo::vpImageIoBackendType. + Depending on its availability, the default backend vpImageIo::IO_DEFAULT_BACKEND is chosen in the following order: + vpImageIo::IO_OPENCV_BACKEND, vpImageIo::IO_SYSTEM_LIB_BACKEND, vpImageIo::IO_SIMDLIB_BACKEND. + */ // Strategy based on benchmark: see https://github.com/lagadic/visp/pull/1004 // Default: 1. opencv, 2. system, 3. simd void vpImageIo::writePNG(const vpImage &I, const std::string &filename, int backend) @@ -680,56 +776,111 @@ void vpImageIo::writePNG(const vpImage &I, const std::string &filename, } } +/*! + Save an image in portable float map format. + \param[in] I : Image to save. + \param[in] filename : Image location. + */ void vpImageIo::writePFM(const vpImage &I, const std::string &filename) { vp_writePFM(I, filename); } +/*! + Save an image in portable gray map format. + \param[in] I : Image to save. + \param[in] filename : Image location. + */ void vpImageIo::writePGM(const vpImage &I, const std::string &filename) { vp_writePGM(I, filename); } +/*! + Save a gray level image in portable gray map format. + \param[in] I : Image to save. + \param[in] filename : Image location. + */ void vpImageIo::writePGM(const vpImage &I, const std::string &filename) { vp_writePGM(I, filename); } +/*! + Save a color image in portable gray map format. + \param[in] I : Image to save. + \param[in] filename : Image location. + */ void vpImageIo::writePGM(const vpImage &I, const std::string &filename) { vp_writePGM(I, filename); } +/*! + Load an image in portable float map format. + \param[out] I : Image read from filename. + \param[in] filename : Image location. + */ void vpImageIo::readPFM(vpImage &I, const std::string &filename) { vp_readPFM(I, filename); } +/*! + Load an image in portable gray map format. If the image is in color, it is converted in gray level. + \param[out] I : Image read from filename. + \param[in] filename : Image location. + */ void vpImageIo::readPGM(vpImage &I, const std::string &filename) { vp_readPGM(I, filename); } +/*! + Load an image in portable float map format. If the image is in gray, it is converted in color. + \param[out] I : Image read from filename. + \param[in] filename : Image location. + */ void vpImageIo::readPGM(vpImage &I, const std::string &filename) { vp_readPGM(I, filename); } +/*! + Load an image in portable pixmap format. If the image is in color, it is converted in gray level. + \param[out] I : Image read from filename. + \param[in] filename : Image location. + */ void vpImageIo::readPPM(vpImage &I, const std::string &filename) { vp_readPPM(I, filename); } +/*! + Load an image in portable pixmap format. If the image is in gray, it is converted in color. + \param[out] I : Image read from filename. + \param[in] filename : Image location. + */ void vpImageIo::readPPM(vpImage &I, const std::string &filename) { vp_readPPM(I, filename); } +/*! + Save a gray level image in portable pixmap format. + \param[in] I : Image to save. + \param[in] filename : Image location. + */ void vpImageIo::writePPM(const vpImage &I, const std::string &filename) { vp_writePPM(I, filename); } +/*! + Save a color level image in portable pixmap format. + \param[in] I : Image to save. + \param[in] filename : Image location. + */ void vpImageIo::writePPM(const vpImage &I, const std::string &filename) { vp_writePPM(I, filename); From 02db38b665b238ee821b1578b9715b7b7a4699bc Mon Sep 17 00:00:00 2001 From: Fabien Spindler Date: Tue, 25 Jan 2022 16:48:40 +0100 Subject: [PATCH 11/12] Fix opencv color image reader --- .../io/src/image/private/vpImageIoOpenCV.cpp | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/modules/io/src/image/private/vpImageIoOpenCV.cpp b/modules/io/src/image/private/vpImageIoOpenCV.cpp index 15ca100032..1b67d76eb5 100644 --- a/modules/io/src/image/private/vpImageIoOpenCV.cpp +++ b/modules/io/src/image/private/vpImageIoOpenCV.cpp @@ -63,12 +63,9 @@ #if defined(VISP_HAVE_OPENCV) /*! - Read the contents of the JPEG file, allocate memory + Read the contents of the image file, allocate memory for the corresponding gray level image, if necessary convert the data in - gray level, and set the bitmap whith the gray level data. That means that - the image \e I is a "black and white" rendering of the original image in \e - filename, as in a black and white photograph. If necessary, the quantization - formula used is \f$0,299 r + 0,587 g + 0,114 b\f$. + gray level, and set the bitmap with the gray level data. If the image has been already initialized, memory allocation is done only if the new image size is different, else we re-use the same @@ -107,12 +104,9 @@ void readOpenCV(vpImage &I, const std::string &filename) } /*! - Read a JPEG file and initialize a scalar image. - - Read the contents of the JPEG file, allocate - memory for the corresponding image, and set - the bitmap whith the content of - the file. + Read the contents of an image file, allocate + memory for the corresponding color image, and set + the bitmap whith the content of the file. If the image has been already initialized, memory allocation is done only if the new image size is different, else we re-use the same @@ -130,11 +124,11 @@ void readOpenCV(vpImage &I, const std::string &filename) { #if defined(VISP_HAVE_OPENCV) && VISP_HAVE_OPENCV_VERSION >= 0x020100 #if VISP_HAVE_OPENCV_VERSION >= 0x030200 - int flags = cv::IMREAD_GRAYSCALE | cv::IMREAD_IGNORE_ORIENTATION; + int flags = cv::IMREAD_COLOR | cv::IMREAD_IGNORE_ORIENTATION; #elif VISP_HAVE_OPENCV_VERSION >= 0x030000 - int flags = cv::IMREAD_GRAYSCALE; + int flags = cv::IMREAD_COLOR; #elif VISP_HAVE_OPENCV_VERSION >= 0x020100 - int flags = CV_LOAD_IMAGE_GRAYSCALE; + int flags = CV_LOAD_IMAGE_COLOR; #endif cv::Mat Ip = cv::imread(filename.c_str(), flags); if (!Ip.empty()) From 984ca626363a3bca77290079523df2b7756b5e76 Mon Sep 17 00:00:00 2001 From: Souriya Trinh Date: Tue, 25 Jan 2022 23:12:00 +0100 Subject: [PATCH 12/12] Correctly link the IO module with the Simd library. Manually copy data from Simd to ViSP type to properly take care of memory padding (introduced in the Simd lib to deal with memory alignment for vec instructions). --- modules/io/CMakeLists.txt | 4 ++-- modules/io/src/image/private/vpImageIoSimd.cpp | 14 ++++++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/modules/io/CMakeLists.txt b/modules/io/CMakeLists.txt index deba785b32..b23f2d537c 100644 --- a/modules/io/CMakeLists.txt +++ b/modules/io/CMakeLists.txt @@ -177,9 +177,9 @@ if(USE_OPENCV) endif(USE_OPENCV) if(ANDROID) - vp_add_module(io visp_core) + vp_add_module(io visp_core PRIVATE_OPTIONAL ${SIMDLIB_LIBRARIES}) else() - vp_add_module(io visp_core WRAP java) + vp_add_module(io visp_core PRIVATE_OPTIONAL ${SIMDLIB_LIBRARIES} WRAP java) endif() vp_glob_module_sources() diff --git a/modules/io/src/image/private/vpImageIoSimd.cpp b/modules/io/src/image/private/vpImageIoSimd.cpp index dc3431b782..b82684528a 100644 --- a/modules/io/src/image/private/vpImageIoSimd.cpp +++ b/modules/io/src/image/private/vpImageIoSimd.cpp @@ -47,8 +47,11 @@ void readSimdlib(vpImage &I, const std::string &filename) size_t stride = 0, width = 0, height = 0; SimdPixelFormatType format = SimdPixelFormatGray8; uint8_t* data = SimdImageLoadFromFile(filename.c_str(), &stride, &width, &height, &format); - const bool copyData = true; - I.init(data, (unsigned int)height, (unsigned int)width, copyData); + // Since the Simd lib use aligned data, some padding are introduced and we need to take care of it when copying + I.init(static_cast(height), static_cast(width)); + for (size_t i = 0; i < height; i++) { + memcpy(reinterpret_cast(I.bitmap) + i*width, data + i*stride, width); + } SimdFree(data); } @@ -57,8 +60,11 @@ void readSimdlib(vpImage &I, const std::string &filename) size_t stride = 0, width = 0, height = 0; SimdPixelFormatType format = SimdPixelFormatRgba32; uint8_t* data = SimdImageLoadFromFile(filename.c_str(), &stride, &width, &height, &format); - const bool copyData = true; - I.init((vpRGBa *)data, (unsigned int)height, (unsigned int)width, copyData); + // Since the Simd lib use aligned data, some padding are introduced and we need to take care of it when copying + I.init(static_cast(height), static_cast(width)); + for (size_t i = 0; i < height; i++) { + memcpy(reinterpret_cast(I.bitmap) + i*width*4, data + i*stride, 4*width); + } SimdFree(data); }