From c45c61fdae730343f0e8bb03670bc444ca566ddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Sun, 19 Feb 2023 12:08:56 +0900 Subject: [PATCH 01/28] WIP: preparation for JPEG XL support --- dlib/CMakeLists.txt | 23 ++- dlib/cmake_utils/find_libjxl.cmake | 49 ++++++ .../test_for_libjxl/CMakeLists.txt | 7 + .../test_for_libjxl/libjxl_test.cpp | 19 +++ dlib/config.h.in | 3 +- dlib/image_loader/jxl_loader.cpp | 134 +++++++++++++++ dlib/image_loader/jxl_loader.h | 141 ++++++++++++++++ dlib/image_loader/jxl_loader_abstract.h | 155 ++++++++++++++++++ dlib/image_loader/load_image.h | 56 ++++++- dlib/image_saver/save_jxl.cpp | 97 +++++++++++ dlib/image_saver/save_jxl.h | 126 ++++++++++++++ dlib/image_saver/save_jxl_abstract.h | 54 ++++++ dlib/image_saver/save_webp.h | 2 +- examples/CMakeLists.txt | 1 + 14 files changed, 856 insertions(+), 11 deletions(-) create mode 100644 dlib/cmake_utils/find_libjxl.cmake create mode 100644 dlib/cmake_utils/test_for_libjxl/CMakeLists.txt create mode 100644 dlib/cmake_utils/test_for_libjxl/libjxl_test.cpp create mode 100644 dlib/image_loader/jxl_loader.cpp create mode 100644 dlib/image_loader/jxl_loader.h create mode 100644 dlib/image_loader/jxl_loader_abstract.h create mode 100644 dlib/image_saver/save_jxl.cpp create mode 100644 dlib/image_saver/save_jxl.h create mode 100644 dlib/image_saver/save_jxl_abstract.h diff --git a/dlib/CMakeLists.txt b/dlib/CMakeLists.txt index 83230606f4..fdc4425d0d 100644 --- a/dlib/CMakeLists.txt +++ b/dlib/CMakeLists.txt @@ -179,6 +179,8 @@ if (NOT TARGET dlib) "Disable this if you don't want to link against libjpeg" ) set (DLIB_WEBP_SUPPORT_STR "Disable this if you don't want to link against libwebp" ) + set (DLIB_JPEGXL_SUPPORT_STR + "Disable this if you don't want to link against libjxl" ) set (DLIB_LINK_WITH_SQLITE3_STR "Disable this if you don't want to link against sqlite3" ) #set (DLIB_USE_FFTW_STR "Disable this if you don't want to link against fftw" ) @@ -237,12 +239,12 @@ if (NOT TARGET dlib) option(DLIB_PNG_SUPPORT ${DLIB_PNG_SUPPORT_STR} OFF) option(DLIB_GIF_SUPPORT ${DLIB_GIF_SUPPORT_STR} OFF) option(DLIB_WEBP_SUPPORT ${DLIB_WEBP_SUPPORT_STR} OFF) + option(DLIB_JPEGXL_SUPPORT ${DLIB_JPEGXL_SUPPORT_STR} OFF) #option(DLIB_USE_FFTW ${DLIB_USE_FFTW_STR} OFF) option(DLIB_USE_MKL_FFT ${DLIB_USE_MKL_FFT_STR} OFF) option(DLIB_USE_FFMPEG ${DLIB_USE_FFMPEG_STR} OFF) else() option(DLIB_JPEG_SUPPORT ${DLIB_JPEG_SUPPORT_STR} ON) - option(DLIB_WEBP_SUPPORT ${DLIB_WEBP_SUPPORT_STR} ON) option(DLIB_LINK_WITH_SQLITE3 ${DLIB_LINK_WITH_SQLITE3_STR} ON) option(DLIB_USE_BLAS ${DLIB_USE_BLAS_STR} ON) option(DLIB_USE_LAPACK ${DLIB_USE_LAPACK_STR} ON) @@ -250,17 +252,20 @@ if (NOT TARGET dlib) set(DLIB_USE_CUDA_COMPUTE_CAPABILITIES 50 CACHE STRING ${DLIB_USE_CUDA_COMPUTE_CAPABILITIES_STR}) option(DLIB_PNG_SUPPORT ${DLIB_PNG_SUPPORT_STR} ON) option(DLIB_GIF_SUPPORT ${DLIB_GIF_SUPPORT_STR} ON) + option(DLIB_WEBP_SUPPORT ${DLIB_WEBP_SUPPORT_STR} ON) + option(DLIB_JPEGXL_SUPPORT ${DLIB_JPEGXL_SUPPORT_STR} ON) #option(DLIB_USE_FFTW ${DLIB_USE_FFTW_STR} ON) option(DLIB_USE_MKL_FFT ${DLIB_USE_MKL_FFT_STR} ON) option(DLIB_USE_FFMPEG ${DLIB_USE_FFMPEG_STR} ON) endif() toggle_preprocessor_switch(DLIB_JPEG_SUPPORT) - toggle_preprocessor_switch(DLIB_WEBP_SUPPORT) toggle_preprocessor_switch(DLIB_USE_BLAS) toggle_preprocessor_switch(DLIB_USE_LAPACK) toggle_preprocessor_switch(DLIB_USE_CUDA) toggle_preprocessor_switch(DLIB_PNG_SUPPORT) toggle_preprocessor_switch(DLIB_GIF_SUPPORT) + toggle_preprocessor_switch(DLIB_WEBP_SUPPORT) + toggle_preprocessor_switch(DLIB_JPEGXL_SUPPORT) #toggle_preprocessor_switch(DLIB_USE_FFTW) toggle_preprocessor_switch(DLIB_USE_MKL_FFT) toggle_preprocessor_switch(DLIB_USE_FFMPEG) @@ -576,6 +581,20 @@ if (NOT TARGET dlib) toggle_preprocessor_switch(DLIB_WEBP_SUPPORT) endif() endif() + if (DLIB_JPEGXL_SUPPORT) + include(cmake_utils/find_libjxl.cmake) + if (JXL_FOUND) + list (APPEND dlib_needed_private_includes ${JXL_INCLUDE_DIR}) + list (APPEND dlib_needed_private_libraries ${JXL_LIBRARY}) + set(source_files ${source_files} + image_loader/jxl_loader.cpp + # image_saver/save_jxl.cpp + ) + else() + set(DLIB_JPEGXL_SUPPORT OFF CACHE BOOL ${DLIB_JPEGXL_SUPPORT_STR} FORCE ) + toggle_preprocessor_switch(DLIB_JPEGXL_SUPPORT) + endif() + endif() if (DLIB_USE_BLAS OR DLIB_USE_LAPACK OR DLIB_USE_MKL_FFT) diff --git a/dlib/cmake_utils/find_libjxl.cmake b/dlib/cmake_utils/find_libjxl.cmake new file mode 100644 index 0000000000..dc6a609dd7 --- /dev/null +++ b/dlib/cmake_utils/find_libjxl.cmake @@ -0,0 +1,49 @@ +#============================================================================= +# Find JPEG XL library +#============================================================================= +# Find the native JPEG XL headers and libraries. +# +# JXL_INCLUDE_DIRS - where to find jxl/decode_cxx.h, etc. +# JXL_LIBRARIES - List of libraries when using jxl. +# JXL_FOUND - True if jxl is found. +#============================================================================= + +# Look for the header file. + +unset(JXL_FOUND) + +find_path(JXL_INCLUDE_DIR NAMES jxl/decode_cxx.h jxl/encode_cxx.h) + +if(NOT JXL_INCLUDE_DIR) + unset(JXL_FOUND) +else() + mark_as_advanced(JXL_INCLUDE_DIR) + + # Look for the library + find_library(JXL_LIBRARY NAMES jxl) + # handle the QUIETLY and REQUIRED arguments and set JXL_FOUND to TRUE if + # all listed variables are TRUE + include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) + find_package_handle_standard_args(JXL DEFAULT_MSG JXL_LIBRARY JXL_INCLUDE_DIR) + + set(JXL_LIBRARIES ${JXL_LIBRARY}) + set(JXL_INCLUDE_DIRS ${JXL_INCLUDE_DIR}) +endif() + +if(JXL_FOUND) + set(JXL_TEST_CMAKE_FLAGS + "-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}" + "-DCMAKE_INCLUDE_PATH=${CMAKE_INCLUDE_PATH}" + "-DCMAKE_LIBRARY_PATH=${CMAKE_LIBRARY_PATH}") + + try_compile(test_for_libjxl_worked + ${PROJECT_BINARY_DIR}/test_for_libjxl_build + ${CMAKE_CURRENT_LIST_DIR}/test_for_libjxl + test_if_libjxl_is_broken + CMAKE_FLAGS "${JXL_TEST_CMAKE_FLAGS}") + + if(NOT test_for_libjxl_worked) + set(JXL_FOUND 0) + message (STATUS "System copy of libjxl is either too old or broken. Will disable JPEG XL support.") + endif() +endif() diff --git a/dlib/cmake_utils/test_for_libjxl/CMakeLists.txt b/dlib/cmake_utils/test_for_libjxl/CMakeLists.txt new file mode 100644 index 0000000000..9bb33422e7 --- /dev/null +++ b/dlib/cmake_utils/test_for_libjxl/CMakeLists.txt @@ -0,0 +1,7 @@ + +cmake_minimum_required(VERSION 3.8.0) +project(test_if_libjxl_is_broken) + +include_directories(${JPEGXL_INCLUDE_DIR}) +add_executable(libjxl_test libjxl_test.cpp) +target_link_libraries(libjxl_test ${JPEGXL_LIBRARY}) diff --git a/dlib/cmake_utils/test_for_libjxl/libjxl_test.cpp b/dlib/cmake_utils/test_for_libjxl/libjxl_test.cpp new file mode 100644 index 0000000000..1d1b2f6cba --- /dev/null +++ b/dlib/cmake_utils/test_for_libjxl/libjxl_test.cpp @@ -0,0 +1,19 @@ +// Copyright (C) 2023 Davis E. King (davis@dlib.net), Adrià Arrufat +// License: Boost Software License See LICENSE.txt for the full license. + +#include +#include +#include +#include + +// This code doesn't really make a lot of sense. It's just calling all the libjpeg functions to make +// sure they can be compiled and linked. + +// using JxlEncoderPtr = std::shared_ptr; +int main() +{ + std::cerr << "This program is just for build system testing. Don't actually run it." << std::endl; + std::abort(); + auto enc = JxlEncoderMake(nullptr); + auto dec = JxlDecoderMake(nullptr); +} diff --git a/dlib/config.h.in b/dlib/config.h.in index abaa655c1f..8d0b1daafb 100644 --- a/dlib/config.h.in +++ b/dlib/config.h.in @@ -20,9 +20,10 @@ // You should also consider telling dlib to link against libjpeg, libpng, libgif, fftw, CUDA, // and a BLAS and LAPACK library. To do this you need to uncomment the following #defines. #cmakedefine DLIB_JPEG_SUPPORT -#cmakedefine DLIB_WEBP_SUPPORT #cmakedefine DLIB_PNG_SUPPORT #cmakedefine DLIB_GIF_SUPPORT +#cmakedefine DLIB_WEBP_SUPPORT +#cmakedefine DLIB_JPEGXL_SUPPORT #cmakedefine DLIB_USE_FFTW #cmakedefine DLIB_USE_BLAS #cmakedefine DLIB_USE_LAPACK diff --git a/dlib/image_loader/jxl_loader.cpp b/dlib/image_loader/jxl_loader.cpp new file mode 100644 index 0000000000..66fe4cdb86 --- /dev/null +++ b/dlib/image_loader/jxl_loader.cpp @@ -0,0 +1,134 @@ +// Copyright (C) 2022 Davis E. King (davis@dlib.net), Martin Sandsmark, Adrià Arrufat +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_JXL_LOADER_CPp_ +#define DLIB_JXL_LOADER_CPp_ + +// only do anything with this file if DLIB_JPEGXL_SUPPORT is defined +#ifdef DLIB_JPEGXL_SUPPORT + +#include "jxl_loader.h" + +#include +#include + +namespace dlib +{ + + static std::vector load_contents(const std::string& filename) + { + std::ifstream stream(filename, std::ios::binary); + stream.exceptions(std::ifstream::failbit | std::ifstream::badbit | std::ifstream::eofbit); + stream.seekg(0, std::ios_base::end); + std::vector buffer(stream.tellg()); + stream.seekg(0); + stream.read(reinterpret_cast(buffer.data()), buffer.size()); + return buffer; + } + +// ---------------------------------------------------------------------------------------- + + jxl_loader:: + jxl_loader(const char* filename) : height_(0), width_(0) + { + data_ = load_contents(filename); + get_info(); + } + +// ---------------------------------------------------------------------------------------- + + jxl_loader:: + jxl_loader(const std::string& filename) : height_(0), width_(0) + { + data_ = load_contents(filename); + get_info(); + } + +// ---------------------------------------------------------------------------------------- + + jxl_loader:: + jxl_loader(const dlib::file& f) : height_(0), width_(0) + { + data_ = load_contents(f.full_name()); + get_info(); + } + +// ---------------------------------------------------------------------------------------- + + jxl_loader:: + jxl_loader(const unsigned char* imgbuffer, size_t imgbuffersize) : height_(0), width_(0) + { + data_.resize(imgbuffersize); + memcpy(data_.data(), imgbuffer, imgbuffersize); + get_info(); + } + +// ---------------------------------------------------------------------------------------- + + void jxl_loader::get_info() + { + JxlSignature signature = JxlSignatureCheck(data_.data(), data_.size()); + if (signature != JXL_SIG_CODESTREAM && signature != JXL_SIG_CONTAINER) + { + throw image_load_error("jxl_loader: Invalid header"); + } + } + +/* ---------------------------------------------------------------------------------------- + + void jxl_loader::read_argb(unsigned char *out, const size_t out_size, const int out_stride) const + { + if (!WebPDecodeARGBInto(data_.data(), data_.size(), out, out_size, out_stride)) + { + throw image_load_error("jxl_loader: decoding failed"); + } + } + +// ---------------------------------------------------------------------------------------- + + void jxl_loader::read_rgba(unsigned char *out, const size_t out_size, const int out_stride) const + { + if (!WebPDecodeRGBAInto(data_.data(), data_.size(), out, out_size, out_stride)) + { + throw image_load_error("jxl_loader: decoding failed"); + } + } + +// ---------------------------------------------------------------------------------------- + + void jxl_loader::read_bgra(unsigned char *out, const size_t out_size, const int out_stride) const + { + if (!WebPDecodeBGRAInto(data_.data(), data_.size(), out, out_size, out_stride)) + { + throw image_load_error("jxl_loader: decoding failed"); + } + } + +// ---------------------------------------------------------------------------------------- + + void jxl_loader::read_rgb(unsigned char *out, const size_t out_size, const int out_stride) const + { + if (!WebPDecodeRGBInto(data_.data(), data_.size(), out, out_size, out_stride)) + { + throw image_load_error("jxl_loader: decoding failed"); + } + } + +// ---------------------------------------------------------------------------------------- + + void jxl_loader::read_bgr(unsigned char *out, const size_t out_size, const int out_stride) const + { + if (!WebPDecodeBGRInto(data_.data(), data_.size(), out, out_size, out_stride)) + { + throw image_load_error("jxl_loader: decoding failed"); + } + } + +// ---------------------------------------------------------------------------------------- +*/ + +} + +#endif // DLIB_JPEG_SUPPORT + +#endif // DLIB_JXL_LOADER_CPp_ + diff --git a/dlib/image_loader/jxl_loader.h b/dlib/image_loader/jxl_loader.h new file mode 100644 index 0000000000..9e10da1702 --- /dev/null +++ b/dlib/image_loader/jxl_loader.h @@ -0,0 +1,141 @@ +// Copyright (C) 2022 Davis E. King (davis@dlib.net), Martin Sandsmark, Adrià Arrufat +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_JPEGXL_IMPORT +#define DLIB_JPEGXL_IMPORT + +#include + +#include "jxl_loader_abstract.h" +#include "image_loader.h" +#include "../pixel.h" +#include "../dir_nav.h" +#include "../test_for_odr_violations.h" + +namespace dlib +{ + + class jxl_loader : noncopyable + { + public: + + jxl_loader(const char* filename); + jxl_loader(const std::string& filename); + jxl_loader(const dlib::file& f); + jxl_loader(const unsigned char* imgbuffer, size_t buffersize); + + template + void get_image(image_type& image) const + { +#ifndef DLIB_JPEGXL_SUPPORT + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + You are getting this error because you are trying to use the jxl_loader + object but you haven't defined DLIB_JPEGXL_SUPPORT. You must do so to use + this object. You must also make sure you set your build environment + to link against the libjxl library. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ + static_assert(sizeof(image_type) == 0, "JPEG XL support not enabled."); +#endif + image_view vimg(image); + vimg.set_size(height_, width_); + typedef typename image_traits::pixel_type pixel_type; + + unsigned char* output = reinterpret_cast(image_data(vimg)); + const int stride = width_step(vimg); + const size_t output_size = stride * height_; + + // if (pixel_traits::rgb_alpha) + // { + // if (pixel_traits::bgr_layout) + // read_bgra(output, output_size, stride); + // else + // read_rgba(output, output_size, stride); + // return; + // } + // if (pixel_traits::rgb) + // { + // if (pixel_traits::bgr_layout) + // read_bgr(output, output_size, stride); + // else + // read_rgb(output, output_size, stride); + // return; + // } + // If we end up here, we are out of our fast path, and have to do it manually + + array2d decoded; + decoded.set_size(height_, width_); + unsigned char* output_dec = reinterpret_cast(image_data(decoded)); + const int stride_dec = width_step(decoded); + const size_t output_dec_size = stride_dec * height_; + + // read_rgba(output_dec, output_dec_size, stride_dec); + + for (int r = 0; r < height_; ++r) + { + for (int c = 0; c < width_; ++c) + { + assign_pixel(vimg[r][c], decoded[r][c]); + } + } + } + + private: + void get_info(); + // void read_bgra(unsigned char *out, const size_t out_size, const int out_stride) const; + // void read_bgr(unsigned char *out, const size_t out_size, const int out_stride) const; + // void read_rgba(unsigned char *out, const size_t out_size, const int out_stride) const; + // void read_rgb(unsigned char *out, const size_t out_size, const int out_stride) const; + // void read_argb(unsigned char *out, const size_t out_size, const int out_stride) const; + + int height_; + int width_; + std::vector data_; + }; + +// ---------------------------------------------------------------------------------------- + + template < + typename image_type + > + void load_jxl ( + image_type& image, + const std::string& file_name + ) + { + jxl_loader(file_name).get_image(image); + } + + template < + typename image_type + > + void load_jxl ( + image_type& image, + const unsigned char* imgbuff, + size_t imgbuffsize + ) + { + jxl_loader(imgbuff, imgbuffsize).get_image(image); + } + + template < + typename image_type + > + void load_jxl ( + image_type& image, + const char* imgbuff, + size_t imgbuffsize + ) + { + jxl_loader(reinterpret_cast(imgbuff), imgbuffsize).get_image(image); + } + +// ---------------------------------------------------------------------------------------- + +} + +#ifdef NO_MAKEFILE +#include "jxl_loader.cpp" +#endif + +#endif // DLIB_JPEGXL_IMPORT + + diff --git a/dlib/image_loader/jxl_loader_abstract.h b/dlib/image_loader/jxl_loader_abstract.h new file mode 100644 index 0000000000..ccca432596 --- /dev/null +++ b/dlib/image_loader/jxl_loader_abstract.h @@ -0,0 +1,155 @@ +// Copyright (C) 2022 Davis E. King (davis@dlib.net), Martin Sandsmark, Adrià Arrufat +// License: Boost Software License See LICENSE.txt for the full license. +#undef DLIB_WEBP_IMPORT_ABSTRACT +#ifdef DLIB_WEBP_IMPORT_ABSTRACT + +#include "image_loader_abstract.h" +#include "../algs.h" +#include "../pixel.h" +#include "../dir_nav.h" +#include "../image_processing/generic_image.h" + +namespace dlib +{ + + class webp_loader : noncopyable + { + /*! + WHAT THIS OBJECT REPRESENTS + This object represents a class capable of loading WEBP image files. + Once an instance of it is created to contain a WEBP file from + disk you can obtain the image stored in it via get_image(). + !*/ + + public: + + webp_loader( + const char* filename + ); + /*! + ensures + - loads the WEBP file with the given file name into this object + throws + - std::bad_alloc + - image_load_error + This exception is thrown if there is some error that prevents + us from loading the given WEBP file. + !*/ + + webp_loader( + const std::string& filename + ); + /*! + ensures + - loads the WEBP file with the given file name into this object + throws + - std::bad_alloc + - image_load_error + This exception is thrown if there is some error that prevents + us from loading the given WEBP file. + !*/ + + webp_loader( + const dlib::file& f + ); + /*! + ensures + - loads the WEBP file with the given file name into this object + throws + - std::bad_alloc + - image_load_error + This exception is thrown if there is some error that prevents + us from loading the given WEBP file. + !*/ + + webp_loader( + const unsigned char* imgbuffer, + size_t buffersize + ); + /*! + ensures + - loads the WEBP from memory imgbuffer of size buffersize into this object + throws + - image_load_error + This exception is thrown if there is some error that prevents + us from loading the given WEBP buffer. + !*/ + + ~webp_loader( + ); + /*! + ensures + - all resources associated with *this has been released + !*/ + + template< + typename image_type + > + void get_image( + image_type& img + ) const; + /*! + requires + - image_type == an image object that implements the interface defined in + dlib/image_processing/generic_image.h + ensures + - loads the WEBP image stored in this object into img + !*/ + + }; + +// ---------------------------------------------------------------------------------------- + + template < + typename image_type + > + void load_webp ( + image_type& image, + const std::string& file_name + ); + /*! + requires + - image_type == an image object that implements the interface defined in + dlib/image_processing/generic_image.h + ensures + - performs: webp_loader(file_name).get_image(image); + !*/ + + template < + typename image_type + > + void load_webp ( + image_type& image, + const unsigned char* imgbuff, + size_t imgbuffsize + ); + /*! + requires + - image_type == an image object that implements the interface defined in + dlib/image_processing/generic_image.h + ensures + - performs: webp_loader(imgbuff, imgbuffsize).get_image(image); + !*/ + + template < + typename image_type + > + void load_webp ( + image_type& image, + const char* imgbuff, + size_t imgbuffsize + ); + /*! + requires + - image_type == an image object that implements the interface defined in + dlib/image_processing/generic_image.h + ensures + - performs: webp_loader((unsigned char*)imgbuff, imgbuffsize).get_image(image); + !*/ + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_WEBP_IMPORT_ABSTRACT + diff --git a/dlib/image_loader/load_image.h b/dlib/image_loader/load_image.h index 9e2c3b0960..8fd340dd87 100644 --- a/dlib/image_loader/load_image.h +++ b/dlib/image_loader/load_image.h @@ -8,6 +8,7 @@ #include "png_loader.h" #include "jpeg_loader.h" #include "webp_loader.h" +#include "jxl_loader.h" #include "image_loader.h" #include #include @@ -27,6 +28,7 @@ namespace dlib DNG, GIF, WEBP, + JXL, UNKNOWN }; @@ -43,20 +45,23 @@ namespace dlib // Determine the true image type using link: // http://en.wikipedia.org/wiki/List_of_file_signatures static const char *pngHeader = "\x89\x50\x4E\x47\x0D\x0A\x1A\x0A"; + static const char *jxlHeader = "\x00\x00\x00\x0C\x4A\x58\x4C\x20\x0D\x0A\x87\x0A"; - if (memcmp(buffer, pngHeader, strlen(pngHeader)) == 0) - return PNG; - else if(buffer[0]=='\xff' && buffer[1]=='\xd8' && buffer[2]=='\xff') + if (buffer[0]=='\xff' && buffer[1]=='\xd8' && buffer[2]=='\xff') return JPG; - else if(buffer[0]=='B' && buffer[1]=='M') + else if (memcmp(buffer, pngHeader, strlen(pngHeader)) == 0) + return PNG; + else if (buffer[0]=='B' && buffer[1]=='M') return BMP; - else if(buffer[0]=='D' && buffer[1]=='N' && buffer[2] == 'G') + else if (buffer[0]=='D' && buffer[1]=='N' && buffer[2] == 'G') return DNG; - else if(buffer[0]=='G' && buffer[1]=='I' && buffer[2] == 'F') + else if (buffer[0]=='G' && buffer[1]=='I' && buffer[2] == 'F') return GIF; - else if(buffer[0]=='R' && buffer[1]=='I' && buffer[2] == 'F' && buffer[3] == 'F' && + else if (buffer[0]=='R' && buffer[1]=='I' && buffer[2] == 'F' && buffer[3] == 'F' && buffer[8]=='W' && buffer[9]=='E' && buffer[10] == 'B' && buffer[11] == 'P') return WEBP; + else if (memcmp(buffer, jxlHeader, 12) == 0) // we can't use strlen because the header starts with \x00. + return JXL; return UNKNOWN; } @@ -91,6 +96,9 @@ namespace dlib #ifdef DLIB_WEBP_SUPPORT case image_file_type::WEBP: load_webp(image, file_name); return; #endif +#ifdef DLIB_JPEGXL_SUPPORT + case image_file_type::JXL: load_jxl(image, file_name); return; +#endif #ifdef DLIB_GIF_SUPPORT case image_file_type::GIF: { @@ -218,6 +226,40 @@ namespace dlib sout << "Note that you must cause DLIB_GIF_SUPPORT to be defined for your entire project.\n"; sout << "So don't #define it in one file. Instead, use a compiler switch like -DDLIB_GIF_SUPPORT\n"; sout << "so it takes effect for your entire application."; +#endif + throw image_load_error(sout.str()); + } + else if (im_type == image_file_type::WEBP) + { + std::ostringstream sout; + sout << "Unable to load image in file " + file_name + ".\n" + + "You must #define DLIB_WEBP_SUPPORT and link to libwebp to read WebP files.\n" + + "Do this by following the instructions at http://dlib.net/compile.html.\n\n"; +#ifdef _MSC_VER + sout << "Note that you must cause DLIB_WEBP_SUPPORT to be defined for your entire project.\n"; + sout << "So don't #define it in one file. Instead, add it to the C/C++->Preprocessor->Preprocessor Definitions\n"; + sout << "field in Visual Studio's Property Pages window so it takes effect for your entire application."; +#else + sout << "Note that you must cause DLIB_WEBP_SUPPORT to be defined for your entire project.\n"; + sout << "So don't #define it in one file. Instead, use a compiler switch like -DDLIB_WEBP_SUPPORT\n"; + sout << "so it takes effect for your entire application."; +#endif + throw image_load_error(sout.str()); + } + else if (im_type == image_file_type::JXL) + { + std::ostringstream sout; + sout << "Unable to load image in file " + file_name + ".\n" + + "You must #define DLIB_JXL_SUPPORT and link to libjxl to read JPEG XL files.\n" + + "Do this by following the instructions at http://dlib.net/compile.html.\n\n"; +#ifdef _MSC_VER + sout << "Note that you must cause DLIB_JXL_SUPPORT to be defined for your entire project.\n"; + sout << "So don't #define it in one file. Instead, add it to the C/C++->Preprocessor->Preprocessor Definitions\n"; + sout << "field in Visual Studio's Property Pages window so it takes effect for your entire application."; +#else + sout << "Note that you must cause DLIB_JXL_SUPPORT to be defined for your entire project.\n"; + sout << "So don't #define it in one file. Instead, use a compiler switch like -DDLIB_JXL_SUPPORT\n"; + sout << "so it takes effect for your entire application."; #endif throw image_load_error(sout.str()); } diff --git a/dlib/image_saver/save_jxl.cpp b/dlib/image_saver/save_jxl.cpp new file mode 100644 index 0000000000..5cb064ce33 --- /dev/null +++ b/dlib/image_saver/save_jxl.cpp @@ -0,0 +1,97 @@ +// Copyright (C) 2022 Davis E. King (davis@dlib.net), Adrià Arrufat +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_JXL_SAVER_CPp_ +#define DLIB_JXL_SAVER_CPp_ + +// only do anything with this file if DLIB_JPEGXL_SUPPORT is defined +#ifdef DLIB_JPEGXL_SUPPORT + +#include "save_webp.h" +#include "image_saver.h" +#include + +#include + +namespace dlib { + +// ---------------------------------------------------------------------------------------- + + namespace impl + { + void impl_save_webp ( + const std::string& filename, + const uint8_t* data, + const int width, + const int height, + const int stride, + const float quality, + const webp_type type + ) + { + if (width > JPEGXL_MAX_DIMENSION || height > JPEGXL_MAX_DIMENSION) + throw image_save_error("Error while encoding " + filename + ". Bad picture dimensions: " + + std::to_string(width) + "x" + std::to_string(height) + + ". Maximum WebP width and height allowed is " + + std::to_string(JPEGXL_MAX_DIMENSION) + " pixels"); + + std::ofstream fout(filename, std::ios::binary); + if (!fout.good()) + throw image_save_error("Unable to open " + filename + " for writing."); + + uint8_t* output; + size_t output_size = 0; + switch (type) + { + case webp_type::rgb: + if (quality > 100) + output_size = WebPEncodeLosslessRGB(data, width, height, stride, &output); + else + output_size = WebPEncodeRGB(data, width, height, stride, quality, &output); + break; + case webp_type::rgba: + if (quality > 100) + output_size = WebPEncodeLosslessRGBA(data, width, height, stride, &output); + else + output_size = WebPEncodeRGBA(data, width, height, stride, quality, &output); + break; + case webp_type::bgr: + if (quality > 100) + output_size = WebPEncodeLosslessBGR(data, width, height, stride, &output); + else + output_size = WebPEncodeBGR(data, width, height, stride, quality, &output); + break; + case webp_type::bgra: + if (quality > 100) + output_size = WebPEncodeLosslessBGRA(data, width, height, stride, &output); + else + output_size = WebPEncodeBGRA(data, width, height, stride, quality, &output); + break; + default: + throw image_save_error("Invalid WebP color type"); + } + + if (output_size > 0) + { + fout.write(reinterpret_cast(output), output_size); + if (!fout.good()) + { + WebPFree(output); + throw image_save_error("Error while writing WebP image to " + filename + "."); + } + } + else + { + throw image_save_error("Error while encoding WebP image to " + filename + "."); + } + WebPFree(output); + } + } + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_JPEGXL_SUPPORT + +#endif // DLIB_JXL_SAVER_CPp_ + diff --git a/dlib/image_saver/save_jxl.h b/dlib/image_saver/save_jxl.h new file mode 100644 index 0000000000..345748a9f2 --- /dev/null +++ b/dlib/image_saver/save_jxl.h @@ -0,0 +1,126 @@ +// Copyright (C) 2022 Davis E. King (davis@dlib.net), Adrià Arrufat +// License: Boost Software License See LICENSE.txt for the full license. +#ifndef DLIB_SAVE_JXL_Hh_ +#define DLIB_SAVE_JXL_Hh_ + +#include "save_jxl_abstract.h" + +#include "../enable_if.h" +#include "image_saver.h" +#include "../matrix.h" +#include "../array2d.h" +#include "../pixel.h" +#include "../image_processing/generic_image.h" +#include + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + namespace impl + { + enum class webp_type + { + rgb, + bgr, + rgba, + bgra + }; + + void impl_save_webp ( + const std::string& filename, + const uint8_t* data, + const int width, + const int height, + const int stride, + const float quality, + const webp_type type + ); + } + +// ---------------------------------------------------------------------------------------- + + template < + typename image_type + > + typename disable_if>::type save_webp ( + const image_type& img_, + const std::string& filename, + float quality = 75 + ) + { +#ifndef DLIB_JPEGXL_SUPPORT + /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + You are getting this error because you are trying to use the save_webp + function but you haven't defined DLIB_JPEGXL_SUPPORT. You must do so to use + this object. You must also make sure you set your build environment + to link against the libwebp library. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ + static_assert(sizeof(image_type) == 0, "webp support not enabled."); +#endif + const_image_view img(img_); + using pixel_type = typename image_traits::pixel_type; + + // make sure requires clause is not broken + DLIB_CASSERT(img.size() != 0, + "\t save_jxl()" + << "\n\t You can't save an empty image as a JPEG XL." + ); + DLIB_CASSERT(0 <= quality, + "\t save_webp()" + << "\n\t Invalid quality value." + << "\n\t quality: " << quality + ); + + auto data = reinterpret_cast(image_data(img)); + const int width = img.nc(); + const int height = img.nr(); + int stride = width_step(img); + if (pixel_traits::rgb_alpha) + { + if (pixel_traits::bgr_layout) + impl::impl_save_webp(filename, data, width, height, stride, quality, impl::webp_type::bgra); + else + impl::impl_save_webp(filename, data, width, height, stride, quality, impl::webp_type::rgba); + } + else if (pixel_traits::rgb) + { + if (pixel_traits::bgr_layout) + impl::impl_save_webp(filename, data, width, height, stride, quality, impl::webp_type::bgr); + else + impl::impl_save_webp(filename, data, width, height, stride, quality, impl::webp_type::rgb); + } + else + { + // This is some other kind of color image so just save it as an RGB image. + // We also need to recompute the stride in case we were given a grayscale image. + array2d temp; + assign_image(temp, img); + stride = width_step(temp); + auto data = reinterpret_cast(image_data(temp)); + impl::impl_save_webp(filename, data, width, height, stride, quality, impl::webp_type::rgb); + } + } + +// ---------------------------------------------------------------------------------------- + + template < + typename EXP + > + void save_webp( + const matrix_exp& img, + const std::string& filename, + float quality = 75 + ) + { + array2d temp; + assign_image(temp, img); + save_webp(temp, filename, quality); + } + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_SAVE_JXL_Hh_ diff --git a/dlib/image_saver/save_jxl_abstract.h b/dlib/image_saver/save_jxl_abstract.h new file mode 100644 index 0000000000..845515955f --- /dev/null +++ b/dlib/image_saver/save_jxl_abstract.h @@ -0,0 +1,54 @@ +// Copyright (C) 2022 Davis E. King (davis@dlib.net), Adrià Arrufat +// License: Boost Software License See LICENSE.txt for the full license. +#undef DLIB_SAVE_JXL_ABSTRACT_Hh_ +#ifdef DLIB_SAVE_JXL_ABSTRACT_Hh_ + +#include "../image_processing/generic_image.h" +#include "../pixel.h" +#include + +namespace dlib +{ + +// ---------------------------------------------------------------------------------------- + + template < + typename image_type + > + void save_webp ( + const image_type& img, + const std::string& filename, + float quality = 75 + ); + /*! + requires + - image_type == an image object that implements the interface defined in + dlib/image_processing/generic_image.h or a matrix expression + - image.size() != 0 + - quality >= 0 + ensures + - writes the image to the file indicated by filename in the WEBP format. + - image[0][0] will be in the upper left corner of the image. + - image[image.nr()-1][image.nc()-1] will be in the lower right corner of the + image. + - This routine can save images containing any type of pixel. However, + save_webp() can only natively store rgb_pixel, bgr_pixel, rgb_alpha_pixel and + bgr_alpha_pixel pixel types. All other pixel types will be converted into one + of these types as appropriate before being saved to disk. + - The quality value determines how lossy the compression is. Larger quality + values result in larger output images but the images will look better. A value + between 0 and 100 will use lossy compression, while any value larger than + 100 will perform lossless compression. + throws + - image_save_error + This exception is thrown if there is an error that prevents us from saving + the image. + - std::bad_alloc + !*/ + +// ---------------------------------------------------------------------------------------- + +} + +#endif // DLIB_SAVE_WEBP_ABSTRACT_Hh_ + diff --git a/dlib/image_saver/save_webp.h b/dlib/image_saver/save_webp.h index e5c00a7168..5a76ed0ee6 100644 --- a/dlib/image_saver/save_webp.h +++ b/dlib/image_saver/save_webp.h @@ -123,4 +123,4 @@ namespace dlib } -#endif // DLIB_WEBP_SUPPORT +#endif // DLIB_SAVE_WEBP_Hh_ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 61af199179..5ee8076428 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -123,6 +123,7 @@ macro(add_gui_example name) endif() endmacro() +add_example(test_jxl_ex) add_example(dnn_metric_learning_ex) add_gui_example(dnn_face_recognition_ex) add_example(dnn_introduction_ex) From d3634cd494617b4ca6c1eda45fd2969980da6486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Thu, 22 Feb 2024 23:27:09 +0900 Subject: [PATCH 02/28] jxl: add loading support --- dlib/CMakeLists.txt | 11 ++- dlib/cmake_utils/find_libjxl.cmake | 32 ++++--- dlib/image_loader/jxl_loader.cpp | 134 ++++++++++++++++++----------- dlib/image_loader/jxl_loader.h | 64 +++++--------- 4 files changed, 126 insertions(+), 115 deletions(-) diff --git a/dlib/CMakeLists.txt b/dlib/CMakeLists.txt index fdc4425d0d..06f3efa9d1 100644 --- a/dlib/CMakeLists.txt +++ b/dlib/CMakeLists.txt @@ -584,15 +584,18 @@ if (NOT TARGET dlib) if (DLIB_JPEGXL_SUPPORT) include(cmake_utils/find_libjxl.cmake) if (JXL_FOUND) - list (APPEND dlib_needed_private_includes ${JXL_INCLUDE_DIR}) - list (APPEND dlib_needed_private_libraries ${JXL_LIBRARY}) + list (APPEND dlib_needed_private_includes ${JXL_INCLUDE_DIRS}) + list (APPEND dlib_needed_private_libraries ${JXL_LIBRARIES}) + list (APPEND dlib_needed_public_cflags ${JXL_CFLAGS}) + list (APPEND dlib_needed_public_ldflags ${JXL_LDFLAGS}) set(source_files ${source_files} image_loader/jxl_loader.cpp # image_saver/save_jxl.cpp ) + enable_preprocessor_switch(DLIB_JPEGXL_SUPPORT) else() - set(DLIB_JPEGXL_SUPPORT OFF CACHE BOOL ${DLIB_JPEGXL_SUPPORT_STR} FORCE ) - toggle_preprocessor_switch(DLIB_JPEGXL_SUPPORT) + set(DLIB_JPEGXL_SUPPORT OFF CACHE BOOL ${DLIB_JPEGXL_SUPPORT_STR} FORCE) + disable_preprocessor_switch(DLIB_JPEGXL_SUPPORT) endif() endif() diff --git a/dlib/cmake_utils/find_libjxl.cmake b/dlib/cmake_utils/find_libjxl.cmake index dc6a609dd7..0f76b310ac 100644 --- a/dlib/cmake_utils/find_libjxl.cmake +++ b/dlib/cmake_utils/find_libjxl.cmake @@ -10,24 +10,22 @@ # Look for the header file. -unset(JXL_FOUND) - -find_path(JXL_INCLUDE_DIR NAMES jxl/decode_cxx.h jxl/encode_cxx.h) - -if(NOT JXL_INCLUDE_DIR) - unset(JXL_FOUND) +message(STATUS "Searching for JPEG XL") +find_package(PkgConfig) +if (PkgConfig_FOUND) + pkg_check_modules(JXL IMPORTED_TARGET libjxl libjxl_threads) + if (JXL_FOUND) + message(STATUS "Found libjxl via pkg-config in `${JXL_LIBRARY_DIRS}`") + else() + message(" *****************************************************************************") + message(" *** No JPEG XL libraries found. ***") + message(" *** On Ubuntu you can install them by executing ***") + message(" *** sudo apt install libjxl-dev ***") + message(" *****************************************************************************") + endif() else() - mark_as_advanced(JXL_INCLUDE_DIR) - - # Look for the library - find_library(JXL_LIBRARY NAMES jxl) - # handle the QUIETLY and REQUIRED arguments and set JXL_FOUND to TRUE if - # all listed variables are TRUE - include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) - find_package_handle_standard_args(JXL DEFAULT_MSG JXL_LIBRARY JXL_INCLUDE_DIR) - - set(JXL_LIBRARIES ${JXL_LIBRARY}) - set(JXL_INCLUDE_DIRS ${JXL_INCLUDE_DIR}) + message(STATUS "PkgConfig could not be found, JPEG XL support won't be available") + set(JXL_FOUND 0) endif() if(JXL_FOUND) diff --git a/dlib/image_loader/jxl_loader.cpp b/dlib/image_loader/jxl_loader.cpp index 66fe4cdb86..1fe4c45e0f 100644 --- a/dlib/image_loader/jxl_loader.cpp +++ b/dlib/image_loader/jxl_loader.cpp @@ -5,10 +5,9 @@ // only do anything with this file if DLIB_JPEGXL_SUPPORT is defined #ifdef DLIB_JPEGXL_SUPPORT - #include "jxl_loader.h" - -#include +#include +#include #include namespace dlib @@ -28,37 +27,37 @@ namespace dlib // ---------------------------------------------------------------------------------------- jxl_loader:: - jxl_loader(const char* filename) : height_(0), width_(0) + jxl_loader(const char* filename) : height(0), width(0) { - data_ = load_contents(filename); + data = load_contents(filename); get_info(); } // ---------------------------------------------------------------------------------------- jxl_loader:: - jxl_loader(const std::string& filename) : height_(0), width_(0) + jxl_loader(const std::string& filename) : height(0), width(0) { - data_ = load_contents(filename); + data = load_contents(filename); get_info(); } // ---------------------------------------------------------------------------------------- jxl_loader:: - jxl_loader(const dlib::file& f) : height_(0), width_(0) + jxl_loader(const dlib::file& f) : height(0), width(0) { - data_ = load_contents(f.full_name()); + data = load_contents(f.full_name()); get_info(); } // ---------------------------------------------------------------------------------------- jxl_loader:: - jxl_loader(const unsigned char* imgbuffer, size_t imgbuffersize) : height_(0), width_(0) + jxl_loader(const unsigned char* imgbuffer, size_t imgbuffersize) : height(0), width(0) { - data_.resize(imgbuffersize); - memcpy(data_.data(), imgbuffer, imgbuffersize); + data.resize(imgbuffersize); + memcpy(data.data(), imgbuffer, imgbuffersize); get_info(); } @@ -66,66 +65,97 @@ namespace dlib void jxl_loader::get_info() { - JxlSignature signature = JxlSignatureCheck(data_.data(), data_.size()); + JxlSignature signature = JxlSignatureCheck(data.data(), data.size()); if (signature != JXL_SIG_CODESTREAM && signature != JXL_SIG_CONTAINER) { - throw image_load_error("jxl_loader: Invalid header"); + throw image_load_error("jxl_loader: JxlSignatureCheck failed"); } - } - -/* ---------------------------------------------------------------------------------------- - - void jxl_loader::read_argb(unsigned char *out, const size_t out_size, const int out_stride) const - { - if (!WebPDecodeARGBInto(data_.data(), data_.size(), out, out_size, out_stride)) + auto dec = JxlDecoderMake(nullptr); + if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO)) { - throw image_load_error("jxl_loader: decoding failed"); + throw image_load_error("jxl_loader: JxlDecoderSubscribeEvents failed"); } - } - -// ---------------------------------------------------------------------------------------- - - void jxl_loader::read_rgba(unsigned char *out, const size_t out_size, const int out_stride) const - { - if (!WebPDecodeRGBAInto(data_.data(), data_.size(), out, out_size, out_stride)) + JxlBasicInfo basic_info; + JxlDecoderSetInput(dec.get(), data.data(), data.size()); + JxlDecoderCloseInput(dec.get()); + if (JXL_DEC_BASIC_INFO != JxlDecoderProcessInput(dec.get())) { + throw image_load_error("jxl_loader: JxlDecoderProcessInput failed"); + } + if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &basic_info)) { - throw image_load_error("jxl_loader: decoding failed"); + throw image_load_error("jxl_loader: JxlDecoderGetBasicInfo failed"); } + width = basic_info.xsize; + height = basic_info.ysize; } - // ---------------------------------------------------------------------------------------- - void jxl_loader::read_bgra(unsigned char *out, const size_t out_size, const int out_stride) const + void jxl_loader::decode(unsigned char* out, const size_t out_size, const long num_channels) const { - if (!WebPDecodeBGRAInto(data_.data(), data_.size(), out, out_size, out_stride)) + // Multi-threaded parallel runner. + auto runner = JxlResizableParallelRunnerMake(nullptr); + auto dec = JxlDecoderMake(nullptr); + if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_FULL_IMAGE)) { - throw image_load_error("jxl_loader: decoding failed"); + throw image_load_error("jxl_loader: JxlDecoderSubscribeEvents failed"); } - } - -// ---------------------------------------------------------------------------------------- - void jxl_loader::read_rgb(unsigned char *out, const size_t out_size, const int out_stride) const - { - if (!WebPDecodeRGBInto(data_.data(), data_.size(), out, out_size, out_stride)) + if (JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec.get(), JxlResizableParallelRunner, runner.get())) { - throw image_load_error("jxl_loader: decoding failed"); + throw image_load_error("jxl_loader: JxlDecoderSetParallelRunner failed"); } - } -// ---------------------------------------------------------------------------------------- - - void jxl_loader::read_bgr(unsigned char *out, const size_t out_size, const int out_stride) const - { - if (!WebPDecodeBGRInto(data_.data(), data_.size(), out, out_size, out_stride)) + JxlPixelFormat format = { + .num_channels = static_cast(num_channels), + .data_type = JXL_TYPE_UINT8, + .endianness = JXL_NATIVE_ENDIAN, + .align=0 + }; + JxlDecoderSetInput(dec.get(), data.data(), data.size()); + JxlDecoderCloseInput(dec.get()); + for (;;) { - throw image_load_error("jxl_loader: decoding failed"); + JxlDecoderStatus status = JxlDecoderProcessInput(dec.get()); + if (status == JXL_DEC_ERROR) + { + throw image_load_error("jxl_loader: JxlDecoderProcessInput failed"); + } + else if (status == JXL_DEC_NEED_MORE_INPUT) + { + throw image_load_error("jxl_loader: Error, expected more input"); + } + else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { + JxlResizableParallelRunnerSetThreads(runner.get(), JxlResizableParallelRunnerSuggestThreads(width, height)); + size_t buffer_size; + if (JXL_DEC_SUCCESS != JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) + { + std::cout << "buffer_size = " << buffer_size << '\n'; + std::cout << "actual_size = " << width * height * num_channels << '\n'; + throw image_load_error("jxl_loader: JxlDecoderImageOutBufferSize failed"); + } + if (buffer_size != width * height * num_channels) + { + throw image_load_error("jxl_loader: invalid output buffer size"); + } + std::cout << "buffer size: " << buffer_size << '\n'; + if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format, out, out_size)) + { + throw image_load_error("jxl_loader: JxlDecoderSetImageOutBuffer failed"); + } + } + else if (status == JXL_DEC_FULL_IMAGE) + { + } + else if (status == JXL_DEC_SUCCESS) + { + return; + } + else + { + throw image_load_error("jxl_loder: Unknown decoder status"); + } } } - -// ---------------------------------------------------------------------------------------- -*/ - } #endif // DLIB_JPEG_SUPPORT diff --git a/dlib/image_loader/jxl_loader.h b/dlib/image_loader/jxl_loader.h index 9e10da1702..dbb9ef0efc 100644 --- a/dlib/image_loader/jxl_loader.h +++ b/dlib/image_loader/jxl_loader.h @@ -36,42 +36,27 @@ namespace dlib static_assert(sizeof(image_type) == 0, "JPEG XL support not enabled."); #endif image_view vimg(image); - vimg.set_size(height_, width_); - typedef typename image_traits::pixel_type pixel_type; - - unsigned char* output = reinterpret_cast(image_data(vimg)); - const int stride = width_step(vimg); - const size_t output_size = stride * height_; - - // if (pixel_traits::rgb_alpha) - // { - // if (pixel_traits::bgr_layout) - // read_bgra(output, output_size, stride); - // else - // read_rgba(output, output_size, stride); - // return; - // } - // if (pixel_traits::rgb) - // { - // if (pixel_traits::bgr_layout) - // read_bgr(output, output_size, stride); - // else - // read_rgb(output, output_size, stride); - // return; - // } - // If we end up here, we are out of our fast path, and have to do it manually + vimg.set_size(height, width); + using pixel_type = typename image_traits::pixel_type; - array2d decoded; - decoded.set_size(height_, width_); - unsigned char* output_dec = reinterpret_cast(image_data(decoded)); - const int stride_dec = width_step(decoded); - const size_t output_dec_size = stride_dec * height_; - - // read_rgba(output_dec, output_dec_size, stride_dec); + // Fast path: rgb, rgb_alpha. + if (pixel_traits::rgb || pixel_traits::rgb_alpha) + { + const long num_channels = pixel_traits::rgb_alpha ? 4 : 3; + const size_t output_size = width * height * num_channels; + unsigned char* output = reinterpret_cast(image_data(vimg)); + decode(output, output_size, num_channels); + return; + } - for (int r = 0; r < height_; ++r) + // Manual decoding + array2d decoded; + decoded.set_size(height, width); + unsigned char* output = reinterpret_cast(image_data(decoded)); + decode(output, width * height * 4, 4); + for (int r = 0; r < height; ++r) { - for (int c = 0; c < width_; ++c) + for (int c = 0; c < width; ++c) { assign_pixel(vimg[r][c], decoded[r][c]); } @@ -80,15 +65,10 @@ namespace dlib private: void get_info(); - // void read_bgra(unsigned char *out, const size_t out_size, const int out_stride) const; - // void read_bgr(unsigned char *out, const size_t out_size, const int out_stride) const; - // void read_rgba(unsigned char *out, const size_t out_size, const int out_stride) const; - // void read_rgb(unsigned char *out, const size_t out_size, const int out_stride) const; - // void read_argb(unsigned char *out, const size_t out_size, const int out_stride) const; - - int height_; - int width_; - std::vector data_; + void decode(unsigned char *out, const size_t out_size, const long num_channels) const; + long height; + long width; + std::vector data; }; // ---------------------------------------------------------------------------------------- From 3114f5a89e4168120cb450fc54391a480e8fe827 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Thu, 22 Feb 2024 23:33:24 +0900 Subject: [PATCH 03/28] update jxl abstract --- dlib/image_loader/jxl_loader_abstract.h | 52 ++++++++++++------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/dlib/image_loader/jxl_loader_abstract.h b/dlib/image_loader/jxl_loader_abstract.h index ccca432596..add36c631d 100644 --- a/dlib/image_loader/jxl_loader_abstract.h +++ b/dlib/image_loader/jxl_loader_abstract.h @@ -1,7 +1,7 @@ // Copyright (C) 2022 Davis E. King (davis@dlib.net), Martin Sandsmark, Adrià Arrufat // License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_WEBP_IMPORT_ABSTRACT -#ifdef DLIB_WEBP_IMPORT_ABSTRACT +#undef DLIB_JPEGXL_IMPORT_ABSTRACT +#ifdef DLIB_JPEGXL_IMPORT_ABSTRACT #include "image_loader_abstract.h" #include "../algs.h" @@ -12,70 +12,70 @@ namespace dlib { - class webp_loader : noncopyable + class jxl_loader : noncopyable { /*! WHAT THIS OBJECT REPRESENTS - This object represents a class capable of loading WEBP image files. - Once an instance of it is created to contain a WEBP file from + This object represents a class capable of loading JPEG XL image files. + Once an instance of it is created to contain a JPEG XL file from disk you can obtain the image stored in it via get_image(). !*/ public: - webp_loader( + jxl_loader( const char* filename ); /*! ensures - - loads the WEBP file with the given file name into this object + - loads the JPEG XL file with the given file name into this object throws - std::bad_alloc - image_load_error This exception is thrown if there is some error that prevents - us from loading the given WEBP file. + us from loading the given JPEG XL file. !*/ - webp_loader( + jxl_loader( const std::string& filename ); /*! ensures - - loads the WEBP file with the given file name into this object + - loads the JPEG XL file with the given file name into this object throws - std::bad_alloc - image_load_error This exception is thrown if there is some error that prevents - us from loading the given WEBP file. + us from loading the given JPEG XL file. !*/ - webp_loader( + jxl_loader( const dlib::file& f ); /*! ensures - - loads the WEBP file with the given file name into this object + - loads the JPEG XL file with the given file name into this object throws - std::bad_alloc - image_load_error This exception is thrown if there is some error that prevents - us from loading the given WEBP file. + us from loading the given JPEG XL file. !*/ - webp_loader( + jxl_loader( const unsigned char* imgbuffer, size_t buffersize ); /*! ensures - - loads the WEBP from memory imgbuffer of size buffersize into this object + - loads the JPEG XL from memory imgbuffer of size buffersize into this object throws - image_load_error This exception is thrown if there is some error that prevents - us from loading the given WEBP buffer. + us from loading the given JPEG XL buffer. !*/ - ~webp_loader( + ~jxl_loader( ); /*! ensures @@ -93,7 +93,7 @@ namespace dlib - image_type == an image object that implements the interface defined in dlib/image_processing/generic_image.h ensures - - loads the WEBP image stored in this object into img + - loads the JPEG XL image stored in this object into img !*/ }; @@ -103,7 +103,7 @@ namespace dlib template < typename image_type > - void load_webp ( + void load_jxl ( image_type& image, const std::string& file_name ); @@ -112,13 +112,13 @@ namespace dlib - image_type == an image object that implements the interface defined in dlib/image_processing/generic_image.h ensures - - performs: webp_loader(file_name).get_image(image); + - performs: jxl_loader(file_name).get_image(image); !*/ template < typename image_type > - void load_webp ( + void load_jxl ( image_type& image, const unsigned char* imgbuff, size_t imgbuffsize @@ -128,13 +128,13 @@ namespace dlib - image_type == an image object that implements the interface defined in dlib/image_processing/generic_image.h ensures - - performs: webp_loader(imgbuff, imgbuffsize).get_image(image); + - performs: jxl_loader(imgbuff, imgbuffsize).get_image(image); !*/ template < typename image_type > - void load_webp ( + void load_jxl ( image_type& image, const char* imgbuff, size_t imgbuffsize @@ -144,12 +144,12 @@ namespace dlib - image_type == an image object that implements the interface defined in dlib/image_processing/generic_image.h ensures - - performs: webp_loader((unsigned char*)imgbuff, imgbuffsize).get_image(image); + - performs: jxl_loader((unsigned char*)imgbuff, imgbuffsize).get_image(image); !*/ // ---------------------------------------------------------------------------------------- } -#endif // DLIB_WEBP_IMPORT_ABSTRACT +#endif // DLIB_JPEGXL_IMPORT_ABSTRACT From 4244e5daa2cd75a9041645126cd0024b0f4d9807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Sat, 24 Feb 2024 00:39:08 +0900 Subject: [PATCH 04/28] add support for saving jxl (lossless not working) --- dlib/CMakeLists.txt | 2 +- dlib/cmake_utils/find_libjxl.cmake | 2 +- .../test_for_libjxl/libjxl_test.cpp | 4 +- dlib/image_io.h | 1 + dlib/image_loader/jxl_loader.cpp | 20 ++- dlib/image_saver/save_jxl.cpp | 148 +++++++++++------- dlib/image_saver/save_jxl.h | 54 +++---- examples/CMakeLists.txt | 1 - 8 files changed, 134 insertions(+), 98 deletions(-) diff --git a/dlib/CMakeLists.txt b/dlib/CMakeLists.txt index 06f3efa9d1..9ef65faa90 100644 --- a/dlib/CMakeLists.txt +++ b/dlib/CMakeLists.txt @@ -590,7 +590,7 @@ if (NOT TARGET dlib) list (APPEND dlib_needed_public_ldflags ${JXL_LDFLAGS}) set(source_files ${source_files} image_loader/jxl_loader.cpp - # image_saver/save_jxl.cpp + image_saver/save_jxl.cpp ) enable_preprocessor_switch(DLIB_JPEGXL_SUPPORT) else() diff --git a/dlib/cmake_utils/find_libjxl.cmake b/dlib/cmake_utils/find_libjxl.cmake index 0f76b310ac..143c16aea3 100644 --- a/dlib/cmake_utils/find_libjxl.cmake +++ b/dlib/cmake_utils/find_libjxl.cmake @@ -13,7 +13,7 @@ message(STATUS "Searching for JPEG XL") find_package(PkgConfig) if (PkgConfig_FOUND) - pkg_check_modules(JXL IMPORTED_TARGET libjxl libjxl_threads) + pkg_check_modules(JXL REQUIRED IMPORTED_TARGET libjxl libjxl_cms libjxl_threads) if (JXL_FOUND) message(STATUS "Found libjxl via pkg-config in `${JXL_LIBRARY_DIRS}`") else() diff --git a/dlib/cmake_utils/test_for_libjxl/libjxl_test.cpp b/dlib/cmake_utils/test_for_libjxl/libjxl_test.cpp index 1d1b2f6cba..49cb37219c 100644 --- a/dlib/cmake_utils/test_for_libjxl/libjxl_test.cpp +++ b/dlib/cmake_utils/test_for_libjxl/libjxl_test.cpp @@ -3,17 +3,19 @@ #include #include +#include +#include #include #include // This code doesn't really make a lot of sense. It's just calling all the libjpeg functions to make // sure they can be compiled and linked. -// using JxlEncoderPtr = std::shared_ptr; int main() { std::cerr << "This program is just for build system testing. Don't actually run it." << std::endl; std::abort(); auto enc = JxlEncoderMake(nullptr); auto dec = JxlDecoderMake(nullptr); + auto runner = JxlResizableParallelRunnerMake(nullptr); } diff --git a/dlib/image_io.h b/dlib/image_io.h index 9edf41db02..88597fffdd 100644 --- a/dlib/image_io.h +++ b/dlib/image_io.h @@ -17,6 +17,7 @@ #include "image_saver/save_png.h" #include "image_saver/save_jpeg.h" #include "image_saver/save_webp.h" +#include "image_saver/save_jxl.h" #endif // DLIB_IMAGe_IO_ diff --git a/dlib/image_loader/jxl_loader.cpp b/dlib/image_loader/jxl_loader.cpp index 1fe4c45e0f..4cd1c78841 100644 --- a/dlib/image_loader/jxl_loader.cpp +++ b/dlib/image_loader/jxl_loader.cpp @@ -70,17 +70,20 @@ namespace dlib { throw image_load_error("jxl_loader: JxlSignatureCheck failed"); } + auto dec = JxlDecoderMake(nullptr); if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO)) { throw image_load_error("jxl_loader: JxlDecoderSubscribeEvents failed"); } - JxlBasicInfo basic_info; + JxlDecoderSetInput(dec.get(), data.data(), data.size()); JxlDecoderCloseInput(dec.get()); if (JXL_DEC_BASIC_INFO != JxlDecoderProcessInput(dec.get())) { throw image_load_error("jxl_loader: JxlDecoderProcessInput failed"); } + + JxlBasicInfo basic_info; if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &basic_info)) { throw image_load_error("jxl_loader: JxlDecoderGetBasicInfo failed"); @@ -92,7 +95,6 @@ namespace dlib void jxl_loader::decode(unsigned char* out, const size_t out_size, const long num_channels) const { - // Multi-threaded parallel runner. auto runner = JxlResizableParallelRunnerMake(nullptr); auto dec = JxlDecoderMake(nullptr); if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_FULL_IMAGE)) @@ -105,14 +107,18 @@ namespace dlib throw image_load_error("jxl_loader: JxlDecoderSetParallelRunner failed"); } + if (JXL_DEC_SUCCESS != JxlDecoderSetInput(dec.get(), data.data(), data.size())) + { + throw image_load_error("jxl_loader: JxlDecoderSetInput failed"); + } + JxlDecoderCloseInput(dec.get()); + JxlPixelFormat format = { .num_channels = static_cast(num_channels), .data_type = JXL_TYPE_UINT8, .endianness = JXL_NATIVE_ENDIAN, .align=0 }; - JxlDecoderSetInput(dec.get(), data.data(), data.size()); - JxlDecoderCloseInput(dec.get()); for (;;) { JxlDecoderStatus status = JxlDecoderProcessInput(dec.get()); @@ -124,7 +130,8 @@ namespace dlib { throw image_load_error("jxl_loader: Error, expected more input"); } - else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { + else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) + { JxlResizableParallelRunnerSetThreads(runner.get(), JxlResizableParallelRunnerSuggestThreads(width, height)); size_t buffer_size; if (JXL_DEC_SUCCESS != JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) @@ -145,6 +152,9 @@ namespace dlib } else if (status == JXL_DEC_FULL_IMAGE) { + // If the image is an animation, more full frames may be decoded. + // This loader only decodes the first one. + return; } else if (status == JXL_DEC_SUCCESS) { diff --git a/dlib/image_saver/save_jxl.cpp b/dlib/image_saver/save_jxl.cpp index 5cb064ce33..24f8a4f461 100644 --- a/dlib/image_saver/save_jxl.cpp +++ b/dlib/image_saver/save_jxl.cpp @@ -6,11 +6,11 @@ // only do anything with this file if DLIB_JPEGXL_SUPPORT is defined #ifdef DLIB_JPEGXL_SUPPORT -#include "save_webp.h" +#include "save_jxl.h" #include "image_saver.h" #include - -#include +#include +#include namespace dlib { @@ -18,72 +18,114 @@ namespace dlib { namespace impl { - void impl_save_webp ( + void impl_save_jxl ( const std::string& filename, - const uint8_t* data, - const int width, - const int height, - const int stride, - const float quality, - const webp_type type + const uint8_t* pixels, + const uint32_t width, + const uint32_t height, + const uint32_t num_channels, + const float quality ) { - if (width > JPEGXL_MAX_DIMENSION || height > JPEGXL_MAX_DIMENSION) - throw image_save_error("Error while encoding " + filename + ". Bad picture dimensions: " - + std::to_string(width) + "x" + std::to_string(height) - + ". Maximum WebP width and height allowed is " - + std::to_string(JPEGXL_MAX_DIMENSION) + " pixels"); - std::ofstream fout(filename, std::ios::binary); if (!fout.good()) + { throw image_save_error("Unable to open " + filename + " for writing."); + } + + auto enc = JxlEncoderMake(nullptr); + auto runner = JxlThreadParallelRunnerMake(nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads()); + + if (JXL_ENC_SUCCESS != JxlEncoderSetParallelRunner(enc.get(), JxlThreadParallelRunner, runner.get())) + { + throw image_save_error("jxl_saver: JxlResizableParallelRunner failed"); + } + + JxlPixelFormat pixel_format{ + .num_channels = num_channels, + .data_type = JXL_TYPE_UINT8, + .endianness = JXL_NATIVE_ENDIAN, + .align = 0 + }; + JxlBasicInfo basic_info; + JxlEncoderInitBasicInfo(&basic_info); + basic_info.xsize = width; + basic_info.ysize = height; + basic_info.bits_per_sample = 8; + basic_info.uses_original_profile = JXL_FALSE; + if (num_channels > 3) + { + basic_info.num_extra_channels = 1; + basic_info.alpha_bits = 8; + } + + if (JXL_ENC_SUCCESS != JxlEncoderSetBasicInfo(enc.get(), &basic_info)) + { + throw image_save_error("jxl_saver: JxlEncoderSetBasicInfo failed"); + } - uint8_t* output; - size_t output_size = 0; - switch (type) + JxlColorEncoding color_encoding = {}; + JxlColorEncodingSetToSRGB(&color_encoding, /* is_gray = */ num_channels < 3); + if (JXL_ENC_SUCCESS != JxlEncoderSetColorEncoding(enc.get(), &color_encoding)) { - case webp_type::rgb: - if (quality > 100) - output_size = WebPEncodeLosslessRGB(data, width, height, stride, &output); - else - output_size = WebPEncodeRGB(data, width, height, stride, quality, &output); - break; - case webp_type::rgba: - if (quality > 100) - output_size = WebPEncodeLosslessRGBA(data, width, height, stride, &output); - else - output_size = WebPEncodeRGBA(data, width, height, stride, quality, &output); - break; - case webp_type::bgr: - if (quality > 100) - output_size = WebPEncodeLosslessBGR(data, width, height, stride, &output); - else - output_size = WebPEncodeBGR(data, width, height, stride, quality, &output); - break; - case webp_type::bgra: - if (quality > 100) - output_size = WebPEncodeLosslessBGRA(data, width, height, stride, &output); - else - output_size = WebPEncodeBGRA(data, width, height, stride, quality, &output); - break; - default: - throw image_save_error("Invalid WebP color type"); + throw image_save_error("jxl_saver: JxlEncoderSetColorEncoding failed"); } - if (output_size > 0) + JxlEncoderFrameSettings* frame_settings = JxlEncoderFrameSettingsCreate(enc.get(), nullptr); + const float distance = JxlEncoderDistanceFromQuality(quality); + if (JXL_ENC_SUCCESS != JxlEncoderSetFrameDistance(frame_settings, distance)) { - fout.write(reinterpret_cast(output), output_size); - if (!fout.good()) + throw image_save_error("jxl_saver: JxlEncoderSetFrameDistance failed"); + } + if (num_channels > 3) + { + if (JXL_ENC_SUCCESS != JxlEncoderSetExtraChannelDistance(frame_settings, 0, distance)) { - WebPFree(output); - throw image_save_error("Error while writing WebP image to " + filename + "."); + throw image_save_error("jxl_saver: JxlEncoderSetExtraChannelDistance failed"); } } - else + // if (quality == 100 || distance < 0.01) + // { + // if (JXL_ENC_SUCCESS != JxlEncoderSetFrameLossless(frame_settings, JXL_TRUE)) + // { + // throw image_save_error("jxl_saver: JxlEncoderSetFrameLossless failed"); + // } + // } + + void* pixels_data = reinterpret_cast(const_cast(pixels)); + const size_t pixels_size = width * height * num_channels; + if (JXL_ENC_SUCCESS != JxlEncoderAddImageFrame(frame_settings, &pixel_format, pixels_data, pixels_size)) + { + throw image_save_error("jxl_saver: JxlEncoderAddImageFrame failed"); + } + JxlEncoderCloseInput(enc.get()); + + std::vector compressed; + compressed.resize(64); + uint8_t* next_out = compressed.data(); + size_t avail_out = compressed.size() - (next_out - compressed.data()); + JxlEncoderStatus process_result = JXL_ENC_NEED_MORE_OUTPUT; + while (process_result == JXL_ENC_NEED_MORE_OUTPUT) + { + process_result = JxlEncoderProcessOutput(enc.get(), &next_out, &avail_out); + if (process_result == JXL_ENC_NEED_MORE_OUTPUT) + { + size_t offset = next_out - compressed.data(); + compressed.resize(compressed.size() * 2); + next_out = compressed.data() + offset; + avail_out = compressed.size() - offset; + } + } + compressed.resize(next_out - compressed.data()); + if (JXL_ENC_SUCCESS != process_result) + { + throw image_save_error("jxl_saver: JxlEncoderProcessOutput failed"); + } + fout.write(reinterpret_cast(compressed.data()), compressed.size()); + if (!fout.good()) { - throw image_save_error("Error while encoding WebP image to " + filename + "."); + throw image_save_error("Error while writing JPEG XL image to " + filename + "."); } - WebPFree(output); } } diff --git a/dlib/image_saver/save_jxl.h b/dlib/image_saver/save_jxl.h index 345748a9f2..e818a17b96 100644 --- a/dlib/image_saver/save_jxl.h +++ b/dlib/image_saver/save_jxl.h @@ -20,22 +20,13 @@ namespace dlib namespace impl { - enum class webp_type - { - rgb, - bgr, - rgba, - bgra - }; - - void impl_save_webp ( + void impl_save_jxl ( const std::string& filename, const uint8_t* data, - const int width, - const int height, - const int stride, - const float quality, - const webp_type type + const uint32_t width, + const uint32_t height, + const uint32_t num_channels, + const float quality ); } @@ -44,20 +35,20 @@ namespace dlib template < typename image_type > - typename disable_if>::type save_webp ( + typename disable_if>::type save_jxl ( const image_type& img_, const std::string& filename, - float quality = 75 + const float quality = 75 ) { #ifndef DLIB_JPEGXL_SUPPORT /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - You are getting this error because you are trying to use the save_webp + You are getting this error because you are trying to use the save_jxl function but you haven't defined DLIB_JPEGXL_SUPPORT. You must do so to use this object. You must also make sure you set your build environment - to link against the libwebp library. + to link against the libjxl library. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ - static_assert(sizeof(image_type) == 0, "webp support not enabled."); + static_assert(sizeof(image_type) == 0, "JPEG XL support not enabled."); #endif const_image_view img(img_); using pixel_type = typename image_traits::pixel_type; @@ -67,8 +58,8 @@ namespace dlib "\t save_jxl()" << "\n\t You can't save an empty image as a JPEG XL." ); - DLIB_CASSERT(0 <= quality, - "\t save_webp()" + DLIB_CASSERT(0 < quality || quality > 100, + "\t save_jxl()" << "\n\t Invalid quality value." << "\n\t quality: " << quality ); @@ -76,30 +67,21 @@ namespace dlib auto data = reinterpret_cast(image_data(img)); const int width = img.nc(); const int height = img.nr(); - int stride = width_step(img); if (pixel_traits::rgb_alpha) { - if (pixel_traits::bgr_layout) - impl::impl_save_webp(filename, data, width, height, stride, quality, impl::webp_type::bgra); - else - impl::impl_save_webp(filename, data, width, height, stride, quality, impl::webp_type::rgba); + impl::impl_save_jxl(filename, data, width, height, 4, quality); } else if (pixel_traits::rgb) { - if (pixel_traits::bgr_layout) - impl::impl_save_webp(filename, data, width, height, stride, quality, impl::webp_type::bgr); - else - impl::impl_save_webp(filename, data, width, height, stride, quality, impl::webp_type::rgb); + impl::impl_save_jxl(filename, data, width, height, 3, quality); } else { // This is some other kind of color image so just save it as an RGB image. - // We also need to recompute the stride in case we were given a grayscale image. array2d temp; assign_image(temp, img); - stride = width_step(temp); auto data = reinterpret_cast(image_data(temp)); - impl::impl_save_webp(filename, data, width, height, stride, quality, impl::webp_type::rgb); + impl::impl_save_jxl(filename, data, width, height, 3, quality); } } @@ -108,15 +90,15 @@ namespace dlib template < typename EXP > - void save_webp( + void save_jxl( const matrix_exp& img, const std::string& filename, - float quality = 75 + uint32_t quality = 75 ) { array2d temp; assign_image(temp, img); - save_webp(temp, filename, quality); + save_jxl(temp, filename, quality); } // ---------------------------------------------------------------------------------------- diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 5ee8076428..61af199179 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -123,7 +123,6 @@ macro(add_gui_example name) endif() endmacro() -add_example(test_jxl_ex) add_example(dnn_metric_learning_ex) add_gui_example(dnn_face_recognition_ex) add_example(dnn_introduction_ex) From 3163dee91991ed2e35991e580897740de60c202e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Sat, 24 Feb 2024 21:55:53 +0900 Subject: [PATCH 05/28] everything works except setting lossless explicitly --- dlib/image_loader/jxl_loader_abstract.h | 8 +-- dlib/image_saver/save_jxl.cpp | 73 +++++++++++++++++++------ dlib/image_saver/save_jxl.h | 2 +- dlib/image_saver/save_jxl_abstract.h | 23 ++++---- 4 files changed, 73 insertions(+), 33 deletions(-) diff --git a/dlib/image_loader/jxl_loader_abstract.h b/dlib/image_loader/jxl_loader_abstract.h index add36c631d..99e373129a 100644 --- a/dlib/image_loader/jxl_loader_abstract.h +++ b/dlib/image_loader/jxl_loader_abstract.h @@ -1,7 +1,7 @@ -// Copyright (C) 2022 Davis E. King (davis@dlib.net), Martin Sandsmark, Adrià Arrufat +// Copyright (C) 2024 Davis E. King (davis@dlib.net), Martin Sandsmark, Adrià Arrufat // License: Boost Software License See LICENSE.txt for the full license. -#undef DLIB_JPEGXL_IMPORT_ABSTRACT -#ifdef DLIB_JPEGXL_IMPORT_ABSTRACT +#undef DLIB_JXL_IMPORT_ABSTRACT +#ifdef DLIB_JXL_IMPORT_ABSTRACT #include "image_loader_abstract.h" #include "../algs.h" @@ -151,5 +151,5 @@ namespace dlib } -#endif // DLIB_JPEGXL_IMPORT_ABSTRACT +#endif // DLIB_JXL_IMPORT_ABSTRACT diff --git a/dlib/image_saver/save_jxl.cpp b/dlib/image_saver/save_jxl.cpp index 24f8a4f461..f9447096cc 100644 --- a/dlib/image_saver/save_jxl.cpp +++ b/dlib/image_saver/save_jxl.cpp @@ -10,7 +10,7 @@ #include "image_saver.h" #include #include -#include +#include namespace dlib { @@ -34,9 +34,15 @@ namespace dlib { } auto enc = JxlEncoderMake(nullptr); - auto runner = JxlThreadParallelRunnerMake(nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads()); + if (JXL_ENC_SUCCESS != JxlEncoderUseContainer(enc.get(), JXL_TRUE)) + { + throw image_save_error("jxl_saver: JxlEncoderUseContainer failed"); + } + + auto runner = JxlResizableParallelRunnerMake(nullptr); + JxlResizableParallelRunnerSetThreads(runner.get(), JxlResizableParallelRunnerSuggestThreads(width, height)); - if (JXL_ENC_SUCCESS != JxlEncoderSetParallelRunner(enc.get(), JxlThreadParallelRunner, runner.get())) + if (JXL_ENC_SUCCESS != JxlEncoderSetParallelRunner(enc.get(), JxlResizableParallelRunner, runner.get())) { throw image_save_error("jxl_saver: JxlResizableParallelRunner failed"); } @@ -53,10 +59,22 @@ namespace dlib { basic_info.ysize = height; basic_info.bits_per_sample = 8; basic_info.uses_original_profile = JXL_FALSE; - if (num_channels > 3) + switch (num_channels) { + case 3: + basic_info.num_color_channels = 3; + basic_info.num_extra_channels = 0; + basic_info.alpha_bits = 0; + basic_info.alpha_exponent_bits = 0; + break; + case 4: + basic_info.num_color_channels = 3; basic_info.num_extra_channels = 1; - basic_info.alpha_bits = 8; + basic_info.alpha_bits = basic_info.bits_per_sample; + basic_info.alpha_exponent_bits = 0; + break; + default: + throw ("jxl_saver: unsupported number of channels"); } if (JXL_ENC_SUCCESS != JxlEncoderSetBasicInfo(enc.get(), &basic_info)) @@ -65,32 +83,53 @@ namespace dlib { } JxlColorEncoding color_encoding = {}; - JxlColorEncodingSetToSRGB(&color_encoding, /* is_gray = */ num_channels < 3); + JxlColorEncodingSetToSRGB(&color_encoding, /* is_gray = */ JXL_FALSE); if (JXL_ENC_SUCCESS != JxlEncoderSetColorEncoding(enc.get(), &color_encoding)) { throw image_save_error("jxl_saver: JxlEncoderSetColorEncoding failed"); } JxlEncoderFrameSettings* frame_settings = JxlEncoderFrameSettingsCreate(enc.get(), nullptr); + JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_DECODING_SPEED, 0); + const float distance = JxlEncoderDistanceFromQuality(quality); - if (JXL_ENC_SUCCESS != JxlEncoderSetFrameDistance(frame_settings, distance)) - { - throw image_save_error("jxl_saver: JxlEncoderSetFrameDistance failed"); - } - if (num_channels > 3) - { - if (JXL_ENC_SUCCESS != JxlEncoderSetExtraChannelDistance(frame_settings, 0, distance)) - { - throw image_save_error("jxl_saver: JxlEncoderSetExtraChannelDistance failed"); - } - } // if (quality == 100 || distance < 0.01) // { + // if (JXL_ENC_SUCCESS != JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 7)) + // { + // throw image_save_error("jxl_saver: JxlEncoderFrameSettingsSetOption failed"); + // } + // if (JXL_ENC_SUCCESS != JxlEncoderSetFrameDistance(frame_settings, 0)) + // { + // throw image_save_error("jxl_saver: JxlEncoderSetFrameDistance failed"); + // } // if (JXL_ENC_SUCCESS != JxlEncoderSetFrameLossless(frame_settings, JXL_TRUE)) // { // throw image_save_error("jxl_saver: JxlEncoderSetFrameLossless failed"); // } // } + // else + { + if (JXL_ENC_SUCCESS != JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 3)) + { + throw image_save_error("jxl_saver: JxlEncoderFrameSettingsSetOption failed"); + } + // if (JXL_ENC_SUCCESS != JxlEncoderSetFrameLossless(frame_settings, JXL_FALSE)) + // { + // throw image_save_error("jxl_saver: JxlEncoderSetFrameLossless false failed"); + // } + if (JXL_ENC_SUCCESS != JxlEncoderSetFrameDistance(frame_settings, distance)) + { + throw image_save_error("jxl_saver: JxlEncoderSetFrameDistance failed"); + } + if (basic_info.alpha_bits > 0) + { + if (JXL_ENC_SUCCESS != JxlEncoderSetExtraChannelDistance(frame_settings, 0, distance)) + { + throw image_save_error("jxl_saver: JxlEncoderSetExtraChannelDistance failed"); + } + } + } void* pixels_data = reinterpret_cast(const_cast(pixels)); const size_t pixels_size = width * height * num_channels; diff --git a/dlib/image_saver/save_jxl.h b/dlib/image_saver/save_jxl.h index e818a17b96..5dd195a43a 100644 --- a/dlib/image_saver/save_jxl.h +++ b/dlib/image_saver/save_jxl.h @@ -93,7 +93,7 @@ namespace dlib void save_jxl( const matrix_exp& img, const std::string& filename, - uint32_t quality = 75 + uint32_t quality = 90 ) { array2d temp; diff --git a/dlib/image_saver/save_jxl_abstract.h b/dlib/image_saver/save_jxl_abstract.h index 845515955f..70d038acdb 100644 --- a/dlib/image_saver/save_jxl_abstract.h +++ b/dlib/image_saver/save_jxl_abstract.h @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Davis E. King (davis@dlib.net), Adrià Arrufat +// Copyright (C) 2024 Davis E. King (davis@dlib.net), Adrià Arrufat // License: Boost Software License See LICENSE.txt for the full license. #undef DLIB_SAVE_JXL_ABSTRACT_Hh_ #ifdef DLIB_SAVE_JXL_ABSTRACT_Hh_ @@ -15,10 +15,10 @@ namespace dlib template < typename image_type > - void save_webp ( + void save_jxl ( const image_type& img, const std::string& filename, - float quality = 75 + float quality = 90 ); /*! requires @@ -27,18 +27,19 @@ namespace dlib - image.size() != 0 - quality >= 0 ensures - - writes the image to the file indicated by filename in the WEBP format. + - writes the image to the file indicated by filename in the JPEG XL format. - image[0][0] will be in the upper left corner of the image. - image[image.nr()-1][image.nc()-1] will be in the lower right corner of the image. - This routine can save images containing any type of pixel. However, - save_webp() can only natively store rgb_pixel, bgr_pixel, rgb_alpha_pixel and - bgr_alpha_pixel pixel types. All other pixel types will be converted into one - of these types as appropriate before being saved to disk. + save_jxl() can only natively store rgb_pixel, and rgb_alpha_pixel pixel types. + All other pixel types will be converted into one of these types as appropriate + before being saved to disk. - The quality value determines how lossy the compression is. Larger quality - values result in larger output images but the images will look better. A value - between 0 and 100 will use lossy compression, while any value larger than - 100 will perform lossless compression. + values result in larger output images but the images will look better. + Although it can range from 0 to 100, the recommended range is between 68 and 96. + A value of 90 means visually lossless, while a value of 100 means mathematically + lossless. throws - image_save_error This exception is thrown if there is an error that prevents us from saving @@ -50,5 +51,5 @@ namespace dlib } -#endif // DLIB_SAVE_WEBP_ABSTRACT_Hh_ +#endif // DLIB_SAVE_JXL_ABSTRACT_Hh_ From 8dac095bcf9a8a1bdc730fdebf463bcc7fbd3f38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Sat, 24 Feb 2024 23:35:40 +0900 Subject: [PATCH 06/28] remove unused header --- dlib/cmake_utils/test_for_libjxl/libjxl_test.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/dlib/cmake_utils/test_for_libjxl/libjxl_test.cpp b/dlib/cmake_utils/test_for_libjxl/libjxl_test.cpp index 49cb37219c..9ec6240d04 100644 --- a/dlib/cmake_utils/test_for_libjxl/libjxl_test.cpp +++ b/dlib/cmake_utils/test_for_libjxl/libjxl_test.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include From e24f2d28c388e9b9b877f8a9ffa43c228cd6e29e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Sun, 25 Feb 2024 19:14:43 +0900 Subject: [PATCH 07/28] fix wrong quality logic --- dlib/image_saver/save_jxl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dlib/image_saver/save_jxl.h b/dlib/image_saver/save_jxl.h index 5dd195a43a..d886473b1b 100644 --- a/dlib/image_saver/save_jxl.h +++ b/dlib/image_saver/save_jxl.h @@ -38,7 +38,7 @@ namespace dlib typename disable_if>::type save_jxl ( const image_type& img_, const std::string& filename, - const float quality = 75 + const float quality = 90 ) { #ifndef DLIB_JPEGXL_SUPPORT @@ -58,7 +58,7 @@ namespace dlib "\t save_jxl()" << "\n\t You can't save an empty image as a JPEG XL." ); - DLIB_CASSERT(0 < quality || quality > 100, + DLIB_CASSERT(0 <= quality && quality <= 100, "\t save_jxl()" << "\n\t Invalid quality value." << "\n\t quality: " << quality From 83df6514ea445f18f8a1d313ce90ce59682b82c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Sun, 25 Feb 2024 19:16:14 +0900 Subject: [PATCH 08/28] remove debugging statements --- dlib/image_loader/jxl_loader.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/dlib/image_loader/jxl_loader.cpp b/dlib/image_loader/jxl_loader.cpp index 4cd1c78841..ed5de2e3c0 100644 --- a/dlib/image_loader/jxl_loader.cpp +++ b/dlib/image_loader/jxl_loader.cpp @@ -136,15 +136,12 @@ namespace dlib size_t buffer_size; if (JXL_DEC_SUCCESS != JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) { - std::cout << "buffer_size = " << buffer_size << '\n'; - std::cout << "actual_size = " << width * height * num_channels << '\n'; throw image_load_error("jxl_loader: JxlDecoderImageOutBufferSize failed"); } if (buffer_size != width * height * num_channels) { throw image_load_error("jxl_loader: invalid output buffer size"); } - std::cout << "buffer size: " << buffer_size << '\n'; if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format, out, out_size)) { throw image_load_error("jxl_loader: JxlDecoderSetImageOutBuffer failed"); From 4551df1f7b000ccfed384746dec3dd8bab6d0e42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Sun, 25 Feb 2024 19:45:39 +0900 Subject: [PATCH 09/28] fix lossless encoding --- dlib/image_saver/save_jxl.cpp | 52 ++++++++++++++++------------------- 1 file changed, 23 insertions(+), 29 deletions(-) diff --git a/dlib/image_saver/save_jxl.cpp b/dlib/image_saver/save_jxl.cpp index f9447096cc..87587d8bda 100644 --- a/dlib/image_saver/save_jxl.cpp +++ b/dlib/image_saver/save_jxl.cpp @@ -58,7 +58,7 @@ namespace dlib { basic_info.xsize = width; basic_info.ysize = height; basic_info.bits_per_sample = 8; - basic_info.uses_original_profile = JXL_FALSE; + basic_info.uses_original_profile = quality == 100; switch (num_channels) { case 3: @@ -93,42 +93,36 @@ namespace dlib { JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_DECODING_SPEED, 0); const float distance = JxlEncoderDistanceFromQuality(quality); - // if (quality == 100 || distance < 0.01) - // { - // if (JXL_ENC_SUCCESS != JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 7)) - // { - // throw image_save_error("jxl_saver: JxlEncoderFrameSettingsSetOption failed"); - // } - // if (JXL_ENC_SUCCESS != JxlEncoderSetFrameDistance(frame_settings, 0)) - // { - // throw image_save_error("jxl_saver: JxlEncoderSetFrameDistance failed"); - // } - // if (JXL_ENC_SUCCESS != JxlEncoderSetFrameLossless(frame_settings, JXL_TRUE)) - // { - // throw image_save_error("jxl_saver: JxlEncoderSetFrameLossless failed"); - // } - // } - // else + if (JXL_ENC_SUCCESS != JxlEncoderSetFrameDistance(frame_settings, distance)) { - if (JXL_ENC_SUCCESS != JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 3)) + throw image_save_error("jxl_saver: JxlEncoderSetFrameDistance failed"); + } + if (basic_info.alpha_bits > 0) + { + if (JXL_ENC_SUCCESS != JxlEncoderSetExtraChannelDistance(frame_settings, 0, distance)) + { + throw image_save_error("jxl_saver: JxlEncoderSetExtraChannelDistance failed"); + } + } + // explictly enable lossless mode + if (distance == 0) + { + if (JXL_ENC_SUCCESS != JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 7)) { throw image_save_error("jxl_saver: JxlEncoderFrameSettingsSetOption failed"); } - // if (JXL_ENC_SUCCESS != JxlEncoderSetFrameLossless(frame_settings, JXL_FALSE)) - // { - // throw image_save_error("jxl_saver: JxlEncoderSetFrameLossless false failed"); - // } - if (JXL_ENC_SUCCESS != JxlEncoderSetFrameDistance(frame_settings, distance)) + if (JXL_ENC_SUCCESS != JxlEncoderSetFrameLossless(frame_settings, JXL_TRUE)) { - throw image_save_error("jxl_saver: JxlEncoderSetFrameDistance failed"); + throw image_save_error("jxl_saver: JxlEncoderSetFrameLossless failed"); } - if (basic_info.alpha_bits > 0) + } + else + { + if (JXL_ENC_SUCCESS != JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 3)) { - if (JXL_ENC_SUCCESS != JxlEncoderSetExtraChannelDistance(frame_settings, 0, distance)) - { - throw image_save_error("jxl_saver: JxlEncoderSetExtraChannelDistance failed"); - } + throw image_save_error("jxl_saver: JxlEncoderFrameSettingsSetOption failed"); } + } void* pixels_data = reinterpret_cast(const_cast(pixels)); From 6a37a3410bf1a2e9602834dab5634c914cf49f54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Sun, 25 Feb 2024 21:04:26 +0900 Subject: [PATCH 10/28] improve support for grayscale images --- dlib/image_loader/jxl_loader.cpp | 7 +++--- dlib/image_loader/jxl_loader.h | 43 +++++++++++++++++++------------- dlib/image_saver/save_jxl.cpp | 8 +++++- dlib/image_saver/save_jxl.h | 30 ++++++++++++++-------- 4 files changed, 56 insertions(+), 32 deletions(-) diff --git a/dlib/image_loader/jxl_loader.cpp b/dlib/image_loader/jxl_loader.cpp index ed5de2e3c0..c2f9ad8b3a 100644 --- a/dlib/image_loader/jxl_loader.cpp +++ b/dlib/image_loader/jxl_loader.cpp @@ -90,10 +90,11 @@ namespace dlib } width = basic_info.xsize; height = basic_info.ysize; + depth = basic_info.num_color_channels + basic_info.num_extra_channels; } // ---------------------------------------------------------------------------------------- - void jxl_loader::decode(unsigned char* out, const size_t out_size, const long num_channels) const + void jxl_loader::decode(unsigned char* out, const size_t out_size) const { auto runner = JxlResizableParallelRunnerMake(nullptr); auto dec = JxlDecoderMake(nullptr); @@ -114,7 +115,7 @@ namespace dlib JxlDecoderCloseInput(dec.get()); JxlPixelFormat format = { - .num_channels = static_cast(num_channels), + .num_channels = depth, .data_type = JXL_TYPE_UINT8, .endianness = JXL_NATIVE_ENDIAN, .align=0 @@ -138,7 +139,7 @@ namespace dlib { throw image_load_error("jxl_loader: JxlDecoderImageOutBufferSize failed"); } - if (buffer_size != width * height * num_channels) + if (buffer_size != width * height * depth) { throw image_load_error("jxl_loader: invalid output buffer size"); } diff --git a/dlib/image_loader/jxl_loader.h b/dlib/image_loader/jxl_loader.h index dbb9ef0efc..e7f0bb6b88 100644 --- a/dlib/image_loader/jxl_loader.h +++ b/dlib/image_loader/jxl_loader.h @@ -39,35 +39,42 @@ namespace dlib vimg.set_size(height, width); using pixel_type = typename image_traits::pixel_type; - // Fast path: rgb, rgb_alpha. - if (pixel_traits::rgb || pixel_traits::rgb_alpha) + // Fast path: rgb, rgb_alpha, grayscale with matching input depth + if (pixel_traits::grayscale && depth == 1 || + pixel_traits::rgb && depth == 3 || + pixel_traits::rgb_alpha && depth == 4) { - const long num_channels = pixel_traits::rgb_alpha ? 4 : 3; - const size_t output_size = width * height * num_channels; + const size_t output_size = width * height * depth; unsigned char* output = reinterpret_cast(image_data(vimg)); - decode(output, output_size, num_channels); + decode(output, output_size); return; } - // Manual decoding - array2d decoded; - decoded.set_size(height, width); - unsigned char* output = reinterpret_cast(image_data(decoded)); - decode(output, width * height * 4, 4); - for (int r = 0; r < height; ++r) + // Manual decoding: we still need to handle the case wether the input data has alpha. + if (depth == 4) { - for (int c = 0; c < width; ++c) - { - assign_pixel(vimg[r][c], decoded[r][c]); - } + array2d decoded; + decoded.set_size(height, width); + unsigned char* output = reinterpret_cast(image_data(decoded)); + decode(output, width * height * depth); + assign_image(vimg, decoded); + } + else + { + array2d decoded; + decoded.set_size(height, width); + unsigned char* output = reinterpret_cast(image_data(decoded)); + decode(output, width * height * depth); + assign_image(vimg, decoded); } } private: void get_info(); - void decode(unsigned char *out, const size_t out_size, const long num_channels) const; - long height; - long width; + void decode(unsigned char *out, const size_t out_size) const; + uint32_t height; + uint32_t width; + uint32_t depth; std::vector data; }; diff --git a/dlib/image_saver/save_jxl.cpp b/dlib/image_saver/save_jxl.cpp index 87587d8bda..68064ec2ae 100644 --- a/dlib/image_saver/save_jxl.cpp +++ b/dlib/image_saver/save_jxl.cpp @@ -61,6 +61,12 @@ namespace dlib { basic_info.uses_original_profile = quality == 100; switch (num_channels) { + case 1: + basic_info.num_color_channels = 1; + basic_info.num_extra_channels = 0; + basic_info.alpha_bits = 0; + basic_info.alpha_exponent_bits = 0; + break; case 3: basic_info.num_color_channels = 3; basic_info.num_extra_channels = 0; @@ -83,7 +89,7 @@ namespace dlib { } JxlColorEncoding color_encoding = {}; - JxlColorEncodingSetToSRGB(&color_encoding, /* is_gray = */ JXL_FALSE); + JxlColorEncodingSetToSRGB(&color_encoding, /* is_gray = */ num_channels < 3); if (JXL_ENC_SUCCESS != JxlEncoderSetColorEncoding(enc.get(), &color_encoding)) { throw image_save_error("jxl_saver: JxlEncoderSetColorEncoding failed"); diff --git a/dlib/image_saver/save_jxl.h b/dlib/image_saver/save_jxl.h index d886473b1b..e3fdfa63fa 100644 --- a/dlib/image_saver/save_jxl.h +++ b/dlib/image_saver/save_jxl.h @@ -67,21 +67,31 @@ namespace dlib auto data = reinterpret_cast(image_data(img)); const int width = img.nc(); const int height = img.nr(); - if (pixel_traits::rgb_alpha) + const int depth = pixel_traits::num; + // Fast path: rgb, rgb_alpha, grayscale + if (pixel_traits::rgb || + pixel_traits::rgb_alpha || + pixel_traits::grayscale) { - impl::impl_save_jxl(filename, data, width, height, 4, quality); - } - else if (pixel_traits::rgb) - { - impl::impl_save_jxl(filename, data, width, height, 3, quality); + impl::impl_save_jxl(filename, data, width, height, depth, quality); } else { // This is some other kind of color image so just save it as an RGB image. - array2d temp; - assign_image(temp, img); - auto data = reinterpret_cast(image_data(temp)); - impl::impl_save_jxl(filename, data, width, height, 3, quality); + if (pixel_traits::has_alpha) + { + array2d temp; + assign_image(temp, img); + auto data = reinterpret_cast(image_data(temp)); + impl::impl_save_jxl(filename, data, width, height, depth, quality); + } + else + { + array2d temp; + assign_image(temp, img); + auto data = reinterpret_cast(image_data(temp)); + impl::impl_save_jxl(filename, data, width, height, depth, quality); + } } } From c7a6bd7c68fee4b2a3506a314732ee7a022fb88b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Sun, 25 Feb 2024 21:24:05 +0900 Subject: [PATCH 11/28] use JXL instead of JPEGXL everywhere --- dlib/CMakeLists.txt | 16 ++++++++-------- dlib/cmake_utils/test_for_libjxl/CMakeLists.txt | 4 ++-- dlib/config.h.in | 2 +- dlib/image_loader/jxl_loader.cpp | 8 +++----- dlib/image_loader/jxl_loader.h | 10 +++++----- dlib/image_loader/load_image.h | 2 +- dlib/image_saver/save_jxl.cpp | 6 +++--- dlib/image_saver/save_jxl.h | 4 ++-- 8 files changed, 25 insertions(+), 27 deletions(-) diff --git a/dlib/CMakeLists.txt b/dlib/CMakeLists.txt index 9ef65faa90..36dab9fb3b 100644 --- a/dlib/CMakeLists.txt +++ b/dlib/CMakeLists.txt @@ -179,7 +179,7 @@ if (NOT TARGET dlib) "Disable this if you don't want to link against libjpeg" ) set (DLIB_WEBP_SUPPORT_STR "Disable this if you don't want to link against libwebp" ) - set (DLIB_JPEGXL_SUPPORT_STR + set (DLIB_JXL_SUPPORT_STR "Disable this if you don't want to link against libjxl" ) set (DLIB_LINK_WITH_SQLITE3_STR "Disable this if you don't want to link against sqlite3" ) @@ -239,7 +239,7 @@ if (NOT TARGET dlib) option(DLIB_PNG_SUPPORT ${DLIB_PNG_SUPPORT_STR} OFF) option(DLIB_GIF_SUPPORT ${DLIB_GIF_SUPPORT_STR} OFF) option(DLIB_WEBP_SUPPORT ${DLIB_WEBP_SUPPORT_STR} OFF) - option(DLIB_JPEGXL_SUPPORT ${DLIB_JPEGXL_SUPPORT_STR} OFF) + option(DLIB_JXL_SUPPORT ${DLIB_JXL_SUPPORT_STR} OFF) #option(DLIB_USE_FFTW ${DLIB_USE_FFTW_STR} OFF) option(DLIB_USE_MKL_FFT ${DLIB_USE_MKL_FFT_STR} OFF) option(DLIB_USE_FFMPEG ${DLIB_USE_FFMPEG_STR} OFF) @@ -253,7 +253,7 @@ if (NOT TARGET dlib) option(DLIB_PNG_SUPPORT ${DLIB_PNG_SUPPORT_STR} ON) option(DLIB_GIF_SUPPORT ${DLIB_GIF_SUPPORT_STR} ON) option(DLIB_WEBP_SUPPORT ${DLIB_WEBP_SUPPORT_STR} ON) - option(DLIB_JPEGXL_SUPPORT ${DLIB_JPEGXL_SUPPORT_STR} ON) + option(DLIB_JXL_SUPPORT ${DLIB_JXL_SUPPORT_STR} ON) #option(DLIB_USE_FFTW ${DLIB_USE_FFTW_STR} ON) option(DLIB_USE_MKL_FFT ${DLIB_USE_MKL_FFT_STR} ON) option(DLIB_USE_FFMPEG ${DLIB_USE_FFMPEG_STR} ON) @@ -265,7 +265,7 @@ if (NOT TARGET dlib) toggle_preprocessor_switch(DLIB_PNG_SUPPORT) toggle_preprocessor_switch(DLIB_GIF_SUPPORT) toggle_preprocessor_switch(DLIB_WEBP_SUPPORT) - toggle_preprocessor_switch(DLIB_JPEGXL_SUPPORT) + toggle_preprocessor_switch(DLIB_JXL_SUPPORT) #toggle_preprocessor_switch(DLIB_USE_FFTW) toggle_preprocessor_switch(DLIB_USE_MKL_FFT) toggle_preprocessor_switch(DLIB_USE_FFMPEG) @@ -581,7 +581,7 @@ if (NOT TARGET dlib) toggle_preprocessor_switch(DLIB_WEBP_SUPPORT) endif() endif() - if (DLIB_JPEGXL_SUPPORT) + if (DLIB_JXL_SUPPORT) include(cmake_utils/find_libjxl.cmake) if (JXL_FOUND) list (APPEND dlib_needed_private_includes ${JXL_INCLUDE_DIRS}) @@ -592,10 +592,10 @@ if (NOT TARGET dlib) image_loader/jxl_loader.cpp image_saver/save_jxl.cpp ) - enable_preprocessor_switch(DLIB_JPEGXL_SUPPORT) + enable_preprocessor_switch(DLIB_JXL_SUPPORT) else() - set(DLIB_JPEGXL_SUPPORT OFF CACHE BOOL ${DLIB_JPEGXL_SUPPORT_STR} FORCE) - disable_preprocessor_switch(DLIB_JPEGXL_SUPPORT) + set(DLIB_JXL_SUPPORT OFF CACHE BOOL ${DLIB_JXL_SUPPORT_STR} FORCE) + disable_preprocessor_switch(DLIB_JXL_SUPPORT) endif() endif() diff --git a/dlib/cmake_utils/test_for_libjxl/CMakeLists.txt b/dlib/cmake_utils/test_for_libjxl/CMakeLists.txt index 9bb33422e7..c91b5d8ff6 100644 --- a/dlib/cmake_utils/test_for_libjxl/CMakeLists.txt +++ b/dlib/cmake_utils/test_for_libjxl/CMakeLists.txt @@ -2,6 +2,6 @@ cmake_minimum_required(VERSION 3.8.0) project(test_if_libjxl_is_broken) -include_directories(${JPEGXL_INCLUDE_DIR}) +include_directories(${JXL_INCLUDE_DIR}) add_executable(libjxl_test libjxl_test.cpp) -target_link_libraries(libjxl_test ${JPEGXL_LIBRARY}) +target_link_libraries(libjxl_test ${JXL_LIBRARY}) diff --git a/dlib/config.h.in b/dlib/config.h.in index 8d0b1daafb..1e21ee72af 100644 --- a/dlib/config.h.in +++ b/dlib/config.h.in @@ -23,7 +23,7 @@ #cmakedefine DLIB_PNG_SUPPORT #cmakedefine DLIB_GIF_SUPPORT #cmakedefine DLIB_WEBP_SUPPORT -#cmakedefine DLIB_JPEGXL_SUPPORT +#cmakedefine DLIB_JXL_SUPPORT #cmakedefine DLIB_USE_FFTW #cmakedefine DLIB_USE_BLAS #cmakedefine DLIB_USE_LAPACK diff --git a/dlib/image_loader/jxl_loader.cpp b/dlib/image_loader/jxl_loader.cpp index c2f9ad8b3a..aa9282f90c 100644 --- a/dlib/image_loader/jxl_loader.cpp +++ b/dlib/image_loader/jxl_loader.cpp @@ -3,8 +3,8 @@ #ifndef DLIB_JXL_LOADER_CPp_ #define DLIB_JXL_LOADER_CPp_ -// only do anything with this file if DLIB_JPEGXL_SUPPORT is defined -#ifdef DLIB_JPEGXL_SUPPORT +// only do anything with this file if DLIB_JXL_SUPPORT is defined +#ifdef DLIB_JXL_SUPPORT #include "jxl_loader.h" #include #include @@ -166,7 +166,5 @@ namespace dlib } } -#endif // DLIB_JPEG_SUPPORT - +#endif // DLIB_JXL_SUPPORT #endif // DLIB_JXL_LOADER_CPp_ - diff --git a/dlib/image_loader/jxl_loader.h b/dlib/image_loader/jxl_loader.h index e7f0bb6b88..d4706e8843 100644 --- a/dlib/image_loader/jxl_loader.h +++ b/dlib/image_loader/jxl_loader.h @@ -1,7 +1,7 @@ // Copyright (C) 2022 Davis E. King (davis@dlib.net), Martin Sandsmark, Adrià Arrufat // License: Boost Software License See LICENSE.txt for the full license. -#ifndef DLIB_JPEGXL_IMPORT -#define DLIB_JPEGXL_IMPORT +#ifndef DLIB_JXL_IMPORT +#define DLIB_JXL_IMPORT #include @@ -26,10 +26,10 @@ namespace dlib template void get_image(image_type& image) const { -#ifndef DLIB_JPEGXL_SUPPORT +#ifndef DLIB_JXL_SUPPORT /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! You are getting this error because you are trying to use the jxl_loader - object but you haven't defined DLIB_JPEGXL_SUPPORT. You must do so to use + object but you haven't defined DLIB_JXL_SUPPORT. You must do so to use this object. You must also make sure you set your build environment to link against the libjxl library. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ @@ -123,6 +123,6 @@ namespace dlib #include "jxl_loader.cpp" #endif -#endif // DLIB_JPEGXL_IMPORT +#endif // DLIB_JXL_IMPORT diff --git a/dlib/image_loader/load_image.h b/dlib/image_loader/load_image.h index 8fd340dd87..d6130b01fd 100644 --- a/dlib/image_loader/load_image.h +++ b/dlib/image_loader/load_image.h @@ -96,7 +96,7 @@ namespace dlib #ifdef DLIB_WEBP_SUPPORT case image_file_type::WEBP: load_webp(image, file_name); return; #endif -#ifdef DLIB_JPEGXL_SUPPORT +#ifdef DLIB_JXL_SUPPORT case image_file_type::JXL: load_jxl(image, file_name); return; #endif #ifdef DLIB_GIF_SUPPORT diff --git a/dlib/image_saver/save_jxl.cpp b/dlib/image_saver/save_jxl.cpp index 68064ec2ae..43a4a6ea1b 100644 --- a/dlib/image_saver/save_jxl.cpp +++ b/dlib/image_saver/save_jxl.cpp @@ -3,8 +3,8 @@ #ifndef DLIB_JXL_SAVER_CPp_ #define DLIB_JXL_SAVER_CPp_ -// only do anything with this file if DLIB_JPEGXL_SUPPORT is defined -#ifdef DLIB_JPEGXL_SUPPORT +// only do anything with this file if DLIB_JXL_SUPPORT is defined +#ifdef DLIB_JXL_SUPPORT #include "save_jxl.h" #include "image_saver.h" @@ -172,7 +172,7 @@ namespace dlib { } -#endif // DLIB_JPEGXL_SUPPORT +#endif // DLIB_JXL_SUPPORT #endif // DLIB_JXL_SAVER_CPp_ diff --git a/dlib/image_saver/save_jxl.h b/dlib/image_saver/save_jxl.h index e3fdfa63fa..a1377ff80d 100644 --- a/dlib/image_saver/save_jxl.h +++ b/dlib/image_saver/save_jxl.h @@ -41,10 +41,10 @@ namespace dlib const float quality = 90 ) { -#ifndef DLIB_JPEGXL_SUPPORT +#ifndef DLIB_JXL_SUPPORT /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! You are getting this error because you are trying to use the save_jxl - function but you haven't defined DLIB_JPEGXL_SUPPORT. You must do so to use + function but you haven't defined DLIB_JXL_SUPPORT. You must do so to use this object. You must also make sure you set your build environment to link against the libjxl library. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!*/ From 6ce5a4a11a82c492666c5a69cd2ceddf572bb969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Sun, 25 Feb 2024 21:32:33 +0900 Subject: [PATCH 12/28] oops do not make libjxl a requirement --- dlib/cmake_utils/find_libjxl.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dlib/cmake_utils/find_libjxl.cmake b/dlib/cmake_utils/find_libjxl.cmake index 143c16aea3..6a5f3f0a1a 100644 --- a/dlib/cmake_utils/find_libjxl.cmake +++ b/dlib/cmake_utils/find_libjxl.cmake @@ -13,7 +13,7 @@ message(STATUS "Searching for JPEG XL") find_package(PkgConfig) if (PkgConfig_FOUND) - pkg_check_modules(JXL REQUIRED IMPORTED_TARGET libjxl libjxl_cms libjxl_threads) + pkg_check_modules(JXL IMPORTED_TARGET libjxl libjxl_cms libjxl_threads) if (JXL_FOUND) message(STATUS "Found libjxl via pkg-config in `${JXL_LIBRARY_DIRS}`") else() From ea8e3cd02f14f05c001827d668c199633697a38d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Tue, 27 Feb 2024 13:45:29 +0900 Subject: [PATCH 13/28] update years --- dlib/image_loader/jxl_loader.cpp | 2 +- dlib/image_loader/jxl_loader.h | 2 +- dlib/image_loader/jxl_loader_abstract.h | 2 +- dlib/image_saver/save_jxl.cpp | 15 ++------------- dlib/image_saver/save_jxl.h | 2 +- 5 files changed, 6 insertions(+), 17 deletions(-) diff --git a/dlib/image_loader/jxl_loader.cpp b/dlib/image_loader/jxl_loader.cpp index aa9282f90c..e17d807c2c 100644 --- a/dlib/image_loader/jxl_loader.cpp +++ b/dlib/image_loader/jxl_loader.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Davis E. King (davis@dlib.net), Martin Sandsmark, Adrià Arrufat +// Copyright (C) 2024 Davis E. King (davis@dlib.net), Adrià Arrufat // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_JXL_LOADER_CPp_ #define DLIB_JXL_LOADER_CPp_ diff --git a/dlib/image_loader/jxl_loader.h b/dlib/image_loader/jxl_loader.h index d4706e8843..916c033efb 100644 --- a/dlib/image_loader/jxl_loader.h +++ b/dlib/image_loader/jxl_loader.h @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Davis E. King (davis@dlib.net), Martin Sandsmark, Adrià Arrufat +// Copyright (C) 2024 Davis E. King (davis@dlib.net), Adrià Arrufat // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_JXL_IMPORT #define DLIB_JXL_IMPORT diff --git a/dlib/image_loader/jxl_loader_abstract.h b/dlib/image_loader/jxl_loader_abstract.h index 99e373129a..741cb26288 100644 --- a/dlib/image_loader/jxl_loader_abstract.h +++ b/dlib/image_loader/jxl_loader_abstract.h @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Davis E. King (davis@dlib.net), Martin Sandsmark, Adrià Arrufat +// Copyright (C) 2024 Davis E. King (davis@dlib.net), Adrià Arrufat // License: Boost Software License See LICENSE.txt for the full license. #undef DLIB_JXL_IMPORT_ABSTRACT #ifdef DLIB_JXL_IMPORT_ABSTRACT diff --git a/dlib/image_saver/save_jxl.cpp b/dlib/image_saver/save_jxl.cpp index 43a4a6ea1b..567b286938 100644 --- a/dlib/image_saver/save_jxl.cpp +++ b/dlib/image_saver/save_jxl.cpp @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Davis E. King (davis@dlib.net), Adrià Arrufat +// Copyright (C) 2024 Davis E. King (davis@dlib.net), Adrià Arrufat // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_JXL_SAVER_CPp_ #define DLIB_JXL_SAVER_CPp_ @@ -110,26 +110,15 @@ namespace dlib { throw image_save_error("jxl_saver: JxlEncoderSetExtraChannelDistance failed"); } } + // explictly enable lossless mode if (distance == 0) { - if (JXL_ENC_SUCCESS != JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 7)) - { - throw image_save_error("jxl_saver: JxlEncoderFrameSettingsSetOption failed"); - } if (JXL_ENC_SUCCESS != JxlEncoderSetFrameLossless(frame_settings, JXL_TRUE)) { throw image_save_error("jxl_saver: JxlEncoderSetFrameLossless failed"); } } - else - { - if (JXL_ENC_SUCCESS != JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 3)) - { - throw image_save_error("jxl_saver: JxlEncoderFrameSettingsSetOption failed"); - } - - } void* pixels_data = reinterpret_cast(const_cast(pixels)); const size_t pixels_size = width * height * num_channels; diff --git a/dlib/image_saver/save_jxl.h b/dlib/image_saver/save_jxl.h index a1377ff80d..3a235508d0 100644 --- a/dlib/image_saver/save_jxl.h +++ b/dlib/image_saver/save_jxl.h @@ -1,4 +1,4 @@ -// Copyright (C) 2022 Davis E. King (davis@dlib.net), Adrià Arrufat +// Copyright (C) 2024 Davis E. King (davis@dlib.net), Adrià Arrufat // License: Boost Software License See LICENSE.txt for the full license. #ifndef DLIB_SAVE_JXL_Hh_ #define DLIB_SAVE_JXL_Hh_ From bbc8fc5383eebfdfa07df5a7454348052dd0e62e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Tue, 27 Feb 2024 14:41:36 +0900 Subject: [PATCH 14/28] silence some warnings --- dlib/image_loader/jxl_loader.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/dlib/image_loader/jxl_loader.h b/dlib/image_loader/jxl_loader.h index 916c033efb..0889749379 100644 --- a/dlib/image_loader/jxl_loader.h +++ b/dlib/image_loader/jxl_loader.h @@ -40,9 +40,9 @@ namespace dlib using pixel_type = typename image_traits::pixel_type; // Fast path: rgb, rgb_alpha, grayscale with matching input depth - if (pixel_traits::grayscale && depth == 1 || - pixel_traits::rgb && depth == 3 || - pixel_traits::rgb_alpha && depth == 4) + if ((pixel_traits::grayscale && depth == 1) || + (pixel_traits::rgb && depth == 3) || + (pixel_traits::rgb_alpha && depth == 4)) { const size_t output_size = width * height * depth; unsigned char* output = reinterpret_cast(image_data(vimg)); @@ -124,5 +124,3 @@ namespace dlib #endif #endif // DLIB_JXL_IMPORT - - From a00419a65425c9d0d40a5561a6bcec26464b204c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Tue, 27 Feb 2024 14:48:31 +0900 Subject: [PATCH 15/28] simplify loader fast path logic --- dlib/image_loader/jxl_loader.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/dlib/image_loader/jxl_loader.h b/dlib/image_loader/jxl_loader.h index 0889749379..6f345cbc37 100644 --- a/dlib/image_loader/jxl_loader.h +++ b/dlib/image_loader/jxl_loader.h @@ -40,9 +40,10 @@ namespace dlib using pixel_type = typename image_traits::pixel_type; // Fast path: rgb, rgb_alpha, grayscale with matching input depth - if ((pixel_traits::grayscale && depth == 1) || - (pixel_traits::rgb && depth == 3) || - (pixel_traits::rgb_alpha && depth == 4)) + if (pixel_traits::num == depth && ( + pixel_traits::rgb || + pixel_traits::rgb_alpha || + pixel_traits::grayscale)) { const size_t output_size = width * height * depth; unsigned char* output = reinterpret_cast(image_data(vimg)); From 3b05531b0be8717880dcb953b169c2cf74c04a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Tue, 27 Feb 2024 23:10:33 +0900 Subject: [PATCH 16/28] allow python to save jxl and webp --- tools/python/src/numpy_returns.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/python/src/numpy_returns.cpp b/tools/python/src/numpy_returns.cpp index 1954ba3825..7ec3fad187 100644 --- a/tools/python/src/numpy_returns.cpp +++ b/tools/python/src/numpy_returns.cpp @@ -51,6 +51,14 @@ void save_image(numpy_image img, const std::string &path) save_png(img, path); } else if(has_ending(lowered_path, ".jpg") || has_ending(lowered_path, ".jpeg")) { save_jpeg(img, path); +#if DLIB_WEBP_SUPPORT + } else if(has_ending(lowered_path, ".webp")) { + save_webp(img, path); +#endif +#if DLIB_JXL_SUPPORT + } else if(has_ending(lowered_path, ".jxl")) { + save_jxl(img, path); +#endif } else { throw dlib::error("Unsupported image type, image path must end with one of [.bmp, .png, .dng, .jpg, .jpeg]"); } From 8adc664dcbcd170c65ec1138fdd5c536ca516be2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Tue, 27 Feb 2024 23:24:57 +0900 Subject: [PATCH 17/28] update error message with supported formats --- tools/python/src/numpy_returns.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tools/python/src/numpy_returns.cpp b/tools/python/src/numpy_returns.cpp index 7ec3fad187..b4acf310d1 100644 --- a/tools/python/src/numpy_returns.cpp +++ b/tools/python/src/numpy_returns.cpp @@ -42,15 +42,33 @@ void save_image(numpy_image img, const std::string &path) { std::string lowered_path = path; std::transform(lowered_path.begin(), lowered_path.end(), lowered_path.begin(), ::tolower); + std::string error_message = "Unsupported image type, image path must end with one of [.bmp, .dng"; +#if DLIB_PNG_SUPPORT + error_message += ", .png"; +#endif +#if DLIB_JPEG_SUPPORT + error_message += ", .jpg, jpeg"; +#endif +#if DLIB_WEBP_SUPPORT + error_message += ", .webp"; +#endif +#if DLIB_JXL_SUPPORT + error_message += ", .jxl"; +#endif + error_message += "]"; if(has_ending(lowered_path, ".bmp")) { save_bmp(img, path); } else if(has_ending(lowered_path, ".dng")) { save_dng(img, path); +#if DLIB_PNG_SUPPORT } else if(has_ending(lowered_path, ".png")) { save_png(img, path); +#endif +#if DLIB_JPEG_SUPPORT } else if(has_ending(lowered_path, ".jpg") || has_ending(lowered_path, ".jpeg")) { save_jpeg(img, path); +#endif #if DLIB_WEBP_SUPPORT } else if(has_ending(lowered_path, ".webp")) { save_webp(img, path); @@ -60,7 +78,7 @@ void save_image(numpy_image img, const std::string &path) save_jxl(img, path); #endif } else { - throw dlib::error("Unsupported image type, image path must end with one of [.bmp, .png, .dng, .jpg, .jpeg]"); + throw dlib::error(error_message); } return; } From bd0c84d81d7ebb6d7c3e3eb62775bf3f1d4b58e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Wed, 28 Feb 2024 11:40:12 +0900 Subject: [PATCH 18/28] Allow setting image quality in Python The setting is ignored where it does not make sense. --- tools/python/src/numpy_returns.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/python/src/numpy_returns.cpp b/tools/python/src/numpy_returns.cpp index b4acf310d1..940c0dd344 100644 --- a/tools/python/src/numpy_returns.cpp +++ b/tools/python/src/numpy_returns.cpp @@ -38,7 +38,7 @@ bool has_ending (std::string const full_string, std::string const &ending) { // ---------------------------------------------------------------------------------------- template -void save_image(numpy_image img, const std::string &path) +void save_image(numpy_image img, const std::string &path, const float quality) { std::string lowered_path = path; std::transform(lowered_path.begin(), lowered_path.end(), lowered_path.begin(), ::tolower); @@ -67,15 +67,15 @@ void save_image(numpy_image img, const std::string &path) #endif #if DLIB_JPEG_SUPPORT } else if(has_ending(lowered_path, ".jpg") || has_ending(lowered_path, ".jpeg")) { - save_jpeg(img, path); + save_jpeg(img, path, put_in_range(0, 100, quality)); #endif #if DLIB_WEBP_SUPPORT } else if(has_ending(lowered_path, ".webp")) { - save_webp(img, path); + save_webp(img, path, std::max(0.f, quality)); #endif #if DLIB_JXL_SUPPORT } else if(has_ending(lowered_path, ".jxl")) { - save_jxl(img, path); + save_jxl(img, path, put_in_range(0, 100, quality)); #endif } else { throw dlib::error(error_message); @@ -166,11 +166,11 @@ void bind_numpy_returns(py::module &m) m.def("save_image", &save_image, "Saves the given image to the specified path. Determines the file type from the file extension specified in the path", - py::arg("img"), py::arg("filename") + py::arg("img"), py::arg("filename"), py::arg("quality") = 75 ); m.def("save_image", &save_image, "Saves the given image to the specified path. Determines the file type from the file extension specified in the path", - py::arg("img"), py::arg("filename") + py::arg("img"), py::arg("filename"), py::arg("quality") = 75 ); m.def("jitter_image", &get_jitter_images, From 3455665027682a5d5aa8bb5aea441597577a65b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Wed, 28 Feb 2024 12:36:59 +0900 Subject: [PATCH 19/28] round quality in JPEG saver --- tools/python/src/numpy_returns.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/python/src/numpy_returns.cpp b/tools/python/src/numpy_returns.cpp index 940c0dd344..e32480ca5d 100644 --- a/tools/python/src/numpy_returns.cpp +++ b/tools/python/src/numpy_returns.cpp @@ -67,7 +67,7 @@ void save_image(numpy_image img, const std::string &path, const float quality #endif #if DLIB_JPEG_SUPPORT } else if(has_ending(lowered_path, ".jpg") || has_ending(lowered_path, ".jpeg")) { - save_jpeg(img, path, put_in_range(0, 100, quality)); + save_jpeg(img, path, put_in_range(0, 100, std::lround(quality))); #endif #if DLIB_WEBP_SUPPORT } else if(has_ending(lowered_path, ".webp")) { From 43a1699ff9c079e126bc21f3409765d25355d3c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Wed, 28 Feb 2024 14:32:32 +0900 Subject: [PATCH 20/28] improve error message in CMake --- dlib/cmake_utils/find_libjxl.cmake | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dlib/cmake_utils/find_libjxl.cmake b/dlib/cmake_utils/find_libjxl.cmake index 6a5f3f0a1a..fabcb48766 100644 --- a/dlib/cmake_utils/find_libjxl.cmake +++ b/dlib/cmake_utils/find_libjxl.cmake @@ -19,8 +19,11 @@ if (PkgConfig_FOUND) else() message(" *****************************************************************************") message(" *** No JPEG XL libraries found. ***") - message(" *** On Ubuntu you can install them by executing ***") + message(" *** On Ubuntu 23.04 and newer you can install them by executing ***") message(" *** sudo apt install libjxl-dev ***") + message(" *** ***") + message(" *** Otherwise, you can find precompiled packages here: ***") + message(" *** https://github.com/libjxl/libjxl/releases ***") message(" *****************************************************************************") endif() else() From fbd6e197ef35d440b5bfd43748b349afc6690d50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Wed, 28 Feb 2024 22:53:05 +0900 Subject: [PATCH 21/28] add jxl support to imglab --- tools/imglab/src/cluster.cpp | 11 +++++- tools/imglab/src/common.cpp | 12 ++++++ tools/imglab/src/common.h | 3 ++ tools/imglab/src/flip_dataset.cpp | 8 ++++ tools/imglab/src/main.cpp | 62 ++++++++++++++++++++++++++++--- 5 files changed, 89 insertions(+), 7 deletions(-) diff --git a/tools/imglab/src/cluster.cpp b/tools/imglab/src/cluster.cpp index 4aaeead9b1..8fcec3418d 100644 --- a/tools/imglab/src/cluster.cpp +++ b/tools/imglab/src/cluster.cpp @@ -301,7 +301,16 @@ int cluster_dataset( if (assignments[i].c == c) temp.push_back(images[assignments[i].idx]); } - +#ifdef DLIB_JXL_SUPPORT + if (parser.option("jxl")) + { + string outfile = "cluster_"+pad_int_with_zeros(c+1, 3) + ".jxl"; + cout << "Saving " << outfile << endl; + const float jxl_quality = std::stof(parser.option("jxl").argument()); + save_jxl(tile_images(temp), outfile, jxl_quality); + } + else +#endif #ifdef DLIB_WEBP_SUPPORT if (parser.option("webp")) { diff --git a/tools/imglab/src/common.cpp b/tools/imglab/src/common.cpp index d23947be92..be57eb6e26 100644 --- a/tools/imglab/src/common.cpp +++ b/tools/imglab/src/common.cpp @@ -58,6 +58,18 @@ std::string to_jpg_name (const std::string& filename) // ---------------------------------------------------------------------------------------- +#ifdef DLIB_JXL_SUPPORT +std::string to_jxl_name (const std::string& filename) +{ + std::string::size_type pos = filename.find_last_of("."); + if (pos == std::string::npos) + throw dlib::error("invalid filename: " + filename); + return filename.substr(0,pos) + ".jxl"; +} +#endif + +// ---------------------------------------------------------------------------------------- + #ifdef DLIB_WEBP_SUPPORT std::string to_webp_name (const std::string& filename) { diff --git a/tools/imglab/src/common.h b/tools/imglab/src/common.h index 3806b2b75c..37ff671e64 100644 --- a/tools/imglab/src/common.h +++ b/tools/imglab/src/common.h @@ -34,6 +34,9 @@ void make_empty_file ( std::string to_png_name (const std::string& filename); std::string to_jpg_name (const std::string& filename); +#ifdef DLIB_JXL_SUPPORT +std::string to_jxl_name (const std::string& filename); +#endif #ifdef DLIB_WEBP_SUPPORT std::string to_webp_name (const std::string& filename); #endif diff --git a/tools/imglab/src/flip_dataset.cpp b/tools/imglab/src/flip_dataset.cpp index 9431531679..19ef4e0123 100644 --- a/tools/imglab/src/flip_dataset.cpp +++ b/tools/imglab/src/flip_dataset.cpp @@ -219,6 +219,14 @@ void flip_dataset(const command_line_parser& parser) filename = to_jpg_name(filename); save_jpeg(temp, filename,JPEG_QUALITY); } +#ifdef DLIB_JXL_SUPPORT + else if (parser.option("jxl")) + { + filename = to_jxl_name(filename); + const float jxl_quality = std::stof(parser.option("jxl").argument()); + save_webp(temp, filename, jxl_quality); + } +#endif #ifdef DLIB_WEBP_SUPPORT else if (parser.option("webp")) { diff --git a/tools/imglab/src/main.cpp b/tools/imglab/src/main.cpp index 57704599a2..ae93e6a4b3 100644 --- a/tools/imglab/src/main.cpp +++ b/tools/imglab/src/main.cpp @@ -21,7 +21,7 @@ #include -const char* VERSION = "1.20"; +const char* VERSION = "1.21"; @@ -61,8 +61,8 @@ void create_new_dataset ( // then parser[i] should be a directory std::vector files = get_files_in_directory_tree(parser[i], - match_endings(".png .PNG .jpeg .JPEG .jpg .JPG .bmp .BMP .dng .DNG .gif .GIF .webp .WEBP"), - depth); + match_endings(".png .PNG .jpeg .JPEG .jpg .JPG .bmp .BMP .dng .DNG .gif .GIF .jxl .JXL .webp .WEBP"), + depth); sort(files.begin(), files.end()); for (unsigned long j = 0; j < files.size(); ++j) @@ -339,6 +339,14 @@ void rotate_dataset(const command_line_parser& parser) filename = to_jpg_name(filename); save_jpeg(temp, filename,JPEG_QUALITY); } +#ifdef DLIB_JXL_SUPPORT + else if (parser.option("jxl")) + { + filename = to_jxl_name(filename); + const float jxl_quality = std::stof(parser.option("jxl").argument()); + save_jxl(temp, filename, jxl_quality); + } +#endif #ifdef DLIB_WEBP_SUPPORT else if (parser.option("webp")) { @@ -491,6 +499,14 @@ int resample_dataset(const command_line_parser& parser) dimg.filename = to_jpg_name(dimg.filename); save_jpeg(chip,dimg.filename, JPEG_QUALITY); } +#ifdef DLIB_JXL_SUPPORT + else if (parser.option("jxl")) + { + dimg.filename = to_jxl_name(dimg.filename); + const float jxl_quality = std::stof(parser.option("jxl").argument()); + save_jxl(chip, dimg.filename, jxl_quality); + } +#endif #ifdef DLIB_WEBP_SUPPORT else if (parser.option("webp")) { @@ -524,9 +540,23 @@ int tile_dataset(const command_line_parser& parser) string out_image = parser.option("tile").argument(); string ext = right_substr(out_image,"."); - if (ext != "png" && ext != "jpg" && ext != "webp") + if (ext != "png" && ext != "jpg" +#if DLIB_JXL_SUPPORT + && ext != "jxl" +#endif +#if DLIB_WEBP_SUPPORT + && ext != "webp" +#endif + ) { - cerr << "The output image file must have either .png, .jpg or .webp extension." << endl; + cerr << "The output image file must have one of these extensions: .png, .jpg" << endl; +#if DLIB_JXL_SUPPORT + cerr << ", .jxl"; +#endif +#if DLIB_WEBP_SUPPORT + cerr << ", .webp"; +#endif + cerr << "."; return EXIT_FAILURE; } @@ -571,6 +601,14 @@ int tile_dataset(const command_line_parser& parser) { save_png(tile_images(images), out_image); } +#ifdef DLIB_JXL_SUPPORT + else if (ext == "jxl") + { + // Lossless by default + const float jxl_quality = get_option(parser, "jxl", 100.f); + save_jxl(tile_images(images), out_image, jxl_quality); + } +#endif #ifdef DLIB_WEBP_SUPPORT else if (ext == "webp") { @@ -657,8 +695,11 @@ int main(int argc, char** argv) parser.add_option("rmignore","Remove all boxes marked ignore and save the results to a new XML file."); parser.add_option("rm-if-overlaps","Remove all boxes labeled if they overlap any box not labeled and save the results to a new XML file.",1); parser.add_option("jpg", "When saving images to disk, write them as jpg files instead of png."); +#ifdef DLIB_JXL_SUPPORT + parser.add_option("jxl", "When saving images to disk, write them as jxl files instead of png, using as the quality factor.", 1); +#endif #ifdef DLIB_WEBP_SUPPORT - parser.add_option("webp", "When saving images to disk, write them as webp files instead of png or jpg, using as the quality factor.", 1); + parser.add_option("webp", "When saving images to disk, write them as webp files instead of png, using as the quality factor.", 1); #endif parser.set_group_name("Cropping sub images"); @@ -775,8 +816,14 @@ int main(int argc, char** argv) parser.check_incompatible_options("rmtrunc", "ignore"); parser.check_incompatible_options("box-images", "rename"); parser.check_incompatible_options("box-images", "ignore"); +#ifdef DLIB_JXL_SUPPORT + parser.check_incompatible_options("jpg", "jxl"); +#endif #ifdef DLIB_WEBP_SUPPORT parser.check_incompatible_options("jpg", "webp"); +#endif +#if DLIB_JXL_SUPPORT && DLIB_WEBP_SUPPORT + parser.check_incompatible_options("jxl", "webp"); #endif const char* convert_args[] = {"pascal-xml","pascal-v1","idl"}; parser.check_option_arg_range("convert", convert_args); @@ -787,6 +834,9 @@ int main(int argc, char** argv) parser.check_option_arg_range("cropped-object-size", 4, 10000*10000); parser.check_option_arg_range("crop-size", 1.0, 100.0); parser.check_option_arg_range("split-train-test", 0.0, 1.0); +#ifdef DLIB_JXL_SUPPORT + parser.check_option_arg_range("jxl", 0.f, 100.f); +#endif #ifdef DLIB_WEBP_SUPPORT parser.check_option_arg_range("webp", 0.f, std::numeric_limits::max()); #endif From f51b1f24c48d9428e6374dd7dbc0546d08deec3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= <1671644+arrufat@users.noreply.github.com> Date: Thu, 29 Feb 2024 09:54:34 +0900 Subject: [PATCH 22/28] add Davis's suggestion Co-authored-by: Davis E. King --- dlib/image_loader/jxl_loader.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dlib/image_loader/jxl_loader.cpp b/dlib/image_loader/jxl_loader.cpp index e17d807c2c..479a54fbc9 100644 --- a/dlib/image_loader/jxl_loader.cpp +++ b/dlib/image_loader/jxl_loader.cpp @@ -17,10 +17,9 @@ namespace dlib { std::ifstream stream(filename, std::ios::binary); stream.exceptions(std::ifstream::failbit | std::ifstream::badbit | std::ifstream::eofbit); - stream.seekg(0, std::ios_base::end); - std::vector buffer(stream.tellg()); - stream.seekg(0); - stream.read(reinterpret_cast(buffer.data()), buffer.size()); + std::vector buffer; + vectorstream temp(buffer); + temp << stream.rdbuf(); return buffer; } From cbec9c34c171024f9eaaed73735f8914bbb26e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= <1671644+arrufat@users.noreply.github.com> Date: Thu, 29 Feb 2024 10:01:33 +0900 Subject: [PATCH 23/28] Apply suggestions from code review Co-authored-by: Davis E. King --- dlib/image_loader/jxl_loader.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlib/image_loader/jxl_loader.h b/dlib/image_loader/jxl_loader.h index 6f345cbc37..aa38c142d9 100644 --- a/dlib/image_loader/jxl_loader.h +++ b/dlib/image_loader/jxl_loader.h @@ -87,7 +87,7 @@ namespace dlib void load_jxl ( image_type& image, const std::string& file_name - ) + ) { jxl_loader(file_name).get_image(image); } @@ -99,7 +99,7 @@ namespace dlib image_type& image, const unsigned char* imgbuff, size_t imgbuffsize - ) + ) { jxl_loader(imgbuff, imgbuffsize).get_image(image); } @@ -111,7 +111,7 @@ namespace dlib image_type& image, const char* imgbuff, size_t imgbuffsize - ) + ) { jxl_loader(reinterpret_cast(imgbuff), imgbuffsize).get_image(image); } From 9e037cbb6fe7e1508e027709628bb81edf725628 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Thu, 29 Feb 2024 13:00:48 +0900 Subject: [PATCH 24/28] make sure grayscale is 8 bit --- dlib/image_saver/save_jxl.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dlib/image_saver/save_jxl.h b/dlib/image_saver/save_jxl.h index 3a235508d0..15ee59e66c 100644 --- a/dlib/image_saver/save_jxl.h +++ b/dlib/image_saver/save_jxl.h @@ -68,10 +68,11 @@ namespace dlib const int width = img.nc(); const int height = img.nr(); const int depth = pixel_traits::num; - // Fast path: rgb, rgb_alpha, grayscale + // Fast path: rgb, rgb_alpha, 8-bit grayscale if (pixel_traits::rgb || pixel_traits::rgb_alpha || - pixel_traits::grayscale) + (pixel_traits::grayscale && + std::is_same::basic_pixel_type, unsigned char>())) { impl::impl_save_jxl(filename, data, width, height, depth, quality); } From 53d3d1a973e95fb259a0e1cbd4d01aa55f6de0e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Thu, 29 Feb 2024 13:03:36 +0900 Subject: [PATCH 25/28] update abstract: JPEG XL can store grayscale images --- dlib/image_saver/save_jxl_abstract.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dlib/image_saver/save_jxl_abstract.h b/dlib/image_saver/save_jxl_abstract.h index 70d038acdb..66ec35f490 100644 --- a/dlib/image_saver/save_jxl_abstract.h +++ b/dlib/image_saver/save_jxl_abstract.h @@ -32,9 +32,9 @@ namespace dlib - image[image.nr()-1][image.nc()-1] will be in the lower right corner of the image. - This routine can save images containing any type of pixel. However, - save_jxl() can only natively store rgb_pixel, and rgb_alpha_pixel pixel types. - All other pixel types will be converted into one of these types as appropriate - before being saved to disk. + save_jxl() can only natively store rgb_pixel, rgb_alpha_pixel and unsigned char + pixel types. All other pixel types will be converted into one of these types as + appropriate before being saved to disk. - The quality value determines how lossy the compression is. Larger quality values result in larger output images but the images will look better. Although it can range from 0 to 100, the recommended range is between 68 and 96. From 66463c2497b6e22005303d6e9afc6e37294978e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Thu, 29 Feb 2024 13:27:43 +0900 Subject: [PATCH 26/28] add more methods to query basic info from JXL --- dlib/image_loader/jxl_loader.cpp | 11 +++++ dlib/image_loader/jxl_loader.h | 9 ++++ dlib/image_loader/jxl_loader_abstract.h | 66 +++++++++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/dlib/image_loader/jxl_loader.cpp b/dlib/image_loader/jxl_loader.cpp index 479a54fbc9..73e44b78da 100644 --- a/dlib/image_loader/jxl_loader.cpp +++ b/dlib/image_loader/jxl_loader.cpp @@ -60,6 +60,16 @@ namespace dlib get_info(); } +// ---------------------------------------------------------------------------------------- + + bool jxl_loader::is_gray() const { return depth == 1; } + bool jxl_loader::is_graya() const { return depth == 2; }; + bool jxl_loader::is_rgb() const { return depth == 3; } + bool jxl_loader::is_rgba() const { return depth == 4; } + unsigned int jxl_loader::bit_depth() const { return bits_per_sample; }; + unsigned int jxl_loader::nr() const { return height; }; + unsigned int jxl_loader::nc() const { return width; }; + // ---------------------------------------------------------------------------------------- void jxl_loader::get_info() @@ -90,6 +100,7 @@ namespace dlib width = basic_info.xsize; height = basic_info.ysize; depth = basic_info.num_color_channels + basic_info.num_extra_channels; + bits_per_sample = basic_info.bits_per_sample; } // ---------------------------------------------------------------------------------------- diff --git a/dlib/image_loader/jxl_loader.h b/dlib/image_loader/jxl_loader.h index aa38c142d9..aa02a79d11 100644 --- a/dlib/image_loader/jxl_loader.h +++ b/dlib/image_loader/jxl_loader.h @@ -23,6 +23,14 @@ namespace dlib jxl_loader(const dlib::file& f); jxl_loader(const unsigned char* imgbuffer, size_t buffersize); + bool is_gray() const; + bool is_graya() const; + bool is_rgb() const; + bool is_rgba() const; + unsigned int bit_depth() const; + unsigned int nr() const; + unsigned int nc() const; + template void get_image(image_type& image) const { @@ -76,6 +84,7 @@ namespace dlib uint32_t height; uint32_t width; uint32_t depth; + uint32_t bits_per_sample; std::vector data; }; diff --git a/dlib/image_loader/jxl_loader_abstract.h b/dlib/image_loader/jxl_loader_abstract.h index 741cb26288..529ecdcbb0 100644 --- a/dlib/image_loader/jxl_loader_abstract.h +++ b/dlib/image_loader/jxl_loader_abstract.h @@ -98,6 +98,72 @@ namespace dlib }; +// ---------------------------------------------------------------------------------------- + + bool is_gray( + ) const; + /*! + ensures + - if (this object contains a grayscale image without an alpha channel) then + - returns true + - else + - returns false + !*/ + + bool is_graya( + ) const; + /*! + ensures + - if (this object contains a grayscale image with an alpha channel) then + - returns true + - else + - returns false + !*/ + + bool is_rgb( + ) const; + /*! + ensures + - if (this object contains a 3 channel RGB image) then + - returns true + - else + - returns false + !*/ + + bool is_rgba( + ) const; + /*! + ensures + - if (this object contains a 4 channel RGB alpha image) then + - returns true + - else + - returns false + !*/ + + unsigned int bit_depth ( + ) const; + /*! + ensures + - returns the number of bits per channel in the image contained by this + object. + !*/ + + unsigned int nr ( + ) const; + /*! + ensures + - returns the number of rows (height) of the image contained by this + object. + !*/ + + unsigned int nc ( + ) const; + /*! + ensures + - returns the number of colums (width) of the image contained by this + object. + !*/ + // ---------------------------------------------------------------------------------------- template < From c97f2848cdc037ed11d295a2e8f2d4005542255f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Thu, 29 Feb 2024 13:35:16 +0900 Subject: [PATCH 27/28] documentation formatting --- dlib/image_loader/jxl_loader_abstract.h | 6 ++++-- dlib/image_saver/save_jxl_abstract.h | 12 ++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/dlib/image_loader/jxl_loader_abstract.h b/dlib/image_loader/jxl_loader_abstract.h index 529ecdcbb0..5edf836f14 100644 --- a/dlib/image_loader/jxl_loader_abstract.h +++ b/dlib/image_loader/jxl_loader_abstract.h @@ -68,7 +68,8 @@ namespace dlib ); /*! ensures - - loads the JPEG XL from memory imgbuffer of size buffersize into this object + - loads the JPEG XL from memory imgbuffer of size buffersize into + this object throws - image_load_error This exception is thrown if there is some error that prevents @@ -104,7 +105,8 @@ namespace dlib ) const; /*! ensures - - if (this object contains a grayscale image without an alpha channel) then + - if (this object contains a grayscale image without an alpha channel) + then - returns true - else - returns false diff --git a/dlib/image_saver/save_jxl_abstract.h b/dlib/image_saver/save_jxl_abstract.h index 66ec35f490..c38aae1272 100644 --- a/dlib/image_saver/save_jxl_abstract.h +++ b/dlib/image_saver/save_jxl_abstract.h @@ -32,14 +32,14 @@ namespace dlib - image[image.nr()-1][image.nc()-1] will be in the lower right corner of the image. - This routine can save images containing any type of pixel. However, - save_jxl() can only natively store rgb_pixel, rgb_alpha_pixel and unsigned char - pixel types. All other pixel types will be converted into one of these types as - appropriate before being saved to disk. + save_jxl() can only natively store rgb_pixel, rgb_alpha_pixel and unsigned + char pixel types. All other pixel types will be converted into one of + these types as appropriate before being saved to disk. - The quality value determines how lossy the compression is. Larger quality values result in larger output images but the images will look better. - Although it can range from 0 to 100, the recommended range is between 68 and 96. - A value of 90 means visually lossless, while a value of 100 means mathematically - lossless. + Although it can range from 0 to 100, the recommended range is between + 68 and 96. A value of 90 means visually lossless, while a value of 100 + means mathematically lossless. throws - image_save_error This exception is thrown if there is an error that prevents us from saving From f9c2ab53a69fc0e298c7ff5c93bb9c9cf08bf60c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Thu, 29 Feb 2024 22:18:11 +0900 Subject: [PATCH 28/28] Apply Davis' suggestions --- dlib/image_loader/jxl_loader.cpp | 4 ++-- dlib/image_loader/jxl_loader.h | 8 +++++--- dlib/image_loader/jxl_loader_abstract.h | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/dlib/image_loader/jxl_loader.cpp b/dlib/image_loader/jxl_loader.cpp index 73e44b78da..97fa705dbc 100644 --- a/dlib/image_loader/jxl_loader.cpp +++ b/dlib/image_loader/jxl_loader.cpp @@ -67,8 +67,8 @@ namespace dlib bool jxl_loader::is_rgb() const { return depth == 3; } bool jxl_loader::is_rgba() const { return depth == 4; } unsigned int jxl_loader::bit_depth() const { return bits_per_sample; }; - unsigned int jxl_loader::nr() const { return height; }; - unsigned int jxl_loader::nc() const { return width; }; + long jxl_loader::nr() const { return static_cast(height); }; + long jxl_loader::nc() const { return static_cast(width); }; // ---------------------------------------------------------------------------------------- diff --git a/dlib/image_loader/jxl_loader.h b/dlib/image_loader/jxl_loader.h index aa02a79d11..6eea595f71 100644 --- a/dlib/image_loader/jxl_loader.h +++ b/dlib/image_loader/jxl_loader.h @@ -28,8 +28,8 @@ namespace dlib bool is_rgb() const; bool is_rgba() const; unsigned int bit_depth() const; - unsigned int nr() const; - unsigned int nc() const; + long nr() const; + long nc() const; template void get_image(image_type& image) const @@ -51,7 +51,9 @@ namespace dlib if (pixel_traits::num == depth && ( pixel_traits::rgb || pixel_traits::rgb_alpha || - pixel_traits::grayscale)) + (pixel_traits::grayscale && + std::is_same::basic_pixel_type, unsigned char>())) + ) { const size_t output_size = width * height * depth; unsigned char* output = reinterpret_cast(image_data(vimg)); diff --git a/dlib/image_loader/jxl_loader_abstract.h b/dlib/image_loader/jxl_loader_abstract.h index 5edf836f14..b50104f0fb 100644 --- a/dlib/image_loader/jxl_loader_abstract.h +++ b/dlib/image_loader/jxl_loader_abstract.h @@ -150,7 +150,7 @@ namespace dlib object. !*/ - unsigned int nr ( + long nr ( ) const; /*! ensures @@ -158,7 +158,7 @@ namespace dlib object. !*/ - unsigned int nc ( + long nc ( ) const; /*! ensures