toruのブログ

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

Jzazbz Color Space について少しだけ調べた

1. 背景

  • 最近、 \displaystyle J_z a_z b_z Color Space [1] という文字を目にする機会が増えた
    • W3CCSS Color HDR Module Level 1 の Draft で  \displaystyle J_z a_z b_z が追加された [2]
  • 流石に触れておかないとマズいという危機感を覚えたので軽く調べることにした

2. 目的

  •  \displaystyle J_z a_z b_z Color Space についての理解を少し深める
  • そのために BT.2100 (ST 2084) の Color Volume を  \displaystyle J_z a_z b_z でプロットしてみる

3. 結論

 \displaystyle J_z a_z b_z Color Space について以下を行った。

  • CIEXYZ to  \displaystyle J_z a_z b_z 変換を実装した
  •  \displaystyle a_z - b_z 平面をプロットした(動画1)※1
  •  \displaystyle C_z - J_z 平面をプロットした(動画2)※1
  • 最大輝度が制限された表示デバイス(100 nits, 1000 nits, 4000 nits, 10000 nits)の  \displaystyle C_z - J_z 平面のプロットをお試しでやってみた(動画3)※1

※1 動画1~動画3 は HDR/WCG のデータであり sRGB モニターで表示すると色のズレが非常に大きい。そのため自作の HDR to SDR 変換をプロット結果に後処理として適用したものも乗せてある。動画1,動画2 は右側のプロットに、動画3 は全てのプロットに HDR to SDR 変換が後処理として適用してある。

動画1.  \displaystyle a_z - b_z 平面のプロット
動画2.  \displaystyle C_z - J_z 平面のプロット
動画3. 最大輝度が 100 nits, 1000 nits, 4000 nits, 10000 nits の場合の  \displaystyle C_z - J_z 平面のプロット

4. 理論

4.1.  \displaystyle J_z a_z b_z Color Space の概要

 \displaystyle J_z a_z b_z Color Space は以下の特徴をもった均等色空間である。

  • HDR/WCG に対応した
  • hue linearity と uniformity のトレードオフをパラメータの最適化で最小化した
  • 少ない計算コストでの変換・逆変換を実現した

4.2.  \displaystyle J_z a_z b_z Color Space への変換

CIEXYZ を  \displaystyle J_z a_z b_z に変換する手順は以下の通り(論文スクリーンショットを転載した)。

f:id:takuver4:20210606115516p:plain:w540

このうち、式(8), 式(10), 式(12) について以下で個人的なメモを残す。

4.2.1. 式(8) に関するメモ

式(8) の  X'_{D65} の計算は blue 領域で chroma が増大するにつれて hue がシフトしてしまう症状を抑えるための変換である。 式(8) の  \displaystyle Z'_{D65} の計算は  \displaystyle X'_{D65} の計算の影響で green-blue の uniformity が悪化する問題を改善するための変換である。

それぞれの計算で色がどのように変化するかを確認するため、BT.2020色域, L* = 50 の ab平面に対して  \displaystyle X'_{D65} しか適用しない場合と  \displaystyle X'_{D65},  \displaystyle Z'_{D65} の両方を適用した場合の結果をそれぞれプロットした。 結果を 図1、図2 に示す。

図1 より blue の chroma が大きくなる箇所で、hue が green の方向に変化していることが分かる。 図2 については…一応プロットはしたものの上手く考察ができなかったのでコメントは控える(残念無念)。

f:id:takuver4:20210608203613g:plain:w540
図1. BT.2020色域, L* = 50 の ab平面に対して 式(8) の X' の計算を適用した様子

f:id:takuver4:20210608203641g:plain:w540
図2. BT.2020色域, L* = 50 の ab平面に対して 式(8) を適用した様子

4.2.1. 式(10) に関するメモ

式(10) は ITU-R BT.2100 の PQ OETF の計算式 [3] とほぼ同じである。ただし、式(10) の パラメータ  \displaystyle p は PQ OETF のパラメータ  \displaystyle m_2 とは異なるため注意が必要である。

  • BT.2100 PQ OETF の パラメータ  \displaystyle m_2 = 2523 / 4096 \times128
  • 式(10) の パラメータ  \displaystyle p = 1.7 \times 2523

ITU-R BT.2100 PQ OETF と 式(10) を比較した結果を図3 に示す。

f:id:takuver4:20210606222948p:plain:w540
図3. BT.2100 PQ OETF と 式(10) 比較

4.2.1. 式(12) に関するメモ

 \displaystyle Iz \displaystyle Jz との関係は以下の通り。 \displaystyle J_z a_z b_z では  \displaystyle d=-0.56 だが、参考までに別のパラメータを適用した場合の例もプロットした。この計算によりHDRの領域を含めた明度が知覚にマッチするようになる。

f:id:takuver4:20210608200253p:plain:w540
図4.  \displaystyle Iz \displaystyle Jz の関係

5. 実装と動作確認

式(8)~(12) の処理を Python で実装した(コードは以下にありますが、例によって筆者以外の方が使う事を想定してないコードです。ご注意下さい)。

sample_code/jzazbz.py at develop · toru-ver4/sample_code · GitHub

さて、今回は元の論文に MATLABソースコードが添付されていたため、動作確認として筆者の実装した Pythonの出力結果と論文に添付のコードの出力結果を比較した。 なお、筆者は MATLAB のライセンスを有していないため、論文に添付のコードは互換のある GNU Octave [4] で実行した。

まずは、テスト用の XYZ ファイルを用意した。SDR の D65光源の Color Checker、黒、SDR の RGBMYCW、HDR の RGBMYCW を用意した。

sample_code/study_jzazbz.py at develop · toru-ver4/sample_code · GitHub

>>> from study_jzazbz.py import create_xyz_values_for_test
>>> large_xyz = create_xyz_values_for_test()
>>> print(large_xyz)
[[  1.31643177e+01   1.04442502e+01   6.29342166e+00]  # Color Checker
 [  4.41184122e+01   3.60556309e+01   2.41116141e+01]
 [  1.50556943e+01   1.80227254e+01   3.55204516e+01]
 [  9.47136828e+00   1.30268941e+01   6.06911704e+00]
 [  2.46341861e+01   2.31295278e+01   4.64081443e+01]
 [  2.12458434e+01   4.00869751e+01   4.53833001e+01]
 [  5.09577070e+01   3.33643003e+01   3.83128751e+00]
 [  1.20679451e+01   1.11553046e+01   4.26300771e+01]
 [  3.93804761e+01   2.13649979e+01   1.29120788e+01]
 [  1.01315841e+01   6.68183785e+00   1.51504316e+01]
 [  3.00798836e+01   4.36539780e+01   7.33873231e+00]
 [  5.60951613e+01   4.52483856e+01   3.70903187e+00]
 [  6.80932825e+00   5.68753059e+00   3.07923966e+01]
 [  8.99908331e+00   2.21114789e+01   8.46474202e+00]
 [  3.01993324e+01   1.43146015e+01   4.42792886e+00]
 [  6.28035204e+01   6.08259352e+01   3.15176998e+00]
 [  4.05485037e+01   2.19770660e+01   3.25897900e+01]
 [  7.97228282e+00   1.81824227e+01   4.29482943e+01]
 [  8.64106453e+01   9.12906414e+01   9.74496472e+01]
 [  5.55153191e+01   5.88901955e+01   6.46533628e+01]
 [  3.41032757e+01   3.62780397e+01   4.00402640e+01]
 [  1.81443117e+01   1.91434321e+01   2.10331886e+01]
 [  8.27873420e+00   8.81612643e+00   1.00698206e+01]
 [  2.94050874e+00   3.10722155e+00   3.56717035e+00]
 [  0.00000000e+00   0.00000000e+00   0.00000000e+00]  # Black
 [  6.36958048e+01   2.62700212e+01   4.91123562e-15]  # RGBMYCW (SDR)
 [  1.44616904e+01   6.77998072e+01   2.80726930e+00]
 [  1.68880975e+01   5.93017165e+00   1.06098506e+02]
 [  8.05839023e+01   3.22001928e+01   1.06098506e+02]
 [  3.13497879e+01   7.37299788e+01   1.08905775e+02]
 [  7.81574952e+01   9.40698284e+01   2.80726930e+00]
 [  9.50455927e+01   1.00000000e+02   1.08905775e+02]
 [  6.36958048e+03   2.62700212e+03   4.91123562e-13]  # RGBMYCW (HDR)
 [  1.44616904e+03   6.77998072e+03   2.80726930e+02]
 [  1.68880975e+03   5.93017165e+02   1.06098506e+04]
 [  8.05839023e+03   3.22001928e+03   1.06098506e+04]
 [  3.13497879e+03   7.37299788e+03   1.08905775e+04]
 [  7.81574952e+03   9.40698284e+03   2.80726930e+02]
 [  9.50455927e+03   1.00000000e+04   1.08905775e+04]]

