Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
c45c61f
WIP: preparation for JPEG XL support
Feb 19, 2023
d3634cd
jxl: add loading support
Feb 22, 2024
3114f5a
update jxl abstract
Feb 22, 2024
4244e5d
add support for saving jxl (lossless not working)
Feb 23, 2024
3163dee
everything works except setting lossless explicitly
Feb 24, 2024
8dac095
remove unused header
Feb 24, 2024
e24f2d2
fix wrong quality logic
Feb 25, 2024
83df651
remove debugging statements
Feb 25, 2024
4551df1
fix lossless encoding
Feb 25, 2024
6a37a34
improve support for grayscale images
Feb 25, 2024
c7a6bd7
use JXL instead of JPEGXL everywhere
Feb 25, 2024
6ce5a4a
oops do not make libjxl a requirement
Feb 25, 2024
ea8e3cd
update years
Feb 27, 2024
ac66884
Merge branch 'master' into add-jxl-support
Feb 27, 2024
bbc8fc5
silence some warnings
Feb 27, 2024
a00419a
simplify loader fast path logic
Feb 27, 2024
3b05531
allow python to save jxl and webp
Feb 27, 2024
8adc664
update error message with supported formats
Feb 27, 2024
bd0c84d
Allow setting image quality in Python
Feb 28, 2024
3455665
round quality in JPEG saver
Feb 28, 2024
43a1699
improve error message in CMake
Feb 28, 2024
fbd6e19
add jxl support to imglab
Feb 28, 2024
f51b1f2
add Davis's suggestion
arrufat Feb 29, 2024
cbec9c3
Apply suggestions from code review
arrufat Feb 29, 2024
9e037cb
make sure grayscale is 8 bit
Feb 29, 2024
53d3d1a
update abstract: JPEG XL can store grayscale images
Feb 29, 2024
66463c2
add more methods to query basic info from JXL
Feb 29, 2024
c97f284
documentation formatting
Feb 29, 2024
f9c2ab5
Apply Davis' suggestions
Feb 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions dlib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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_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" )
#set (DLIB_USE_FFTW_STR "Disable this if you don't want to link against fftw" )
Expand Down Expand Up @@ -237,30 +239,33 @@ 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_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)
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)
option(DLIB_USE_CUDA ${DLIB_USE_CUDA_STR} ON)
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_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)
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_JXL_SUPPORT)
#toggle_preprocessor_switch(DLIB_USE_FFTW)
toggle_preprocessor_switch(DLIB_USE_MKL_FFT)
toggle_preprocessor_switch(DLIB_USE_FFMPEG)
Expand Down Expand Up @@ -576,6 +581,23 @@ if (NOT TARGET dlib)
toggle_preprocessor_switch(DLIB_WEBP_SUPPORT)
endif()
endif()
if (DLIB_JXL_SUPPORT)
include(cmake_utils/find_libjxl.cmake)
if (JXL_FOUND)
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_JXL_SUPPORT)
else()
set(DLIB_JXL_SUPPORT OFF CACHE BOOL ${DLIB_JXL_SUPPORT_STR} FORCE)
disable_preprocessor_switch(DLIB_JXL_SUPPORT)
endif()
endif()


if (DLIB_USE_BLAS OR DLIB_USE_LAPACK OR DLIB_USE_MKL_FFT)
Expand Down
50 changes: 50 additions & 0 deletions dlib/cmake_utils/find_libjxl.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#=============================================================================
# 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.

message(STATUS "Searching for JPEG XL")
find_package(PkgConfig)
if (PkgConfig_FOUND)
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()
message(" *****************************************************************************")
message(" *** No JPEG XL libraries found. ***")
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()
message(STATUS "PkgConfig could not be found, JPEG XL support won't be available")
set(JXL_FOUND 0)
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()
7 changes: 7 additions & 0 deletions dlib/cmake_utils/test_for_libjxl/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

cmake_minimum_required(VERSION 3.8.0)
project(test_if_libjxl_is_broken)

include_directories(${JXL_INCLUDE_DIR})
add_executable(libjxl_test libjxl_test.cpp)
target_link_libraries(libjxl_test ${JXL_LIBRARY})
20 changes: 20 additions & 0 deletions dlib/cmake_utils/test_for_libjxl/libjxl_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (C) 2023 Davis E. King (davis@dlib.net), Adrià Arrufat
// License: Boost Software License See LICENSE.txt for the full license.

#include <jxl/encode_cxx.h>
#include <jxl/decode_cxx.h>
#include <jxl/resizable_parallel_runner_cxx.h>
#include <iostream>
#include <memory>

// 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.

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);
}
3 changes: 2 additions & 1 deletion dlib/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -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_JXL_SUPPORT
#cmakedefine DLIB_USE_FFTW
#cmakedefine DLIB_USE_BLAS
#cmakedefine DLIB_USE_LAPACK
Expand Down
1 change: 1 addition & 0 deletions dlib/image_io.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_

180 changes: 180 additions & 0 deletions dlib/image_loader/jxl_loader.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// 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_

// only do anything with this file if DLIB_JXL_SUPPORT is defined
#ifdef DLIB_JXL_SUPPORT
#include "jxl_loader.h"
#include <jxl/decode_cxx.h>
#include <jxl/resizable_parallel_runner_cxx.h>
#include <fstream>

namespace dlib
{

static std::vector<unsigned char> load_contents(const std::string& filename)
{
std::ifstream stream(filename, std::ios::binary);
stream.exceptions(std::ifstream::failbit | std::ifstream::badbit | std::ifstream::eofbit);
std::vector<unsigned char> buffer;
vectorstream temp(buffer);
temp << stream.rdbuf();
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();
}

// ----------------------------------------------------------------------------------------

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; };
long jxl_loader::nr() const { return static_cast<long>(height); };
long jxl_loader::nc() const { return static_cast<long>(width); };

// ----------------------------------------------------------------------------------------

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: 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");
}

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");
}
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;
}
// ----------------------------------------------------------------------------------------

void jxl_loader::decode(unsigned char* out, const size_t out_size) const
{
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: JxlDecoderSubscribeEvents failed");
}

if (JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec.get(), JxlResizableParallelRunner, runner.get()))
{
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 = depth,
.data_type = JXL_TYPE_UINT8,
.endianness = JXL_NATIVE_ENDIAN,
.align=0
};
for (;;)
{
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))
{
throw image_load_error("jxl_loader: JxlDecoderImageOutBufferSize failed");
}
if (buffer_size != width * height * depth)
{
throw image_load_error("jxl_loader: invalid output buffer size");
}
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)
{
// 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)
{
return;
}
else
{
throw image_load_error("jxl_loder: Unknown decoder status");
}
}
}
}

#endif // DLIB_JXL_SUPPORT
#endif // DLIB_JXL_LOADER_CPp_
Loading