toruのブログ

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

【静止画編】Windows 11 で HDR コンテンツを「オリジナルのまま加工せずに」モニターへ出力する

0. 更新履歴

日付 内容
2024.07.30 公開
2024.07.31 ・図3 のキャプションの誤記修正
・3.5. に検証環境を追記

1. はじめに

本記事は前回の記事の続き(静止画編)である。

trev16.hatenablog.com

2. 目的

  • Windows 11 で静止画の HDRコンテンツをオリジナルのまま加工せずにモニターへ出力する方法をまとめる
  • 具体的には以下の手順で調査を行う
    • Windows で現実的に利用可能な HDR対応の静止画フォーマットについて調べる
    • HDR対応の静止画が各種アプリケーション上でどのように加工されるか調べる
    • ③ 加工を防ぐ方法をまとめる

3. まとめ

3.1. Windows で現実的に利用可能な HDR対応の静止画フォーマットについて

ISO HDR と呼ばれる規格をサポートした静止画のフォーマットの代表例として HEIF, AVIF, PNG, JPEG XL がある[1]。 筆者の独断と偏見で Windows での利便性を調査したところ以下となった。

フォーマット Chrome/Edge での表示 Lightroom からの Export Affinity Photo 2 からの Export
HEIC - - -
AVIF -
PNG -
JPEG XL - - *1

上記の結果から筆者は AVIF または PNGWindows での利用に適していると考える*2

なお本記事では、筆者の開発環境(Linux)で評価用のカラーパッチを作成できた AVIF を使用して検証を行った*3

3.2. 各種アプリケーションで AVIF を表示した際の挙動について

Windows 上のアプリケーションで AVIF の表示がどう加工されるか調べた。結果を以下に示す。

アプリケーション アプリケーションよって適用される加工の内容
Chrome/Edge ・SDR content brightness の設定に応じて明るさが変わる
・tone mapping が適用される
Lightroom ・SDR content brightness の設定に応じて明るさが変わる
・clipping が適用される
・高輝度領域の彩度が落ちる
Adobe Camra Raw ・SDR content brightness の設定に応じて明るさが変わる
・clipping が適用される
・高輝度領域の彩度が落ちる
Photoshop - (HDR表示は非対応)
Affinity Photo 2 - (AVIF は非対応)
Photos*4 - (AVIF の HDR表示は非対応)

SDR content brightness という設定値に応じて HDRコンテンツも明るさが変わる仕様となっていた 。 加えて Adobe製品では clipping と高輝度領域の彩度低下が起こっていた。

Chrome/Edge で明るさが変わってしまう様子を図1に、Lightroom で高輝度領域の彩度低下が起こる様子を図2に、Lightroom で clipping が適用される様子を図3 に示す。

図1. Chrome/Edge で表示した HDRコンテンツの明るさが変わってしまう様子 図2. LightroomHDRコンテンツを表示した際に高輝度領域の彩度が低下してしまう様子

図3. LightroomHDRコンテンツを表示した際に clipping が適用されてしまう様子。波形モニターを見ると 3300 nits くらいで clipping されている

3.3. 静止画の HDRコンテンツの加工を防ぐ方法

以下の 3条件を満たすことで、静止画の HDRコンテンツを殆ど加工せずにモニターへ出力することに成功した。

図4. モニターのスペックを偽装する MHC Profile を適用した様子 図5. SDR content brightness を 31 に設定した様子

なお「殆ど加工せずに」と表現した通り、ごく僅かに加工は行われてしまった。具体的には 204/203 = 1.0049 倍された*6。詳細は後述する。

3.4. 静止画の SDRの表示確認用に作成したページ

解析および効果確認用に以下の Webページを作成した*7

toru-ver4.github.io

3.5. 環境

検証に使用した環境を以下に記しておく。

項目 詳細
OS Windows 11 23H2 build 22631.3958
CPU AMD Ryzen 9 5950X
GPU RTX 3070 (Studio Driver - 560.70)
Microsoft Edge Version 127.0.2651.74
Google Chrome Version 127.0.6533.73
Lightroom Version 7.4.1
Adobe Camera RAW Version 16.4.0.1906
Affinity Photo 2 Version 2.5.3

4. 詳細

ここから先は冒頭のまとめに至るまでに筆者が調査した内容を述べていく。なお、以下の内容は Chrome/Edge について調査した内容となっている。 LightroomAdobe Camera Raw での細かい調査はしていない。ご容赦いただきたい*8