これのデータを MATLAB 形式でエクスポートした。

>>> from study_jzazbz.py import create_xyz_values_for_test
>>> from scipy.io import savemat
>>> large_xyz = create_xyz_values_for_test()
>>> save_data = dict(large_xyz=large_xyz)
>>> savemat("./test_data.mat", save_data)

次に GNU Octave にて以下のコマンドを実行して論文に添付のコードで XYZ to Jzazbz 変換を行った。

>> addpath C:\xxxx\sample_code\2021\10_study_jzazbz
>> load test_data.mat
>> large_xyz
large_xyz =

   1.3164e+01   1.0444e+01   6.2934e+00
   4.4118e+01   3.6056e+01   2.4112e+01
   1.5056e+01   1.8023e+01   3.5520e+01
   9.4714e+00   1.3027e+01   6.0691e+00
   2.4634e+01   2.3130e+01   4.6408e+01
   2.1246e+01   4.0087e+01   4.5383e+01
   5.0958e+01   3.3364e+01   3.8313e+00
   1.2068e+01   1.1155e+01   4.2630e+01
   3.9380e+01   2.1365e+01   1.2912e+01
   1.0132e+01   6.6818e+00   1.5150e+01
   3.0080e+01   4.3654e+01   7.3387e+00
   5.6095e+01   4.5248e+01   3.7090e+00
   6.8093e+00   5.6875e+00   3.0792e+01
   8.9991e+00   2.2111e+01   8.4647e+00
   3.0199e+01   1.4315e+01   4.4279e+00
   6.2804e+01   6.0826e+01   3.1518e+00
   4.0549e+01   2.1977e+01   3.2590e+01
   7.9723e+00   1.8182e+01   4.2948e+01
   8.6411e+01   9.1291e+01   9.7450e+01
   5.5515e+01   5.8890e+01   6.4653e+01
   3.4103e+01   3.6278e+01   4.0040e+01
   1.8144e+01   1.9143e+01   2.1033e+01
   8.2787e+00   8.8161e+00   1.0070e+01
   2.9405e+00   3.1072e+00   3.5672e+00
            0            0            0
   6.3696e+01   2.6270e+01   4.9112e-15
   1.4462e+01   6.7800e+01   2.8073e+00
   1.6888e+01   5.9302e+00   1.0610e+02
   8.0584e+01   3.2200e+01   1.0610e+02
   3.1350e+01   7.3730e+01   1.0891e+02
   7.8157e+01   9.4070e+01   2.8073e+00
   9.5046e+01   1.0000e+02   1.0891e+02
   6.3696e+03   2.6270e+03   4.9112e-13
   1.4462e+03   6.7800e+03   2.8073e+02
   1.6888e+03   5.9302e+02   1.0610e+04
   8.0584e+03   3.2200e+03   1.0610e+04
   3.1350e+03   7.3730e+03   1.0891e+04
   7.8157e+03   9.4070e+03   2.8073e+02
   9.5046e+03   1.0000e+04   1.0891e+04

>> result = JzAzBz(large_xyz, 'f')
result =

   0.0650   0.0327   0.0294
   0.1135   0.0416   0.0363
   0.0783  -0.0284  -0.0424
   0.0649  -0.0239   0.0355
   0.0918   0.0021  -0.0428
   0.1042  -0.0788  -0.0115
   0.1142   0.0714   0.0984
   0.0665  -0.0254  -0.0815
   0.0985   0.0965   0.0429
   0.0555   0.0359  -0.0317
   0.1113  -0.0439   0.0902
   0.1248   0.0401   0.1105
   0.0494  -0.0314  -0.0891
   0.0767  -0.0829   0.0454
   0.0848   0.1042   0.0616
   0.1367   0.0101   0.1240
   0.1005   0.0930  -0.0084
   0.0714  -0.1045  -0.0641
   0.1609  -0.0006   0.0016
   0.1340  -0.0016  -0.0009
   0.1088  -0.0019  -0.0013
   0.0818  -0.0005  -0.0007
   0.0566  -0.0017  -0.0025
   0.0333  -0.0007  -0.0018
        0  -0.0000  -0.0000
   0.1146   0.1405   0.1111
   0.1193  -0.1748   0.1223
   0.0662  -0.0599  -0.1724
   0.1295   0.1359  -0.0699
   0.1322  -0.1289  -0.0414
   0.1582  -0.0287   0.1422
   0.1672  -0.0001  -0.0001
   0.6931   0.2631   0.2587
   0.7194  -0.3498   0.2600
   0.4386  -0.1875  -0.3542
   0.7752   0.2539  -0.1275
   0.7892  -0.2417  -0.0734
   0.9366  -0.0620   0.2752
   0.9886  -0.0002  -0.0002

>> save -v7 C:\xxxx\sample_code\2021\10_study_jzazbz\result.mat result

そして、GNU Octave の結果と Python の結果を以下で確認した。

>>> from study_jzazbz.py import create_xyz_values_for_test
>>> from jzazbz.py import large_xyz_to_jzazbz
>>> import numpy as np
>>> ref_jzazbz = loadmat("./result.mat")['result']
>>> large_xyz = create_xyz_values_for_test()
>>> my_jzazbz = large_xyz_to_jzazbz(xyz=large_xyz)
>>> np.testing.assert_array_almost_equal(ref_jzazbz, my_jzazbz, decimal=7)

5.  \displaystyle a_z b_z 平面と  \displaystyle C_z J_z平面のプロット

実装したソースコードを使って  \displaystyle a_z b_z 平面と  \displaystyle C_z J_z をプロットしてみた(冒頭の結論で示したものと同じ内容)。

動画1.  \displaystyle a_z - b_z 平面のプロット
動画2.  \displaystyle C_z - J_z 平面のプロット
動画3. 最大輝度が 100 nits, 1000 nits, 4000 nits, 10000 nits の場合の  \displaystyle a_z - b_z 平面のプロット

6. 感想

まずは基本的なところを確認した。引き続き Hue-Chroma テストパターンの作成や BT.2407 ライクな Gamut Mapping の実装などを試していく予定である。

参考資料

[1] Muhammad Safdar, Guihua Cui, Youn Jin Kim, Ming Ronnier Luo, "Perceptually uniform color space for image signals including high dynamic range and wide gamut", Opt. Express, 25(13):15131, June 2017.

[2] W3C, "CSS Color HDR Module Level 1", https://drafts.csswg.org/css-color-hdr/#Jzazbz

[3] Recommendation ITU-R BT.2100-2, "Image parameter values for high dynamic range television for use in production and international programme exchange", https://www.itu.int/dms_pubrec/itu-r/rec/bt/R-REC-BT.2100-2-201807-I!!PDF-E.pdf

[4] "GNU Octave", https://www.gnu.org/software/octave/

BT.2020 色域確認用の Hue-Chroma テストパターンの改善

0. おことわり

この記事は筆者の備忘録として書いており、一般の方が読むことを想定していません。
未来の自分が読み返して理解できるように、できるだけ分かりやすく書くつもりですが、前提となる説明が省略されている箇所が多数あります。ご了承下さい。

1. 背景

  • 昨年に BT.2407 実装の効果確認のために 図1に示す Hue-Chromaテストパターンを作成した
  • 最近、とある作業で当該パターンを使ったところ Yellow で想定外の色ズレを発見した
  • 個人的に許せない事だったので修正することにした

