モノトーンの伝説日記

Apex Legends, Splatoon, Programming, and so on...

量子化,YUV⇔RGB変換式,色空間,ガンマ,光源変換について

 眠いなぁ,寝よう… って思いながら,頭の中で思考してたら,とうとう気になりだしてスマフォ片手に調べだして,挙句の果てに眠れなくなってブログを書き始めた人の文章です。

 Twitter でほざいてたのをきれいにまとめておこうかと。間違ってたら指摘してください。色空間に関してはまだまだ知識不足ですね。片手間で書ける記事ではないので,順次追記,公開予定。これがすべての色空間実装への土台になると思うし,集大成だと思っているのでよろしくお願いします。

この資料は適切に加筆修正されると思います。というか,していきます。変換式とかも徐々に増やしていく予定なのでよろしくお願いします。

概要

  1. 量子化
  2. YUV⇔RGB変換式
  3. 色空間
  4. ガンマ
  5. 光源変換
  6. 色空間変換(概論)

1. 量子化 (Quantization levels)

 BT.709,BT.2020 ともにほぼ同じ。ただし,BT.709 は 8-bit または 10-bit,BT.2020 は 10-bit または 12-bit のみ定義されている。BT.601 は定義が見つからなかったがあるのだろうか…?

値\ビット数 8-bit 10-bit 12-bit
黒レベル (Black level) 16 64 256
Achromatic 128 512 2048
名目ピーク
(Nominal Peak)
R, G, B, Y 235 940 3760
CB, CR min 16 64 256
max 240 960 3840
データ*1
(Video Data)
min 1 4 16
max 254 1019 4079
時間参照
(Timing reference)
0
255
0-3
1020-1023
0-15
4080-4095

※ Achromatic は順応輝度(背景輝度)とか訳す? わかんない

2. YUV⇔RGB変換式 (Color Matrix)

 ここが一番目立つところですね。RGB と YUV を変換するために使う式です。言ってただ変換するだけで,色空間やガンマまで変換して sRGB にする,といった魔法は起きません。まずは有名な YCbCr 系について書きます。

2.1 YCbCr

色空間\係数 Kr Kg Kb
BT.601 0.299 0.587 0.114
BT.709 0.2126 0.7152 0.0722
BT.2020 0.2627 0.6780 0.0593
FCC 0.30 0.59 0.11

KrKb を使うのが一般的…? [要出展]

 変換行列を提示します。

{
\displaystyle

\begin{equation}
\begin{bmatrix}
R \\
G \\
B \\
1
\end{bmatrix}
=
\begin{bmatrix}
1 && 0 && 1-K_r && 0 \\
1 && -\dfrac{K_b(1-K_b)}{K_g} && -\dfrac{K_r(1-K_r)}{K_g} && 0 \\
1 && 1-K_b && 0 && 0 \\
0 && 0 && 0 && 1
\end{bmatrix}
\begin{bmatrix}
\dfrac{A_M}{Y_D} && \dfrac{A_M}{U_D} && \dfrac{A_M}{V_D} &&  1
\end{bmatrix}
\left(
\begin{bmatrix}
Y \\ U \\ V \\ 1
\end{bmatrix}
-
\begin{bmatrix}
Y_B \\ U_B \\ V_B \\ 1
\end{bmatrix}
\right)
\end{equation}

}……(2.1a)

for:
{
\displaystyle

8-bit: 
\begin{eqnarray}
(Y_D, U_D, V_D) &=& \left(235-16, \dfrac{240-16}{2}, \dfrac{240-16}{2}\right)\\
(Y_B, U_B, V_B) &=& (16, 128, 128) \\
            A_M &=& 255
\end{eqnarray}
}

 これは,まず YUV の 16-235, 16-240, 16-240 を 0~219, -112~112, -112~112 にします。そして次にフルレンジに戻します。0~255, -128~127,-128~127 にです。そして Yfull,Ufull,Vfull を RGB に変換する行列をかけてやります。

 整理した式は割愛します。こっちの式のほうがより理解が深まるでしょう。

 逆変換。