4.1. HDR表示において動画と静止画は処理が異なる

はじめに筆者が衝撃を受けたことを述べておく。それは同じ輝度を持つコンテンツであっても、動画と静止画では表示用の処理が異なるという点である。具体例を図6 に示す。

図6. ある条件下で静止画と動画とで表示が異なる様子。左側は表示内容、右側はResolveでの波形表示結果。

図6 の上部はテストパターンを静止画の AVIF で表示したもの、下部は YouTube の動画として表示したものである。双方とも色空間は Rec.2100-PQ であり輝度情報は等しい。 しかし、右側の波形モニター表示を見ると輝度が異なっていることが分かる。後述する適切な設定を行わなければ、このように表示輝度に差異が生まれる状況となっている。

4.2. 静止画の HDR表示の内部処理について

それでは Chrome/Edge を例に、静止画の HDR表示について筆者が理解した点を述べていく。

筆者は調査を経て、静止画の HDR表示を理解することは、アプリケーション内部の SDR/HDR 合成処理を理解することと同じだと実感した。 そうした背景もあり、ここでは以下の順序で説明を行っていく。

  • Windows 内部の合成用カラースペースについて
  • SDR reference white について
  • SDR コンテンツの合成処理について
  • HDR コンテンツの合成処理について
Windows 内部の合成用カラースペースについて

はじめに Windows 内部の合成用カラースペースである scRGB について簡単に説明をしておく*9

scRGB は、color primary は sRGB と同じでありつつも 1.0 を超える値や負の値をサポートすることで WCG や HDR をサポートした色空間である。 値は half float (FP16) の浮動小数点数で表現され、gamma は 1.0 の linear な空間となっている。

アプリケーションは SDRコンテンツと HDRコンテンツを scRGB にマッピングすることで合成処理を行う。

ここで重要なのは scRGB では (R, G, B) = (1.0, 1.0, 1.0) が 80 nits に紐づけられている点である[2]。 そのため scRGB では 100 nits や 1000 nits のパッチは (1.25, 1.25, 1.25) や (12.5, 12.5, 12.5) という値で表現される。

一般的には (R, G, B) = (1.0, 1.0, 1.0) は 100 nits を意味することが多いと思うので、ここは混乱を生じやすいポイントである。 こうした背景があり、以後に登場する数式には「80」という数字が登場する。注意して頂きたい。

SDR reference white について

続いて内部処理で重要な役割を果たす SDR reference white というパラメータについて説明する。

これは名前の通り「SDR の基準の白」の明るさを意味するパラメータであり、Windows では scRGB での明るさ調整に使われる。

Windows では System -> Display -> HDR -> SDR content brightness から設定できる(図7)。

図7. SDR content brightness の設定画面

SDR content brightness のスライダーは 0~100 まであり、スライダーを右に動かすと Windows 内部の SDR reference white の値が増加し、 画面が明るくなる仕様となっている。

SDR content brightness と SDR reference white の関係性を以下に示す。SDR reference white はスライダーの値に応じて 80~480 nits まで変化する。

SDR content brightness SDR reference white
0 80 nits
1 84 nits
5 100 nits
31 204 nits
100 480 nits

表から分かるように 1目盛りで 4 nits の変化が生じる。なお、上記の仕様はドキュメントが見つからなかったので自力で調べた。 詳細は本記事の「付録1」を参照。

SDR コンテンツの合成処理について

続いて SDRコンテンツの scRGB への合成処理について説明する。

これは非常にシンプルであり、SDRコンテンツは Windows のドキュメント 通りに SDR reference white の値に応じてスケールしてから scRGB に合成される[3]。

合成前の SDRコンテンツの輝度を  L_S、合成語の輝度を  L_S、SDR reference white を  W_S とすると、 L_S は以下の数式で計算できる。


 \displaystyle
\begin{aligned}
L'_S &= \frac{L_S}{80} \cdot W_S
\end{aligned}
\tag{1}


なお、  L_S の範囲は sRGB準拠で 0~80 nits である。

HDR コンテンツの輝度調整

本命の HDRコンテンツの scRGB への合成処理について説明する。 筆者がテストパターンを使い検証を繰り返した結果、次の事がわかった。

