Skip to content

Commit b36121d

Browse files
committed
Yuv to RGB Rec.709 and Rec.2020
1 parent a836494 commit b36121d

File tree

7 files changed

+184
-57
lines changed

7 files changed

+184
-57
lines changed

HDR10Capture2019/MLColorConvShaderConstants.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ struct MLColorConvShaderConstants {
1010
int imgGammaType; //< MLImage::GammaType
1111
int flags; //< FlagsType
1212
float outOfRangeNits;
13+
float scale;
1314

1415
enum FlagsType {
1516
FLAG_OutOfRangeColor = 1,

HDR10Capture2019/MLConverter.cpp

Lines changed: 140 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -41,74 +41,162 @@ Saturate01(const float v)
4141
}
4242
}
4343

44+
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
45+
// YUV ⇔ RGB
46+
// https://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.2020_conversion
47+
48+
#define Yuv_i8_to_f32 \
49+
const float y = y8 / 255.0f; \
50+
const float u = u8 / 255.0f; \
51+
const float v = v8 / 255.0f; \
52+
53+
#define Yuv_i10_to_f32 \
54+
const float y = y10 / 1023.0f; \
55+
const float u = u10 / 1023.0f; \
56+
const float v = v10 / 1023.0f;
57+
58+
#define Rgb_f32_to_i8_return \
59+
*r8_return = (uint8_t)(r * 255.0f); \
60+
*g8_return = (uint8_t)(g * 255.0f); \
61+
*b8_return = (uint8_t)(b * 255.0f);
62+
63+
#define Rgb_f32_to_i10_return \
64+
*r10_return = (uint16_t)(r * 1023.0f); \
65+
*g10_return = (uint16_t)(g * 1023.0f); \
66+
*b10_return = (uint16_t)(b * 1023.0f);
67+
68+
#define BT601_YuvRgb \
69+
const float r = Saturate01(1.1644f * y + 0.0000f * u + 1.5960f * v - 0.8742f); \
70+
const float g = Saturate01(1.1644f * y - 0.3918f * u - 0.8130f * v + 0.5317f); \
71+
const float b = Saturate01(1.1644f * y + 2.0172f * u + 0.0000f * v - 1.0856f);
72+
73+
#define Rec709_YuvRgb \
74+
const float r = Saturate01(1.1646f * y + 0.0005f * u + 1.7928f * v - 0.9733f); \
75+
const float g = Saturate01(1.1646f * y - 0.2134f * u - 0.5331f * v + 0.3017f); \
76+
const float b = Saturate01(1.1646f * y + 2.1125f * u + 0.0002f * v - 1.1336f);
77+
78+
#define Rec2020_YuvRgb \
79+
const float r = Saturate01(1.1644f * y + 0.0002f * u + 1.6786f * v - 0.9156f); \
80+
const float g = Saturate01(1.1644f * y - 0.1873f * u - 0.6504f * v + 0.3474f); \
81+
const float b = Saturate01(1.1644f * y + 2.1417f * u + 0.0001f * v - 1.1481f);
82+
4483
/// <summary>
45-
/// 0~1の範囲のfloat値のYUVを0~1の範囲のfloat値のRGBにする。
84+
/// BT.601 8bit YUV → 8bit RGB
4685
/// </summary>
4786
static void
48-
YuvToRgb(
49-
const float y, const float u, const float v,
50-
float* r_return, float *g_return, float *b_return)
87+
Bt601_Yuv8ToRgb8(
88+
const uint8_t y8, const uint8_t u8, const uint8_t v8,
89+
uint8_t* r8_return, uint8_t* g8_return, uint8_t* b8_return)
5190
{
52-
*r_return = Saturate01(1.1644f * y + 0.0000f * u + 1.5960f * v - 0.8742f);
53-
*g_return = Saturate01(1.1644f * y - 0.3918f * u - 0.8130f * v + 0.5317f);
54-
*b_return = Saturate01(1.1644f * y + 2.0172f * u + 0.0000f * v - 1.0856f);
91+
Yuv_i8_to_f32;
92+
BT601_YuvRgb;
93+
Rgb_f32_to_i8_return;
5594
}
5695

