opsin_image_test.cc (4173B)
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 <jxl/cms.h> 7 8 #include <cstddef> 9 #include <utility> 10 11 #include "lib/jxl/base/compiler_specific.h" 12 #include "lib/jxl/base/matrix_ops.h" 13 #include "lib/jxl/cms/opsin_params.h" 14 #include "lib/jxl/dec_xyb.h" 15 #include "lib/jxl/enc_xyb.h" 16 #include "lib/jxl/image.h" 17 #include "lib/jxl/image_bundle.h" 18 #include "lib/jxl/image_metadata.h" 19 #include "lib/jxl/opsin_params.h" 20 #include "lib/jxl/testing.h" 21 22 namespace jxl { 23 namespace { 24 25 // Convert a single linear sRGB color to xyb, using the exact image conversion 26 // procedure that jpeg xl uses. 27 void LinearSrgbToOpsin(float rgb_r, float rgb_g, float rgb_b, 28 float* JXL_RESTRICT xyb_x, float* JXL_RESTRICT xyb_y, 29 float* JXL_RESTRICT xyb_b) { 30 JXL_ASSIGN_OR_DIE(Image3F linear, Image3F::Create(1, 1)); 31 linear.PlaneRow(0, 0)[0] = rgb_r; 32 linear.PlaneRow(1, 0)[0] = rgb_g; 33 linear.PlaneRow(2, 0)[0] = rgb_b; 34 35 ImageMetadata metadata; 36 metadata.SetFloat32Samples(); 37 metadata.color_encoding = ColorEncoding::LinearSRGB(); 38 ImageBundle ib(&metadata); 39 ib.SetFromImage(std::move(linear), metadata.color_encoding); 40 JXL_ASSIGN_OR_DIE(Image3F opsin, Image3F::Create(1, 1)); 41 (void)ToXYB(ib, /*pool=*/nullptr, &opsin, *JxlGetDefaultCms()); 42 43 *xyb_x = opsin.PlaneRow(0, 0)[0]; 44 *xyb_y = opsin.PlaneRow(1, 0)[0]; 45 *xyb_b = opsin.PlaneRow(2, 0)[0]; 46 } 47 48 // Convert a single XYB color to linear sRGB, using the exact image conversion 49 // procedure that jpeg xl uses. 50 void OpsinToLinearSrgb(float xyb_x, float xyb_y, float xyb_b, 51 float* JXL_RESTRICT rgb_r, float* JXL_RESTRICT rgb_g, 52 float* JXL_RESTRICT rgb_b) { 53 JXL_ASSIGN_OR_DIE(Image3F opsin, Image3F::Create(1, 1)); 54 opsin.PlaneRow(0, 0)[0] = xyb_x; 55 opsin.PlaneRow(1, 0)[0] = xyb_y; 56 opsin.PlaneRow(2, 0)[0] = xyb_b; 57 JXL_ASSIGN_OR_DIE(Image3F linear, Image3F::Create(1, 1)); 58 OpsinParams opsin_params; 59 opsin_params.Init(/*intensity_target=*/255.0f); 60 OpsinToLinear(opsin, Rect(opsin), nullptr, &linear, opsin_params); 61 *rgb_r = linear.PlaneRow(0, 0)[0]; 62 *rgb_g = linear.PlaneRow(1, 0)[0]; 63 *rgb_b = linear.PlaneRow(2, 0)[0]; 64 } 65 66 void OpsinRoundtripTestRGB(float r, float g, float b) { 67 float xyb_x; 68 float xyb_y; 69 float xyb_b; 70 LinearSrgbToOpsin(r, g, b, &xyb_x, &xyb_y, &xyb_b); 71 float r2; 72 float g2; 73 float b2; 74 OpsinToLinearSrgb(xyb_x, xyb_y, xyb_b, &r2, &g2, &b2); 75 EXPECT_NEAR(r, r2, 1e-3); 76 EXPECT_NEAR(g, g2, 1e-3); 77 EXPECT_NEAR(b, b2, 1e-3); 78 } 79 80 TEST(OpsinImageTest, VerifyOpsinAbsorbanceInverseMatrix) { 81 float matrix[9]; // writable copy 82 for (int i = 0; i < 9; i++) { 83 matrix[i] = GetOpsinAbsorbanceInverseMatrix()[i]; 84 } 85 EXPECT_TRUE(Inv3x3Matrix(matrix)); 86 for (int i = 0; i < 9; i++) { 87 EXPECT_NEAR(matrix[i], jxl::cms::kOpsinAbsorbanceMatrix[i], 1e-6); 88 } 89 } 90 91 TEST(OpsinImageTest, OpsinRoundtrip) { 92 OpsinRoundtripTestRGB(0, 0, 0); 93 OpsinRoundtripTestRGB(1. / 255, 1. / 255, 1. / 255); 94 OpsinRoundtripTestRGB(128. / 255, 128. / 255, 128. / 255); 95 OpsinRoundtripTestRGB(1, 1, 1); 96 97 OpsinRoundtripTestRGB(0, 0, 1. / 255); 98 OpsinRoundtripTestRGB(0, 0, 128. / 255); 99 OpsinRoundtripTestRGB(0, 0, 1); 100 101 OpsinRoundtripTestRGB(0, 1. / 255, 0); 102 OpsinRoundtripTestRGB(0, 128. / 255, 0); 103 OpsinRoundtripTestRGB(0, 1, 0); 104 105 OpsinRoundtripTestRGB(1. / 255, 0, 0); 106 OpsinRoundtripTestRGB(128. / 255, 0, 0); 107 OpsinRoundtripTestRGB(1, 0, 0); 108 } 109 110 TEST(OpsinImageTest, VerifyZero) { 111 // Test that black color (zero energy) is 0,0,0 in xyb. 112 float x; 113 float y; 114 float b; 115 LinearSrgbToOpsin(0, 0, 0, &x, &y, &b); 116 EXPECT_NEAR(0, x, 1e-9); 117 EXPECT_NEAR(0, y, 1e-7); 118 EXPECT_NEAR(0, b, 1e-7); 119 } 120 121 TEST(OpsinImageTest, VerifyGray) { 122 // Test that grayscale colors have a fixed y/b ratio and x==0. 123 for (size_t i = 1; i < 255; i++) { 124 float x; 125 float y; 126 float b; 127 LinearSrgbToOpsin(i / 255., i / 255., i / 255., &x, &y, &b); 128 EXPECT_NEAR(0, x, 1e-6); 129 EXPECT_NEAR(jxl::cms::kYToBRatio, b / y, 3e-5); 130 } 131 } 132 133 } // namespace 134 } // namespace jxl