合成前の HDRコンテンツの絶対輝度を  L_H、合成後の絶対輝度を  L'_H、SDR reference white を  W_S とすると、  L'_H は以下の式で決まる。


 \displaystyle
\begin{aligned}
L'_H &= 
  \begin{cases}
    \displaystyle \frac{L_H}{203} \cdot W_S & \text{if $L_H \leq 203 $} \\
    \displaystyle f_{t}\left(\frac{L_H}{203} \cdot W_S \right) & \text{if $L_H > 203$}
  \end{cases}
\end{aligned}
\tag{2}


ここで  f_t(x) は tone mapping を行う関数を意味する。なお、具体的な数式は分からなかった。

ここでポイントは 3つある。

  • ① 203 という数値で正規化が行われる
  • ② 絶対輝度の HDRコンテンツも SDR reference white の影響を受ける
  • ③ SDR reference white の値が大きくなると tone mapping が適用される

まず①について述べる。式から分かるように HDRコンテンツは 203 nits に対して正規化が行われる*10。 (1)式と(2)式を見比べると分かる通り、この処理で「SDRコンテンツの白輝度」と「HDRコンテンツの 203 nits」は同じ値となる。

具体例を WindowsHDRスクリーンショット機能*11を使って示す*12。なお、HDRスクリーンショット機能の詳細については本記事の「付録2」を参照して頂きたい。

さて、以下の図8、図9 は筆者が作成した 検証用ページ のカラーパッチの HDRスクリーンショットである(SDR contents brightness 設定は 0)。Nuke で開き Linear値を確認している様子している。

これを見ると HDRコンテンツ(図8)も SDRコンテンツ(図9)も共に (1.0, 1.0, 1.0) となっていることがわかる。

図8. 203 nits の AVIF パッチの値 図9. 背景の SDR の白色の値

続いて②について述べる。これは SDR reference white を変えた際の挙動を実際に見てもらうのが早い。 SDR reference white を 80 nits -> 100 nits に変えて同じように HDRスクリーンショットを確認した結果を図10、図11 に示す。

図10. 図8 の SDR reference white を 100 nits に変えたもの 図11. 図9 の SDR reference white を 100 nits に変えたもの

図10、図11 を見ればわかるように、SDR reference white が変わると SDRコンテンツの明るさだけでなく HDRコンテンツの明るさも変わることが分かる。

最後に③について述べる。SDR reference white は内部的に 480 nits まで設定可能だが、単純に SDR reference white でスケールした場合、SDR reference white が 203 nits を超えると高輝度領域に白飛びが生じてしまう。これを防ぐために tone mapping が適用される。筆者が解析した様子を図12 に示す。なお、具体的にどのような数式で tone mapping を行っているかは分からなかった。

図12. SDR reference white と表示輝度の関係。SDR reference white が大きくなると tone mapping が適用される

以上の観察結果から筆者は HDRコンテンツは (2)式に従って scRGB に合成されると考えている。なお、厳密には Rec.2020色域から sRGB色域への変換も行われるのだが、本記事ではその辺りの詳細は割愛する。

4.3. 静止画の HDR表示でアプリケーション側の加工を防ぐ方法について

先ほどの (2)式 および 図12 を見れば分かると思うが、SDR reference white を 203 nits に設定すれば良い。 しかし、残念ながら SDR reference white は 4 nits 刻みでしか設定できない。そのため 204 nits に設定するのが加工を最小限に抑える方法となる。

SDR reference white を 204 nits にするには SDR content brightness を 31 に設定すれば良い。

ということで冒頭のまとめで述べた通り、アプリケーション側の加工を防ぐには以下の 3条件を満たせば良い。

5. 感想

HDR表示は複雑すぎるうぅぅぅぅぅぅぅぅぅぅぅぅぅぅぅぅぅぅぅぅぅぅ!!!

その一方で、必死に調査したかいもあって Windows の内部処理については だいぶ理解が深まった。 この調子で次は DaVinci Resolve 19 の HDR表示について調査を行いたい。

付録1. SDR content brightness と SDR reference white の関係

SDR content brightness と SDR reference white の関係の詳細についてはドキュメントが見つからなかったため、筆者は自力で確認を行った。

Windows のドキュメント を参照するとデスクトップアプリケーションの場合、SDR reference white は DISPLAYCONFIG_SDR_WHITE_LEVEL 構造体 を参照することで確認できる。

