Skip to content

Commit 7fe4283

Browse files
committed
update weights used in rgb to yuv conversion equations
Display P3 color space is using bt601 equations. Updated these to use the ones derived using p3 primaries Test: ./ultrahdr_unit_test Change-Id: I9306fe13c4651c7d55a830d0597924a8d26d2745
1 parent d52a0d1 commit 7fe4283

File tree

8 files changed

+300
-129
lines changed

8 files changed

+300
-129
lines changed

examples/ultrahdr_app.cpp

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,23 @@
3232

3333
#include "ultrahdr_api.h"
3434

35-
const float BT601YUVtoRGBMatrix[9] = {
36-
1.f, 0.f, 1.402f, 1.f, (-0.202008f / 0.587f), (-0.419198f / 0.587f), 1.0f, 1.772f, 0.0f};
35+
const float DisplayP3YUVtoRGBMatrix[9] = {
36+
1.f, 0.f, 1.542f, 1.f, (-0.146023f / 0.6917f), (-0.353118f / 0.6917f), 1.0f, 1.8414f, 0.0f};
3737
const float BT709YUVtoRGBMatrix[9] = {
3838
1.f, 0.f, 1.5748f, 1.f, (-0.13397432f / 0.7152f), (-0.33480248f / 0.7152f),
3939
1.0f, 1.8556f, 0.0f};
4040
const float BT2020YUVtoRGBMatrix[9] = {
4141
1.f, 0.f, 1.4746f, 1.f, (-0.11156702f / 0.6780f), (-0.38737742f / 0.6780f), 1.f, 1.8814f, 0.f};
4242