2. 目的

  • 昨年に作成した Hue-Chromaテストパターンの色ズレの原因を特定する
  • 色ズレを修正したテストパターンを作成する

3. 結論

  • 色ズレ の原因は CIELAB 空間での Yellow の BT.709 Cups と BT.2020 Cups の位置関係の考察不足
    • 設定した Focal Point に問題があり BT.709 の Gamut内を想定したパッチが BT.709 の Gamut外の値になっていた
    • 詳細は 4.3. を参照
  • 修正前の色ズレのあるテストパターンを図1に、修正後のテストパターンを図2に示す。
    • テストパターンの各ブロックの左上に "■" or "□" マークのある箇所は BT.709 の色域外を意味している
    • 修正前のパターンでは Yellow において想定外の箇所に "■" マークが出てしまっている
f:id:takuver4:20210528142116p:plain f:id:takuver4:20210525194845p:plain
図1. 修正前(色ズレあり) 図2. 修正後(色ズレなし)

4. 詳細

4.1. テストパターンの特性

まずは作成したテストパターンの想定する特性について説明する。 下の図3 に書いた通り、このテストパターンは CIELAB色空間で 水平方向で Hue を、垂直方向で Chroma を変化させたものである。 加えて、各パッチが BT.709 の色域内か判断できるように BT.709 の Gamut Boundary を超えた場合はパッチの左上に小さな ”■” マークを付与してある。

f:id:takuver4:20210528142401p:plain
図3. テストパターンの簡単な説明

4.2. テストパターンの作成方法

続いてテストパターンの具体的な作成方法について説明する。

まず概要から説明する。このパターンは以下の2つを満たすように作成した。

  • ① 垂直方向でなるべく BT.709 の領域が広くなる
  • ② 垂直方向の再下段は各Hue で最も Chroma が大きな値(BT.2020 Cups)となる

Chroma-Lightness 平面での例を以下の図4 に示す。条件①と②を満たすように 各Hue のパッチは BT.2020 Cups と BT.709 Cups を結んだ直線上の値を取るようにした。 図4 は Hue=40° の例であるが、これを各Hue において実施することで最終的なパターンを作成した。

f:id:takuver4:20210528153342p:plain
図4. Chroma-Lightness 平面を使用したパッチの説明

続いて、BT.709 Cups、BT.2020 Cups の求め方について説明する。まず初めに BT.709 と BT.2020 の Gamut Boundary を求めた。 今回は以下の手順で求めた。

  1. 任意の Hue の Chroma-Lightness 平面を Chroma 方向に N_c個、Lightness方向に N_l個に分割する(N_c=20, N_l=10 の例を図5に示す)
  2. 各ブロックの LCH値を RGB値に変換し、0 ≦ R ≦ 1 かつ 0 ≦ G ≦ 1 かつ 0 ≦ B ≦ 1 を満たすブロックを有効ブロックとする(有効ブロックを黄色にした例を図6に示す)
  3. 各 Lightness値に対して Chromaが大きくなる方向にブロックを走査し、最初に無効ブロックとなる直前のブロックを、その Lightness値の Gamut Boundary とする(Gamut Boundary を黒色にした例を図7に示す)
  4. これを Hue を少しずつ変更しながら 0~360°の範囲で行う
f:id:takuver4:20210528172010p:plain f:id:takuver4:20210528172019p:plain f:id:takuver4:20210528172028p:plain
図5. Chroma-Lightness 平面をブロックに分割した例 図6. 有効ブロック (Out-of-Gamut でない領域)を黄色で塗った様子 図7. Gamut Boundary を黒色で塗った様子

もう少しブロック分割を細かくして、N_c=240, N_l=135 とした例を 図8, 図9 に示す。

f:id:takuver4:20210528172515p:plain f:id:takuver4:20210528172525p:plain
図8. N_c=240, N_l=135, Hue=40° の例 図9. N_c=240, N_l=135, Hue=99° の例

更にブロック分割を細かくして、N_c=1024, N_l=32768 とした例を 図10, 図11 に示す。

f:id:takuver4:20210528172644p:plain f:id:takuver4:20210528172657p:plain
図10. N_c=1024, N_l=32768, Hue=40° の例 図11. N_c=1024, N_l=32768, Hue=99° の例

なお、上記で計算した Gamut Boundary は LUT化して気楽に使えるようにした。自分が使い方を思い出せるようにサンプルコードを以下に示す( なお、本コードは筆者環境以外では動かないことが予想される)。

# LUT の作成
from create_gamut_booundary_lut import calc_chroma_boundary_lut
import color_space as cs
import numpy as np

if __name__ == '__main__':
    hue_sample = 1024
    chroma_sample = 32768
    ll_num = 1024
    cs_name = cs.BT2020
    lut = calc_chroma_boundary_lut(
        lightness_sample=ll_num, chroma_sample=chroma_sample,
        hue_sample=hue_sample, cs_name=cs_name)
    np.save(
        f"./lut/lut_sample_{ll_num}_{hue_sample}_{chroma_sample}_{cs_name}.npy",
        lut)
# 任意の Hue, Lightness の Gamut Boundary の LCH値を取得
from create_gamut_booundary_lut import calc_chroma_boundary_lut,\
    get_gamut_boundary_lch_from_lut
import color_space as cs
import numpy as np
from colour.utilities import tstack

if __name__ == '__main__':
    lut = np.load("./lut/lut_sample_1024_1024_32768_ITU-R BT.2020.npy")
    hue = 40
    lightness_num = 11
    lightness_array = np.linspace(0, 100, lightness_num)
    hue_array = np.ones_like(lightness_array) * hue
    lh_array = tstack([lightness_array, hue_array])
    gamut_boundary_lch = get_gamut_boundary_lch_from_lut(
        lut=lut, lh_array=lh_array)
    print(gamut_boundary_lch)

    """
    [[   0.            0.           40.        ]
     [  10.           26.41562112   40.        ]
     [  20.           53.00088577   40.        ]
     [  30.           79.69424616   40.        ]
     [  40.          106.60514221   40.        ]
     [  50.          133.87300364   40.        ]
     [  60.          145.00828857   40.        ]
     [  70.00000153   99.57528305   40.        ]
     [  80.00000153   60.49513295   40.        ]
     [  90.00000076   27.6507864    40.        ]
     [ 100.            0.           40.        ]]
    """

github.com

BT.709/BT.2020 の Gamut Boundary が求まったら、各Hue に対して Chroma が最大となる点を探し、そこを BT.709 Cups/BT.2020 Cups とした。

さて、図9、図11 から分かるとおり、今回の Gamut Boundary の計算方法には Yellow で理論値とズレる欠点がある。 これについて改善方法を色々と検討したのだが、対応するには費用対効果が悪いと判断して Yellow のズレは修正を諦めた(残念無念)。

ちなみに、Chroma-Lightness 平面ではなく a*-b* 平面で観測するとズレは 図12のようになる。

f:id:takuver4:20210528221255p:plain:w540
図12. ab 平面での様子

長々と書いてきたが上記の通りに Gamut Boundary、Cups を求めることでテストパターンを作成した。 以後で、なぜ色ズレが生じてしまったのかを解説していく。

4.3. テストパターンの色ズレの原因と対策

それでは問題のあった Yellow について Chroma-Lightness 平面の様子を確認してみる。 以下の図13は全体の様子を、図14 は一部を拡大して表示したものである。 図14 を見ると分かるように、BT.709 Cups と BT.2020 Cups を結んだ直線が BT.709 の Gamut をはみ出している。 これにより意図せぬ色ズレが発生してしまっていた。

f:id:takuver4:20210528225409p:plain f:id:takuver4:20210528231706p:plain
図13. Hue=104° の全体 図14. 左図の Lightness 90~100 の範囲を拡大して表示

今回はこれを防ぐために、図13 の Focal Point の値を 50~90 に制限することにした。制限した結果を 図15、図16 に示す。

f:id:takuver4:20210528233049p:plain f:id:takuver4:20210528233058p:plain
図15. 修正後の Hue=104° の全体 図16. 左図の Lightness 90~100 の範囲を拡大して表示

