Skip to content

Commit

Permalink
signal alternate image color space in gainmap image
Browse files Browse the repository at this point in the history
If sdr intent and hdr intent gamut space are not identical, it is
required to send hdr intent color profile as part of gainmap image. This
is addressed.

Test: ./ultrahdr_app <options>

Co-authored-by: Vivek R Jadhav <vivek.jadhav@ittiam.com>
Change-Id: I0155a1dc043f3c3e0493e04e5a6eabe160756476
  • Loading branch information
2 people authored and DichenZhang1 committed Dec 4, 2024
1 parent 2b73304 commit 0af937e
Show file tree
Hide file tree
Showing 8 changed files with 163 additions and 73 deletions.
2 changes: 1 addition & 1 deletion fuzzer/ultrahdr_legacy_fuzzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ void UltraHdrEncFuzzer::process() {

// dest
// 2 * p010 size as input data is random, DCT compression might not behave as expected
jpegImgR.maxLength = std::max(8 * 1024 /* min size 8kb */, width * height * 3 * 2);
jpegImgR.maxLength = std::max(64 * 1024 /* min size 8kb */, width * height * 3 * 2);
auto jpegImgRaw = std::make_unique<uint8_t[]>(jpegImgR.maxLength);
jpegImgR.data = jpegImgRaw.get();
// #define DUMP_PARAM
Expand Down
7 changes: 7 additions & 0 deletions lib/include/ultrahdr/gainmapmath.h
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,13 @@ void putYuv444Pixel(uhdr_raw_image_t* image, size_t x, size_t y, Color& pixel);
// Color space conversions

// color gamut conversion (rgb) functions
extern const std::array<float, 9> kBt709ToP3;
extern const std::array<float, 9> kBt709ToBt2100;
extern const std::array<float, 9> kP3ToBt709;
extern const std::array<float, 9> kP3ToBt2100;
extern const std::array<float, 9> kBt2100ToBt709;
extern const std::array<float, 9> kBt2100ToP3;

inline Color identityConversion(Color e) { return e; }
Color bt709ToP3(Color e);
Color bt709ToBt2100(Color e);
Expand Down
2 changes: 2 additions & 0 deletions lib/include/ultrahdr/icc.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,10 +153,12 @@ static constexpr Matrix3x3 kRec2020 = {{
{-0.00193139f, 0.0299794f, 0.797162f},
}};

static constexpr uint32_t kCICPPrimariesUnSpecified = 2;
static constexpr uint32_t kCICPPrimariesSRGB = 1;
static constexpr uint32_t kCICPPrimariesP3 = 12;
static constexpr uint32_t kCICPPrimariesRec2020 = 9;

static constexpr uint32_t kCICPTrfnUnSpecified = 2;
static constexpr uint32_t kCICPTrfnSRGB = 1;
static constexpr uint32_t kCICPTrfnLinear = 8;
static constexpr uint32_t kCICPTrfnPQ = 16;
Expand Down
60 changes: 25 additions & 35 deletions lib/src/gainmapmath.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -606,41 +606,31 @@ void putYuv444Pixel(uhdr_raw_image_t* image, size_t x, size_t y, Color& pixel) {
// 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.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.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.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.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.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.343578f * e.r + -0.282179f * e.g + -0.061399f * e.b,
-0.065298f * e.r + 1.075788f * e.g + -0.01049f * e.b,
0.002822f * e.r + -0.019598f * e.g + 1.016777f * e.b}}};
}
const std::array<float, 9> kBt709ToP3 = {0.822462f, 0.177537f, 0.000001f, 0.033194f, 0.966807f,
-0.000001f, 0.017083f, 0.072398f, 0.91052f};
const std::array<float, 9> kBt709ToBt2100 = {0.627404f, 0.329282f, 0.043314f, 0.069097f, 0.919541f,
0.011362f, 0.016392f, 0.088013f, 0.895595f};
const std::array<float, 9> kP3ToBt709 = {1.22494f, -0.22494f, 0.0f, -0.042057f, 1.042057f,
0.0f, -0.019638f, -0.078636f, 1.098274f};
const std::array<float, 9> kP3ToBt2100 = {0.753833f, 0.198597f, 0.04757f, 0.045744f, 0.941777f,
0.012479f, -0.00121f, 0.017601f, 0.983608f};
const std::array<float, 9> kBt2100ToBt709 = {1.660491f, -0.587641f, -0.07285f,
-0.124551f, 1.1329f, -0.008349f,
-0.018151f, -0.100579f, 1.11873f};
const std::array<float, 9> kBt2100ToP3 = {1.343578f, -0.282179f, -0.061399f, -0.065298f, 1.075788f,
-0.01049f, 0.002822f, -0.019598f, 1.016777f};

