色空間:正しいRGB-YUV色変換

Last-modified: 2016-04-11 (月) 03:34:10

「インターネットで「YUV」と調べても、いまひとつ RGB との相互変換がよくわからない!」
と嘆く人が最後に到達するページでありたい、そういう思いで、とことん解説します。
手っ取り早い答えを求めるなら #afbb2d1c をご参照ください。

参考とすべき規格書

まず何よりも大事なのは「規格」です。規格に則ることで、あなたが作ったプログラムで変換した YUV を、別の誰かが作ったプログラムで RGB に変換した際に、元のただし色に戻すことができます。逆にそうしなければ、色味がずれたりすることになります。
YUV は RGB からの変換時にいくつかのパターンがあります。
それぞれのパターンには規格書がありますが、ビデオコーデックH.264の規格書ではそれらをまとめて扱っていますので、下記のページから規格書を入手すると良いでしょう。
https://www.itu.int/rec/T-REC-H.264

ベースとなる係数は3つ。

H.264規格書では ITU-R BT.601 や ITU-R BT.709 の規格書を参照し、色変換係数を定義しています。
H.264 規格書では「Table E-5 Matrix coefficients」に下表のように変換係数を定義しています。

ValueMatrixInformative remark
1Kr=0.2126, Kb=0.0722ITU-R BT.709
4Kr=0.30, Kb=0.11USFCC Table 47
5Kr=0.299, Kb=0.114ITU-R BT.601 (PAL), IEC 61966-2-4
6Kr=0.299, Kb=0.114ITU-R BT.601 (NTSC), SMPTE 170M
7Kr=0.212, Kb=0.087SMPTE 240M
9Kr=0.2627, Kb=0.0593ITU-R BT.2020
10Kr=0.2627, Kb=0.0593ITU-R BT.2020

※一部を抜粋しています。
※記号は、規格書にほぼ準じた表現を使用しています。

ここで定義しているのは、Kr、Kb が記載されていますが、Kg は下記の等式から求められます。

1 = Kr + Kg + Kb

Kr, Kg, Kb は、それぞれ R, G, B 成分用の係数です。

輝度(0~1)を求める。

輝度は、 H.264 規格書の式 (E-16) で定義されます。

Ey = Kr * Er + Kg * Eg + Kb * Eb  ・・・・・・(E-16)

ここで、Er, Eg, Eb は 0 ~ 1 までの範囲のアナログ値で、それぞれ R, G, B の値です。
出力値 Ey も Kr + Kg + Kb = 1 であることから、 0 ~ 1 であることがわかります。

また、この式は加重平均であることがわかると思います。さらに先ほどの「Table E-5 Matrix coefficients」に掲載した係数を見ていただくとわかりますが、G成分の係数Kgが他よりも大きいことがわかります。これは、人間の目が緑に対してよく反応する(受容野が他より大きい)ことに起因しています。画像をグレースケール化する際も、Y値を採用すると良い結果が得られます。

輝度(整数値)を求める。

ややこしいことに、2種類の式があります。

Y = Clip1y( Round( {1<<(BitDepthY-8)} * {219*Ey+16} ) )  ・・・・・・(E-4)
Y = Clip1y( Round(((1<<BitDepthY)-1)*Ey))  ・・・・・・・・・・・・・(E-10)

E-4は、リミテッドレンジと呼ばれ、E-10はフルレンジと呼ばれるものです。
ここで、Ey は先ほどの式(E-16) で求めたものです。
他、ややこしく書いてあるので整理します。
BitDepthYは輝度成分のビット数で、8bit もしくは 10bit が使われています。
今回は、8bit を当てはめてみますと、前記の式は下記のように変わります。

Y = Clip1y( Round( 219*Ey + 16 ) ) ・・・・・(E-4)'
Y = Clip1y( Round( 255*Ey ) )  ・・・・・・・・(E-10)'

ここで、Round(x) 関数は、いわゆる四捨五入です。(整数と整数の間の中央値は0から遠い方向へ丸める)。
また、Clip1y(x)関数は、xが 0 ~ (輝度成分の階調数-1) の範囲からはみ出さないようにする関数で、例えば256の時に255とするものです。

ということで、Clip1yとRoundは深く考えず、単に次の式だと思えばわかりやすくなります。

Y = 219*Ey + 16 ・・・・・(E-4)'
Y = 255*Ey  ・・・・・・・・(E-10)'

Ey の値域が 0 ~ 1 でしたので、それぞれの値域は、下記のようになります。

Y(リミテッドレンジ)の値域:16~235
Y(フルレンジ)の値域   : 0~255

色差成分(0~1)を求める。

色差成分は、 H.264 規格書の下記の式で定義されます。

Epb = 0.5 * ( Eb - Ey ) / ( 1 - Kb ) ・・・・ (E-17)
Epr = 0.5 * ( Er - Ey ) / ( 1 - Kr ) ・・・・ (E-18)