Focal Point の上限を 90 にした理由は Yellow で BT.709 の外側に出てしまうのを防ぐためである。 下限を 50 にした理由は、本記事では詳細を省略するが DCI-P3 Cups と BT.2020 Cups を結んだ直線を考慮すると下限値も設ける必要があったからである。

各Hue に対して Focal Point の制限前後をプロットした結果を動画1 に示す。

動画1. Focal Point を制限した場合の様子 (BT.709 - BT.2020)

また、DCI-P3 と BT.2020 のペアに対して同様のことを行った結果を 動画2 に示す。

動画2. Focal Point を制限した場合の様子 (DCI-P3 - BT.2020)

4.4 対策後のテストパターン

このようにして対策を施したテストパターンを図17、図18 に示す。パッチの左上に付いている "□" は DCI-P3 領域であることを、"■" は BT.2020 領域であることを意味している。

f:id:takuver4:20210529093019p:plain f:id:takuver4:20210529093031p:plain
図17 修正後のテストパターン(水平ブロック数32) 図17 修正後のテストパターン(水平ブロック数128)

ブロック数を変化させた版や4K版をまとめた zip は以下で公開している。(誰も欲しがらないと思うが)再配布さえしなければ自由に使ってもらって構わない。 なお、今回は ICC Profile(Gamma2.4, BT.2020, D65) をテストパターンに埋め込んである。注意して頂きたい。

drive.google.com

5. 感想

今回の記事、死ぬほど書きづらかった。自分に説明するだけで精一杯。第三者に説明するとか無理ゲーやで…。

HDRでプレイしてるゲームを YouTube で SDR配信する

1. 背景

  • 筆者はここ最近、YouTube でのゲーム配信に関心を持っていた(※1)
  • 筆者の自宅には HDR対応の TV と HDRに対応したゲーム機(PS4 Pro)があるため HDR のゲーム配信をしようと試みた
  • しかし、筆者の環境だと PS4 Pro の HDR出力を YouTubeHDR配信することが不可能だと分かった
  • 結局 SDRで配信することにしたが、自宅には HDR対応の TV があるので自宅では絶対に HDR でプレイしたかった
  • そこで、自宅では HDRでプレイしつつ YouTube には SDRで配信する環境を作ることを考えた

※1 どういうツールを使用するのか、YouTube の管理画面はどうなっているのか、遅延はどのくらいか、といった内容に興味があった

2. 目的

  • 自宅では HDRPS4 のゲームをプレイしつつ、YouTube では SDR で配信をする

3. おことわり

本記事の「HDR」は High-dynamic-range video を意味します。具体的には BT.2020色域、SMPTE ST2084 の HDR10信号を指します。ご了承下さい。

4. 結論

図1 の構成で目的を達成した。

具体的には OBS 上で HDR to SDR を行う Effect Filter を適用することで達成した。HDR to SDR 変換は以前に作成したものを少し改造して 3DLUT 化して使用した。

f:id:takuver4:20210412221950p:plain
図1. 今回作成した配信の構成

実際に配信を行った様子を以下に示す。

上記の動画が成果物だが、本動画のみでは HDR と SDR の差が分かりづらいため比較用の動画を作成した。 結果を以下に示す。四分割された動画のうち左下が今回作成した 3DLUT を適用したものである。なお本動画は HDRに対応した表示デバイスで意図通りに表示される。SDRの表示デバイスで見る場合は代わりに こちら を見て頂きたい。

5. 結論に至るまでの経緯

5.1. YouTube での HDRライブ配信を試みた

一般家庭にある普通の機材で YouTube での HDRライブ配信を行うには Mirillis Action! というソフトウェアを使用すれば良い[1]。 筆者は Mirillis Action! とHDMIキャプチャデバイス(4K60 S+)を購入し PS4 ゲームの HDR配信を試みた。

しかし、よく確認したところ Mirillis Action! が対応しているのは Windows上で動作する HDRのゲームの配信であり、HDMIでキャプチャした HDRのゲームの配信には対応していなかった

ここで筆者のモチベーションはゼロになってしまい、1ヶ月以上放心状態となった(4k60 S+ が文鎮と化した)。

5.2. HDR to SDR 変換して SDRで配信する機会が発生した

筆者は3月下旬に技術系の勉強をする気力がゼロになってしまったため、1週間ほどゲームばかりプレイする生活をしていた。 その際、特に意味もなく YouTube で配信をしていたのだが、筆者はどうしてもゲームを HDR でプレイしたかった。そのため OBS で HDR の信号を BT.709 の信号として強引に解釈させ配信していた。その結果、配信画面は図2のようにコントラストの低い見た目になっていた。

f:id:takuver4:20210412215754p:plain
図2. HDRのゲームをSDRで無理矢理配信した際の画面
© 1997, 2020 SQUARE ENIX CO., LTD. All Rights Reserved.
CHARACTER DESIGN: TETSUYA NOMURA / ROBERTO FERRARI LOGO ILLUSTRATION:©1997 YOSHITAKA AMANO

しかし、こうした色異常が生じるのは制作者の方々に申し訳なかったため、OBS で HDR to SDR 変換を行って配信を行うことにした。HDR to SDR 変換には以下の記事で作成した 3DLUT を使用した。OBS に 3DLUT を適用する際は Effect Filters を使用した[2]。

trev16.hatenablog.com

5.3. HDR to SDR 変換 3DLUT の改良

前節で使用した 3DLUT には個人的に好ましくない特性があった。そこで改良を行うことにした。

以前に作成した BT.2446 + BT.2407 の HDR to SDR 変換をベースにトーンマッピングの特性のみ前節の 3DLUT の特性を利用することにした。 具体的には以下の記事で作成した HDR to SDR 変換について、 Tone mapping の数式のみ本記事の図3 に置き換えたものを作成した(図3の元ネタは この記事 を参照)。

trev16.hatenablog.com

f:id:takuver4:20210414214944p:plain:w540
図3. 置き換えた Tone mapping の数式(x は ST2084 で Encode された Non-Linear値)。元ネタは この記事 を参照

5.4. OBS で HDRの信号を扱う際の注意点

これで HDR to SDR 変換する配信環境は完成かと思われたが、OBS上で 3DLUT の効果確認を行っていたところ想定外の色ズレが生じている事が分かった。 具体的には以下の図4に示す通り、OBS で 3DLUT を適用した場合は、特に赤色の輝度が上がる方向にズレることが分かった。

図4 OBS と DaVinci Resolve で同一の HDR10 ファイルに対して 3DLUT を適用した結果の比較

色々と調べていると、本症状は OBS が BT.2020色域の動画ファイルをサポートしていない<要出典>ことが原因だと分かった。 具体的には、BT.2020 の Matrix Coefficients の動画ファイルを開く際に BT.709 の Matrix Coefficients を適用してファイルを開いており、色ズレが生じていると推測している()。

これは OBS側には何も問題は無く、サポート外の BT.2020色域のファイルを OBSで無理矢理開こうとした筆者に問題がある。

そこで、これを補正する 3x3 の Matrix を前節で作成した HDR to SDR の 3DLUT に組み込んだ(式の導出はメモを消失して書き直すのが面倒になったので省略します)。 3x3 の Matrix を組み込んだ 3DLUT を使用した場合の結果を図5 に示す。

図5 3x3 Matrix補正適用済みの 3DLUT を適用した結果の比較

若干、高輝度領域で差が生じているが、Color Checker の差異は無くなったので今回はOKとする(おそらく 3x3 の Matrix で補正しきれなかった領域だと思うのですが、面倒で解析していません。手抜きでスミマセン…)。

6. 作成した 3DLUT

誰も使わないと思いますが作成した 3DLUT を以下で公開しておきます。 再配布をしなければ自由に使って頂いて構いません(なお、何か問題が発生しても筆者は責任を取れませんのでご了承下さい)。

drive.google.com