そこで筆者は WindowsGUI から SDR content brightness を変化させつつ、DISPLAYCONFIG_SDR_WHITE_LEVEL の値を確認した。 ソースコードリンク先 を参照して頂きたい*14

こうして SDR content brightness と SDR reference white の関係を導きだした。筆者としては「30.75」という目盛りを用意して SDR reference white が 203 nits を選択可能にして欲しかった。

付録2. HDRスクリーンショットの撮影と Nuke/DaVinci Resolve で開くための OpenEXR 変換について

WindowsHDR が有効になっているとき、通常の手法を使ってスクリーンショットを撮影すると、得られるデータは SDR となってしまい HDRスクリーンショットを撮影することができない。代替として Win+Alt+PrtScn キーを押下することで選択しているウィンドウの HDRスクリーンショットを撮影することができる。

なお、この HDRスクリーンショットには注意点が 2点ある。

  • ① 値は scRGB であり (1.0, 1.0, 1.0) は 80 nits に相当する
  • ② ファイル形式が JPEG XR であり対応アプリケーションが少ない*15

①は気をつけるだけで良いが、②は筆者としてはかなり面倒であった。

調査したところ ②については Python にて imagecodecscolour-science を使えば OpenEXR へと変換できることがわかった。そのため筆者は以下のようなコードを書いて JPEGXR to OpenEXR 変換をしてから画像解析を行った。

from imagecodecs import JPEGXR, imread
from colour.io import write_image  # OpenImageIO も必要なので別途入れて下さい

def jpeg_xr_to_exr(src_fname="./Windows_HDR_Capture/600.jxr"):
    if not JPEGXR.available:
        print("JPEG XR is not supported")
        return

    dst_fname = src_fname.replace(".jxr", ".exr")
    image = imread(src_fname)
    write_image(image=image, path=dst_fname)

参考資料

[1] Apple, "WWDC24: Use HDR for dynamic image experiences in your app", https://youtu.be/5jP4j9d71i0?si=Ooso6INKL2_6DdZf&t=537
[2] Microsoft, "Use FP16 pixel format and scRGB color space", https://learn.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range#option-1-use-fp16-pixel-format-and-scrgb-color-space
[3] Microsoft, "Match your app's reference white to the OS SDR reference white level", https://learn.microsoft.com/en-us/windows/win32/direct3darticles/high-dynamic-range#match-your-apps-reference-white-to-the-os-sdr-reference-white-level

*1:HDRフォーマットでのエクスポートは非サポート

*2:あくまでも 2024年7月時点での話。今後はサポートするアプリケーションが増えて状況は変わっていくと推測する

*3:PNGHDR用の CICPタグを埋め込んだカラーパッチの作成に失敗した…。正確には CICP の埋め込みには成功したのだが Chrome/Edge で HDRとして認識されなかった。

*4:Windows標準アプリ

*5:モニターのスペックを、ピーク輝度: 10000 nits、Color primaries: BT.2020 に偽装するための ICC プロファイル

*6:加えてごく僅かに tone mapping も適用されていると推測する

*7:筆者に Webページを書くスキルはないので ChatGPT にお願いして HTML を書いてもらった

*8:軽く確認した感じだと概ね Chrome/Edge と似た挙動のようである

*9:なお、本記事を読んでいる方は「scRGB は Windows内部の話であって、Chrome/Edge などの各種アプリケーションの内部処理は別の色空間で行われるのでは?」と疑問を持つかもしれない。確かにその通りなのだが、説明が複雑になるため本記事では Chrome/Edge は内部で Windows と同じ scRGB を使って合成している、という前提で説明をしていく

*10:203 という数値は ITU-R BT.2408 の HDR Reference White から来ていると推測する

*11:HDRスクリーンショット機能」という名称は筆者が勝手に読んでいる名称である。公式の名称が見つからなかったので本記事ではこの呼び方をする

*12:本質的にはこの検証もPCからの出力信号をキャプチャして確認すべきなのだが、203 という数値が本当に正しいかを確認するには HDMI のキャプチャだと誤差があり難しかったので変わりに HDRスクリーンショットを使った

*13:モニターのスペックを、ピーク輝度: 10000 nits、Color primaries: BT.2020 に偽装するための ICC プロファイル

*14:Chromium のコードを参考に ChatGPT 先生に作ってもらいました

*15:筆者がよく使う Nuke や DaVinci Resolve は対応していない