5796
/// <summary>
58-
/// 8bit YUV → 8bit RGB
97+
/// BT.601 10bit YUV → 10bit RGB
5998
/// </summary>
6099
static void
61-
Yuv8ToRgb8(
62-
const uint8_t y8, const uint8_t u8, const uint8_t v8,
63-
uint8_t* r8_return, uint8_t* g8_return, uint8_t* b8_return)
100+
Bt601_Yuv10ToRgb10(
101+
const uint16_t y10, const uint16_t u10, const uint16_t v10,
102+
uint16_t* r10_return, uint16_t* g10_return, uint16_t* b10_return)
64103
{
65-
const float y = y8 / 255.0f;
66-
const float u = u8 / 255.0f;
67-
const float v = v8 / 255.0f;
68-
69-
const float r = Saturate01(1.1644f * y + 0.0000f * u + 1.5960f * v - 0.8742f);
70-
const float g = Saturate01(1.1644f * y - 0.3918f * u - 0.8130f * v + 0.5317f);
71-
const float b = Saturate01(1.1644f * y + 2.0172f * u + 0.0000f * v - 1.0856f);
72-
73-
*r8_return = (uint8_t)(r * 255.0f);
74-
*g8_return = (uint8_t)(g * 255.0f);
75-
*b8_return = (uint8_t)(b * 255.0f);
104+
Yuv_i10_to_f32;
105+
BT601_YuvRgb;
106+
Rgb_f32_to_i10_return;
76107
}
77108

78109
/// <summary>
79-
/// 10bit YUV → 10bit RGB
110+
/// Rec.709 8bit YUV → 8bit RGB
80111
/// </summary>
81112
static void
82-
Yuv10ToRgb10(
83-
const uint16_t y10, const uint16_t u10, const uint16_t v10,
84-
uint16_t* r10_return, uint16_t* g10_return, uint16_t* b10_return)
113+
Rec709_Yuv8ToRgb8(
114+
const uint8_t y8, const uint8_t u8, const uint8_t v8,
115+
uint8_t* r8_return, uint8_t* g8_return, uint8_t* b8_return)
85116
{
86-
const float y = y10 / 1023.0f;
87-
const float u = u10 / 1023.0f;
88-
const float v = v10 / 1023.0f;
117+
Yuv_i8_to_f32;
118+
Rec709_YuvRgb;
119+
Rgb_f32_to_i8_return;
120+
}
89121

90-
const float r = Saturate01(1.1644f * y + 0.0000f * u + 1.5960f * v - 0.8742f);
91-
const float g = Saturate01(1.1644f * y - 0.3918f * u - 0.8130f * v + 0.5317f);
92-
const float b = Saturate01(1.1644f * y + 2.0172f * u + 0.0000f * v - 1.0856f);
122+
/// <summary>
123+
/// Rec.709 10bit YUV → 10bit RGB
124+
/// </summary>
125+
static void
126+
Rec709_Yuv10ToRgb10(
127+
const uint16_t y10, const uint16_t u10, const uint16_t v10,
128+
uint16_t* r10_return, uint16_t* g10_return, uint16_t* b10_return)
129+
{
130+
Yuv_i10_to_f32;
131+
Rec709_YuvRgb;
132+
Rgb_f32_to_i10_return;
133+
}
93134

94-
*r10_return = (uint16_t)(r * 1023.0f);
95-
*g10_return = (uint16_t)(g * 1023.0f);
96-
*b10_return = (uint16_t)(b * 1023.0f);
135+
/// <summary>
136+
/// Rec.2020 8bit YUV → 8bit RGB
137+
/// </summary>
138+
static void
139+
Rec2020_Yuv8ToRgb8(
140+
const uint8_t y8, const uint8_t u8, const uint8_t v8,
141+
uint8_t* r8_return, uint8_t* g8_return, uint8_t* b8_return) {
142+
Yuv_i8_to_f32;
143+
Rec2020_YuvRgb;
144+
Rgb_f32_to_i8_return;
97145
}
98146