7. 改善点

  • 配信動画を見ると、なんとなくコントラストが低く感じる

    • ITU-R BT.1886 を参考に Gamma=2.4 のモニターをターゲットにしているのが悪いのかもしれない
    • 明るい環境用に Gamma=2.2 ターゲットの 3DLUT も上記リンクに入れた。必要に応じて試して頂きたい
  • PS4 の「HDR調整」の結果を全く考慮していない

    • 可能であれば HDR調整の結果を取得して それに適した Tone mapping を行いたい(無理だけど)
  • バレットのシーンを見ていて思ったが、暗部が潰れ気味な気がしている。

    • 暗部の特性を改善する Tone mapping も試してみたい
    • 本当は Piecewise Power Curves[3] を試したかったのだが今回は諸事情により断念した

8. 考察というか感想

我ながら意味不明な記事を書いてしまいました。ここまで読んで下さった方、ありがとうございます。

9. 参考資料

[1] YouTube Help, "Stream HDR video on YouTube", https://support.google.com/youtube/answer/10265272

[2] OBS Beginners Guide, "Color Grading using LUTs in OBS", https://streamshark.io/obs-guide/color-grading-lut

[3] "Piece-Wise Power Curves", https://www.desmos.com/calculator/12vlon6rpu

Docker Desktop 3.2.0 以降では docker.for.win.localhost は使えない

背景と目的

Windows10 に Docker Desktop をインストールした環境で、 久しぶりに Python でグラフをプロットしようとしたら以下のエラーメッセージが表示された。

TclError: couldn't connect to display "docker.for.win.localhost:0.0"

そこで原因と対処方法を調べた。

結論

コンテナ起動時に設定する環境変数 DISPLAY の値を以下の通りに変更する。

docker.for.win.localhost:0.0
↓
host.docker.internal:0.0

原因

Docker Desktop 3.2.0 から docker.for.win.localhost が非サポートとなっていたため。 以下にリリースノート[1] からの引用を示す。

Removed the deprecated DNS name docker.for.win.localhost. Use DNS name host.docker.internal in a container to access services that are running on the host. docker/for-win#10619

その他

環境変数 DISPLAYdocker.for.win.localhost に設定していた理由は、筆者が使用している X server である X410 のドキュメント[2]に記載があったからである。 以下に公式ドキュメントからの引用を添付する。

X410 can seamlessly be used with Docker for Windows. You don't need to enable any special option in X410 or Windows to have X-Window GUI apps running in Docker containers show up in Windows; you simply need to set the DISPLAY environment variable with a special address, i.e., docker.for.win.localhost, before launching your X-Window GUI apps:

余談ですが、下の方に host.docker.internal:0.0 が使える旨が書いてありましたね。全然気づいていませんでした…。

参考資料

[1] Docker Documentation, "Docker for Windows release notes", https://docs.docker.com/docker-for-windows/release-notes/#docker-desktop-320 (2021/04/04 に確認)

[2] X410, "Cookbook", https://x410.dev/cookbook/ (2021/04/04 に確認)

10bit 表示か確認するテストパターンを作った(SDR用)

1. 背景

  • 筆者は手持ちの表示デバイスが 10bit で表示できているか目視で確認したくなる事がある
  • 表示デバイスHDR で表示している場合は簡単に 10bit表示の目視確認ができる。しかし SDR 表示の場合は目視確認は難しい
  • そこで簡単に判別するためのパターンを自作することにした

2. 目的

  • 表示デバイスが 10bit表示かどうかを目視で確認するパターンを作成する
    • なお表示デバイスは SDR(※1) で表示しているものとする

※1 Gamma: 2.4, Luminance: 80cd/m2~200cd/m2, Gamut: BT.709

3. 結論

目的を達成するパターンの作成に成功した。

作成したパターンを図1に示す。このパターンは 9つのブロックに分かれており、各ブロックの上側が 8bit 精度のパターン、下側が 10bit 精度のパターンとなっている。 10bit表示であれば上側と比較して下側が滑らかな変化となる。一方で 8bit表示であれば上側と下側で同じ見た目となる。

なお、それぞれのブロックは下に行くほど Lightness が高く、右に行くほど Chroma が高くなるよう調整した。 ただし、Gamma=2.4 の特性上 Lightness が高くなるほど 1CV あたりの輝度差は小さくなるので、再下段のパターンでは目視の識別は少々厳しいかもしれない。

zu1
図1. 作成した 10bit表示確認パターン(ダウンロードして 10bit 対応の環境でお試し下さい)

パターンが欲しい方は以下からダウンロードして下さい。再配布をしなければ自由に使って頂いて構いません。

drive.google.com

加えて効果確認として自宅の TV(SONY BRAVIA KJ-65X9500G)で表示した結果を図2, 図3 に示す。 図2 は 10bit 表示している場合の見え方、図3 は 8bit 表示の場合の見え方である(8bitとなるよう出力デバイスの設定を意図的に変更した)。 図2 と 図3 の差はごく僅かなので注意深く見比べて頂きたい。

f:id:takuver4:20210312172158p:plain f:id:takuver4:20210312172219p:plain
図2. 10bit 表示時のパターンの見え方 図3. 8bit 表示時のパターンの見え方

4. 詳細

4.1. 10bit 確認用パターンを自作した理由

10bit 表示の確認パターンは既に ELSAのサイト [1]で .psd ファイルが公開されており、 Photoshop を所有するユーザーは誰でも簡単に確認が可能である。

その一方で筆者は .png 形式のファイルも欲しいと考えていた。理由は DaVinci Resolve などの映像制作ソフトウェアでも簡単に扱うためである。 もちろん、自分で ELSA のパターンを .png に変換して使用すれば目的は達成できる。 しかし ELSA のサイトには .psd ファイルの利用規約が明記されておらず、勝手に変換して使用すると権利関係のトラブルに発展する可能性があった。

そこで勉強も兼ねてパターンを自作することにした。

4.2. 作成するパターンの基本的な考え方

作成する 10bit 表示確認パターンは以下の特性を持たせることにした。

  • 10bit 表示時はグラデーションにバンディングが見えない
  • 8bit 表示時はグラデーションにバンディングが見える

この特性を分かりやすく示したものを図4, 図5 に示す(図は理解しやすいように加工してあり厳密な描画ではない)。

f:id:takuver4:20210312180527p:plain f:id:takuver4:20210312180543p:plain
図4. 10bit 表示時に期待する見え方 図5. 8bit 表示時に期待する見え方

ここで1点問題がある。それはバンディングを目視確認可能なグラデーションを作るのは意外に難しい という点である。 例えば 図4 は実は 8bit精度で作成したパターンである。 しかしバンディングは全く見えない。何も考えずランダムに色(Code Value)を選んだ場合、バンディングが目視可能となる可能性は非常に低い。 8bit でバンディングが目視可能なパターンを作成するには、理論的な裏付けをした上で使用する Code Value を選ぶ必要がある。

4.3. CIE DE2000 を利用したバンディングの見えやすい Code Value の探索

そこで筆者は CIE DE2000 [2] を用いて 8bit でバンディングが目視可能なグラデーションの探索を行うことにした。

細かい説明する前に「8bit でバンディングが見えるグラデーション」がどのような条件で生じるかを整理しておく。 筆者の考える条件を以下に示す。

  • グラデーションでは Code Value が 1ずつ変化する
  • Code Value が 1変化した際の色差が JND(※2) を上回る

※2 Just-noticeable difference [3]

このような特徴を持つ場合、少々乱暴な表現になるが、以下の2ステップで目標を達成できると考える。

  1. 8bit の 全ての RGB値(約1600万通り)に対して Code Value が1 変化した場合の色差を計算する
  2. 色差の値が大きい Code Value を基準としてグラデーションを作成する

例えば探索を行った結果、(R, G, B) = (110, 88, 63) に対して Green を 1変化させた際の色差が大きい、という事が分かったとする。この場合、 グラデーションの Code Value を "(110, 88, 63), (110, 89, 63), (110, 90, 63), ..." と指定すればバンディングが目視可能となる可能性が高い(※3)。

ということで、8bit の RGB空間の 約1600万色に対して色差を計算する処理を行った。

