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/