Color ConvertGamut(Color e, const std::array<float, 9>& coeffs) {
return {{{coeffs[0] * e.r + coeffs[1] * e.g + coeffs[2] * e.b,
coeffs[3] * e.r + coeffs[4] * e.g + coeffs[5] * e.b,
coeffs[6] * e.r + coeffs[7] * e.g + coeffs[8] * e.b}}};
}
Color bt709ToP3(Color e) { return ConvertGamut(e, kBt709ToP3); }
Color bt709ToBt2100(Color e) { return ConvertGamut(e, kBt709ToBt2100); }
Color p3ToBt709(Color e) { return ConvertGamut(e, kP3ToBt709); }
Color p3ToBt2100(Color e) { return ConvertGamut(e, kP3ToBt2100); }
Color bt2100ToBt709(Color e) { return ConvertGamut(e, kBt2100ToBt709); }
Color bt2100ToP3(Color e) { return ConvertGamut(e, kBt2100ToP3); }

// All of these conversions are derived from the respective input YUV->RGB conversion followed by
// the RGB->YUV for the receiving encoding. They are consistent with the RGB<->YUV functions in
Expand Down
109 changes: 85 additions & 24 deletions lib/src/gpu/applygainmap_gl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,10 +154,6 @@ static const std::string applyGainMapShader = R"__SHADER__(
}
)__SHADER__";

static const std::string linearOETFShader = R"__SHADER__(
vec3 OETF(const vec3 linear) { return linear; }
)__SHADER__";

static const std::string hlgOETFShader = R"__SHADER__(
float OETF(const float linear) {
const float kHlgA = 0.17883277;
Expand Down Expand Up @@ -195,12 +191,60 @@ static const std::string hlgInverseOOTFShader = R"__SHADER__(
}
)__SHADER__";

static const std::string IdentityInverseOOTFShader = R"__SHADER__(
vec3 InverseOOTF(const vec3 linear) { return linear; }
)__SHADER__";
template <typename... Args>
std::string StringFormat(const std::string& format, Args... args) {
auto size = std::snprintf(nullptr, 0, format.c_str(), args...);
if (size < 0) return std::string();
std::vector<char> buffer(size + 1); // Add 1 for terminating null byte
std::snprintf(buffer.data(), buffer.size(), format.c_str(), args...);
return std::string(buffer.data(), size); // Exclude the terminating null byte
}

std::string getClampPixelFloatShader(uhdr_color_transfer_t output_ct) {
return StringFormat(
" vec3 clampPixelFloat(const vec3 color) {\n"
" return clamp(color, 0.0, %f);\n"
" }\n",
output_ct == UHDR_CT_LINEAR ? kMaxPixelFloatHdrLinear : kMaxPixelFloat);
}

std::string getGamutConversionShader(uhdr_color_gamut_t src_cg, uhdr_color_gamut_t dst_cg) {
const float* coeffs = nullptr;
if (dst_cg == UHDR_CG_BT_709) {
if (src_cg == UHDR_CG_DISPLAY_P3) {
coeffs = kP3ToBt709.data();
} else if (src_cg == UHDR_CG_BT_2100) {
coeffs = kBt2100ToBt709.data();
}
} else if (dst_cg == UHDR_CG_DISPLAY_P3) {
if (src_cg == UHDR_CG_BT_709) {
coeffs = kBt709ToP3.data();
}
if (src_cg == UHDR_CG_BT_2100) {
coeffs = kBt2100ToP3.data();
}
} else if (dst_cg == UHDR_CG_BT_2100) {
if (src_cg == UHDR_CG_BT_709) {
coeffs = kBt709ToBt2100.data();
} else if (src_cg == UHDR_CG_DISPLAY_P3) {
coeffs = kP3ToBt2100.data();
}
}
return StringFormat(
" vec3 gamutConversion(const vec3 color) {\n"
" const mat3 transform = mat3(\n"
" %f, %f, %f,\n"
" %f, %f, %f,\n"
" %f, %f, %f);\n"
" return transform * color;\n"
" }\n",
coeffs[0], coeffs[3], coeffs[6], coeffs[1], coeffs[4], coeffs[7], coeffs[2], coeffs[5],
coeffs[8]);
}