{
\displaystyle

\begin{equation}
\begin{bmatrix}
Y \\
U \\
V \\
1
\end{bmatrix}
=
\begin{bmatrix}
\dfrac{Y_D}{A_M} && \dfrac{U_D}{A_M} && \dfrac{V_D}{A_M} &&  1
\end{bmatrix}
\begin{bmatrix}
K_r                 && K_g                 && K_b                 && 0 \\
1                   && -\dfrac{K_g}{1-K_r} && -\dfrac{K_b}{1-K_r} && 0 \\
-\dfrac{K_r}{1-K_b} && -\dfrac{K_g}{1-K_b} && 1                   && 0 \\
0                   && 0                   && 0                   && 1
\end{bmatrix}
\begin{bmatrix}
R \\ G \\ B \\ 1
\end{bmatrix}
+
\begin{bmatrix}
Y_B \\ U_B \\ V_B \\ 1
\end{bmatrix}
\end{equation}

}……(2.1b)

 また,AviSynth や YCbCr の wiki も参照するとよいです。

http://avisynth.nl/index.php/Color_conversions

https://en.wikipedia.org/wiki/YCbCr

 これのアナログバージョンを YPbPr って言いますが,まあコンポーネント端子に Y, Pb/Cb, Pr/Cr って書いてあったぐらいですし,実利用ではあんまり言い訳してないですね。

 次は YCbCr (YUV) でない色について書きます。

2.2 YCgCo

 緑の色差 CG橙の色差 CO です。

{
\displaystyle

\begin{equation}
\begin{bmatrix}
Y \\ C_G \\ C_O
\end{bmatrix}
=
\begin{bmatrix}
 \dfrac{1}{4} && \dfrac{1}{2} &&  \dfrac{1}{4} \\
-\dfrac{1}{4} && \dfrac{1}{2} && -\dfrac{1}{4} \\
 \dfrac{1}{2} &&            0 && -\dfrac{1}{2}
\end{bmatrix}
\begin{bmatrix}
R \\ G \\ B
\end{bmatrix}
\end{equation}
}……(2.2a)
{
\displaystyle

\begin{equation}
\begin{bmatrix}
R \\ G \\ B
\end{bmatrix}
=
\begin{bmatrix}
 1 && -1 &&  1 \\
 1 &&  1 &&  0 \\
 1 && -1 && -1
\end{bmatrix}
\begin{bmatrix}
Y \\ C_G \\ C_O
\end{bmatrix}
\end{equation}
}……(2.2b)

https://en.wikipedia.org/wiki/YCgCo

2.3 YDbDr

 青の色差 DB赤の色差 DR です。SECAM の規格です。

{
\displaystyle

\begin{equation}
\begin{bmatrix}
Y \\ D_B \\ D_R
\end{bmatrix}
=
\begin{bmatrix}
 0.299 &&  0.587 && 0.114 \\
-0.450 && -0.883 && 1.333 \\
-1.333 &&  1.116 && 0.217
\end{bmatrix}
\begin{bmatrix}
R \\ G \\ B
\end{bmatrix}
\end{equation}
}……(2.3a)
{
\displaystyle

\begin{equation}
\begin{bmatrix}
R \\ G \\ B
\end{bmatrix}
=
\begin{bmatrix}
1 &&  0.000092303716148 && -0.525912630661865 \\
1 && -0.129132898890509 &&  0.267899328207599 \\
1 &&  0.664679059978955 && -0.000079202543533
\end{bmatrix}
\begin{bmatrix}
Y \\ D_B \\ D_R
\end{bmatrix}
\end{equation}
}……(2.3b)

where:
R, G, B, Y ∈ [0, 1]
Db, Dr ∈ [-1.333, 1.333]

 正直なところ,これはアナログフォーマットだし載せる必要ないのですが,x264 で使えるっぽいので,参考までに。

https://en.wikipedia.org/wiki/YDbDr