43-
const float BT601RGBtoYUVMatrix[9] = {0.299f,
44-
0.587f,
45-
0.114f,
46-
(-0.299f / 1.772f),
47-
(-0.587f / 1.772f),
48-
0.5f,
49-
0.5f,
50-
(-0.587f / 1.402f),
51-
(-0.114f / 1.402f)};
43+
const float DisplayP3RGBtoYUVMatrix[9] = {0.229f,
44+
0.6917f,
45+
0.0793f,
46+
(-0.229f / 1.8414f),
47+
(-0.6917f / 1.8414f),
48+
0.5f,
49+
0.5f,
50+
(-0.6917f / 1.542f),
51+
(-0.0793f / 1.542f)};
5252
const float BT709RGBtoYUVMatrix[9] = {0.2126f,
5353
0.7152f,
5454
0.0722f,
@@ -892,7 +892,7 @@ bool UltraHdrAppInput::convertP010ToRGBImage() {
892892
} else if (mHdrCg == UHDR_CG_BT_2100) {
893893
coeffs = BT2020YUVtoRGBMatrix;
894894
} else if (mHdrCg == UHDR_CG_DISPLAY_P3) {
895-
coeffs = BT601YUVtoRGBMatrix;
895+
coeffs = DisplayP3YUVtoRGBMatrix;
896896
} else {
897897
std::cerr << "color matrix not present for gamut " << mHdrCg << " using BT2020Matrix"
898898
<< std::endl;
@@ -984,15 +984,15 @@ bool UltraHdrAppInput::convertYuv420ToRGBImage() {
984984
uint8_t* u = static_cast<uint8_t*>(mRawYuv420Image.planes[UHDR_PLANE_U]);
985985
uint8_t* v = static_cast<uint8_t*>(mRawYuv420Image.planes[UHDR_PLANE_V]);
986986

987-
const float* coeffs = BT601YUVtoRGBMatrix;
987+
const float* coeffs = BT709YUVtoRGBMatrix;
988988
if (mSdrCg == UHDR_CG_BT_709) {
989989
coeffs = BT709YUVtoRGBMatrix;
990990
} else if (mSdrCg == UHDR_CG_BT_2100) {
991991
coeffs = BT2020YUVtoRGBMatrix;
992992
} else if (mSdrCg == UHDR_CG_DISPLAY_P3) {
993-
coeffs = BT601YUVtoRGBMatrix;
993+
coeffs = DisplayP3YUVtoRGBMatrix;
994994
} else {
995-
std::cerr << "color matrix not present for gamut " << mSdrCg << " using BT601Matrix"
995+
std::cerr << "color matrix not present for gamut " << mSdrCg << " using BT709Matrix"
996996
<< std::endl;
997997
}
998998
for (size_t i = 0; i < mRawYuv420Image.h; i++) {
@@ -1054,16 +1054,16 @@ bool UltraHdrAppInput::convertRgba8888ToYUV444Image() {
10541054
uint8_t* uData = static_cast<uint8_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_U]);
10551055
uint8_t* vData = static_cast<uint8_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_V]);
10561056

1057-
const float* coeffs = BT601RGBtoYUVMatrix;
1057+
const float* coeffs = BT709RGBtoYUVMatrix;
10581058
if (mDecodedUhdrRgbImage.cg == UHDR_CG_BT_709) {
10591059
coeffs = BT709RGBtoYUVMatrix;
10601060
} else if (mDecodedUhdrRgbImage.cg == UHDR_CG_BT_2100) {
10611061
coeffs = BT2020RGBtoYUVMatrix;
10621062
} else if (mDecodedUhdrRgbImage.cg == UHDR_CG_DISPLAY_P3) {
1063-
coeffs = BT601RGBtoYUVMatrix;
1063+
coeffs = DisplayP3RGBtoYUVMatrix;
10641064
} else {
10651065
std::cerr << "color matrix not present for gamut " << mDecodedUhdrRgbImage.cg
1066-
<< " using BT601Matrix" << std::endl;
1066+
<< " using BT709Matrix" << std::endl;
10671067
}
10681068

10691069
for (size_t i = 0; i < mDecodedUhdrRgbImage.h; i++) {
@@ -1108,7 +1108,7 @@ bool UltraHdrAppInput::convertRgba1010102ToYUV444Image() {
11081108
} else if (mDecodedUhdrRgbImage.cg == UHDR_CG_BT_2100) {
11091109
coeffs = BT2020RGBtoYUVMatrix;
11101110
} else if (mDecodedUhdrRgbImage.cg == UHDR_CG_DISPLAY_P3) {
1111-
coeffs = BT601RGBtoYUVMatrix;
1111+
coeffs = DisplayP3RGBtoYUVMatrix;
11121112
} else {
11131113
std::cerr << "color matrix not present for gamut " << mDecodedUhdrRgbImage.cg
11141114
<< " using BT2020Matrix" << std::endl;

lib/include/ultrahdr/gainmapmath.h

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,13 @@ Color pqInvOetfLUT(Color e_gamma);
347347
constexpr int32_t kPqInvOETFPrecision = 12;
348348
constexpr int32_t kPqInvOETFNumEntries = 1 << kPqInvOETFPrecision;
349349

350+
////////////////////////////////////////////////////////////////////////////////
351+
// BT.601 transformations
352+
353+
// BT.601 rgb <-> yuv conversion
354+
Color bt601RgbToYuv(Color e_gamma);
355+
Color bt601YuvToRgb(Color e_gamma);
356+
350357
// util class to prepare look up tables for oetf/eotf functions
351358
class LookUpTable {
352359
public:
@@ -415,20 +422,26 @@ Color bt2100ToP3(Color e);
415422

416423
// convert between yuv encodings
417424
extern const std::array<float, 9> kYuvBt709ToBt601;
425+
extern const std::array<float, 9> kYuvBt709ToDisplayP3;
418426
extern const std::array<float, 9> kYuvBt709ToBt2100;
419-
extern const std::array<float, 9> kYuvBt601ToBt709;
420-
extern const std::array<float, 9> kYuvBt601ToBt2100;
421-
extern const std::array<float, 9> kYuvBt2100ToBt709;
427+
extern const std::array<float, 9> kYuvDisplayP3ToBt601;
428+
extern const std::array<float, 9> kYuvDisplayP3ToBt709;
429+
extern const std::array<float, 9> kYuvDisplayP3ToBt2100;
422430
extern const std::array<float, 9> kYuvBt2100ToBt601;
431+
extern const std::array<float, 9> kYuvBt2100ToBt709;
432+
extern const std::array<float, 9> kYuvBt2100ToDisplayP3;
423433

424434
#if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON)))
425435

426436
extern const int16_t kYuv709To601_coeffs_neon[8];
437+
extern const int16_t kYuv709ToP3_coeffs_neon[8];
427438
extern const int16_t kYuv709To2100_coeffs_neon[8];
428-
extern const int16_t kYuv601To709_coeffs_neon[8];
429-
extern const int16_t kYuv601To2100_coeffs_neon[8];
430-
extern const int16_t kYuv2100To709_coeffs_neon[8];
439+
extern const int16_t kYuvP3To601_coeffs_neon[8];
440+
extern const int16_t kYuvP3To709_coeffs_neon[8];
441+
extern const int16_t kYuvP3To2100_coeffs_neon[8];
431442
extern const int16_t kYuv2100To601_coeffs_neon[8];
443+
extern const int16_t kYuv2100To709_coeffs_neon[8];
444+
extern const int16_t kYuv2100ToP3_coeffs_neon[8];
432445

433446
/*
434447
* The Y values are provided at half the width of U & V values to allow use of the widening
@@ -608,10 +621,11 @@ std::unique_ptr<uhdr_raw_image_ext_t> copy_raw_image(uhdr_raw_image_t* src);
608621
uhdr_error_info_t copy_raw_image(uhdr_raw_image_t* src, uhdr_raw_image_t* dst);
609622

610623
std::unique_ptr<uhdr_raw_image_ext_t> convert_raw_input_to_ycbcr(
611-
uhdr_raw_image_t* src, bool chroma_sampling_enabled = false);
624+
uhdr_raw_image_t* src, bool use_bt601 = false, bool chroma_sampling_enabled = false);
612625

613626
#if (defined(UHDR_ENABLE_INTRINSICS) && (defined(__ARM_NEON__) || defined(__ARM_NEON)))
614-
std::unique_ptr<uhdr_raw_image_ext_t> convert_raw_input_to_ycbcr_neon(uhdr_raw_image_t* src);
627+
std::unique_ptr<uhdr_raw_image_ext_t> convert_raw_input_to_ycbcr_neon(uhdr_raw_image_t* src,
628+
bool use_bt601 = false);
615629
#endif
616630

617631
bool floatToSignedFraction(float v, int32_t* numerator, uint32_t* denominator);

lib/include/ultrahdr/ultrahdrcommon.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,8 @@
159159

160160
static const uhdr_error_info_t g_no_error = {UHDR_CODEC_OK, 0, ""};
161161

162+
static const int UHDR_CG_BT_601 = 3; /**< BT.601 */
163+
162164
namespace ultrahdr {
163165

164166
// ===============================================================================================

lib/src/dsp/arm/gainmapmath_neon.cpp

Lines changed: 70 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -35,47 +35,68 @@ namespace ultrahdr {
3535
// {Y1, Y2, U1, U2, V1, V2, 0, 0}
3636

3737
// Yuv Bt709 -> Yuv Bt601
38-
// Y' = (1.0f * Y) + ( 0.101579f * U) + ( 0.196076f * V)
39-
// U' = (0.0f * Y) + ( 0.989854f * U) + (-0.110653f * V)
40-
// V' = (0.0f * Y) + (-0.072453f * U) + ( 0.983398f * V)
38+
// Y' = (1.0 * Y) + ( 0.101579 * U) + ( 0.196076 * V)
39+
// U' = (0.0 * Y) + ( 0.989854 * U) + (-0.110653 * V)
40+
// V' = (0.0 * Y) + (-0.072453 * U) + ( 0.983398 * V)
4141
ALIGNED(16)
4242
const int16_t kYuv709To601_coeffs_neon[8] = {1664, 3213, 16218, -1813, -1187, 16112, 0, 0};
4343

44+
// Yuv Bt709 -> Display P3
45+
// Y' = (1.0 * Y) + ( 0.017545 * U) + ( 0.03677 * V)
46+
// U' = (0.0 * Y) + ( 0.998169 * U) + (-0.019968 * V)
47+
// V' = (0.0 * Y) + (-0.011378 * U) + ( 0.997393 * V)
48+
ALIGNED(16)
49+
const int16_t kYuv709ToP3_coeffs_neon[8] = {287, 602, 16354, -327, -186, 16341, 0, 0};
50+
4451
// Yuv Bt709 -> Yuv Bt2100
4552
// Y' = (1.0f * Y) + (-0.016969f * U) + ( 0.096312f * V)
4653
// U' = (0.0f * Y) + ( 0.995306f * U) + (-0.051192f * V)
4754
// V' = (0.0f * Y) + ( 0.011507f * U) + ( 1.002637f * V)
4855
ALIGNED(16)
4956
const int16_t kYuv709To2100_coeffs_neon[8] = {-278, 1578, 16307, -839, 189, 16427, 0, 0};
5057

51-
// Yuv Bt601 -> Yuv Bt709
52-
// Y' = (1.0f * Y) + (-0.118188f * U) + (-0.212685f * V),
53-
// U' = (0.0f * Y) + ( 1.018640f * U) + ( 0.114618f * V),
54-
// V' = (0.0f * Y) + ( 0.075049f * U) + ( 1.025327f * V);
58+
// Yuv Display P3 -> Yuv Bt601
59+
// Y' = (1.0 * Y) + ( 0.086028 * U) + ( 0.161445 * V)
60+
// U' = (0.0 * Y) + ( 0.990631 * U) + (-0.091109 * V)
61+
// V' = (0.0 * Y) + (-0.061361 * U) + ( 0.98474 * V)
5562
ALIGNED(16)
56-
const int16_t kYuv601To709_coeffs_neon[8] = {-1936, -3485, 16689, 1878, 1230, 16799, 0, 0};
63+
const int16_t kYuvP3To601_coeffs_neon[8] = {1409, 2645, 16230, -1493, -1005, 16134, 0, 0};
5764

58-
// Yuv Bt601 -> Yuv Bt2100
59-
// Y' = (1.0f * Y) + (-0.128245f * U) + (-0.115879f * V)
60-
// U' = (0.0f * Y) + ( 1.010016f * U) + ( 0.061592f * V)
61-
// V' = (0.0f * Y) + ( 0.086969f * U) + ( 1.029350f * V)
65+
// Yuv Display P3 -> Yuv Bt709
66+
// Y' = (1.0 * Y) + (-0.018002 * U) + (-0.037226 * V)
67+
// U' = (0.0 * Y) + ( 1.002063 * U) + ( 0.020061 * V)
68+
// V' = (0.0 * Y) + ( 0.011431 * U) + ( 1.002843 * V)
6269
ALIGNED(16)
63-
const int16_t kYuv601To2100_coeffs_neon[8] = {-2101, -1899, 16548, 1009, 1425, 16865, 0, 0};
70+
const int16_t kYuvP3To709_coeffs_neon[8] = {-295, -610, 16418, 329, 187, 16431, 0, 0};
6471

65-
// Yuv Bt2100 -> Yuv Bt709
66-
// Y' = (1.0f * Y) + ( 0.018149f * U) + (-0.095132f * V)
67-
// U' = (0.0f * Y) + ( 1.004123f * U) + ( 0.051267f * V)
68-
// V' = (0.0f * Y) + (-0.011524f * U) + ( 0.996782f * V)
72+
// Yuv Display P3 -> Yuv Bt2100
73+
// Y' = (1.0 * Y) + (-0.033905 * U) + ( 0.059019 * V)
74+
// U' = (0.0 * Y) + ( 0.996774 * U) + ( -0.03137 * V)
75+
// V' = (0.0 * Y) + ( 0.022992 * U) + ( 1.005718 * V)
6976
ALIGNED(16)
70-
const int16_t kYuv2100To709_coeffs_neon[8] = {297, -1559, 16452, 840, -189, 16331, 0, 0};
77+
const int16_t kYuvP3To2100_coeffs_neon[8] = {-555, 967, 16331, -514, 377, 16478, 0, 0};
7178

7279
// Yuv Bt2100 -> Yuv Bt601
73-
// Y' = (1.0f * Y) + ( 0.117887f * U) + ( 0.105521f * V)
74-
// U' = (0.0f * Y) + ( 0.995211f * U) + (-0.059549f * V)
75-
// V' = (0.0f * Y) + (-0.084085f * U) + ( 0.976518f * V)
80+
// Y' = (1.0 * Y) + ( 0.117887 * U) + ( 0.105521 * V)
81+
// U' = (0.0 * Y) + ( 0.995211 * U) + (-0.059549 * V)
82+
// V' = (0.0 * Y) + (-0.084085 * U) + ( 0.976518 * V)
7683
ALIGNED(16)
7784
const int16_t kYuv2100To601_coeffs_neon[8] = {1931, 1729, 16306, -976, -1378, 15999, 0, 0};
7885

86+
// Yuv Bt2100 -> Yuv Bt709
87+
// Y' = (1.0 * Y) + ( 0.018149 * U) + (-0.095132 * V)
88+
// U' = (0.0 * Y) + ( 1.004123 * U) + ( 0.051267 * V)
89+
// V' = (0.0 * Y) + (-0.011524 * U) + ( 0.996782 * V)
90+
ALIGNED(16)
91+
const int16_t kYuv2100To709_coeffs_neon[8] = {297, -1559, 16452, 840, -189, 16331, 0, 0};
92+
93+
// Yuv Bt2100 -> Yuv Display P3
94+
// Y' = (1.0 * Y) + ( 0.035343 * U) + ( -0.057581 * V)
95+
// U' = (0.0 * Y) + ( 1.002515 * U) + ( 0.03127 * V)
96+
// V' = (0.0 * Y) + (-0.022919 * U) + ( 0.9936 * V)
97+
ALIGNED(16)
98+
const int16_t kYuv2100ToP3_coeffs_neon[8] = {579, -943, 16425, 512, -376, 16279, 0, 0};
99+
79100
static inline int16x8_t yConversion_neon(uint8x8_t y, int16x8_t u, int16x8_t v, int16x8_t coeffs) {
80101
int32x4_t lo = vmull_lane_s16(vget_low_s16(u), vget_low_s16(coeffs), 0);
81102
int32x4_t hi = vmull_lane_s16(vget_high_s16(u), vget_low_s16(coeffs), 0);
@@ -240,11 +261,14 @@ uhdr_error_info_t convertYuv_neon(uhdr_raw_image_t* image, uhdr_color_gamut_t sr
240261

241262
switch (src_encoding) {
242263
case UHDR_CG_BT_709:
243-
switch (dst_encoding) {
264+
switch ((int)dst_encoding) {
265+
case UHDR_CG_BT_601:
266+
coeffs = kYuv709To601_coeffs_neon;
267+
break;
244268
case UHDR_CG_BT_709:
245269
return status;
246270
case UHDR_CG_DISPLAY_P3:
247-
coeffs = kYuv709To601_coeffs_neon;
271+
coeffs = kYuv709ToP3_coeffs_neon;
248272
break;
249273
case UHDR_CG_BT_2100:
250274
coeffs = kYuv709To2100_coeffs_neon;
@@ -258,14 +282,17 @@ uhdr_error_info_t convertYuv_neon(uhdr_raw_image_t* image, uhdr_color_gamut_t sr
258282
}
259283
break;
260284
case UHDR_CG_DISPLAY_P3:
261-
switch (dst_encoding) {
285+
switch ((int)dst_encoding) {
286+
case UHDR_CG_BT_601:
287+
coeffs = kYuvP3To601_coeffs_neon;
288+
break;
262289
case UHDR_CG_BT_709:
263-
coeffs = kYuv601To709_coeffs_neon;
290+
coeffs = kYuvP3To709_coeffs_neon;
264291
break;
265292
case UHDR_CG_DISPLAY_P3:
266293
return status;
267294
case UHDR_CG_BT_2100:
268-
coeffs = kYuv601To2100_coeffs_neon;
295+
coeffs = kYuvP3To2100_coeffs_neon;
269296
break;
270297
default:
271298
status.error_code = UHDR_CODEC_INVALID_PARAM;
@@ -276,12 +303,15 @@ uhdr_error_info_t convertYuv_neon(uhdr_raw_image_t* image, uhdr_color_gamut_t sr
276303
}
277304
break;
278305
case UHDR_CG_BT_2100:
279-
switch (dst_encoding) {
306+
switch ((int)dst_encoding) {
307+
case UHDR_CG_BT_601:
308+
coeffs = kYuv2100To601_coeffs_neon;
309+
break;
280310
case UHDR_CG_BT_709:
281311
coeffs = kYuv2100To709_coeffs_neon;
282312
break;
283313
case UHDR_CG_DISPLAY_P3:
284-
coeffs = kYuv2100To601_coeffs_neon;
314+
coeffs = kYuv2100ToP3_coeffs_neon;
285315
break;
286316
case UHDR_CG_BT_2100:
287317
return status;
@@ -323,6 +353,13 @@ uhdr_error_info_t convertYuv_neon(uhdr_raw_image_t* image, uhdr_color_gamut_t sr
323353
// In the 3x3 conversion matrix, 0.5 is duplicated. But represented as only one entry in lut leaving
324354
// with an array size of 8 elements.
325355

356+
// RGB Bt601 -> Yuv Bt601
357+
// Y = 0.299 * R + 0.587 * G + 0.114 * B
358+
// U = -0.168735892 * R + -0.331264108 * G + 0.5 * B
359+
// V = 0.5 * R + -0.418687589 * G + -0.081312411 * B
360+
ALIGNED(16)
361+
const uint16_t kRgb601ToYuv_coeffs_neon[8] = {4899, 9617, 1868, 2765, 5427, 8192, 6860, 1332};
362+
326363
// RGB Bt709 -> Yuv Bt709
327364
// Y = 0.212639 * R + 0.715169 * G + 0.072192 * B
328365
// U = -0.114592135 * R + -0.385407865 * G + 0.5 * B
@@ -454,12 +491,15 @@ static void ConvertRgba8888ToYuv444_neon(uhdr_raw_image_t* src, uhdr_raw_image_t
454491
} while (++h < src->h);
455492
}
456493

457-
std::unique_ptr<uhdr_raw_image_ext_t> convert_raw_input_to_ycbcr_neon(uhdr_raw_image_t* src) {
494+
std::unique_ptr<uhdr_raw_image_ext_t> convert_raw_input_to_ycbcr_neon(uhdr_raw_image_t* src,
495+
bool use_bt601) {
458496
if (src->fmt == UHDR_IMG_FMT_32bppRGBA8888) {
459497
std::unique_ptr<uhdr_raw_image_ext_t> dst = nullptr;
460498
const uint16_t* coeffs_ptr = nullptr;
461499

462-
if (src->cg == UHDR_CG_BT_709) {
500+
if (use_bt601) {
501+
coeffs_ptr = kRgb601ToYuv_coeffs_neon;
502+
} else if (src->cg == UHDR_CG_BT_709) {
463503
coeffs_ptr = kRgb709ToYuv_coeffs_neon;
464504
} else if (src->cg == UHDR_CG_BT_2100) {
465505
coeffs_ptr = kRgbDispP3ToYuv_coeffs_neon;

0 commit comments

Comments
 (0)