探索は任意の RGB値に対して、Code Value が増加する方向に隣接する 7種類のRGB値と色差を計算することで行った。 文だと意味が分からないと思うので、具体的な例を以下に挙げる。

探索対象の RGB値 を (64, 64, 64) とする。この RGB値に Code Value が増加方向で隣接する RGB値は以下の7種類である。

表1. 増加方向で隣接する7種類の RGB値の例
No. RGB offset RGB Value
1 (+0, +0, +1) (64, 64, 65)
2 (+0, +1, +0) (64, 65, 64)
3 (+0, +1, +1) (64, 65, 65)
4 (+1, +0, +0) (65, 64, 64)
5 (+1, +0, +1) (65, 64, 65)
6 (+1, +1, +0) (65, 65, 64)
7 (+1, +1, +1) (65, 65, 65)

上記の例は RGB = (64, 64, 64) の場合だが、これを R: 0~254, G: 0~254, B: 0~254 の範囲で行うことで 8bit RGB空間で隣接する Code Value で色差が大きい場所を探索した。

計算した結果 255x255x255x7 = 約1億通りの色差のデータを得た。

※3 本当に厳密に検討するのであれば、環境光や表示デバイスのピーク輝度などを考慮して JND を超えるか否か判断が必要だと考えるが、 今回は色差が大きい Code Value を選ぶだけでお茶を濁した(ガバガバな判断方法でゴメンナサイ)。

4.4. 色差の計算結果から Code Value を選ぶ

続いて先ほどの色差の計算結果から条件を設けて Code Value を選択した。 なぜならば単純に色差の大きい Code Value を選ぶと 得られる Code Value が低すぎて(暗すぎて)一般的な室内環境での確認が困難だからである。

今回は以下の9つの条件を設けて Code Value を選択した。条件を表2. に示す。ここで h idx, v idx は図1 のパターンのブロックのインデックスを意味する。 また、L*, C* は CIELAB における Lightness, Chroma を意味している。

表2. グラデーションに使用する Code Value を選択する際の条件
h idx = 0 h idx = 1 h idx = 2
v idx = 0 L* > 20 & C* > 0 L* > 20 & C* > 10 L* > 20 & C* > 20
v idx = 1 L* > 35 & C* > 0 L* > 35 & C* > 10 L* > 35 & C* > 20
v idx = 2 L* > 50 & C* > 0 L* > 50 & C* > 10 L* > 50 & C* > 20

表2. の条件で色差が大きかった Code Value を基準に グラデーションを作成したものが図1である。

なお 表1. で説明した増加方向は Green単色のみ増加した場合が最も色差が大きかった(表1. の No.2)。 そのため図1のパターンは水平方向で色相が変化するパターンとなっている。

5. 感想

これで権利関係に怯えることなくSDR環境での 10bit表示の確認ができる。良かった…。

参考資料

[1] ELSA, "Quadro × 10bit for Photoshop - 株式会社 エルザ ジャパン", https://www.elsa-jp.co.jp/html/quadro/contents14_3.html

[2] コニカミノルタ, "新しい色差式(CIE DE2000)について", https://www.konicaminolta.jp/instruments/knowledge/color/section2/06.html

[3] Wikipedia(EN), "Just-noticeable difference", https://en.wikipedia.org/wiki/Just-noticeable_difference

AVIF を利用した静止画の HDR表示を試した

1. 背景

  • 以前から Windows OS 上で簡単に静止画の HDR を表示したいと考えていた
  • HDR の Metadata が付与された AVIF[1] 形式の静止画を用意すれば Google Chrome 上で静止画の HDR表示が可能との情報を得た[2]
  • 気になったので簡単に確認することにした

2. 目的

  • AVIF 形式の 静止画の HDR ファイルを作成する
  • HDR対応の表示デバイスGoogle Chrome を使って表示して意図通りになるか確認する

3. 結論

  • AVIF 形式で 静止画の HDRファイルを作成することに成功した
  • 作成した AVIFファイルが Google Chrome 上で HDRとして表示されることを確認した
    • その一方で、Chrome上で表示されるテストパターンの Code Value は想定よりも僅かに小さな値となることが分かった
    • この現象は Chrome以外のアプリケーションでも発生しており、OS または GPU 側の処理の問題だと考える(要追加調査)

作成した AVIF形式のテストパターンを図1(※1)に、テストパターンの表示特性を図2に示す。

※1 PC版 Google Chrome 以外だと表示されない可能性があります。ご了承下さい。

図1ではテストパターンが縮小表示されているため、気になる方は右クリックして「Open image in new tab」で表示して頂きたい。

zu1
図1. 作成した AVIF形式のテストパターン。HDRモニターで表示すれば HDRとして表示される。

f:id:takuver4:20210302223340p:plain:w540
図2. AVIF のテストパターンを Chrome で表示した場合の特性。理論値よりも少し値が小さくなる。

4. 理論

4.1. HDR の静止画ファイルに必要なメタデータについて

静止画ファイルを SDR ではなく ”HDR” としてアプリケーションに認識させるためには、静止画ファイルにメタデータを埋め込む必要がある。 現時点で広く使われているメタデータの一つに CICP(Coding-independent code points) がある[3]。

CICP とは静止画と動画に対する Colour Primaries や Transfer Characteristics などの特性を1byteの整数型で表現したものである。 CICP は符号化方式に依存しない情報であるため Coding-independent code points という名称になっている。

CICP は ITU-T H.273[3] の中で以下の6種類が定義されている。

f:id:takuver4:20210301230951p:plain
図3. H.273 で定義されている CICP

このうち SDR/HDR の判別を含めた "色に関する情報" として使用されるのは以下の3つである。

  • ColourPrimaries
  • TransferCharacteristics
  • MatrixCoefficients and VideoFullRangeFlag

さて、CICP の具体的な表記方法であるが、ColourPrimaries, TransferCharacteristics, MatrixCoefficients, VideoFullRangeFlag をハイフンで繋ぎ 1-1-1-0, 9-16-9-0 のようにする書き方がある[6]。本記事でもこの書き方を使用する。CICP の具体例を以下の表1 に示す。

表1. CICP の具体例
CICP Colour Primaries Transfer Characteristics Matrix Coefficients Video Full Range Flag
1-1-1-0 BT.709 BT.709 BT.709 Narrow Range
12-16-0-1 P3D65 SMPTE ST 2084 Identity Full Range
9-16-9-1 BT.2020 SMPTE ST 2084 BT.2020nc Narrow Range
9-18-9-1 BT.2020 HLG BT.2020nc Narrow Range

4.2. AVIF フォーマットを採用した理由

CICP を埋め込める静止画のフォーマットには HEIC[7] と AVIF がある(※2)。AVIF は Google Chrome でサポートされており Windows環境で簡単に取り扱うことができる。一方で HEIC は Google Chrome では非サポートである。 こうした背景もあり、本記事では AVIF にのみ注目し 静止画のHDR表示が行えるか検証することにした。

※2 筆者が知らないだけで他にも対応しているフォーマットがある可能性はある

5. AVIF の作成

今回は Alliance for Open Media の libavif[8] をビルドして作成される avifenc を使って AVIF を作成した。

5.1. avifenc のビルド

筆者の Dockerfile からビルド部分を引用したものを以下に示す。-DAVIF_BUILD_APPS=1 を指定すると avifenc, avifdec がビルドされる。

RUN mkdir -p /root/local/src \
    && cd /root/local/src \
    && git clone https://github.com/AOMediaCodec/libavif.git -b v0.8.4 libavif_aom \
    && cd /root/local/src/libavif_aom/ext \
    && $SHELL ./aom.cmd \
    && $SHELL ./libjpeg.cmd \
    && cd /root/local/src/libavif_aom/ \
    && mkdir build && cd build \
    && cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DAVIF_CODEC_AOM=ON -DAVIF_LOCAL_AOM=ON -DAVIF_LOCAL_JPEG=ON -DAVIF_BUILD_TESTS=1 -DAVIF_BUILD_APPS=1 \
    && ninja

5.2. 静止画 PNG ファイルから AVIF への変換