それぞれの値域は、少し解りにくいですが、-0.5 から +0.5 です。

値域の検証

二つの式は、bかrかしか変わらないので、以降、r だけ考えます。
最初に「Er - Ey」の値域だけを考えることでわかります。
まず、この式 (E-18) に現れる Ey は式 (E-16) にあるものです。ちょっと前なので、再度掲載します。

Ey = Kr * Er + Kg * Eg + Kb * Eb  ・・・・・・(E-16)

この式 (E-16) を使うと、次のように式変形できます。

Er - Ey = Er - Kr * Er - Kg * Eg - Kb * Eb
Er - Ey = (1 - Kr) * Er - Kg * Eg - Kb * Eb

ここで、「( (1 - Kr) * Er - Kg * Eg - Kb * Eb )」の値域を考えます。
この1番目の項は正の数で、2番目、3番目は負の数になります。
このことから、「( (1 - Kr) * Er - Kg * Eg - Kb * Eb )」は
(Er, Eg, Eb) = (1, 0, 0) の時に最大値を取りますので「1 - Kr」が最大値で
(Er, Eg, Eb) = (0, 1, 1) の時に最小値を取りますので「-Kg - Kb」が最小値になります。
さらに「1 = Kr + Kg + Kb」だったことから、最小値は「- (1 - Kr)」であることがわかります。
つまり「Er - Ey」の値域は、[ -(1 - Kr), +(1 - Kr) ] となります。

さて、元の式に戻ります。

Epr = 0.5 * ( Er - Ey ) / ( 1 - Kr ) ・・・・ (E-18)

すでに「Er - Ey」の値域が、[ -(1 - Kr), +(1 - Kr) ] であることがわかっていますので、あとは簡単です。
最小値は -0.5で、最大値は +0.5 となります。

色差成分(整数値)を求める。

輝度成分の時と同様、リミテッドレンジとフルレンジがあります。
Cb, Cr 両方について記述すると量が多いので、Cr だけについて記述します。
基本的には、輝度成分の時と同様ですが、値域が異なります。

Cr = Clip1c( Round( {1<<(BitDepthC-8)} * {224*Epr+128} ) )  ・・・・・・(E-6)
Cr = Clip1c( Round( [{(1<<BitDepthC)-1} * Epr + {1 << (BitDepthC - 1)}] ) ・・・(E-12)

あとの考え方は輝度と同じなので端折ります。

まとめ

ちょっとここで、一旦まとめます。

Ey, Epb, Epr

Ey = Kr * Er + Kg * Eg + Kb * Eb ・・・ (E-16)
Epb = 0.5 * ( Eb - Ey ) / ( 1 - Kb ) ・・・ (E-17)
Epr = 0.5 * ( Er - Ey ) / ( 1 - Kr ) ・・・ (E-18)

ここで、Er, Eg, Eb は入力値 0 ~ 1 の R, G, B 成分。
Kr, Kg, Kb は規格書で与えられる定数値。値域は (0, 1)。
1 = Kr + Kg + Kb の等式が成り立つ。

リミテッドレンジ(オリジナル)

規格書に現れるリミテッドレンジの Y, Cb, Cr の定義。

Y = Clip1y( Round( {1<<(BitDepthY-8)} * {219*Ey+16} ) ) ・・・ (E-4)
Cb = Clip1c( Round( {1<<(BitDepthC-8)} * {224*Epb+128} ) ) ・・・ (E-5)
Cr = Clip1c( Round( {1<<(BitDepthC-8)} * {224*Epr+128} ) ) ・・・ (E-6)

リミテッドレンジ(端折り版)

クリップや丸め処理を端折り、ビット深度を 8 で固定したもの。

Y = 219*Ey + 16
Cb = 224*Epb + 128
Cr = 224*Epr + 128

フルレンジ(オリジナル)

規格書に現れるフルレンジの Y, Cb, Cr の定義。

Y = Clip1y( Round(((1<<BitDepthY)-1)*Ey)) ・・・ (E-10)
Cb = Clip1c( Round( [{(1<<BitDepthC)-1} * Epb + {1 << (BitDepthC - 1)}] ) ・・・(E-11)
Cr = Clip1c( Round( [{(1<<BitDepthC)-1} * Epr + {1 << (BitDepthC - 1)}] ) ・・・(E-12)

フルレンジ(端折り版)

クリップや丸め処理を端折り、ビット深度を 8 で固定したもの。

Y = 255*Ey
Cb = 255*Epb + 128
Cr = 255*Epr + 128

Ey, Epb, Epr を除去する。

前記は、式としてはすっきりしたものですが、具体的に数値を入れた場合には、
Ey, Epb, Epr を除去できます。以降では、Kr, Kb の定義から式をより簡素化します。

まとめ