Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
71 changes: 38 additions & 33 deletions lib/src/gainmapmath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,15 +89,15 @@ void ShepardsIDW::fillShepardsIDW(float* weights, int incR, int incB) {
// sRGB transformations

// See IEC 61966-2-1/Amd 1:2003, Equation F.7.
static const float kSrgbR = 0.2126f, kSrgbG = 0.7152f, kSrgbB = 0.0722f;
static const float kSrgbR = 0.212639f, kSrgbG = 0.715169f, kSrgbB = 0.072192f;

float srgbLuminance(Color e) { return kSrgbR * e.r + kSrgbG * e.g + kSrgbB * e.b; }

// See ITU-R BT.709-6, Section 3.
// Uses the same coefficients for deriving luma signal as
// IEC 61966-2-1/Amd 1:2003 states for luminance, so we reuse the luminance
// function above.
static const float kSrgbCb = 1.8556f, kSrgbCr = 1.5748f;
static const float kSrgbCb = (2 * (1 - kSrgbB)), kSrgbCr = (2 * (1 - kSrgbR));

Color srgbRgbToYuv(Color e_gamma) {
float y_gamma = srgbLuminance(e_gamma);
Expand All @@ -121,15 +121,14 @@ float srgbInvOetf(float e_gamma) {
if (e_gamma <= 0.04045f) {
return e_gamma / 12.92f;
} else {
return pow((e_gamma + 0.055f) / 1.055f, 2.4);
return pow((e_gamma + 0.055f) / 1.055f, 2.4f);
}
}

Color srgbInvOetf(Color e_gamma) {
return {{{srgbInvOetf(e_gamma.r), srgbInvOetf(e_gamma.g), srgbInvOetf(e_gamma.b)}}};
}

// See IEC 61966-2-1, Equations F.5 and F.6.
float srgbInvOetfLUT(float e_gamma) {
int32_t value = static_cast<int32_t>(e_gamma * (kSrgbInvOETFNumEntries - 1) + 0.5);
// TODO() : Remove once conversion modules have appropriate clamping in place
Expand All @@ -142,24 +141,25 @@ Color srgbInvOetfLUT(Color e_gamma) {
return {{{srgbInvOetfLUT(e_gamma.r), srgbInvOetfLUT(e_gamma.g), srgbInvOetfLUT(e_gamma.b)}}};
}

// See IEC 61966-2-1/Amd 1:2003, Equations F.10 and F.11.
float srgbOetf(float e) {
constexpr float kThreshold = 0.0031308;
constexpr float kLowSlope = 12.92;
constexpr float kHighOffset = 0.055;
constexpr float kPowerExponent = 1.0 / 2.4;
constexpr float kThreshold = 0.0031308f;
constexpr float kLowSlope = 12.92f;
constexpr float kHighOffset = 0.055f;
constexpr float kPowerExponent = 1.0f / 2.4f;
if (e <= kThreshold) {
return kLowSlope * e;
}
return (1.0 + kHighOffset) * std::pow(e, kPowerExponent) - kHighOffset;
return (1.0f + kHighOffset) * std::pow(e, kPowerExponent) - kHighOffset;
}

Color srgbOetf(Color e) { return {{{srgbOetf(e.r), srgbOetf(e.g), srgbOetf(e.b)}}}; }

////////////////////////////////////////////////////////////////////////////////
// Display-P3 transformations

// See SMPTE EG 432-1, Equation 7-8.
static const float kP3R = 0.20949f, kP3G = 0.72160f, kP3B = 0.06891f;
// See SMPTE EG 432-1, Equation G-7.
static const float kP3R = 0.2289746f, kP3G = 0.6917385f, kP3B = 0.0792869f;

float p3Luminance(Color e) { return kP3R * e.r + kP3G * e.g + kP3B * e.b; }

Expand Down Expand Up @@ -190,14 +190,14 @@ Color p3YuvToRgb(Color e_gamma) {
// BT.2100 transformations - according to ITU-R BT.2100-2

// See ITU-R BT.2100-2, Table 5, HLG Reference OOTF
static const float kBt2100R = 0.2627f, kBt2100G = 0.6780f, kBt2100B = 0.0593f;
static const float kBt2100R = 0.2627f, kBt2100G = 0.677998f, kBt2100B = 0.059302f;

float bt2100Luminance(Color e) { return kBt2100R * e.r + kBt2100G * e.g + kBt2100B * e.b; }

// See ITU-R BT.2100-2, Table 6, Derivation of colour difference signals.
// BT.2100 uses the same coefficients for calculating luma signal and luminance,
// so we reuse the luminance function here.
static const float kBt2100Cb = 1.8814f, kBt2100Cr = 1.4746f;
static const float kBt2100Cb = (2 * (1 - kBt2100B)), kBt2100Cr = (2 * (1 - kBt2100R));

Color bt2100RgbToYuv(Color e_gamma) {
float y_gamma = bt2100Luminance(e_gamma);
Expand Down Expand Up @@ -239,7 +239,7 @@ Color bt2100YuvToRgb(Color e_gamma) {
}

// See ITU-R BT.2100-2, Table 5, HLG Reference OETF.
static const float kHlgA = 0.17883277f, kHlgB = 0.28466892f, kHlgC = 0.55991073;
static const float kHlgA = 0.17883277f, kHlgB = 0.28466892f, kHlgC = 0.55991073f;

float hlgOetf(float e) {
if (e <= 1.0f / 12.0f) {
Expand Down Expand Up @@ -286,9 +286,11 @@ Color hlgInvOetfLUT(Color e_gamma) {
return {{{hlgInvOetfLUT(e_gamma.r), hlgInvOetfLUT(e_gamma.g), hlgInvOetfLUT(e_gamma.b)}}};
}

// 1.2f + 0.42 * log(kHlgMaxNits / 1000)
// See ITU-R BT.2100-2, Table 5, Note 5f
// Gamma = 1.2 + 0.42 * log(kHlgMaxNits / 1000)
static const float kOotfGamma = 1.2f;

// See ITU-R BT.2100-2, Table 5, HLG Reference OOTF
Color hlgOotf(Color e, LuminanceFn luminance) {
float y = luminance(e);
return e * std::pow(y, kOotfGamma - 1.0f);
Expand All @@ -298,6 +300,7 @@ Color hlgOotfApprox(Color e, [[maybe_unused]] LuminanceFn luminance) {
return {{{std::pow(e.r, kOotfGamma), std::pow(e.g, kOotfGamma), std::pow(e.b, kOotfGamma)}}};
}

// See ITU-R BT.2100-2, Table 5, Note 5i
Color hlgInverseOotf(Color e, LuminanceFn luminance) {
float y = luminance(e);
return e * std::pow(y, (1.0f / kOotfGamma) - 1.0f);
Expand Down Expand Up @@ -600,41 +603,43 @@ void putYuv444Pixel(uhdr_raw_image_t* image, size_t x, size_t y, Color& pixel) {

////////////////////////////////////////////////////////////////////////////////
// Color space conversions
// Sample, See,
// https://registry.khronos.org/DataFormat/specs/1.3/dataformat.1.3.html#_bt_709_bt_2020_primary_conversion_example

Color bt709ToP3(Color e) {
return {{{0.82254f * e.r + 0.17755f * e.g + 0.00006f * e.b,
0.03312f * e.r + 0.96684f * e.g + -0.00001f * e.b,
0.01706f * e.r + 0.07240f * e.g + 0.91049f * e.b}}};
return {{{0.822462f * e.r + 0.177537f * e.g + 0.000001f * e.b,
0.033194f * e.r + 0.966807f * e.g + -0.000001f * e.b,
0.017083f * e.r + 0.072398f * e.g + 0.91052f * e.b}}};
}

Color bt709ToBt2100(Color e) {
return {{{0.62740f * e.r + 0.32930f * e.g + 0.04332f * e.b,
0.06904f * e.r + 0.91958f * e.g + 0.01138f * e.b,
0.01636f * e.r + 0.08799f * e.g + 0.89555f * e.b}}};
return {{{0.627404f * e.r + 0.329282f * e.g + 0.043314f * e.b,
0.069097f * e.r + 0.919541f * e.g + 0.011362f * e.b,
0.016392f * e.r + 0.088013f * e.g + 0.895595f * e.b}}};
}

Color p3ToBt709(Color e) {
return {{{1.22482f * e.r + -0.22490f * e.g + -0.00007f * e.b,
-0.04196f * e.r + 1.04199f * e.g + 0.00001f * e.b,
-0.01961f * e.r + -0.07865f * e.g + 1.09831f * e.b}}};
return {{{1.22494f * e.r + -0.22494f * e.g + 0.0f * e.b,
-0.042057f * e.r + 1.042057f * e.g + 0.0f * e.b,
-0.019638f * e.r + -0.078636f * e.g + 1.098274f * e.b}}};
}

Color p3ToBt2100(Color e) {
return {{{0.75378f * e.r + 0.19862f * e.g + 0.04754f * e.b,
0.04576f * e.r + 0.94177f * e.g + 0.01250f * e.b,
-0.00121f * e.r + 0.01757f * e.g + 0.98359f * e.b}}};
return {{{0.753833f * e.r + 0.198597f * e.g + 0.04757f * e.b,
0.045744f * e.r + 0.941777f * e.g + 0.012479f * e.b,
-0.00121f * e.r + 0.017601f * e.g + 0.983608f * e.b}}};
}

Color bt2100ToBt709(Color e) {
return {{{1.66045f * e.r + -0.58764f * e.g + -0.07286f * e.b,
-0.12445f * e.r + 1.13282f * e.g + -0.00837f * e.b,
-0.01811f * e.r + -0.10057f * e.g + 1.11878f * e.b}}};
return {{{1.660491f * e.r + -0.587641f * e.g + -0.07285f * e.b,
-0.124551f * e.r + 1.1329f * e.g + -0.008349f * e.b,
-0.018151f * e.r + -0.100579f * e.g + 1.11873f * e.b}}};
}

Color bt2100ToP3(Color e) {
return {{{1.34369f * e.r + -0.28223f * e.g + -0.06135f * e.b,
-0.06533f * e.r + 1.07580f * e.g + -0.01051f * e.b,
0.00283f * e.r + -0.01957f * e.g + 1.01679f * e.b}}};
return {{{1.343578f * e.r + -0.282179f * e.g + -0.061399f * e.b,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you double check if this matrix is correct?

I use
[2.4934969119,−0.9313836179,−0.4027107845; −0.8294889696,1.7626640603,0.0236246858; 0.0358458302,−0.0761723893,0.9568845240;] x [0.636958,0.144617,0.168881; 0.262700,0.677998,0.059302; 0.000000,0.028073,1.060985;]

and got this: (maybe I am wrong)

[ 1.33736487 -0.33736476 -7.08830856e-07 ] [ 0.02128283 0.97871719 -1.14552781e-07 ] [ 0.00418868 0.03418871 0.99581128 ]

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the matrix multiplication seems to be wrong. The individual matrices are same as the ones i am using.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK I re-did the math with gemini and this time the result matches with yours. My previous result with chatGPT was wrong. Sorry for the confusion.

-0.065298f * e.r + 1.075788f * e.g + -0.01049f * e.b,
0.002822f * e.r + -0.019598f * e.g + 1.016777f * e.b}}};
}

// All of these conversions are derived from the respective input YUV->RGB conversion followed by
Expand Down
2 changes: 1 addition & 1 deletion lib/src/icc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ std::shared_ptr<DataStruct> IccHelper::writeIccProfile(uhdr_color_transfer_t tf,

bool IccHelper::tagsEqualToMatrix(const Matrix3x3& matrix, const uint8_t* red_tag,
const uint8_t* green_tag, const uint8_t* blue_tag) {
const float tolerance = 0.001;
const float tolerance = 0.001f;
Fixed r_x_fixed = Endian_SwapBE32(reinterpret_cast<int32_t*>(const_cast<uint8_t*>(red_tag))[2]);
Fixed r_y_fixed = Endian_SwapBE32(reinterpret_cast<int32_t*>(const_cast<uint8_t*>(red_tag))[3]);
Fixed r_z_fixed = Endian_SwapBE32(reinterpret_cast<int32_t*>(const_cast<uint8_t*>(red_tag))[4]);
Expand Down
8 changes: 4 additions & 4 deletions lib/src/jpegr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ uhdr_error_info_t JpegR::generateGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_
min_content_boost_log2 = (std::max)(min_content_boost_log2, suggestion);
}
if (fabs(max_content_boost_log2 - min_content_boost_log2) < FLT_EPSILON) {
max_content_boost_log2 += 0.1; // to avoid div by zero during affine transform
max_content_boost_log2 += 0.1f; // to avoid div by zero during affine transform
}

std::function<void()> encodeMap = [this, gainmap_data, map_width, dest, min_content_boost_log2,
Expand Down Expand Up @@ -1422,7 +1422,7 @@ uhdr_error_info_t JpegR::applyGainMap(uhdr_raw_image_t* sdr_intent, uhdr_raw_ima
float gainmap_aspect_ratio = (float)gainmap_img->w / gainmap_img->h;
float delta_aspect_ratio = fabs(primary_aspect_ratio - gainmap_aspect_ratio);
// Allow 1% delta
const float delta_tolerance = 0.01;
const float delta_tolerance = 0.01f;
if (delta_aspect_ratio / primary_aspect_ratio > delta_tolerance) {
resized_gainmap = resize_image(gainmap_img, sdr_intent->w, sdr_intent->h);
if (resized_gainmap == nullptr) {
Expand Down Expand Up @@ -1697,8 +1697,8 @@ uhdr_error_info_t JpegR::parseJpegInfo(uhdr_compressed_image_t* jpeg_image, j_in
}

static float ReinhardMap(float y_hdr, float headroom) {
float out = 1.0 + y_hdr / (headroom * headroom);
out /= 1.0 + y_hdr;
float out = 1.0f + y_hdr / (headroom * headroom);
out /= 1.0f + y_hdr;
return out * y_hdr;
}

Expand Down
16 changes: 8 additions & 8 deletions tests/gainmapmath_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -540,9 +540,9 @@ TEST_F(GainMapMathTest, ColorDivideFloat) {
TEST_F(GainMapMathTest, SrgbLuminance) {
EXPECT_FLOAT_EQ(srgbLuminance(RgbBlack()), 0.0f);
EXPECT_FLOAT_EQ(srgbLuminance(RgbWhite()), 1.0f);
EXPECT_FLOAT_EQ(srgbLuminance(RgbRed()), 0.2126f);
EXPECT_FLOAT_EQ(srgbLuminance(RgbGreen()), 0.7152f);
EXPECT_FLOAT_EQ(srgbLuminance(RgbBlue()), 0.0722f);
EXPECT_FLOAT_EQ(srgbLuminance(RgbRed()), 0.212639f);
EXPECT_FLOAT_EQ(srgbLuminance(RgbGreen()), 0.715169f);
EXPECT_FLOAT_EQ(srgbLuminance(RgbBlue()), 0.072192f);
}

TEST_F(GainMapMathTest, SrgbYuvToRgb) {
Expand Down Expand Up @@ -607,9 +607,9 @@ TEST_F(GainMapMathTest, SrgbTransferFunction) {
TEST_F(GainMapMathTest, P3Luminance) {
EXPECT_FLOAT_EQ(p3Luminance(RgbBlack()), 0.0f);
EXPECT_FLOAT_EQ(p3Luminance(RgbWhite()), 1.0f);
EXPECT_FLOAT_EQ(p3Luminance(RgbRed()), 0.20949f);
EXPECT_FLOAT_EQ(p3Luminance(RgbGreen()), 0.72160f);
EXPECT_FLOAT_EQ(p3Luminance(RgbBlue()), 0.06891f);
EXPECT_FLOAT_EQ(p3Luminance(RgbRed()), 0.2289746f);
EXPECT_FLOAT_EQ(p3Luminance(RgbGreen()), 0.6917385f);
EXPECT_FLOAT_EQ(p3Luminance(RgbBlue()), 0.0792869f);
}

TEST_F(GainMapMathTest, P3YuvToRgb) {
Expand Down Expand Up @@ -666,8 +666,8 @@ TEST_F(GainMapMathTest, Bt2100Luminance) {
EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlack()), 0.0f);
EXPECT_FLOAT_EQ(bt2100Luminance(RgbWhite()), 1.0f);
EXPECT_FLOAT_EQ(bt2100Luminance(RgbRed()), 0.2627f);
EXPECT_FLOAT_EQ(bt2100Luminance(RgbGreen()), 0.6780f);
EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlue()), 0.0593f);
EXPECT_FLOAT_EQ(bt2100Luminance(RgbGreen()), 0.677998f);
EXPECT_FLOAT_EQ(bt2100Luminance(RgbBlue()), 0.059302f);
}

TEST_F(GainMapMathTest, Bt2100YuvToRgb) {
Expand Down