99147
/// <summary>
100-
/// 0~1の範囲のfloat値のRGBを0~1の範囲のfloat値のYUVにする。
148+
/// Rec.2020 10bit YUV → 10bit RGB
101149
/// </summary>
102150
static void
103-
RgbToYuv(
104-
const float r, const float g, const float b,
105-
float* y_return, float* u_return, float* v_return)
151+
Rec2020_Yuv10ToRgb10(
152+
const uint16_t y10, const uint16_t u10, const uint16_t v10,
153+
uint16_t* r10_return, uint16_t* g10_return, uint16_t* b10_return) {
154+
Yuv_i10_to_f32;
155+
Rec2020_YuvRgb;
156+
Rgb_f32_to_i10_return;
157+
}
158+
159+
static void
160+
Yuv8ToRgb8(
161+
MLConverter::ColorSpace colorSpace,
162+
const uint8_t y8, const uint8_t u8, const uint8_t v8,
163+
uint8_t* r8_return, uint8_t* g8_return, uint8_t* b8_return)
106164
{
107-
*y_return = Saturate01( 0.2568f * r + 0.5041f * g + 0.0979f * b + 0.0627f);
108-
*u_return = Saturate01(-0.1482f * r - 0.2910f * g + 0.4392f * b + 0.5020f);
109-
*v_return = Saturate01( 0.4392f * r - 0.3678f * g - 0.0714f * b + 0.5020f);
165+
switch (colorSpace) {
166+
case MLConverter::CS_Rec601:
167+
Bt601_Yuv8ToRgb8(y8, u8, v8, r8_return, g8_return, b8_return);
168+
break;
169+
case MLConverter::CS_Rec709:
170+
Rec709_Yuv8ToRgb8(y8, u8, v8, r8_return, g8_return, b8_return);
171+
break;
172+
case MLConverter::CS_Rec2020:
173+
Rec2020_Yuv8ToRgb8(y8, u8, v8, r8_return, g8_return, b8_return);
174+
break;
175+
}
110176
}
111177

178+
static void
179+
Yuv10ToRgb10(
180+
MLConverter::ColorSpace colorSpace,
181+
const uint16_t y10, const uint16_t u10, const uint16_t v10,
182+
uint16_t* r10_return, uint16_t* g10_return, uint16_t* b10_return)
183+
{
184+
switch (colorSpace) {
185+
case MLConverter::CS_Rec601:
186+
Bt601_Yuv10ToRgb10(y10, u10, v10, r10_return, g10_return, b10_return);
187+
break;
188+
case MLConverter::CS_Rec709:
189+
Rec709_Yuv10ToRgb10(y10, u10, v10, r10_return, g10_return, b10_return);
190+
break;
191+
case MLConverter::CS_Rec2020:
192+
Rec2020_Yuv10ToRgb10(y10, u10, v10, r10_return, g10_return, b10_return);
193+
break;
194+
}
195+
}
196+
197+
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
198+
// ガンマカーブ
199+
112200
static const float pq_m1 = 0.1593017578125f; // ( 2610.0 / 4096.0 ) / 4.0;
113201
static const float pq_m2 = 78.84375f; // ( 2523.0 / 4096.0 ) * 128.0;
114202
static const float pq_c1 = 0.8359375f; // 3424.0 / 4096.0 or pq_c3 - pq_c2 + 1.0;
@@ -149,7 +237,7 @@ MLConverter::MLConverter(void)
149237
}
150238