事前に作成しておいた PNG のテストパターン画像 を以下のコマンドで AVIF に変換した。 --cicp オプションで指定している 9/16/9ColourPrimaries, TransferCharacteristics, MatrixCoefficients に該当する。 今回は HDR10 ライクな静止画を作成したかったので 9-16-9 (BT.2020 - SMPTE ST 2084 - BT.2020nc) を指定した。 なお VideoFullRangeFlagavifenc では -r オプションで別途指定する必要がある。

avifenc ./png/SMPTE_ST2084_ITU-R_BT.2020_D65_1920x1080_rev04_type1.png -d 10 -y 444 --cicp 9/16/9 -r full --min 0 --max 0 ./avif/aom_10-bit_yuv444_cicp-9-16-9_full-range.avif

6. AVIF の表示確認

まず、作成した AVIF ファイルを BRAVIA KJ-65X9500G に表示して HDRとなっていることを目視で確認した。 次に、本当に意図取りの Code Value となっているかを確認するためにPCから出力している HDMI信号をキャプチャすることにした。

筆者は諸事情により elgato製の 4K60 S+ というキャプチャデバイスを所有している。 このデバイスHDR信号を受信可能だったため、このデバイスを使用して Code Value のキャプチャを行った。

キャプチャしたのは 図3 の黄色枠で示したグレーパッチの部分である。ここには 0, 16, 32, ..., 992, 1008, 1023 CV の合計65種類のパッチがある。 このパッチをキャプチャして想定する Code Value と一致するか確認した。

f:id:takuver4:20210308222110p:plain
図3. キャプチャに使用したパッチ(黄色枠の部分)

なお、キャプチャは H.265 で行ったため ProRes や DNxHR 形式でキャプチャできる業務用デバイスと比較すると若干精度が劣る。ご了承頂きたい(※3)。

※3 当該製品は精度確認用途のデバイスではないため、筆者の使い方が悪いだけである。メーカー側には何の非も無い。

こうして Code Value を確認した結果が冒頭の 図2 である(以下に再掲する)。

f:id:takuver4:20210302223340p:plain:w540
図2. AVIF のテストパターンを Chrome で表示した場合の特性。理論値よりも少し値が小さくなる。

理想値と比較すると若干のズレが生じている。しかし本症状は Windows10 標準の Movies & TV アプリや MPC-BE などでも観測されており、 AVIF とは関係の無い OS or GPU 側の処理が原因だと推測している。本件については現在調査中である。もし何らかの事実が判明した場合は本ブログで報告する。

最後に筆者のPC環境を記載しておく。今回の検証は以下のバージョンで行った。

項目 バージョン
OS Windows10 20H2
Browser Google Chrome Version 88.0.4324.190 (Official Build) (64-bit)
GPU GeForce GTX 1660 SUPER 461.72

7. 感想

購入してからずっと自宅で文鎮と化していた 4K60 S+ が役立つ時が遂に来た!本記事では全く紹介できなかったが 4K60 S+ はカスタムした EDID を設定することが可能である。 これにより Windows 10 が表示デバイスのスペックに応じた処理を行うことについても考察が可能となった。これは夢が広がる!!(本編とあまり関係のない感想でした)。

参考資料

[1] Alliance for Open Media, "AV1 Image File Format (AVIF)", https://aomediacodec.github.io/av1-avif/

[2] TANY_FMPMD, https://twitter.com/TANY_FMPMD/status/1362050202819915785

[3] Recommendation ITU-T H.273, "Coding-independent code points for video signal type identification", https://www.itu.int/rec/T-REC-H.273/en

[4] Wikipedia(EN), "AV1 Image File Format (AVIF)", https://en.wikipedia.org/wiki/AV1#AV1_Image_File_Format_(AVIF)

[5] ITU-T H-series Recommendations - Supplement 19, "Usage of video signal type code points", https://www.itu.int/rec/T-REC-H.Sup19/en

[6] INTERNATIONAL COLOR CONSORTIUM, "CICP Votable Proposal 2020-12-08.docx", http://color.org/groups/hdr/CICP_Votable_Proposal_2020-12-08.pdf

[7] Wikipedia(EN), "HEIC: HEVC in HEIF", https://en.wikipedia.org/wiki/High_Efficiency_Image_File_Format#HEIC:_HEVC_in_HEIF

[8] Alliance for Open Media, "libavif", https://github.com/AOMediaCodec/libavif

Google Chrome で動画を再生した際のカラーマネジメントに関する簡単な調査

1. 背景

2. おことわり

この記事は 2020年1月に筆者のPC環境で検証しました。 そのため、この記事を読んでいる方の環境で同じ結果が得られるかは不明です。ご了承ください。

筆者のPC環境は以下の通りです。

項目 バージョン 備考
OS Windows10 20H2 -
Browser Google Chrome Version 87.0.4280.141 (Official Build) (64-bit) -
GPU GeForce GTX 1660 SUPER 461.09 Adjust video color settings は "User the video player setting" を選択した
Monitor EIZO EV3237 - ICC Profile は OS に設定していない

3. 目的

  • Google Chrome で動画を再生した際のカラーマネジメントについて調査する
  • 具体的には以下の場合の動作を調査する
    • 動画のメタデータ(※1) の Color primaries のパラメータ (BT.709, P3D65, BT.2020) を変えた場合
    • 動画のメタデータ(※1) の Transfer characteristics (BT.709, sRGB, ST 2084 など) を変えた場合
    • Windows のカラマーマネジメント機能でモニターの ICC Profile を設定した場合

※1 カラーマネジメントに関係する動画のメタデータついては以下の記事を参照(なお、今回の調査も以下の記事と同じく H.264 の .mp4 ファイルで行った)

trev16.hatenablog.com

4. 結論

  • SDR動画では Color primaries に対するカラーマネジメントのみ行われる (図1, 図2)

    • Transfer characteristics に対するカラーマネジメントは行われない (図3)
    • 「Web コンテンツなので sRGB の Transfer characteristics に変換する」といった処理は実行されない
  • SDR動画ではモニターの ICC Profile は無視される

  • HDR動画 では Color primaries と Transfer characteristics(※2) に対するカラーマネジメントが行われる (図6)

  • HDR動画 ではモニターの ICC Profile を何らかの形で参照している (図7)
    • 細かい事は分かっていない。現時点では ICC Profile の設定値に依って HDR to SDR 変換の結果に差異が生じることのみ確認した
    • 本件については別記事で調査する予定(調査しない可能性も 40%くらいある)

※2 ただし HDR の Transfer characteristics は ST2084 しか確認していません。HLG は個人的に苦手で確認しませんでした。

f:id:takuver4:20210109135932p:plain f:id:takuver4:20210109140003p:plain
図1. Color primaries が "BT.2020" の動画を再生した様子。右の図2 と比較するとカラーマネジメントが働いていることが分かる 図2. 左の 図1 をメタデータなしで表示した様子

f:id:takuver4:20210109144123p:plain:w540
図3. Transfer characteristics を変化させた場合に、0-255 の Code Value 値に変化が生じるか確認した様子。Transfer characteristics を変化させても Chrome の動画表示に変化が無いことが分かる(グラフが滑らかではないのは、動画データが 16-235 CV の Limited Range であるため)。

f:id:takuver4:20210110133539p:plain f:id:takuver4:20210110133554p:plain
図4. モニターの ICC Profile を設定せずに静止画と動画が混在するページ[2] を見た例 図5. モニターの ICC Profile に ACES_AP0-Gamma3.5-D65 を設定して静止画と動画が混在するページ[2] を見た例。

f:id:takuver4:20210109152452p:plain f:id:takuver4:20210109152515p:plain
図6. HDRの動画を再生した様子。モニターの ICC Profile は設定していない 図7. HDRの動画を再生した様子。モニターの ICC Profile に ACES_AP0-Gamma3.5-D65 を設定した。左の図6 と比較すると表示に変化が起こっていることが分かる

5. 調査に仕様したデータ

5.1. 動画データ

今回の調査に使用した動画データは以下の記事で再生可能である。興味のある方は、自分の PC の Webブラウザでも再現するか確認して頂きたい。

trev16.hatenablog.com

