enc_comparator.cc (5147B)
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 "lib/jxl/enc_comparator.h" 7 8 #include <jxl/memory_manager.h> 9 10 #include <algorithm> 11 #include <cstddef> 12 13 #include "lib/jxl/base/compiler_specific.h" 14 #include "lib/jxl/base/status.h" 15 #include "lib/jxl/enc_gamma_correct.h" 16 #include "lib/jxl/enc_image_bundle.h" 17 18 namespace jxl { 19 namespace { 20 21 // color is linear, but blending happens in gamma-compressed space using 22 // (gamma-compressed) grayscale background color, alpha image represents 23 // weights of the sRGB colors in the [0 .. (1 << bit_depth) - 1] interval, 24 // output image is in linear space. 25 void AlphaBlend(const Image3F& in, const size_t c, float background_linear, 26 const ImageF& alpha, Image3F* out) { 27 const float background = LinearToSrgb8Direct(background_linear); 28 29 for (size_t y = 0; y < out->ysize(); ++y) { 30 const float* JXL_RESTRICT row_a = alpha.ConstRow(y); 31 const float* JXL_RESTRICT row_i = in.ConstPlaneRow(c, y); 32 float* JXL_RESTRICT row_o = out->PlaneRow(c, y); 33 for (size_t x = 0; x < out->xsize(); ++x) { 34 const float a = row_a[x]; 35 if (a <= 0.f) { 36 row_o[x] = background_linear; 37 } else if (a >= 1.f) { 38 row_o[x] = row_i[x]; 39 } else { 40 const float w_fg = a; 41 const float w_bg = 1.0f - w_fg; 42 const float fg = w_fg * LinearToSrgb8Direct(row_i[x]); 43 const float bg = w_bg * background; 44 row_o[x] = Srgb8ToLinearDirect(fg + bg); 45 } 46 } 47 } 48 } 49 50 void AlphaBlend(float background_linear, ImageBundle* io_linear_srgb) { 51 // No alpha => all opaque. 52 if (!io_linear_srgb->HasAlpha()) return; 53 54 for (size_t c = 0; c < 3; ++c) { 55 AlphaBlend(*io_linear_srgb->color(), c, background_linear, 56 *io_linear_srgb->alpha(), io_linear_srgb->color()); 57 } 58 } 59 60 Status ComputeScoreImpl(const ImageBundle& rgb0, const ImageBundle& rgb1, 61 Comparator* comparator, ImageF* distmap, float& score) { 62 JXL_RETURN_IF_ERROR(comparator->SetReferenceImage(rgb0)); 63 JXL_RETURN_IF_ERROR(comparator->CompareWith(rgb1, distmap, &score)); 64 return true; 65 } 66 67 } // namespace 68 69 Status ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1, 70 Comparator* comparator, const JxlCmsInterface& cms, 71 float* score, ImageF* diffmap, ThreadPool* pool, 72 bool ignore_alpha) { 73 JxlMemoryManager* memory_manager = rgb0.memory_manager(); 74 // Convert to linear sRGB (unless already in that space) 75 ImageMetadata metadata0 = *rgb0.metadata(); 76 ImageBundle store0(memory_manager, &metadata0); 77 const ImageBundle* linear_srgb0; 78 JXL_RETURN_IF_ERROR( 79 TransformIfNeeded(rgb0, ColorEncoding::LinearSRGB(rgb0.IsGray()), cms, 80 pool, &store0, &linear_srgb0)); 81 ImageMetadata metadata1 = *rgb1.metadata(); 82 ImageBundle store1(memory_manager, &metadata1); 83 const ImageBundle* linear_srgb1; 84 JXL_RETURN_IF_ERROR( 85 TransformIfNeeded(rgb1, ColorEncoding::LinearSRGB(rgb1.IsGray()), cms, 86 pool, &store1, &linear_srgb1)); 87 88 // No alpha: skip blending, only need a single call to Butteraugli. 89 if (ignore_alpha || (!rgb0.HasAlpha() && !rgb1.HasAlpha())) { 90 JXL_RETURN_IF_ERROR(ComputeScoreImpl(*linear_srgb0, *linear_srgb1, 91 comparator, diffmap, *score)); 92 return true; 93 } 94 95 // Blend on black and white backgrounds 96 97 const float black = 0.0f; 98 JXL_ASSIGN_OR_RETURN(ImageBundle blended_black0, linear_srgb0->Copy()); 99 JXL_ASSIGN_OR_RETURN(ImageBundle blended_black1, linear_srgb1->Copy()); 100 AlphaBlend(black, &blended_black0); 101 AlphaBlend(black, &blended_black1); 102 103 const float white = 1.0f; 104 JXL_ASSIGN_OR_RETURN(ImageBundle blended_white0, linear_srgb0->Copy()); 105 JXL_ASSIGN_OR_RETURN(ImageBundle blended_white1, linear_srgb1->Copy()); 106 107 AlphaBlend(white, &blended_white0); 108 AlphaBlend(white, &blended_white1); 109 110 ImageF diffmap_black; 111 ImageF diffmap_white; 112 float dist_black; 113 JXL_RETURN_IF_ERROR(ComputeScoreImpl(blended_black0, blended_black1, 114 comparator, &diffmap_black, dist_black)); 115 float dist_white; 116 JXL_RETURN_IF_ERROR(ComputeScoreImpl(blended_white0, blended_white1, 117 comparator, &diffmap_white, dist_white)); 118 119 // diffmap and return values are the max of diffmap_black/white. 120 if (diffmap != nullptr) { 121 const size_t xsize = rgb0.xsize(); 122 const size_t ysize = rgb0.ysize(); 123 JXL_ASSIGN_OR_RETURN(*diffmap, 124 ImageF::Create(memory_manager, xsize, ysize)); 125 for (size_t y = 0; y < ysize; ++y) { 126 const float* JXL_RESTRICT row_black = diffmap_black.ConstRow(y); 127 const float* JXL_RESTRICT row_white = diffmap_white.ConstRow(y); 128 float* JXL_RESTRICT row_out = diffmap->Row(y); 129 for (size_t x = 0; x < xsize; ++x) { 130 row_out[x] = std::max(row_black[x], row_white[x]); 131 } 132 } 133 } 134 *score = std::max(dist_black, dist_white); 135 return true; 136 } 137 138 } // namespace jxl