151239
void
152-
MLConverter::Uyvy8bitToR8G8B8A8(const uint32_t* pFrom, uint32_t* pTo, const int width, const int height)
240+
MLConverter::Uyvy8bitToR8G8B8A8(ColorSpace colorSpace, const uint32_t* pFrom, uint32_t* pTo, const int width, const int height)
153241
{
154242
// widthは2で割り切れる。
155243
assert((width & 1) == 0);
@@ -182,10 +270,10 @@ MLConverter::Uyvy8bitToR8G8B8A8(const uint32_t* pFrom, uint32_t* pTo, const int
182270
// yuv → RGB
183271
uint8_t r, g, b;
184272

185-
Yuv8ToRgb8(y0, u, v, &r, &g, &b);
273+
Yuv8ToRgb8(colorSpace, y0, u, v, &r, &g, &b);
186274
pTo[writeP + 0] = (a << 24) + (b << 16) + (g << 8) + r;
187275

188-
Yuv8ToRgb8(y1, u, v, &r, &g, &b);
276+
Yuv8ToRgb8(colorSpace, y1, u, v, &r, &g, &b);
189277
pTo[writeP + 1] = (a << 24) + (b << 16) + (g << 8) + r;
190278
}
191279
}
@@ -194,7 +282,7 @@ MLConverter::Uyvy8bitToR8G8B8A8(const uint32_t* pFrom, uint32_t* pTo, const int
194282
/// bmdFormat10BitYUV v210 → DXGI_FORMAT_R10G10B10A2_UNORM
195283
/// </summary>
196284
void
197-
MLConverter::Yuv422_10bitToR10G10B10A2(const uint32_t* pFrom, uint32_t* pTo, const int width, const int height, const uint8_t alpha)
285+
MLConverter::Yuv422_10bitToR10G10B10A2(ColorSpace colorSpace, const uint32_t* pFrom, uint32_t* pTo, const int width, const int height, const uint8_t alpha)
198286
{
199287
const uint32_t a = (alpha >> 6) & 0x3;
200288

@@ -255,22 +343,22 @@ MLConverter::Yuv422_10bitToR10G10B10A2(const uint32_t* pFrom, uint32_t* pTo, con
255343
// yuv → RGB
256344
uint16_t r, g, b;
257345

258-
Yuv10ToRgb10(y0, u0, v0, &r, &g, &b);
346+
Yuv10ToRgb10(colorSpace, y0, u0, v0, &r, &g, &b);
259347
pTo[writeP + 0] = (a << 30) + (b << 20) + (g << 10) + r;
260348

261-
Yuv10ToRgb10(y1, u0, v0, &r, &g, &b);
349+
Yuv10ToRgb10(colorSpace, y1, u0, v0, &r, &g, &b);
262350
pTo[writeP + 1] = (a << 30) + (b << 20) + (g << 10) + r;
263351

264-
Yuv10ToRgb10(y2, u2, v2, &r, &g, &b);
352+
Yuv10ToRgb10(colorSpace, y2, u2, v2, &r, &g, &b);
265353
pTo[writeP + 2] = (a << 30) + (b << 20) + (g << 10) + r;
266354

267-
Yuv10ToRgb10(y3, u2, v2, &r, &g, &b);
355+
Yuv10ToRgb10(colorSpace, y3, u2, v2, &r, &g, &b);
268356
pTo[writeP + 3] = (a << 30) + (b << 20) + (g << 10) + r;
269357

270-
Yuv10ToRgb10(y4, u4, v4, &r, &g, &b);
358+
Yuv10ToRgb10(colorSpace, y4, u4, v4, &r, &g, &b);
271359
pTo[writeP + 4] = (a << 30) + (b << 20) + (g << 10) + r;
272360

273-
Yuv10ToRgb10(y5, u4, v4, &r, &g, &b);
361+
Yuv10ToRgb10(colorSpace, y5, u4, v4, &r, &g, &b);
274362
pTo[writeP + 5] = (a << 30) + (b << 20) + (g << 10) + r;
275363
}
276364
}

HDR10Capture2019/MLConverter.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,21 @@ class MLConverter {
88
MLConverter(void);
99
~MLConverter(void) { }
1010

11+
enum ColorSpace {
12+
CS_Rec601,
13+
CS_Rec709,
14+
CS_Rec2020,
15+
};
16+
1117
/// <summary>
1218
/// bmdFormat8BitYUV UYVY ¨ DXGI_FORMAT_R8G8B8A8_UNORM
1319
/// </summary>
14-
static void Uyvy8bitToR8G8B8A8(const uint32_t* pFrom, uint32_t* pTo, const int width, const int height);
20+
static void Uyvy8bitToR8G8B8A8(ColorSpace colorSpace, const uint32_t* pFrom, uint32_t* pTo, const int width, const int height);
1521

1622
/// <summary>
1723
/// bmdFormat10BitYUV v210 ¨ DXGI_FORMAT_R10G10B10A2_UNORM
1824
/// </summary>
19-
static void Yuv422_10bitToR10G10B10A2(const uint32_t* pFrom, uint32_t* pTo, const int width, const int height, const uint8_t alpha = 0xff);
25+
static void Yuv422_10bitToR10G10B10A2(ColorSpace colorSpace, const uint32_t* pFrom, uint32_t* pTo, const int width, const int height, const uint8_t alpha = 0xff);
2026

2127
/// <summary>
2228
/// bmdFormat8BitARGB ¨ DXGI_FORMAT_R8G8B8A8_UNORM
@@ -34,6 +40,7 @@ class MLConverter {
3440
static void Rgb10bitToR10G10B10A2(const uint32_t* pFrom, uint32_t* pTo, const int width, const int height, const uint8_t alpha);
3541

3642
enum GammaType {
43+
GT_Linear = -1,
3744
GT_SDR_22,
3845
GT_HDR_PQ,
3946
};

HDR10Capture2019/MLDX12App.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ MLDX12App::MLDX12App(UINT width, UINT height, UINT options) :
6565
mShaderConsts.imgGammaType = MLImage::MLG_Linear;
6666
mShaderConsts.flags = 0;
6767
mShaderConsts.outOfRangeNits = (float)outOfRangeNits;
68+
mShaderConsts.scale = 1.0f;
6869
}
6970

7071
MLDX12App::~MLDX12App(void) {
@@ -1293,6 +1294,9 @@ MLDX12App::ShowSettingsWindow(void) {
12931294
MLImage::MLImageFileFormatTypeToStr(img.imgFileFormat));
12941295
ImGui::Text("%d x %d, %s",
12951296
img.width, img.height, MLImage::MLImageBitFormatToStr(img.bitFormat));
1297+
1298+
ImGui::SliderFloat("Image Brightness Scaling", &mShaderConsts.scale, 1.0f, 100.0f);
1299+
12961300
{
12971301
bool b = 0 != (mShaderConsts.flags & MLColorConvShaderConstants::FLAG_SwapRedBlue);
12981302
ImGui::Checkbox("Swap Red and Blue", &b);
@@ -1302,8 +1306,8 @@ MLDX12App::ShowSettingsWindow(void) {
13021306
mShaderConsts.flags = mShaderConsts.flags & (~MLColorConvShaderConstants::FLAG_SwapRedBlue);
13031307
}
13041308
}
1305-
13061309
}
1310+
13071311
if (ImGui::TreeNodeEx("Image Color Gamut", ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_CollapsingHeader)) {
13081312
//ImGui::Text("Color Gamut is %s", MLColorGamutToStr(img.colorGamut));
13091313

HDR10Capture2019/MLExrWriter.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ MLExrWriter::Write(const char* exrFilePath, const MLImage& img)
2323
case MLImage::MLG_ST2084:
2424
gamma = MLConverter::GT_HDR_PQ;
2525
break;
26+
case MLImage::MLG_Linear:
27+
gamma = MLConverter::GT_Linear;
28+
break;
2629
default:
2730
// 作ってない。
2831
return E_NOTIMPL;
@@ -37,6 +40,10 @@ MLExrWriter::Write(const char* exrFilePath, const MLImage& img)
3740
buff = new uint8_t[buffBytes];
3841
mConv.R10G10B10A2ToExrHalfFloat((uint32_t*)img.data, (uint16_t*)buff, img.width, img.height, 0xff, gamma);
3942
break;
43+
case MLImage::BFT_HalfFloatR16G16B16A16:
44+
buff = new uint8_t[buffBytes];
45+
memcpy(buff, img.data, buffBytes);
46+
break;
4047
default:
4148
// 作ってない。
4249
return E_NOTIMPL;

0 commit comments

Comments
 (0)