加えて、動画データは上の記事の中で「右クリック」→「Save video as...」で保存可能である。DaVinci Resolve で「Bypass Color Management」オプションを有効にすればカラーマネジメント前のデータを参照することが可能である (図8)。動画の生データを確認したい方は DaVinci Resolve でこの手順を試して頂きたい。

f:id:takuver4:20210109161245p:plain
図8. 動画の 生データを "Bypass Color Management" オプションを使って確認する様子

5.2. ICC Profile

調査に使用した ACES_AP0-Gamma3.5-D65 の ICC Profile は リンク先 からダウンロード可能である。なお、このプロファイルは以下の記事で作成したものである。

trev16.hatenablog.com

6. 調査の詳細

ここから調査の詳細について書いていく。

6.1. 検証用のテストパターン

今回の検証用に 図9 に示すテストパターンを作成した。本検証の主目的は動画データでの 0-255 CV の観測であったため、このようなパターンとなった。 各ブロックは 64x64 px のサイズとし、エンコード時に余計なノイズが生じないようにした。

f:id:takuver4:20210110141019p:plain
図9. 作成したテストパターン
① 0-255 の Code Value 確認パターン(Transfer characteristics の値とは関係なく 0-255 の値)
② R, G, B, M, Y, C。(Color primaries の値とは無関係)
③ Color Checkr。Color primaries の値に合わせて色変換を行ってある

6.2. Transfer characteristics に関する検証

Transfer characteristics の値によって Google Chromeカラーマネジメントを行うか確認するため、6.1. で作成したテストパターンに対して、複数種類の Transfer characteristics のメタデータを付与して Code Value の変化を確認した。今回は BT.709, sRGB, Unknown, SMPTE ST 2084 の 4パターンを作成した。

各動画を Google Chrome で再生し、OSのスクリーンショット機能を利用して Code Value の変化を確認した。結果は結論の図4に示した通りである。Transfer characteristics は Google Chromeカラーマネジメントに何も影響を与えなかった。

参考として、動画のエンコード オプションを以下に示す。

# BT.709
ffmpeg -color_primaries bt709 -color_trc bt709 -colorspace bt709 -r 24 -i input_seq_%4d.png -c:v libx264 -movflags write_colr -pix_fmt yuv444p -qp 0 -color_primaries bt709 -color_trc bt709 -colorspace bt709 output_bt709.mp4 -y

# sRGB
ffmpeg -color_primaries bt709 -color_trc iec61966-2-1 -colorspace bt709 -r 24 -i input_seq_%4d.png -c:v libx264 -movflags write_colr -pix_fmt yuv444p -qp 0 -color_primaries bt709 -color_trc iec61966-2-1 -colorspace bt709 output_srgb.mp4 -y

# Unknown
ffmpeg -color_primaries bt709 -color_trc unknown -colorspace bt709 -r 24 -i input_seq_%4d.png -c:v libx264 -movflags write_colr -pix_fmt yuv444p -qp 0 -color_primaries bt709 -color_trc unknown -colorspace bt709 output_unknown.mp4 -y

# SMPTE ST 2084
ffmpeg -color_primaries bt709 -color_trc smpte2084 -colorspace bt709 -r 24 -i input_seq_%4d.png -c:v libx264 -movflags write_colr -pix_fmt yuv444p -qp 0 -color_primaries bt709 -color_trc smpte2084 -colorspace bt709 output_st2084.mp4 -y

6.3. Color primaries に関する検証

Color primaries の値によって Google Chromeカラーマネジメントを行うか確認するため、6.1. で作成したテストパターンに対して、複数種類の Color primaries のメタデータを付与して色の変化を確認した。今回は BT.709, P3D65, BT.2020 の 3パターンを作成した。また、 P3D65, BT.2020 に関しては HDR版も作成した。

各動画を Google Chrome で再生しカラーマネジメントが働くかを、目視およびOSのスクリーンショット機能で確認した。結果は結論の 図1, 図2 に示した通りである。Google Chrome は Color primaries の値に対してカラーマネジメントを行うことが分かった。

参考として、動画のエンコード オプションを以下に示す。

# BT.709
ffmpeg -color_primaries bt709 -color_trc bt709 -colorspace bt709 -r 24 -i input_seq_bt709_%4d.png -c:v libx264 -movflags write_colr -pix_fmt yuv420p -crf 18 -color_primaries bt709 -color_trc bt709 -colorspace bt709 output_bt709_cms.mp4 -y

# P3D65 (SDR)
ffmpeg -color_primaries smpte432 -color_trc bt709 -colorspace bt709 -r 24 -i input_seq_p3d65_%4d.png -c:v libx264 -movflags write_colr -pix_fmt yuv420p -crf 18 -color_primaries smpte432 -color_trc bt709 -colorspace bt709 output_p3d65_cms.mp4 -y

# BT.2020 (SDR)
ffmpeg -color_primaries bt2020 -color_trc bt709 -colorspace bt2020nc -r 24 -i input_seq_bt2020_%4d.png -c:v libx264 -movflags write_colr -pix_fmt yuv420p -crf 18 -color_primaries bt2020 -color_trc bt709 -colorspace bt2020nc output_bt2020_cms.mp4 -y

# P3D65 (HDR)
ffmpeg -color_primaries smpte432 -color_trc smpte2084 -colorspace bt709 -r 24 -i input_seq_p3d65_hdr_%4d.png -c:v libx264 -movflags write_colr -pix_fmt yuv420p -crf 18 -color_primaries smpte432 -color_trc smpte2084 -colorspace bt709 output_p3d65_hdr_cms.mp4 -y

# BT.2020 (HDR)
ffmpeg -color_primaries bt2020 -color_trc smpte2084 -colorspace bt2020nc -r 24 -i input_seq_bt2020_hdr_%4d.png -c:v libx264 -movflags write_colr -pix_fmt yuv420p -crf 18 -color_primaries bt2020 -color_trc smpte2084 -colorspace bt2020nc output_bt2020_hdr_cms.mp4 -y

6.4. ICC Profile に関する検証

OS に対して モニターの ICC Profile が設定されていた場合に Google ChromeICC Profile に応じたカラーマネジメントを行うか確認を行った。具体的には以下の2通りの状況を用意して、Google Chrome での見え方を確認した。

  • (a) モニターの ICC Profile を設定していない場合
  • (b) モニターの ICC Profile に ACES_AP0-Gamma3.5-D65 を設定した場合

ICC Profile は 図10 のようにして設定した。

f:id:takuver4:20210110223440p:plain
図10. ACES AP0, Gamam3.5, D65 の ICC Profile をモニターに設定した様子

結果は結論の 図4, 図5 に示した通りである。ICC Profile に対するカラーマネジメントは静止画にのみ行われ、動画には行われない事が分かった。

7. 感想

調査を通じて動画のカラーマネジメントに少しだけ詳しくなれた。 疑問点のいくつかを解消できたので時間をかけて調査を行って良かったと考えている。

引き続き以下の点について調査していきたい。

  • BT.2020 to BT.709 変換が働くことは分かったが、Chrome上の BT.709 で想定されている Gamma は 2.2, 2.4, sRGB のどれなのか?
  • Google ChromeHDR の .mp4 ファイルを再生した場合の HDR to SDR 変換の特性の調査
  • Google ChromeYouTubeHDR Live 配信を再生した場合の HDR to SDR 変換の特性の調査

加えて新たな疑問点も生じた。動画に対するカラーマネジメントが、Chrome のブラウザアプリ上で行われているのか、それとも Windows OS が持つ機能(Enhanced Video Renderer ?) の中で行われているのかが分からなかった。こちらも機会があれば調査したい。筆者は Chrome のドキュメント[3]を読んでも細かい所は何も分からなかった。

参考資料

[1] YouTube Official Blog, "Seeing is believing: Launching HDR for live streams", https://blog.youtube/news-and-events/seeing-believing-launching-hdr-live-streams/

[2] Nintendo, "Nintendo Switch Online", https://www.nintendo.com/switch/online-service/

[3] chromium.googlesource.com, "media/", https://chromium.googlesource.com/chromium/src/+/master/media/README.md