toruのブログ

月1くらいで記事書きたい。

色域変換と色域外データのクリッピングについて

目的

3x3 の Matrix を使用した単純な変換な色域変換を行うと、変換先の色域によっては(※)色域のクリッピングが生じて想定外の色相のズレが生じることを図で書く。

※BT.2020 のような広色域から BT.709 のような狭い色域に変換した場合など

背景

前回の記事で作成した色域確認用のテストパターンは、色域変換による色域のクリッピングの特性を利用した特殊パターンとなっている。なので、そのアイデア元となる「色域変換と色域外データのクリッピング」について簡単に情報を残すことにした。

結論

BT.2020 を BT.709/DCI-P3(D65) に変換した際の、色域外の色の変化を可視化した。結果を図1に示す。D65に向かって内側の色域に張り付いている色もあれば、不自然な色相のズレを生じている色もある。

BT.2020 to BT.709 BT.2020 to DCI-P3(D65)
f:id:takuver4:20191019140408p:plain f:id:takuver4:20191019140429p:plain
図1 色域外の色の移動を示した図

色域変換のやり方とクリッピングの仕組みについて

色域変換は様々な制作ツールでサポートされており、誰でも簡単に実行することができる。

ここでは筆者の環境の都合で Colour Science for Python を使った例を示す。

import numpy as np
from colour import xyY_to_XYZ, XYZ_to_RGB, RGB_to_XYZ, XYZ_to_xyY
from colour.models import BT709_COLOURSPACE

# BT.709色域外の xyY データ。Y は Green の Primariy に合わせた。
xyY_bt2020 = np.array([[0.26666249, 0.47998497, 0.67799807],
                       [0.25055208, 0.5328208,  0.67799807],
                       [0.23444166, 0.58565664, 0.67799807],
                       [0.21833125, 0.63849248, 0.67799807],
                       [0.20222083, 0.69132832, 0.67799807],
                       [0.18611042, 0.74416416, 0.67799807],
                       [0.17,       0.797,      0.67799807]])

d65 = np.array([0.3127, 0.3290])

if __name__ == '__main__':
    # とりあえず XYZ にする
    large_xyz_bt2020 = xyY_to_XYZ(xyY_bt2020)

    # BT.2020 の XYZ --> BT.709 の RGB へ変換
    rgb_linear_bt709 = XYZ_to_RGB(
        XYZ=large_xyz_bt2020, illuminant_XYZ=d65, illuminant_RGB=d65,
        XYZ_to_RGB_matrix=BT709_COLOURSPACE.XYZ_to_RGB_matrix)

    # BT.709 の色域内にクリッピング
    print(rgb_linear_bt709)
    rgb_linear_bt709_clipped = np.clip(rgb_linear_bt709, 0.0, 1.0)

    # xyY に変換して最終出力する
    large_xyz_bt709_clipped = RGB_to_XYZ(
        RGB=rgb_linear_bt709_clipped, illuminant_RGB=d65, illuminant_XYZ=d65,
        RGB_to_XYZ_matrix=BT709_COLOURSPACE.RGB_to_XYZ_matrix)
    xyY_bt709_clipped = XYZ_to_xyY(large_xyz_bt709_clipped)
    print(xyY_bt709_clipped)

ポイントは BT.709 の RGB に変換後 [0.0:1.0] にクリッピング しているところである。 XYZ to RGB 変換後の RGB (表1 左側)を見ると分かるのだが、BT.709 の色域外の値は [0.0:1.0] の範囲をはみ出しており不正な値となっている。なので [0.0:1.0] の範囲にクリッピングを行わなければならない(表1 右側)。これにより、R:G:B のバランスが崩れて結果的に色相のズレが発生している。

R G B R(After Clipping) G(After Clipping) B(After Clipping)
-0.000 0.922 0.261 0.000 0.922 0.261
-0.147 0.974 0.171 0.000 0.974 0.171
-0.267 1.017 0.097 0.000 1.000 0.097
-0.367 1.054 0.035 0.000 1.000 0.035
-0.452 1.084 -0.017 0.000 1.000 0.000
-0.524 1.110 -0.062 0.000 1.000 0.000
-0.588 1.133 -0.101 0.000 1.000 0.000
表1. BT.709色域へ XYZ to RGB 変換した際のBT.709色域外のRGB値

感想

Matplotlib の annotation 便利だと思った。