1. 背景
- 最近、 Color Space [1] という文字を目にする機会が増えた
- 流石に触れておかないとマズいという危機感を覚えたので軽く調べることにした
2. 目的
- Color Space についての理解を少し深める
- そのために BT.2100 (ST 2084) の Color Volume を でプロットしてみる
3. 結論
Color Space について以下を行った。
- CIEXYZ to 変換を実装した
- 平面をプロットした(動画1)※1
- 平面をプロットした(動画2)※1
- 最大輝度が制限された表示デバイス(100 nits, 1000 nits, 4000 nits, 10000 nits)の 平面のプロットをお試しでやってみた(動画3)※1
※1 動画1~動画3 は HDR/WCG のデータであり sRGB モニターで表示すると色のズレが非常に大きい。そのため自作の HDR to SDR 変換をプロット結果に後処理として適用したものも乗せてある。動画1,動画2 は右側のプロットに、動画3 は全てのプロットに HDR to SDR 変換が後処理として適用してある。
動画1. 平面のプロット |
動画2. 平面のプロット |
動画3. 最大輝度が 100 nits, 1000 nits, 4000 nits, 10000 nits の場合の 平面のプロット |
4. 理論
4.1. Color Space の概要
Color Space は以下の特徴をもった均等色空間である。
4.2. Color Space への変換
CIEXYZ を に変換する手順は以下の通り(論文のスクリーンショットを転載した)。
このうち、式(8), 式(10), 式(12) について以下で個人的なメモを残す。
4.2.1. 式(8) に関するメモ
式(8) の の計算は blue 領域で chroma が増大するにつれて hue がシフトしてしまう症状を抑えるための変換である。 式(8) の の計算は の計算の影響で green-blue の uniformity が悪化する問題を改善するための変換である。
それぞれの計算で色がどのように変化するかを確認するため、BT.2020色域, L* = 50 の ab平面に対して しか適用しない場合と , の両方を適用した場合の結果をそれぞれプロットした。 結果を 図1、図2 に示す。
図1 より blue の chroma が大きくなる箇所で、hue が green の方向に変化していることが分かる。 図2 については…一応プロットはしたものの上手く考察ができなかったのでコメントは控える(残念無念)。
4.2.1. 式(10) に関するメモ
式(10) は ITU-R BT.2100 の PQ OETF の計算式 [3] とほぼ同じである。ただし、式(10) の パラメータ は PQ OETF のパラメータ とは異なるため注意が必要である。
- BT.2100 PQ OETF の パラメータ
- 式(10) の パラメータ
ITU-R BT.2100 PQ OETF と 式(10) を比較した結果を図3 に示す。
4.2.1. 式(12) に関するメモ
と との関係は以下の通り。 では だが、参考までに別のパラメータを適用した場合の例もプロットした。この計算によりHDRの領域を含めた明度が知覚にマッチするようになる。
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. 平面と 平面のプロット
実装したソースコードを使って 平面と をプロットしてみた(冒頭の結論で示したものと同じ内容)。
動画1. 平面のプロット |
動画2. 平面のプロット |
動画3. 最大輝度が 100 nits, 1000 nits, 4000 nits, 10000 nits の場合の 平面のプロット |
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