std::string getApplyGainMapFragmentShader(uhdr_img_fmt sdr_fmt, uhdr_img_fmt gm_fmt,
uhdr_color_transfer output_ct) {
uhdr_color_transfer output_ct, uhdr_color_gamut_t sdr_cg,
uhdr_color_gamut_t hdr_cg) {
std::string shader_code = R"__SHADER__(#version 300 es
precision highp float;
precision highp int;
Expand All @@ -221,27 +265,42 @@ std::string getApplyGainMapFragmentShader(uhdr_img_fmt sdr_fmt, uhdr_img_fmt gm_
shader_code.append(gm_fmt == UHDR_IMG_FMT_8bppYCbCr400 ? getGainMapSampleSingleChannel
: getGainMapSampleMultiChannel);
shader_code.append(applyGainMapShader);
if (output_ct == UHDR_CT_LINEAR) {
shader_code.append(IdentityInverseOOTFShader);
shader_code.append(linearOETFShader);
} else if (output_ct == UHDR_CT_HLG) {
if (sdr_cg != hdr_cg) shader_code.append(getGamutConversionShader(sdr_cg, hdr_cg));
shader_code.append(getClampPixelFloatShader(output_ct));
if (output_ct == UHDR_CT_HLG) {
shader_code.append(hlgInverseOOTFShader);
shader_code.append(hlgOETFShader);
} else if (output_ct == UHDR_CT_PQ) {
shader_code.append(IdentityInverseOOTFShader);
shader_code.append(pqOETFShader);
}

shader_code.append(R"__SHADER__(
void main() {
vec3 yuv_gamma_sdr = getYUVPixel();
vec3 rgb_gamma_sdr = p3YuvToRgb(yuv_gamma_sdr);
vec3 rgb_sdr = sRGBEOTF(rgb_gamma_sdr);
vec3 gain = sampleMap(gainMapTexture);
vec3 rgb_hdr = applyGain(rgb_sdr, gain);
)__SHADER__");
if (sdr_cg != hdr_cg) {
shader_code.append(R"__SHADER__(
rgb_hdr = gamutConversion(rgb_hdr);
)__SHADER__");
}
shader_code.append(R"__SHADER__(
rgb_hdr = clampPixelFloat(rgb_hdr);
)__SHADER__");
if (output_ct == UHDR_CT_HLG) {
shader_code.append(R"__SHADER__(
rgb_hdr = InverseOOTF(rgb_hdr);
vec3 rgb_gamma_hdr = OETF(rgb_hdr);
FragColor = vec4(rgb_gamma_hdr, 1.0);
rgb_hdr = OETF(rgb_hdr);
)__SHADER__");
} else if (output_ct == UHDR_CT_PQ) {
shader_code.append(R"__SHADER__(
rgb_hdr = OETF(rgb_hdr);
)__SHADER__");
}
shader_code.append(R"__SHADER__(
FragColor = vec4(rgb_hdr, 1.0);
}
)__SHADER__");
return shader_code;
Expand Down Expand Up @@ -279,9 +338,10 @@ bool isBufferDataContiguous(uhdr_raw_image_t* img) {
uhdr_error_info_t applyGainMapGLES(uhdr_raw_image_t* sdr_intent, uhdr_raw_image_t* gainmap_img,
uhdr_gainmap_metadata_ext_t* gainmap_metadata,
uhdr_color_transfer_t output_ct, float display_boost,
uhdr_raw_image_t* dest, uhdr_opengl_ctxt_t* opengl_ctxt) {
GLuint shaderProgram = 0; // shader program
GLuint yuvTexture = 0; // sdr intent texture
uhdr_color_gamut_t sdr_cg, uhdr_color_gamut_t hdr_cg,
uhdr_opengl_ctxt_t* opengl_ctxt) {
GLuint shaderProgram = 0; // shader program
GLuint yuvTexture = 0; // sdr intent texture
GLuint frameBuffer = 0;

#define RET_IF_ERR() \
Expand All @@ -294,7 +354,8 @@ uhdr_error_info_t applyGainMapGLES(uhdr_raw_image_t* sdr_intent, uhdr_raw_image_

shaderProgram = opengl_ctxt->create_shader_program(
vertex_shader.c_str(),
getApplyGainMapFragmentShader(sdr_intent->fmt, gainmap_img->fmt, output_ct).c_str());
getApplyGainMapFragmentShader(sdr_intent->fmt, gainmap_img->fmt, output_ct, sdr_cg, hdr_cg)
.c_str());
RET_IF_ERR()

yuvTexture = opengl_ctxt->create_texture(sdr_intent->fmt, sdr_intent->w, sdr_intent->h,
Expand Down Expand Up @@ -342,8 +403,10 @@ uhdr_error_info_t applyGainMapGLES(uhdr_raw_image_t* sdr_intent, uhdr_raw_image_
}
glUniform1f(weightLocation, gainmap_weight);
float normalize = 1.0f;
if (output_ct == UHDR_CT_HLG) normalize = kHlgMaxNits / kSdrWhiteNits;
else if (output_ct == UHDR_CT_PQ) normalize = kPqMaxNits / kSdrWhiteNits;
if (output_ct == UHDR_CT_HLG)
normalize = kHlgMaxNits / kSdrWhiteNits;
else if (output_ct == UHDR_CT_PQ)
normalize = kPqMaxNits / kSdrWhiteNits;
glUniform1f(normalizeLocation, normalize);

glActiveTexture(GL_TEXTURE0);
Expand All @@ -364,8 +427,6 @@ uhdr_error_info_t applyGainMapGLES(uhdr_raw_image_t* sdr_intent, uhdr_raw_image_
opengl_ctxt->check_gl_errors("reading gles output");
RET_IF_ERR()

dest->cg = sdr_intent->cg;

if (frameBuffer) glDeleteFramebuffers(1, &frameBuffer);
if (yuvTexture) glDeleteTextures(1, &yuvTexture);
if (shaderProgram) glDeleteProgram(shaderProgram);
Expand Down
17 changes: 11 additions & 6 deletions lib/src/icc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,6 @@ std::shared_ptr<DataStruct> IccHelper::writeIccProfile(uhdr_color_transfer_t tf,

// Compute profile description tag
std::string desc = get_desc_string(tf, gamut);

tags.emplace_back(kTAG_desc, write_text_tag(desc.c_str()));

Matrix3x3 toXYZD50;
Expand Down Expand Up @@ -465,26 +464,32 @@ std::shared_ptr<DataStruct> IccHelper::writeIccProfile(uhdr_color_transfer_t tf,
write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
tags.emplace_back(kTAG_bTRC,
write_trc_tag(kTrcTableSize, reinterpret_cast<uint8_t*>(trc_table.data())));
} else {
} else if (tf == UHDR_CT_SRGB) {
tags.emplace_back(kTAG_rTRC, write_trc_tag(kSRGB_TransFun));
tags.emplace_back(kTAG_gTRC, write_trc_tag(kSRGB_TransFun));
tags.emplace_back(kTAG_bTRC, write_trc_tag(kSRGB_TransFun));
} else if (tf == UHDR_CT_LINEAR) {
tags.emplace_back(kTAG_rTRC, write_trc_tag(kLinear_TransFun));
tags.emplace_back(kTAG_gTRC, write_trc_tag(kLinear_TransFun));
tags.emplace_back(kTAG_bTRC, write_trc_tag(kLinear_TransFun));
}
}

// Compute CICP.
if (tf == UHDR_CT_HLG || tf == UHDR_CT_PQ) {
// Compute CICP - for hdr images icc profile shall contain cicp.
if (tf == UHDR_CT_HLG || tf == UHDR_CT_PQ || tf == UHDR_CT_LINEAR) {
// The CICP tag is present in ICC 4.4, so update the header's version.
header.version = Endian_SwapBE32(0x04400000);

uint32_t color_primaries = 0;
uint32_t color_primaries = kCICPPrimariesUnSpecified;
if (gamut == UHDR_CG_BT_709) {
color_primaries = kCICPPrimariesSRGB;
} else if (gamut == UHDR_CG_DISPLAY_P3) {
color_primaries = kCICPPrimariesP3;
} else if (gamut == UHDR_CG_BT_2100) {
color_primaries = kCICPPrimariesRec2020;
}

uint32_t transfer_characteristics = 0;
uint32_t transfer_characteristics = kCICPTrfnUnSpecified;
if (tf == UHDR_CT_SRGB) {
transfer_characteristics = kCICPTrfnSRGB;
} else if (tf == UHDR_CT_LINEAR) {
Expand Down
Loading

0 comments on commit 0af937e

Please sign in to comment.