toruのブログ

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

Windows 11 にて ICC Profile に MHC2 タグを埋め込んで HDR信号の出力を変化させてみた

背景

  • 筆者は自宅で使っている OLEDモニター (AW3225QF) の表示特性に不満を持っていた(前回の記事で触れた[1])
    • HDR Peak 1000 モードでは APL の増加に伴う輝度低下が非常に大きい
    • 一方で DisplayHDR True Black モードでは高輝度領域の白飛びが激しい
  • 調べ物をしていたところ、Windows ではモニター用の ICC Profile に MHC2タグを追加することで HDR信号の出力を制御できることが分かった
  • これにより筆者の不満点をある程度解消できる可能性が出てきた
  • ということで実際に試してみた

目的

  • モニター用の ICC Profile に MHC2タグを追加して HDR信号の出力を変化させ、筆者の不満点の解消を試みる

結論

  • MHC2 タグの仕様を理解した
    • OS の出力信号に対して任意の 3x3 Matrix と 1DLUT 適用することができる *1
    • SMPTE ST 2086 のメタデータを含めることができる(詳細は本記事では省略する*2
    • MHC2タグが含まれる ICC Profile は MHC ICC Profile または MHC Profile と呼ばれる
    • MHC は Microsoft Hardware Calibration の略称である
  • MHC2 タグを使い Windows が出力する HDR信号を任意の値に変換することに成功した
    • OLEDモニターの DisplayHDR True Black モードとの組み合わせにより「白飛びの軽減」と「 APL増加にともなう輝度低下の軽減」を実現した
  • 作成した MHC Profile を参考データとして以下に添付する *3

drive.google.com

  • ただし、現状では以下の問題があるため実運用には向いていない

    • Windows のログイン直後は MHC2 タグの内容が反映されない(ログイン後にプロファイルの再設定が必要)
    • Photoshop 2024 にて SDR 画像の色ズレが発生する *4
  • 確認に使用したソフトウェアのバージョンは以下の通り。

Software Version
Windows Windows 11 Pro 23H2 Build 22631.3374
Photoshop 25.6.0
Affinity Photo 2.4.1

詳細

筆者が所有している OLEDモニター(AW3225QF)の不満点

冒頭でも述べた通り、筆者は AW3225QF に対して以下の不満点を持っている。

  • HDR Peak 1000 モードでは APL*5 の増加に伴い急激な輝度低下が発生する(図1 参照)
  • DisplayHDR True Black モードでは高輝度領域の白飛びが激しくディテールが消えてしまう(図2 参照)
図1. 急激な輝度低下が発生している様子
XDR Display は比較用の表示。撮影は同一条件で実施
図2. 高輝度領域が白飛びする様子
XDR Display は比較用の表示。撮影は同一条件で実施

輝度低下および白飛びが発生するのは AW3225QF が特殊な表示特性を有しているからである。 以前に筆者が測定したデータを以下に再掲する[2]。

図3. HDR Peak 1000 の測定結果 図4. DisplayHDR True Black の測定結果

図3 と図4 から以下のことが分かる。

  • 図3: HDR Peak 1000 では APL の増加に伴い急激に輝度が低下する
  • 図4: DisplayHDR True Black では 300 nits 以降のデータに強めのトーンマッピングが適用され白飛びしやすい

筆者はこの問題への対処方法の 1つとして「DisplayHDR True Black で表示しつつ、入力信号の輝度を半分に低下させる」手法を試したいと考えていた。そうすることで以下の効果が得られると考えたからである。

  • 画面全体は暗くなるものの、600 nits 程度までのデータに対して強いトーンマッピングの適用を避けられる
  • HDR Peak 1000 で観測した「APL に応じた急激な輝度変化」を避けられる

ということで、後述の MHC2 タグを使って輝度を下げることを試した。

ICC Profile の MHC2 タグについて

Windows 10 (20H1) 以降、Windows にはモニターのキャリブレーションの仕組みが導入されている[3]。 図5 の 2b, 3b にある通り Windows の出力信号に対して 3x3 の Matrix と 1DLUT を適用することができる。

図5. Window に用意されているカラーパイプライン。緑色の部分がユーザー開放されている箇所。参考資料[3] より引用

3x3 の Matrix と 1DLUT はモニターの ICC Profile に MHC2 タグを埋め込むことで有効化できる。

MHC2 タグの詳細は以下の通り[3]。なお、MHC2 タグは Windows 独自のタグであり ICC の仕様書 には存在しないので注意して頂きたい。

"MHC2" private tag definition
Byte Position Field Length (bytes) Content Data type
0 to 3 4 'MHC2' (4D484332h) Type Signature MHC2Type
4 to 7 4 Offset to beginning of tag data element uint32Number
8 to 13 4 Size of tag data element uint32Number
MHC2Type structure definition
Byte Position Field Length (bytes) Content Data type
0 to 3 4 'MHC2' (4D484332h) Type Signature
4 to 7 4 Reserved, set to 0
8 to 11 4 Number of 1DLUT entries (4096 or less) ※a
OPTIONAL: 0 = Identity Transform
uint32Number
12 to 15 4 ST.2086 min luminance in nits S15Fixed16Number
16 to 19 4 ST.2086 peak luminance in nits S15Fixed16Number
20 to 23 4 Offset in bytes to matrix ※b
OPTIONAL: 0 = Identity Transform
uint32Number
24 to 27 4 Offset in bytes to red 1DLUT ※b uint32Number
28 to 31 4 Offset in bytes to green 1DLUT ※b uint32Number
32 to 35 4 Offset in bytes to blue 1DLUT ※b uint32Number

※a OS はハードウェアがサポートするエントリの数にデータを補間する
※b オフセットは、ファイルの始まりからではなく、MHC2 タグの開始位置からのオフセットである

Matrix definition
Byte Position Field Length (bytes) Content Data type
0 to 23 24 3x4 XYZ to XYZ adjustment matrix stored in row major order, column 4 is ignored ※c ※d s15Fixed16Number

※c Matrix は 3x3 なのだが MHC2 タグのデータ上は 3x4 として定義する必要がある(最後の1要素は 0 で良い)
※d 例えば何も変換を行わない単位行列を宣言する場合は [0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00010000, 0x00000000] となる(S15Fixed16Number にて記述)

1DLUT definition
Byte Position Field Length (bytes) Content Data type
0 to 3 4 'sf32' (73663332h) Type Signature
4 to 7 4 Reserved, set to 0
8 to end Variable (0 to 16384) Calibration LUT values normalized to [0.0, 1.0] ※e s15Fixed16Number

※e この 1DLUT は Non-Linear 空間に対して適用される。Linear 空間ではないので注意が必要である

MHC2タグ用のデータ作成 (1DLUT)

タグの仕様を理解したところで筆者は MHC2タグ用のデータ作成を行った。今回は輝度低下が目的であったため図5 (3b) の 1DLUT の値のみ変えることにして、図5 (2b) の Matrix にはデータ変換を行わない単位行列を設定することにした。

図5 (3a) を見ると分かる通り、1DLUT が適用される空間は Linear 空間ではなく Non-Linear 空間である。具体的には SMPTE ST 2084 の EOTF^-1 が適用された後の空間である。 そのため 1DLUT は以下の計算式で作成した。

  •  E'' = F^{-1}\left(F(E') \times g \right)
    •  E': 図5 (3a) の出力信号
    •  E'': 1DLUT 適用後の信号
    •  F: SMPTE ST 2084 EOTF
    •  g: 輝度の低下率。今回は 0.5 とする

少し分かりづらいかもしれないので、3ステップに分けて処理内容を書いておく。

  •  E' に対して SMPTE ST 2084 EOTF を適用して Linear化
  • ② Linear空間でゲイン  g を適用
  • ③ SMPTE ST 2084 EOTF^-1 を適用して Non-Linear化

実際に 1DLUT を計算した様子を以下に示す。

from colour.models import eotf_ST2084, eotf_inverse_ST2084
import numpy as np
import matplotlib.pylab as plt

num_of_sample = 256
gain = 0.5
step1 = np.linspace(0, 1, num_of_sample)
step2 = eotf_ST2084(step1) * gain
step3 = eotf_inverse_ST2084(step2)

plt.plot(step1, step3)
plt.grid(True)  # Enable grid
plt.xlim(0.0, 1.0)  # Set x-axis range
plt.ylim(0.0, 1.0)  # Set y-axis range
plt.show()

図6. 1DLUT をプロットした様子

MHC2タグを埋め込んだ ICC Profile の作成

データができたので、あとは仕様通りに ICC Profile にデータを埋め込めば良い。 筆者は過去に ICC Profile を作った経験があったので、今回もその環境を流用して MHC2タグの埋め込みを行った。

なお、SMPTE ST 2086 のメタデータは以下の値を設定した*6

Item Value
Peak luminance (nits) 10000
Max full frame luminance (nits) 10000
Min luminance (nits) 0.001
RGB color primaries (xy coordinates) BT.2020 の値
White point (xy coordinates) いつもの D50

trev16.hatenablog.com

MHC2タグを埋め込んだ ICC Profile の確認

作成した MHC2タグは Microsoft独自のタグであり、ICC が提供する公式のツールではデータの正当性を確認できない。 やむを得ず筆者はバイナリエディタを使って目視確認することにした。

1DLUT のエントリ数を 8、輝度低下のゲインを 0.5 とした際の ICC Profile についてバイナリエディタで確認を行った様子を以下に示す*7

図番号と詳細
図7
①MHC2のデータ開始アドレスは 0x0260
図8
②1DLUTのエントリ数は 8

③Matrix の開始位置は 0x0260+0x0024=0x0284

④1DLUT (R) の開始位置は 0x0260+0x0054=0x02B4

⑤1DLUT (G) の開始位置は 0x0260+0x007C=0x02DC

⑥1DLUT (B) の開始位置は 0x0260+0x00A4=0x0304
図9
⑦3x4 Matrix データ (色変換を行わないスルー設定)
図10
⑧1DLUTデータ。それっぽい単調増加データとなっている

図7~図10 の確認を経て、筆者は正しく ICC Profile が作成できたと判断した。

Windows 上で ICC Profile の適用

Color Management のダイアログボックスを表示後、「Add...」ボタンから作成したプロファイルを登録し、その後に「Set as Default Profile」で Default のプロファイルに設定すれば良い。gain=0.5 のプロファイルを設定している様子を図11 に示す。

図11. 作成した ICC Profile を Windows に適用している様子

実コンテンツを使った効果確認

Netflix が評価用に無償公開している Sol Levante を使って確認した。

opencontent.netflix.com

主観評価で大変申し訳ないのだが確認結果は以下の通りである。

  • 筆者は MHC2 タグで輝度を半分に落とすことで以下の問題を改善できたと知覚した
    • APL の増加に伴う急激な輝度低下
    • 高輝度領域の白飛び
  • よって目的を達成できたと判断した

なお、HDR動画の再生プレイヤーには MPC-BE を使用した。また MPC-BE の Video renderer には MPC Video Renderer を使用した。

現状の問題点

目的を達成できた一方で、冒頭でも述べた通り現状では少なくとも 2点の問題がある。

  • Windows のログイン直後は MHC2 タグの内容が反映されない
    • ログイン後に図11 のダイアログを表示してプロファイルの再設定が必要
  • Photoshop 2024 にて SDR 画像の色ズレが発生する
    • Photoshop と Affinity Photo 2 の比較を図12、図13 に示す
    • なぜこのような結果になるかは不明(筆者の作成したプロファイルに問題がある可能性もある)
図12. Photoshop で画像を開いた様子 図13. Affinity Photo 2 で画像を開いた様子

こういった状況のため、一般的なユーザーが使うのは少し難しいと考えている。 一方で自分のような人間が趣味で使う分には問題ないと考える。

感想

MHC2タグのデータを色々と変えて信号の変化を観察するのは面白かった。 しかし、一般的なユーザーが使いこなすのは相当に困難だと感じている。

今回の記事では述べなかったが Windows OS を経由した HDRの表示は「OS側の色変換」「アプリの色変換」「モニターの色変換」が複雑に絡み合うため挙動を理解するのが大変難しい。とても他の人にはオススメできない。

Windows がこんな状況なので、やはり現時点では iPhone, iPad, MacBook Pro が自分の中でのオススメ製品になってしまう。 うーん、何とかこの複雑な状況を整理してあらゆるユーザーが安心して HDR表示できる環境を整えたい…。

参考資料

[1] toruのブログ, "AW3225QF を使い FINAL FANTASY VII REBIRTH をプレイする際に生じる HDR表示のトレードオフについて", https://trev16.hatenablog.com/entry/2024/03/17/222252
[2] toruのブログ, "DaVinci Resolve と Argyll CMS を使ってゲーミングモニター (AW3225QF) の特性を測ってみた", https://trev16.hatenablog.com/entry/2024/02/28/215831
[3] Microsoft Learn, "Windows hardware display color calibration pipeline", https://learn.microsoft.com/en-us/windows/win32/wcs/display-calibration-mhc

*1:後ほど軽く述べるがモニターのキャリブレーションを行うための仕組みである

*2:次の記事でアプリケーションのトーンマッピングの話と絡めて詳しく解説するかも

*3:筆者は MHC2_10000-10000-nits_gain-0.50.icm を日常的に使っている

*4:ちなみに Affinity Photo 2 は問題なかった

*5:APL は Average Picture Level の略語

*6:ピーク輝度を 10000 nits としたのは意図しないトーンマッピングの適用を避けるためである。詳細は次の記事で述べる…かもしれない

*7:ICC Profile は Big Endian である。そのため普段は Little Endian で暮らしている人は注意が必要である