libjxl

FORK: libjxl patches used on blog
git clone https://git.neptards.moe/blog/libjxl.git
Log | Files | Refs | Submodules | README | LICENSE

exr_to_pq.cc (6362B)


      1 // Copyright (c) the JPEG XL Project Authors. All rights reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style
      4 // license that can be found in the LICENSE file.
      5 
      6 #include <stdio.h>
      7 #include <stdlib.h>
      8 
      9 #include "lib/extras/codec.h"
     10 #include "lib/extras/dec/decode.h"
     11 #include "lib/extras/packed_image_convert.h"
     12 #include "lib/extras/tone_mapping.h"
     13 #include "lib/jxl/cms/jxl_cms_internal.h"
     14 #include "lib/jxl/image_bundle.h"
     15 #include "tools/cmdline.h"
     16 #include "tools/file_io.h"
     17 #include "tools/hdr/image_utils.h"
     18 #include "tools/thread_pool_internal.h"
     19 
     20 namespace {
     21 
     22 struct LuminanceInfo {
     23   enum class Kind { kWhite, kMaximum };
     24   Kind kind = Kind::kWhite;
     25   float luminance = 100.f;
     26 };
     27 
     28 bool ParseLuminanceInfo(const char* argument, LuminanceInfo* luminance_info) {
     29   if (strncmp(argument, "white=", 6) == 0) {
     30     luminance_info->kind = LuminanceInfo::Kind::kWhite;
     31     argument += 6;
     32   } else if (strncmp(argument, "max=", 4) == 0) {
     33     luminance_info->kind = LuminanceInfo::Kind::kMaximum;
     34     argument += 4;
     35   } else {
     36     fprintf(stderr,
     37             "Invalid prefix for luminance info, expected white= or max=\n");
     38     return false;
     39   }
     40   return jpegxl::tools::ParseFloat(argument, &luminance_info->luminance);
     41 }
     42 
     43 }  // namespace
     44 
     45 int main(int argc, const char** argv) {
     46   jpegxl::tools::ThreadPoolInternal pool;
     47 
     48   jpegxl::tools::CommandLineParser parser;
     49   LuminanceInfo luminance_info;
     50   auto luminance_option =
     51       parser.AddOptionValue('l', "luminance", "<max|white=N>",
     52                             "luminance information (defaults to whiteLuminance "
     53                             "header if present, otherwise to white=100)",
     54                             &luminance_info, &ParseLuminanceInfo, 0);
     55   const char* input_filename = nullptr;
     56   auto input_filename_option = parser.AddPositionalOption(
     57       "input", true, "input image", &input_filename, 0);
     58   const char* output_filename = nullptr;
     59   auto output_filename_option = parser.AddPositionalOption(
     60       "output", true, "output image", &output_filename, 0);
     61 
     62   if (!parser.Parse(argc, argv)) {
     63     fprintf(stderr, "See -h for help.\n");
     64     return EXIT_FAILURE;
     65   }
     66 
     67   if (parser.HelpFlagPassed()) {
     68     parser.PrintHelp();
     69     return EXIT_SUCCESS;
     70   }
     71 
     72   if (!parser.GetOption(input_filename_option)->matched()) {
     73     fprintf(stderr, "Missing input filename.\nSee -h for help.\n");
     74     return EXIT_FAILURE;
     75   }
     76   if (!parser.GetOption(output_filename_option)->matched()) {
     77     fprintf(stderr, "Missing output filename.\nSee -h for help.\n");
     78     return EXIT_FAILURE;
     79   }
     80 
     81   jxl::extras::PackedPixelFile ppf;
     82   std::vector<uint8_t> input_bytes;
     83   JXL_CHECK(jpegxl::tools::ReadFile(input_filename, &input_bytes));
     84   JXL_CHECK(jxl::extras::DecodeBytes(jxl::Bytes(input_bytes),
     85                                      jxl::extras::ColorHints(), &ppf));
     86 
     87   jxl::CodecInOut image;
     88   JXL_CHECK(
     89       jxl::extras::ConvertPackedPixelFileToCodecInOut(ppf, &pool, &image));
     90   image.metadata.m.bit_depth.exponent_bits_per_sample = 0;
     91   jxl::ColorEncoding linear_rec_2020 = image.Main().c_current();
     92   JXL_CHECK(linear_rec_2020.SetPrimariesType(jxl::Primaries::k2100));
     93   linear_rec_2020.Tf().SetTransferFunction(jxl::TransferFunction::kLinear);
     94   JXL_CHECK(linear_rec_2020.CreateICC());
     95   JXL_CHECK(
     96       jpegxl::tools::TransformCodecInOutTo(image, linear_rec_2020, &pool));
     97 
     98   float primaries_xyz[9];
     99   const jxl::PrimariesCIExy p = image.Main().c_current().GetPrimaries();
    100   const jxl::CIExy wp = image.Main().c_current().GetWhitePoint();
    101   JXL_CHECK(jxl::PrimariesToXYZ(p.r.x, p.r.y, p.g.x, p.g.y, p.b.x, p.b.y, wp.x,
    102                                 wp.y, primaries_xyz));
    103 
    104   float max_value = 0.f;
    105   float max_relative_luminance = 0.f;
    106   float white_luminance = ppf.info.intensity_target != 0 &&
    107                                   !parser.GetOption(luminance_option)->matched()
    108                               ? ppf.info.intensity_target
    109                           : luminance_info.kind == LuminanceInfo::Kind::kWhite
    110                               ? luminance_info.luminance
    111                               : 0.f;
    112   bool out_of_gamut = false;
    113   for (size_t y = 0; y < image.ysize(); ++y) {
    114     const float* const rows[3] = {image.Main().color()->ConstPlaneRow(0, y),
    115                                   image.Main().color()->ConstPlaneRow(1, y),
    116                                   image.Main().color()->ConstPlaneRow(2, y)};
    117     for (size_t x = 0; x < image.xsize(); ++x) {
    118       if (!out_of_gamut &&
    119           (rows[0][x] < 0 || rows[1][x] < 0 || rows[2][x] < 0)) {
    120         out_of_gamut = true;
    121         fprintf(stderr,
    122                 "WARNING: found colors outside of the Rec. 2020 gamut.\n");
    123       }
    124       max_value = std::max(
    125           max_value, std::max(rows[0][x], std::max(rows[1][x], rows[2][x])));
    126       const float luminance = primaries_xyz[1] * rows[0][x] +
    127                               primaries_xyz[4] * rows[1][x] +
    128                               primaries_xyz[7] * rows[2][x];
    129       if (luminance_info.kind == LuminanceInfo::Kind::kMaximum &&
    130           luminance > max_relative_luminance) {
    131         max_relative_luminance = luminance;
    132         white_luminance = luminance_info.luminance / luminance;
    133       }
    134     }
    135   }
    136 
    137   bool needs_gamut_mapping = false;
    138 
    139   white_luminance *= max_value;
    140   if (white_luminance > 10000) {
    141     fprintf(stderr,
    142             "WARNING: the image is too bright for PQ (would need (1, 1, 1) to "
    143             "be %g cd/m^2).\n",
    144             white_luminance);
    145 
    146     max_value *= 10000 / white_luminance;
    147     white_luminance = 10000;
    148     needs_gamut_mapping = true;
    149   } else {
    150     fprintf(stderr,
    151             "The resulting image should be compressed with "
    152             "--intensity_target=%g.\n",
    153             white_luminance);
    154   }
    155   image.metadata.m.SetIntensityTarget(white_luminance);
    156 
    157   jxl::ScaleImage(1.f / max_value, image.Main().color());
    158 
    159   if (needs_gamut_mapping) {
    160     JXL_CHECK(jxl::GamutMap(&image, 0.f, &pool));
    161   }
    162 
    163   jxl::ColorEncoding pq = image.Main().c_current();
    164   pq.Tf().SetTransferFunction(jxl::TransferFunction::kPQ);
    165   JXL_CHECK(pq.CreateICC());
    166   JXL_CHECK(jpegxl::tools::TransformCodecInOutTo(image, pq, &pool));
    167   image.metadata.m.color_encoding = pq;
    168   std::vector<uint8_t> encoded;
    169   JXL_CHECK(jpegxl::tools::Encode(image, output_filename, &encoded, &pool));
    170   JXL_CHECK(jpegxl::tools::WriteFile(output_filename, encoded));
    171 }