2.4 YIQ

 - I と - Q です。NTSC の規格です。

{
\displaystyle

\begin{equation}
\begin{bmatrix}
Y \\ I \\ Q
\end{bmatrix}
=
\begin{bmatrix}
0.299 &&  0.587 &&  0.114 \\
0.596 && -0.274 && -0.322 \\
0.211 && -0.523 &&  0.312
\end{bmatrix}
\begin{bmatrix}
R \\ G \\ B
\end{bmatrix}
\end{equation}
}……(2.4a)
{
\displaystyle

\begin{equation}
\begin{bmatrix}
R \\ G \\ B
\end{bmatrix}
=
\begin{bmatrix}
1 &&  0.956 &&  0.621 \\
1 && -0.272 && -0.647 \\
1 && -1.106 &&  1.703
\end{bmatrix}
\begin{bmatrix}
Y \\ I \\ Q
\end{bmatrix}
\end{equation}
}……(2.4b)

where:
R, G, B, Y ∈ [0, 1]
I ∈ [-0.5957, 0.5957]
Q ∈ [-0.5226, 0.5226]

https://en.wikipedia.org/wiki/YIQ

3. 色空間 (Color Primaries)

 PC 使ってる人ならよく知っている sRGB,AdobeRGB,DCI-P3 など… 色をきちんと扱う人ならキャリブレーションとかしたことあるのではないのでしょうか?

 CIE XYZ 色空間を使って表すので,XYZ 色空間について知らない人は次を一読するとよい。

http://w3.kcua.ac.jp/~fujiwara/infosci/colorspace/colorspace1.html

色空間 R G B W
x y x y x y
4 NTSC (1953)
[BT.470-6 System M]
0.670.33 0.210.71 0.140.08 C
6 NTSC (1987) [SMPTE 170M] 0.630.34 0.310.595 0.1550.07 D65
- NTSC-J (1987) D93
5 PAL [BT.470-6 System B, G] 0.640.33 0.290.60 0.150.06 D65
1 sRGB [IEC 61966-2-1]
BT.709-5
0.6400.330 0.3000.600 0.1500.060 D65
- Adobe RGB 0.64000.3300 0.21000.7100 0.15000.0600 D65
- DCI-P3 D65
[SMPTE EG 432-1, RP 431-2]
0.6800.320 0.2650.690 0.1500.060 D65
- DCI-P3 Theater 0.314, 0.351
9 BT.2020-2 0.7080.292 0.1700.797 0.1310.046 D65
- Wide-gamut RGB 0.73470.2653 0.11520.8264 0.15660.0177 0.3457, 0.3585
光源名 色温度 x y
A 0.4476 0.4075
B 0.3486 0.3516
C 6774K 0.31006 0.31616
D50 0.3457 0.3586
D55 0.3325 0.3475
D60 0.3217 0.3377
D65 6504K 0.3127 0.3290
D75 0.2991 0.3149
D93 9305K 0.2831 0.2970

 RGB 色空間は英語版 wikipedia を参照するといいかも。

https://en.wikipedia.org/wiki/RGB_color_space

 RGB⇔XYZ 変換式について記述する。

{
\displaystyle

\begin{eqnarray}
\bf{V_x} &=& (r_x, g_x, b_x, w_x) \\
\bf{V_y} &=& (r_y, g_y, b_y, w_y) \\
\bf{V_z} &=& (r_z, g_z, b_z, w_z) = 1 - \bf{V_x} - \bf{V_y}
\end{eqnarray}
}

{
\displaystyle

\begin{equation}
S=
\begin{bmatrix}
s_a \\ s_b \\ s_c
\end{bmatrix}
=
\begin{bmatrix}
r_x && g_x && b_x \\
r_y && g_y && b_y \\
r_z && g_z && b_z
\end{bmatrix}
^{-1}
\begin{bmatrix}
w_x/w_y \\ 1 \\ w_z/w_y
\end{bmatrix}
\end{equation}
}……(3.1)

{
\displaystyle

\begin{equation}
\begin{bmatrix}
X \\ Y \\ Z
\end{bmatrix}
=
\begin{bmatrix}
r_x && g_x && b_x \\
r_y && g_y && b_y \\
r_z && g_z && b_z
\end{bmatrix}
\begin{bmatrix}
s_a &&   0 &&   0 \\
  0 && s_b &&   0 \\
  0 &&   0 && s_c
\end{bmatrix}
\begin{bmatrix}
R \\ G \\ B
\end{bmatrix}
\end{equation}
}……(3.2)

 算出については次を見るとよい。

http://vig.is.env.kitakyu-u.ac.jp/Japanese/tutorial/rgb2xyz.html

http://www.ite.or.jp/study/musen/tips/tip07.html

4. ガンマ (Transfer characteristics)

 ガンマとはごく一般的に次のような定義です。

{
\displaystyle V_{output}=V_{input}^γ
}……(4.0)

右上の指数の部分だけを取り出して表現されるのが γ です。

γ = 0.45 γ = 1.0 γ = 2.2
f:id:mntone:20170401105825p:plain f:id:mntone:20170401105559p:plain f:id:mntone:20170401105620p:plain

※ グラフはここで作成 http://keisan.casio.jp/exec/system/1180917567

 一般にカメラのガンマ補正が 0.45,ディスプレイガンマが 2.2*2が使われる,が…

 ガンマについてもっと詳しいことを知りたいなら,次のサイトを見るとよい。

http://compojigoku.blog.fc2.com/blog-entry-23.html

 大雑把な理解はこれでいいんだけど細かな数式が規格によって違うんだよ! って話。

4.1 BT.601 / BT.709 / SMTPE 170M

 筆者注釈としては L は Linear な信号 Vlinear。E は γ: BT.709 な信号 Vγ: BT.709

{
\displaystyle

\begin{equation}
E=
\begin{cases}
1.099L^{0.45}-0.099 && (0.018 &\leq L \leq& 1) \\
4.500L              && (0 &\leq L <& 0.018)
\end{cases}
\end{equation}

}……(4.1a)

where:
L: 元の明るさ L (0 ≤ L ≤ 1) (luminance of the image 0 ≤ L ≤ 1)
E: 電気信号 (corresponding electrical signal)

 逆変換

{
\displaystyle

\begin{equation}
L=
\begin{cases}
\left(\dfrac{V+0.099}{1.099}\right)^\dfrac{1}{0.45} && (0.0812 &\leq V \leq& 1) \\
\dfrac{V}{4.5}                                      && (0 &\leq V <& 0.0812)
\end{cases}
\end{equation}

}……(4.1b)

https://en.wikipedia.org/wiki/Rec._709

4.1.1 xvYCC (IEC61966-2-4)

 BT.709 でガンマカーブのみ定義を拡張します。リニアなパラメーターが 0~1 に制約されているのを負値を受け入れるようにし,最終的に YUV 1-254 を最大限まで使うようにします。

{
\displaystyle

\begin{equation}
L'=
\begin{cases}
1.099L^{0.45}-0.099  && (0.018 \leq &L&) \\
4.500L               && (-0.018 < &L& < 0.018) \\
-1.099L^{0.45}-0.099 && (&L& \leq -0.018)
\end{cases}
\end{equation}

}……(4.1.1a)
{
\displaystyle

\begin{equation}
L=
\begin{cases}
\left(\dfrac{L'+0.099}{1.099}\right)^\dfrac{1}{0.45} && (0.0812 \leq &L'& \leq 1) \\
\dfrac{L'}{4.5}                                      && (-0.0812 < &L'& < 0.0812) \\
\left(-\dfrac{L'+0.099}{1.099}\right)^\dfrac{1}{0.45} && (&L'& \leq -0.0812) \\
\end{cases}
\end{equation}

}……(4.1.1b)

where:
L: linear
L': non-linear

4.2 BT.2020

 10-bit と 12-bit で値が違います。12-bit では精度のためか桁が増えています。

{
\displaystyle

\begin{equation}
E'=
\begin{cases}
4.5E && (0 &\leq E <& \beta) \\
\alpha E^{0.45}-(\alpha -1) && (\beta &\leq E \leq& 1)
\end{cases}
\end{equation}

}……(4.2)

where:
E: linear (規格はもっとうだうだ書いてます)
E´: non-linear signal

For practical purpose, the following values can be used:
10-bit: α = 1.099, β = 0.018
12-bit: α = 1.0993, β = 0.0181

4.3 sRGB (IEC 61966-2-1)

{
\displaystyle

\begin{equation}
C_{srgb}=
\begin{cases}
12.92 C_{linear}               && (0 &\leq C_{linear} <& 0.0031308) \\
1.055 C_{linear}^{1/2.4}-0.055 && (0.0031308 &\leq C_{linear} \leq& 1)
\end{cases}
\end{equation}

}……(4.3a)

{
\displaystyle

\begin{equation}
C_{linear}=
\begin{cases}
\dfrac{C_{srgb}}{12.92}                          && (0 &\leq C_{srgb} <& 0.04045) \\
\left(\dfrac{C_{srgb}+0.055}{1.055}\right)^{2.4} && (0.04045 &\leq C_{srgb} \leq& 1)
\end{cases}
\end{equation}

}……(4.3b)

https://en.wikipedia.org/wiki/SRGB

4.4 Adobe RGB (1998)

{
\displaystyle

E'=E^{\frac{1}{563/256}}

}……(4.4a)

{
\displaystyle

E=E'^{\frac{563}{256}}

}……(4.4b)

https://en.wikipedia.org/wiki/Adobe_RGB_color_space

4.5 DCI-P3

 sRGB と同じらしい。D65 規格においては? 詳しいことはわからないです。

4.6 SMTPE 240M

{
\displaystyle

\begin{equation}
V_c=
\begin{cases}
4L_c                    && (0 &\leq L_c <& 0.0228) \\
1.1115L_c^{0.45}-0.1115 && (0.0228 &\leq L_c \leq& 1)
\end{cases}
\end{equation}

}……(4.6a)
{
\displaystyle

\begin{equation}
L_r=
\begin{cases}
\dfrac{V_r}{4}                                          && (0 &\leq V_r <& 0.0913) \\
\left(\dfrac{V_r+0.1115}{1.1115}\right)^\dfrac{1}{0.45} && (0.0913 &\leq V_r \leq& 1)
\end{cases}
\end{equation}

}……(4.6b)

5. 光源変換 (Bradford 変換)

 Bradford 変換のみ取り扱う。

 光源における白色点 (White Point) の XYZ を用意する。ぶっちゃけ,D50,D65,D93 光源の 3 つについて考えれば完結するので,用意するのもさほど手間ではないと思う。変換式を用意する手順について説明する。

 まず,変換元白色点 (Xs, Ys, Zs) と変換先白色点 (Xd, Yd, Zd) を用意する。これらからそれぞれの値 (L, M, S)*3 を作成する。

{
\displaystyle

\begin{equation}
\begin{bmatrix}
L_s \\
M_s \\
S_s
\end{bmatrix}
=
B_A
\begin{bmatrix}
X_s \\
Y_s \\
Z_s
\end{bmatrix}
\end{equation}

}……(5.1)
{
\displaystyle

\begin{equation}
\begin{bmatrix}
L_d \\
M_d \\
S_d
\end{bmatrix}
=
B_A
\begin{bmatrix}
X_d \\
Y_d \\
Z_d
\end{bmatrix}
\end{equation}

}……(5.2)

用意ができたら,次の変換行列を作成。

{
\displaystyle

\begin{equation}
M=B_A^{-1}
\begin{bmatrix}
L_d/L_s && 0 && 0 \\
0 && M_d/M_s && 0 \\
0 && 0 && S_d/S_s
\end{bmatrix}
B_A
\end{equation}

}……(5.3)

 単に XYZ 空間から感覚に近い LMS 空間に変換し,そこで波長それぞれの倍率でスケールして,再び XYZ 空間に戻すというだけである。

 それぞれの定数は次の通り。

{
\displaystyle

\begin{equation}
B_A=
\begin{bmatrix}
 0.8951000 &&  0.2664000 && -0.1614000 \\
-0.7502000 &&  1.7135000 &&  0.0367000 \\
 0.0389000 && -0.0685000 &&  1.0296000
\end{bmatrix}
\end{equation}
}……(5.4)
{
\displaystyle

\begin{equation}
B_A^{-1}=
\begin{bmatrix}
 0.9869929 && -0.1470543 &&  0.1599627 \\
 0.4323053 &&  0.5183603 &&  0.0492912 \\
-0.0085287 &&  0.0400428 &&  0.9684867
\end{bmatrix}
\end{equation}
}……(5.5)

 より詳しい解説は次を参照するとよい。

http://w3.kcua.ac.jp/~fujiwara/infosci/colorspace/bradford.html

追記: おまけ

 ガンマカーブ(OETF)グラフ作った。

f:id:mntone:20170406133404p:plain

6. 色空間変換(概論)

 前知識はそろった! いざ色空間変換概論へ。実際に実装する OBS Studio で考える。

 OBS Studio は DirectXOpenGL 上に展開され,その上で演算される。1 回目,YUV である場合 RGB に展開され,処理フローを流れていく。このとき RGB の色空間は全く気にしていない! (てか気にされていない!)

 色空間の流れについて書いていく。

5.1 逆ガンマ変換

 まずガンマを linear (1.0) に戻す処理を施す。

【format = RGB, cs = NTSC,γ =  NTSC, wp = D93】 (NTSC-J)
 ↓逆ガンマ変換
【format = RGB, cs = NTSC,γ = linear, wp = D93】

5.2 RGB → XYZ 変換

 XYZ にすると全色空間を扱うことができる。

【format = RGB, cs = NTSC,γ = linear, wp = D93】
 ↓RGB to XYZ変換
【format = XYZ, cs = -,γ = linear, wp = D93】

5.3 光源変換

 D93 光源を D65 に変換する

【format = XYZ, cs = -,γ = linear, wp = D93】
 ↓XYZ to LMS変換
【format = LMS, cs = -,γ = linear, wp = D93】
 ↓光源変換
【format = LMS, cs = -,γ = linear, wp = D65】
 ↓LMS to XYZ変換
【format = XYZ, cs = -,γ = linear, wp = D65】

5.4 XYZ → RGB 変換

 BT.709 RGB に戻す。

【format = XYZ, cs = -,γ = linear, wp = D65】
 ↓XYZ to RGB変換
【format = RGB, cs = BT.709,γ = linear, wp = D65】

5.5 ガンマ変換

 ガンマをもとに戻す。

【format = RGB, cs = BT.709,γ = linear, wp = D65】
 ↓ガンマ変換
【format = RGB, cs = BT.709,γ = BT.709, wp = D65】

5.6 まとめ

 RGB~XYZ~光源変換 (LMS)~XYZ~RGB は 1 つの行列として演算することができるが,ガンマ変換は無理ぽ(近似すればいけるだろうけど)

 したがって,この演算を追加するコストは,GPGPU において,3 回の演算(逆ガンマ変換,行列の掛け算,ガンマ変換)のみである。この程度の変換なら内部でちゃんと情報扱えるようにすればいいのかなーって。整理してて理解したので,あとは実際にコードに落とし込むだけなんだけど,それをどう設計するかが今後の課題…

 4/1 ガンマ,光源変換について加筆。

*1:映像信号を伝送するのに使っていい範囲

*2:CRTのガンマ特性がこの値であり,あくまでこれに合わせるためにカメラで 0.45が使われた。今の液晶や OLED では linear でもよい

*3:目の3種類の錐体の応答値を算出します