27. HDRと高度な画像処理

第15章では、検定のシラバスの画像処理を学びました。ヒストグラム、トーンカーブ、畳み込み、バイラテラルフィルタ、ピラミッド、そして大域的なトーンマッピング演算子を1つ、ReinhardのL/(1+L)です。
其の章は1つの約束で終わりました:「局所的な演算子は目の様に領域毎に適応します(第2部)」。
第16章では、露出を変えた写真をHDR画像に合成し、逆畳み込みが何故脆いかを見て、フラッシュ/ノーフラッシュ撮影にジョイントバイラテラルフィルタを使いました。
此の章で其の約束を果たし、更に先へ進みます。

レンダラーから画面までの光を追います:シーン参照とディスプレイ参照の値、本物のカメラの数値を使った露出、ゲームで使われるフィルミックなカーブ、局所的なトーンマッピング、そしてHDRの伝達関数PQとHLGです。
其の後、高度なフィルタリングの道具箱(FFTと畳み込み定理、ウィーナーデコンボリューション、バイラテラルグリッド、ガイデッドフィルタ、非局所平均)を開き、最後にオプティカルフロー、JPEGのDCT、そしてGPUが直接読むブロック形式を扱います。

此れは第2部の内容でシラバスを超えますが、殆ど全てが第15章のシラバス項目、画素ごとの濃淡変換と色変換と領域に基づく画像変換(空間フィルタリング)を深める物です。更に第3章の色空間とガンマ、第2章の圧縮も深めます。どの項目に繋がるかは検定のポイントで述べます。
学習によるアップスケーラーとニューラルデノイザーは第29章の担当です。
テスト画像は全てプログラムで生成し、結果は全て本文の式で計算しています。

27.1 HDRパイプラインの全体像

シーン参照とディスプレイ参照

レンダラーは放射輝度を計算し、カメラのセンサーは光子を数えます。どちらもシーンの光に比例する数値を作ります。
太陽は影より百万倍も明るい事があるので、此れらの数値は0から「幾らでも」の範囲を取ります。
此の様な値はシーン参照です。画面ではなく、シーンを表しています。
画面が出せるのは黒から其のピークまでの光だけです。普通のモニターは約100〜300 cd/m²(22.1節の輝度の単位で、nit(ニト)とも呼びます)、HDRテレビは執筆時点で1,000 cd/m²以上です。
ディスプレイが出すべき光を表す値はディスプレイ参照です。
其の間にある物全てが表示変換で、ダイナミックレンジが17段のシーンを10段の装置でどう見せるかを決める、パイプラインの部分です。

HDRパイプライン:描画/撮影、露出、グレーディング、トーンマッピング、符号化、ディスプレイ。最初の3つはシーン参照の値、最後の3つはディスプレイ参照の値を扱う

段階を順に並べます:

  1. リニアなシーンの光:レンダラー(浮動小数点のフレームバッファ)又はカメラ(RAW、又は16.2節の様なHDR合成)から。
  2. 露出:全ての画素に同じ掛け算を1回。シーンの範囲のどの部分を画面の真ん中に置くかを選びます。
  3. グレーディング:色補正と芸術的な「ルック」。まだシーンの値の上で行います。
  4. トーンマッピング:範囲を圧縮するカーブ又は演算子。此処でシーン参照がディスプレイ参照になります。
  5. 符号化:SDRには3.7節のsRGBのカーブ、HDRにはPQ又はHLG(27.4節)。ディスプレイが其れを光に戻します。

露出は何も圧縮しません。シーンをカーブに沿って滑らせるだけです。情報を手放すのはトーンマッピングです。

段数で考える露出

2.3節ではF値Nとシャッター時間tから露出値を定義し、第15章では露出を2EVの掛け算として使いました。
物理ベースのレンダラーは此の2つを繋げ、アーティストが本物の単位(ルクスやカンデラ)でシーンを照らし、本物のカメラ設定で「撮影」出来る様にします。
先ず感度S(ISO値)を組み込み、ISO 100での露出値を求めます:

EV100=log2N2t−log2S100

次に、センサーを丁度満たす輝度が必要です。
ISO 12232の飽和に基づく感度によれば、センサーは焦点面の露光量78/Sルクス秒で飽和し、無限遠に合わせたレンズはシーンの輝度Lを焦点面の露光量qLt/N2(q≈0.65)に変えます。
此の2つを等しいと置くと、カメラが記録出来る最大の輝度が得られます:

Lmax=78qS⋅N2t=1.2⋅2EV100

78/(0.65⋅100)=1.2だからです。
レンダラーの露出の段階は単純にx=L/Lmaxで、x=1は「丁度飽和」を意味します。
此れはLagardeとde Rousiersの物理ベースのFrostbiteのコースノート(SIGGRAPH 2014)の約束事で、幾つかのエンジンが此れに従っています。

計算例:夕方の部屋。
F2.8、1/60秒、ISO 800:log2(2.82⋅60)=log2470.4=8.878、log2(800/100)=3なので、EV100=5.878です。
Lmax=1.2⋅25.878=1.2⋅58.8=70.6 cd/m²。
20 cd/m²の壁はx=20/70.6=0.283、300 cd/m²のランプシェードはx=4.25で、1を遥かに超えますが、其れで構いません。其れはトーンカーブの仕事です。
屋外のF16、1/100秒、ISO 100(写真家の「サニー16」)ではEV100=log225600=14.64、Lmax=30720 cd/m²で、9段感度が低くなります。

自動露出と順応

ゲームでは普通、カメラの露出計や目の様に、露出を自動で選びます。
方法は15.4節で見ました。フレームの対数平均輝度を測り(普通はヒストグラムから、両端の数パーセントを無視して)、其れを0.18の様なキー値に置きます。
新しい露出に1フレームで飛ぶのは不自然に見えます。目は徐々に順応するからです。
其の為、EVで測った露出は目標に向かって指数的に動きます:

EVnew=EV+(EVtarget−EV)(1−e−Δt/τ)

計算例。
プレイヤーが暗い部屋に入り、目標が−1から+3 EVに跳ねます。
60フレーム毎秒(Δt=1/60秒)、時定数τ=0.5秒では、各フレームで差の1−e−1/30=0.0328を詰めます。最初のフレームの動きは4⋅0.0328=0.131 EVです。
1秒後に残る差は4e−2=0.54 EVで、露出は3−0.54=2.46にあります。
フレームレートに関係無く同じ様に振る舞い、多くのゲームは明るくなる方向に短いτを使います。目は暗さより光の方に速く順応するからです。

27.2 大域的なトーンマッピング演算子

良いカーブの条件

トーンマッピング演算子は、露出後のシーンの値xを表示値に写します。
大域的な物は画像全体に1本のカーブなので、15.3節のトーンカーブと全く同じ画素ごとの濃淡変換です。只、入力の範囲に上限がありません。
写真家や下の図の様にlogxに対して描くと、良いカーブには3つの部分があります:

  • 暗い端のトウ:傾きが下がり、黒が黒のまま残ります。
  • 中間の灰色(0.18)の周りの真っ直ぐな部分:其の傾きがコントラストを決めます。
  • ショルダー:ハイライトをクリップせずに、範囲の上端へ徐々に曲げます。

此のS字は写真フィルムの光への応答の形で、其の為此の様なカーブはフィルミックと呼ばれます。

0.01から100までの対数軸上の4本のトーンカーブ:クリップ、Reinhard、Hableのフィルミックカーブ、ACESのフィット。中間の灰色0.18での値0.153、0.128、0.267に印付き

Reinhardのx/(1+x)にはショルダーはありますがトウが無く、中間が平らで(画像が少し白っぽく見えます)、決して1に達しません(15.4節の拡張版は、白の値を使って最後の点だけを直します)。

Hableのフィルミックカーブ

ゲーム「Uncharted 2」の為に、John Hable(2010年)は6つのパラメータを持つ1つの有理関数で、トウとショルダーを持つカーブを公開しました:

h(x)=x(Ax+CB)+DEx(Ax+B)+DF−EF

A=0.15(ショルダーの強さ)、B=0.50、C=0.10(直線部分)、D=0.20(トウの強さ)、E=0.02、F=0.30です。
彼のシェーダーは入力に露出バイアス2を掛け、白点W=11.2でのカーブの値で割ります。従ってx=W/2=5.6が丁度1になります:

T(x)=h(2x)h(W)

計算例:中間の灰色。
h(W):分子11.2(0.15⋅11.2+0.05)+0.004=19.380、分母11.2(0.15⋅11.2+0.5)+0.06=24.476なので、h(W)=0.79180−0.06667=0.72513。
x=0.18では2x=0.36:分子0.36(0.054+0.05)+0.004=0.04144、分母0.36(0.054+0.5)+0.06=0.25944なので、h(0.36)=0.15973−0.06667=0.09306。
T(0.18)=0.09306/0.72513=0.128:トウの為にReinhardの0.153より少し暗くなります。其の上ではカーブが急で、x=1で0.493、x=4で0.918に達します。

ACES

映画芸術科学アカデミーのAcademy Color Encoding System(ACES)は、映画制作の為の完全なカラーマネジメントの枠組みです(27.5節)。
ACES 1.xの表示変換は2段階です。Reference Rendering Transform(RRT)、つまりフィルミックなカーブと色の調整と、ディスプレイの種類毎のOutput Device Transform(ODT)(sRGBモニター、1,000 nitのHDR、映画のプロジェクター…)です。
2025年にリリースされたACES 2.0は、其れらを再設計した出力変換のファミリーに置き換え、トーンスケールをより穏やかにし、ガマットマッピングを改善しました。

完全な変換は長いので、ゲームではフィットが良く使われます。
一番有名なのはKrzysztof Narkowiczのフィット(2016年)で、ACES 1.xのRRT + sRGBのODTに当てはめた有理関数のカーブです:

T(x)=x(2.51x+0.03)x(2.43x+0.59)+0.14

[0,1]にクランプします。
事前の露出が組み込まれています(Narkowiczによれば、元のカーブにするには入力に0.6を掛けます)。
計算例: T(0.18)=0.18⋅0.48180.18⋅1.0274+0.14=0.086720.32493=0.267、T(1)=2.54/3.16=0.804。
x≈7.24で1に達します(大きなxでの極限は2.51/2.43=1.033で、クランプが切り取ります)。
Hableのカーブより明るくコントラストが強く、トウも強めです(T(0.01)=0.0038に対して0.0077)。

x 0.01 0.18 0.5 1 2 4 16
Reinhard 0.0099 0.153 0.333 0.500 0.667 0.800 0.941
Hable 0.0077 0.128 0.304 0.493 0.713 0.918 1(クランプ)
ACESのフィット 0.0038 0.267 0.616 0.804 0.915 0.973 1(クランプ)

他のカーブもあります。例えばAgXは、バージョン4.0でBlenderの既定のビューとして「Filmic」に代わりました。カーブの違いは、主に彩度の高いハイライトの扱い方にあります。

チャンネル毎か、輝度か

トーンカーブは1つの数を1つの数に写しますが、画素には3つの数があります。
適用の仕方は2通りあり、失敗の仕方が違います。

計算例。 リニアな色(4,1,0.25)のオレンジの光:

  • チャンネル毎にReinhard:(4/5,1/2,0.25/1.25)=(0.8,0.5,0.2)。赤と青の比は16でしたが、4になりました。色は彩度がずっと低くなり、光が明るい程白に近付きます。チャンネル毎のHableは(0.918,0.493,0.172)で、同じ効果です。
  • 輝度に(15.4節):L=0.2126⋅4+0.7152⋅1+0.0722⋅0.25=1.584、Ld=1.584/2.584=0.613で、色をLd/L=0.387倍して(1.548,0.387,0.097)。比は正確に保たれますが、赤が1を超えます。(1,0.387,0.097)にクランプすると色相が黄色寄りにずれ(赤と緑の比が4から2.6に下がります)、明るさも失われます。

中間の灰色の6段下から7段上までの6色のランプ。クリップ、輝度にReinhardを掛けてクランプ、チャンネル毎のHable、チャンネル毎のACESのフィットで表示

クリップは全てのランプを黄、シアン、マゼンタ、白の平らな塊にします。輝度に掛けたReinhardは色の彩度を何処までも保ちます(太陽が濃いオレンジのままになります)。チャンネル毎のフィルミックなカーブは、フィルムの様に明るい色を白へ向けて彩度を下げますが、途中で色相がずれます(青のランプが紫に寄るのは、チャンネル毎のACESに対する良く知られた不満です)。
ACES 2.0やAgXの様な現代の表示変換は、知覚的な色空間で処理して、色相をずらさずに此の「白への道」を実現します。

シェーダーで

トーンカーブは、15.4節と同じフルスクリーンのパスで、露出の後に実行します:

// Hable's Uncharted 2 curve, exposure bias 2, white point W = 11.2.
float3 hableRaw(float3 x) {
  const float A = 0.15, B = 0.50, C = 0.10, D = 0.20, E = 0.02, F = 0.30;
  return (x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F) - E / F;
}

float3 toneMapHable(float3 c) {
  const float W = 11.2;
  return saturate(hableRaw(2.0 * c) / hableRaw(float3(W, W, W)));
}

// Narkowicz's fit of the ACES 1.x RRT + sRGB ODT.
float3 toneMapAcesFit(float3 x) {
  return saturate((x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14));
}
// Hable's Uncharted 2 curve, exposure bias 2, white point W = 11.2.
vec3 hableRaw(vec3 x) {
  const float A = 0.15, B = 0.50, C = 0.10, D = 0.20, E = 0.02, F = 0.30;
  return (x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F) - E / F;
}

vec3 toneMapHable(vec3 c) {
  const float W = 11.2;
  return clamp(hableRaw(2.0 * c) / hableRaw(vec3(W)), 0.0, 1.0);
}

// Narkowicz's fit of the ACES 1.x RRT + sRGB ODT.
vec3 toneMapAcesFit(vec3 x) {
  return clamp((x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14), 0.0, 1.0);
}
// Hable's Uncharted 2 curve, exposure bias 2, white point W = 11.2.
float3 hableRaw(float3 x) {
  const float A = 0.15, B = 0.50, C = 0.10, D = 0.20, E = 0.02, F = 0.30;
  return (x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F) - E / F;
}

float3 toneMapHable(float3 c) {
  const float W = 11.2;
  return saturate(hableRaw(2.0 * c) / hableRaw(float3(W, W, W)));
}

// Narkowicz's fit of the ACES 1.x RRT + sRGB ODT.
float3 toneMapAcesFit(float3 x) {
  return saturate((x * (2.51 * x + 0.03)) / (x * (2.43 * x + 0.59) + 0.14));
}

どちらもリニアな表示値を返します。其の後のsRGBの符号化は、手で行うかsRGBのフレームバッファに任せます。OpenGLシリーズの第21章と全く同じです。

検定のポイント

此れは第15章のシラバス項目画素ごとの濃淡変換と色変換に繋がります:

  • 大域的な演算子はトーンカーブです。同じ入力には同じ出力。露出(× 2EV)は其の前に、リニアな空間で行います。
  • フィルミックなカーブにはトウ、真っ直ぐな中間部、ショルダーがあります。Reinhardのx/(1+x)はショルダーだけで、x=1で0.5になります。
  • チャンネル毎に掛けると明るい色の彩度が下がり、輝度に掛けると比は保たれますが、チャンネルが1を超える事があります。

27.3 局所的なトーンマッピング

大域的なカーブが奪う物

17段を10段に押し込むカーブは、何処かでコントラストを下げなければなりません。大域的なカーブは其れを至る所で行います。他の場所に明るい窓があるというだけで、日陰の壁の模様のコントラストが下がります。
目は代わりに局所的に順応します。局所的なトーンマッピングの演算子は其れを真似て、写像を近傍に依存させます。従って同じ輝度の2つの画素が違う結果になり得ます。

数字で仕組みを見ましょう。
シーンのダイナミックレンジの殆どは大きなスケールの明るさの変化(部屋と窓)にあり、見たい模様は其の上に乗った小さな比です(目地より1.6倍明るいレンガ)。
比が差になる輝度のlog10で考え、各画素を大きなスケールの部分であるベース層と、残りのディテール層に分けます:

logL=B⏟base+(logL−B)⏟detail

そしてベースだけを圧縮し、ディテールを其のまま足し戻します。

バイラテラルのベース:DurandとDorsey

Frédo DurandとJulie Dorsey(「Fast Bilateral Filtering for the Display of High-Dynamic-Range Images」、SIGGRAPH 2002)は、15.5節のバイラテラルフィルタを対数輝度に掛けてベースを求めます。模様は滑らかにしますが、大きなエッジでは止まるからです。
ベースは、其の範囲全体が目標のコントラストになる様に選んだ係数で圧縮します:

c=log10TmaxB−minB,logLout=cB+(logL−B)−cmaxB

Durand自身のメモでは目標のコントラストをT=5(従ってlog10T=0.699)にしています。cmaxBを引く事で、一番明るいベースがlogLout=0、つまり画面の白になります。
色は15.4節と同じ様に運びます。各チャンネルをLout/L倍します。

計算例:窓を横切る1本の走査線。
部屋に4画素、窓に4画素、100.2=1.58倍で交互に変わる模様があります:

log10L=(−1.3,−1.1,−1.3,−1.1,1.6,1.8,1.6,1.8)

シーンの範囲は103.1、約1,260:1です。
範囲のσr=0.4(模様の0.2よりずっと大きく、段差の2.9よりずっと小さい)のバイラテラルフィルタは各側を平均しますが、エッジを越えては平均しません。ベースは各側の平均から0.02以内に収まるので、部屋でB=−1.2、窓でB=1.7とします。
するとディテールは至る所で±0.1です。

  • 圧縮:c=0.699/(1.7−(−1.2))=0.699/2.9=0.241。
  • 部屋:0.241⋅(−1.2)−0.241⋅1.7=−0.699にディテールを足してlogLout=−0.799と−0.599、つまりLout=0.159と0.252。
  • 窓:0にディテールを足して−0.1と0.1、つまり0.794と1.259(一番明るいテクセルは1を少し超えるのでクリップするか、結果全体を少し暗くします)。

部屋と窓の比は794:1から丁度5:1に下がりましたが、模様の比は両側とも100.2=1.58のままです。
同じ強さの大域的な圧縮(全ての対数を0.241倍)なら、模様は100.2⋅0.241=100.048=1.12に下がっていました。殆ど見えません。

ハロー

何故普通のガウシアンぼかしでベースを求めないのでしょうか?
ぼかしはエッジで止まらないからです。
窓のすぐ隣では、ぼかしたベースが部屋と窓の中間にあるので、其処のディテール層にはエッジ自体の大きな一部が入ります。ディテールは圧縮しないので、其の部分が全力で表示されます。

上:暗い部屋から明るい窓へ横切る走査線の対数輝度と、エッジを斜めに横切るガウシアンのベース、階段状のバイラテラルのベース。下:ベースを圧縮した後、ガウシアン版はエッジの直前で部屋のレベルより大きく沈み、直後で行き過ぎる。此れがハロー

此の走査線の32画素の長い版で、σ=3画素のガウシアンは部屋の最後の画素のベースを−1.2ではなく+0.06にするので、ディテールは+0.1ではなく−1.1−0.06=−1.16になります。圧縮後の値はバイラテラルのベースの場合よりlog10で0.94低く、本来の明るさの約9分の1です。
明るいエッジに沿った此の暗い帯(と反対側の明るい帯)がハローで、局所的なトーンマッピングの典型的なアーティファクトです。
エッジを保つフィルタ(バイラテラル、ガイデッド、27.7節のバイラテラルグリッド)がハローを減らし、圧縮を控えめにすれば残りも目立たなくなります。

露出融合

HDR画像を丸ごと飛ばす方法もあります。
Tom Mertens、Jan Kautz、Frank Van Reethの露出融合(「Exposure Fusion」、Pacific Graphics 2007)は、露出を変えて撮ったLDRの写真其の物を直接混ぜ合わせ、画素毎に其の画素が一番良く見える写真を選びます。
各写真kは、3つの尺度の積である重みのマップを持ちます:

  • コントラスト:グレー画像に掛けたラプラシアンフィルタ(15.5節)の絶対値。エッジや模様が高い点になります。
  • 彩度:其の画素のR、G、Bの標準偏差。色の抜けた画素は低い点になります。
  • 露出の良さ:各チャンネルが0.5にどれだけ近いか、exp(−(v−0.5)22⋅0.22)を全チャンネルで掛けた物。

重みは各画素で和が1になる様に正規化します。

計算例:露出の良さだけ。
1つの灰色の画素が、2段ずつ離れた3枚の写真で0.05、0.45、0.97に写っています。
重みはe−0.2025/0.08=0.0796、e−0.0025/0.08=0.969、e−0.2209/0.08=0.0632で、和は1.112なので、正規化した重みは0.072、0.872、0.057です。
画素毎に混ぜると0.072⋅0.05+0.872⋅0.45+0.057⋅0.97=0.451になります。

画素毎に混ぜると、ある写真から別の写真に切り替わる所に継ぎ目が出来ます。其の為、混合は帯域毎に行います(15.1節)。各写真をラプラシアンピラミッドに、各重みのマップをガウシアンピラミッドにし、各レベルを其のレベルの重みで混ぜ、結果をまとめ直します。
粗いレベルは滑らかな重み(継ぎ目無し)、細かいレベルは鋭い重み(各ディテールは一番露出の良い写真から)を受け取ります。パノラマのマルチバンドブレンディング(16.3節)を、位置ではなく露出に使った物です。

窓のある合成のHDRの部屋を4通りにトーンマッピングした物:大域的なReinhardは空が白っぽくなり、局所的なバイラテラル演算子は部屋と雲の両方を見せ、ガウシアンのベースは窓とランプの周りに暗いハローと明るいハローを作り、4つの露出の露出融合は自然な結果になる

合成の部屋の範囲は17.2段(0.013〜1,900)です。
大域的なカーブは部屋を読める様に保ちますが、雲は殆ど消えます。バイラテラルの演算子は両方を見せますが、少し平板です。ガウシアンのベースは窓とランプの周りにハローを描きます。3段ずつ離れた4つの露出の露出融合は、HDR画像を一度も作らずに自然に見えます。

実際の局所的な演算子

  • Reinhardの2002年の論文には、写真家の覆い焼きと焼き込みを手本にした局所的な版もあります。勾配領域の圧縮(Fattal等、2002年)は対数輝度の大きな勾配を縮めてポアソン方程式で積分し直し(15.7節)、局所ラプラシアンフィルタ(Paris、Hasinoff、Kautz、2011年)はピラミッドを直接操作して、ハローを非常に良く避けます。
  • スマートフォンの「HDR」写真は、バースト合成(16.6節)の局所的なトーンマッピングです。
  • ゲームでは殆ど、自動露出付きの大域的なカーブを使います。執筆時点では、Unreal Engine 5にバイラテラルのベース/ディテール方式と露出融合方式を持つ「ローカル露出」の設定があります。

27.4 HDRディスプレイ

nit、ピーク、ペーパーホワイト

SDRの信号は相対的です。1.0は「此のディスプレイが出せる一番の明るさ」を意味し、伝統的には約100 cd/m²です(sRGBは80を想定します)。
HDRディスプレイは10倍明るいハイライトを出せますが、紙やメニューは見やすいままにすべきで、明るくするのは太陽、火花、鏡面反射だけです。
其の為HDRの制作では、拡散反射の白のレベルである基準白、別名ペーパーホワイトを決めます。
ITU-RのレポートBT.2408は203 cd/m²を推奨し、WindowsではユーザーがSDRの白のレベルを設定出来ます。デスクトップのHDRモニターでは普通200 cd/m²前後です。
203から1,000 cd/m²のピークまではlog2(1000/203)=2.3段のハイライトの余裕があります。

PQ(SMPTE ST 2084)

sRGBのカーブを10,000 cd/m²まで引き伸ばすと、10ビットでは暗部に目に見える縞が出ます。
PQ(知覚量子化、perceptual quantizer。SMPTE ST 2084、ITU-R BT.2100)は、目に見える最小の輝度の段差のモデル(Bartenのコントラスト感度モデル)から設計され、0〜10,000 cd/m²で12ビットの符号の1段が見えない大きさに収まります。実用上は10ビットで足ります。
此れまでのどのカーブとも違い、PQは絶対的です。符号値は其の輝度を出せる全てのディスプレイで、同じcd/m²を意味します。

逆EOTF(ディスプレイの輝度FDから信号E′へ。EOTF、電光変換関数はディスプレイ側の向きです)は

E′=(c1+c2Ym11+c3Ym1)m2,Y=FD10000

で、定数は正確な2進の分数です:

定数 定義 値
m1 2610 / 16384 0.1593017578125
m2 2523 / 4096 × 128 78.84375
c1 3424 / 4096 = c3−c2+1 0.8359375
c2 2413 / 4096 × 32 18.8515625
c3 2392 / 4096 × 32 18.6875

ディスプレイが適用するEOTF其の物は其の逆関数です:

FD=10000(max(E′1/m2−c1,0)c2−c3E′1/m2)1/m1

計算例:100 cd/m²。
Y=0.01、Ym1=0.010.15930=e0.15930ln0.01=e−0.73360=0.48017。
分子0.8359375+18.8515625⋅0.48017=9.88792、分母1+18.6875⋅0.48017=9.97321、比は0.991449。
E′=0.99144978.84375=e78.84375ln0.991449=e−0.67712=0.5081。
つまりSDRの全範囲、0〜100 cd/m²がPQの信号の約半分を使い、残りの半分が100〜10,000の為にあります。
同じ手順で、1,000 cd/m²はE′=0.7518、203 cd/m²は0.5807になります。

10ビットの整数としては、PQはフルレンジ(D=1023E′)か、映像ではナローレンジ(D=4(219E′+16)、符号64〜940、BT.2100)で送ります:

輝度(cd/m²) 0.1 80 100 203 1,000 10,000
E′ 0.0623 0.4859 0.5081 0.5807 0.7518 1.0000
10ビット、フルレンジ 64 497 520 594 769 1023
10ビット、ナローレンジ 119 490 509 573 723 940

検算:MicrosoftのWindows HDRのドキュメントは、80 cd/m²の白をHDR10の符号497としていて、表と一致します。
10ビットの1段は100 cd/m²付近で約1%、0.01付近で9%です。目が必要とする通りの相対的な段差です。

HLG(ハイブリッドログガンマ)

BBCとNHKが開発したHLG(ARIB STD-B67、2015年、其の後BT.2100)は逆の考え方を取ります。
相対的でシーン参照です。カメラはシーンの光E(カメラが記録する一番明るい値を1に正規化)をOETF(光電変換関数、カメラ側の向き)で符号化します:

E′={3E0≤E≤112aln(12E−b)+c112<E≤1

a=0.17883277、b=1−4a=0.28466892、c=0.5−aln(4a)=0.55991073です。
平方根の下の部分はSDRのカメラが行う事に似ているので、HLGの信号は古いSDRのテレビでも其れなりに見えます。対数の上の部分がハイライトを詰め込み、定数は2つの部分がE=1/12、E′=0.5で同じ傾きで繋がる様に選ばれています。

左:0.001から10,000 cd/m²までの対数軸に対するPQのカーブ。100が0.508、203が0.581、1000が0.752。右:HLGのカーブ。Eが12分の1、信号0.5までは平方根、其の後1までは対数の部分。基準白の点0.265, 0.75に印付き

計算例。
E=1/12:3/12=0.5、対数の枝は0.17883ln(1−0.28467)+0.55991=0.17883⋅(−0.33503)+0.55991=0.5000で、同じです。
E=1:0.17883ln11.71533+0.55991=0.17883⋅2.46090+0.55991=1.0000。
ディスプレイは其の後、自分のピークLWに依存するシステムガンマを持つOOTFを適用します。チャンネル毎にFD=LWYSγ−1E(YSはシーンの輝度)、γ=1.2+0.42log10(LW/1000)です。
1,000 cd/m²のディスプレイではγ=1.2です。BT.2408はHLGの基準白を信号0.75に置き、此れはE=0.265で、1000⋅0.2651.2=203 cd/m²、PQと同じ基準白になります。
PQはメタデータ付きのマスタリングされたコンテンツに、HLGはSDRとHDRのテレビで同時に正しく見えなければならない生放送に向いています。

Rec.2020とHDR10の信号

BT.2100は両方のカーブを3.4節のBT.2020(Rec.2020)の原色と組み合わせます。
「HDR10」はPQ、BT.2020、10ビットの組み合わせで、マスタリングディスプレイと、コンテンツの一番明るい画素とフレーム(MaxCLL、MaxFALL)についての静的メタデータを持ちます。HDR10+やDolby Visionはシーン毎のメタデータを加えます。

リニアなsRGB(BT.709)の色からBT.2020への変換は、3.4節と全く同じ様に原色から作った3 × 3の行列です:

[RGB]2020=[0.62740.32930.04330.06910.91950.01140.01640.08800.8956][RGB]709

各行の和は1(白は白のまま)で、sRGBの純粋な赤はBT.2020で(0.627,0.069,0.016)になります。大きな三角形の内側なので、値は全て正のままです。

HDRディスプレイ向けのレンダリング

HDR出力を持つゲームは、レンダラーは其のままにパイプラインの最後を変えます。1ではなくディスプレイのピーク(システムから、又はキャリブレーション画面で)に向けてトーンマッピングし、拡散反射の白とUIをプレイヤーの選んだペーパーホワイトに置き(1,000 cd/m²のUIは目に痛いです)、BT.2020をPQで10ビットのスワップチェーンに符号化するか、リニアな値を16ビットの浮動小数点のスワップチェーンに書きます。
Windowsでは後者がscRGBです。sRGBの原色、リニア、1.0 = 80 cd/m²なので、1,000 cd/m²は12.5になり、sRGBの外の色は負の成分を持ちます。

符号化では、色を上の行列でBT.2020に変換し、ペーパーホワイトのcd/m²を掛け、各チャンネルを符号化します:

// PQ inverse EOTF (SMPTE ST 2084): luminance in cd/m^2 -> signal 0..1.
float pqEncode(float nits) {
  const float m1 = 2610.0 / 16384.0, m2 = 2523.0 / 4096.0 * 128.0;
  const float c1 = 3424.0 / 4096.0, c2 = 2413.0 / 4096.0 * 32.0, c3 = 2392.0 / 4096.0 * 32.0;
  float p = pow(max(nits, 0.0) / 10000.0, m1);
  return pow((c1 + c2 * p) / (1.0 + c3 * p), m2);
}
// PQ inverse EOTF (SMPTE ST 2084): luminance in cd/m^2 -> signal 0..1.
float pqEncode(float nits) {
  const float m1 = 2610.0 / 16384.0, m2 = 2523.0 / 4096.0 * 128.0;
  const float c1 = 3424.0 / 4096.0, c2 = 2413.0 / 4096.0 * 32.0, c3 = 2392.0 / 4096.0 * 32.0;
  float p = pow(max(nits, 0.0) / 10000.0, m1);
  return pow((c1 + c2 * p) / (1.0 + c3 * p), m2);
}
// PQ inverse EOTF (SMPTE ST 2084): luminance in cd/m^2 -> signal 0..1.
float pqEncode(float nits) {
  const float m1 = 2610.0 / 16384.0, m2 = 2523.0 / 4096.0 * 128.0;
  const float c1 = 3424.0 / 4096.0, c2 = 2413.0 / 4096.0 * 32.0, c3 = 2392.0 / 4096.0 * 32.0;
  float p = pow(max(nits, 0.0) / 10000.0, m1);
  return pow((c1 + c2 * p) / (1.0 + c3 * p), m2);
}

27.5 カラーマネジメントとガマットマッピング

ICCプロファイル

3.4節で見た通り、「RGB」は原色、白、伝達カーブが分かるまで何も意味しません。
カラーマネジメントは、此の情報を全ての画像と一緒に運び、装置の間で正しく変換する為の帳簿付けです。
古典的な仕組みはInternational Color ConsortiumのICCプロファイルです。各装置やファイルは、其の値をプロファイル接続空間(PCS、D50の白を持つCIE XYZ又はCIELAB)と相互に変換するプロファイルを持ちます。
カメラからプリンターへの変換は「カメラのプロファイルでPCSへ、プリンターのプロファイルでPCSから」となり、n2個の変換器の代わりにn個のプロファイルで済みます。
プリンターが出せない色は、プロファイルのレンダリングインテントが決めます。知覚的は色域全体を滑らかに圧縮し、相対的な測色的は色域内の色を正確に保って残りをクリップし、彩度は色を鮮やかに保ち(グラフ用)、絶対的な測色的は紙の白まで再現します(色校正用)。

OCIOとACES

映画やゲームのパイプラインはOpenColorIO(OCIO。Sony Pictures Imageworks発で、今はAcademy Software Foundationのプロジェクト)を使います。1つの設定ファイルが制作の全ての色空間と其の間の変換を記述し、全てのアプリケーションが同じファイルを読みます。
一番良く使われる設定がACESです。保管用の空間ACES2065-1は全ての可視の色を囲むAP0の原色を使い、レンダリングと合成はBT.2020より少し広いAP1の原色を持つリニアなACEScgで行い、出力変換が画面毎の画像を作ります。
作業空間はレンダリングにとって重要です。色をチャンネル毎に掛け合わせると、原色が違えば結果も違うからです。

ガマットマッピング

大きな色域から小さな色域へ移ると、移り先に存在しない色が出来る事があります。
計算例。
BT.2020の純粋な緑(0,1,0)を上の行列の逆行列でリニアなBT.709に変換すると(−0.588,1.133,−0.101)です。2つの負の成分と1を超える成分を持つ、sRGBの三角形の外の色です。
各チャンネルを[0,1]にクリップすると(0,1,0)、sRGBの緑になります。輝度は0.678から0.715に上がり、sRGBの緑より外のBT.2020の色は全て同じ点に潰れます。
ガマットマッピング(色域マッピング)はもっと上手くやります。色域外の色を色相と明度を大体保ったまま灰色の軸の方へ動かし、境界付近の色を圧縮して隣同士の違いを残します。明るさではなく彩度に掛けるショルダーです。

検定のポイント

此れらの考え方は第3章(色空間、色域、ガンマ)と第15章の画素ごとの変換の項目を広げます:

  • sRGB、DCI-P3、BT.2020は原色が違います。リニアなRGB空間の間の変換はXYZを経由する3 × 3の行列で、伝達カーブは其の外で適用します。
  • SDRの信号は相対的です。PQは絶対的(cd/m²、10,000まで)、HLGは相対的でシーン参照、SDRと互換性のある平方根の部分を持ちます。
  • 色域外の値をクリップすると色相と明るさが変わります。ガマットマッピングは代わりに灰色の軸の方へ圧縮します。

27.6 フーリエ変換とFFT

DFT

15.5節では周波数領域を言葉で説明しました。画像は波の和で、平滑化はローパスフィルタ、空間での畳み込みは周波数での掛け算です。
計算をしてみましょう。先ずは1次元です。
N個の標本x0,…,xN−1の離散フーリエ変換(DFT)は

Xk=∑n=0N−1xne−2πikn/N,k=0,…,N−1

で、逆変換はxn=1N∑kXke+2πikn/Nです。
e−iθ=cosθ−isinθなので、各Xkは信号と、k周期のコサイン及びサインとの2つの内積(線型代数の第4章)です。其の大きさは其の周波数がどれだけ含まれるか、角度(位相)は山が何処にあるかを表し、X0は単なる和です。

計算例:N=4。
4つの標本ではe−2πi/4=−iなので、係数は−iの累乗に過ぎません:Xk=∑nxn(−i)kn。
x=(1,3,5,3)とします:

  • X0=1+3+5+3=12、
  • X1=1+3(−i)+5(−1)+3(i)=−4、
  • X2=1−3+5−3=0、
  • X3=1+3(i)+5(−1)+3(−i)=−4。

信号が対称なので値は実数です。課題4では複素数になります。

畳み込み定理の計算例

ではxをカーネルh=14(2,1,0,1)でぼかします。此れは第15章の14[121]のカーネルを標本0を中心に置いた物で、信号は端で折り返します(循環畳み込み、yn=∑mhmx(n−m)modN):

  • 直接:y0=2⋅1+3+34=2、y1=2⋅3+1+54=3、y2=2⋅5+3+34=4、y3=3。
  • 周波数経由:Hk=14(2+(−i)k+(−i)3k)からH=(1,0.5,0,0.5)、従ってY=X⋅H=(12,−2,0,−2)で、逆DFTはy0=12−2+0−24=2、y1=12+(−2)(i)+0+(−2)(−i)4=3、y2=12+2+0+24=4、y3=3。

4つの標本での畳み込み定理:xが1, 3, 5, 3、hが0.5, 0.25, 0, 0.25の畳み込みはyが2, 3, 4, 3。其のDFTのXが12, −4, 0, −4、Hが1, 0.5, 0, 0.5を掛けるとYが12, −2, 0, −2で、其の逆DFTがy

同じ答えで、周波数の側が理由を説明します。ぼかしは平均を保ち(H0=1)、周波数1を半分にし、ナイキスト周波数を完全に取り除きます(H2=0)。15.5節で市松模様について見た通りです。
此れが畳み込み定理です:標本毎にDFT(h∗x)=DFT(h)⋅DFT(x)。
DFTは信号を周期的と見なすので、普通の畳み込みにするには画像とカーネルを先にゼロで埋めて広げます。そうしないと右端が左端に漏れ込みます。

FFT

定義通りに計算すると、DFTは出力1つにつきN回、全部でN2回の掛け算が必要です。
高速フーリエ変換(FFT。CooleyとTukey、1965年。但しガウスが1805年頃に既に此の考えを持っていました)は和を偶数番目と奇数番目の標本に分けます:

Xk=∑mx2me−2πikm/(N/2)⏟Ek+e−2πik/N∑mx2m+1e−2πikm/(N/2)⏟Ok

EとOは半分の長さのDFTで、Xk=Ek+wkOk、Xk+N/2=Ek−wkOkです(バタフライ、1回の掛け算から2つの出力)。
分割をlog2N回繰り返すと、N2log2N個のバタフライが残ります。
計算例。 x=(1,3,5,3)では、偶数番目(1,5)の2点DFTはE=(6,−4)、奇数番目(3,3)はO=(6,0)で、w=−iとしてX0=6+6=12、X2=6−6=0、X1=−4+(−i)⋅0=−4、X3=−4−0=−4。
N=1024では、FFTは1,048,576回の積和の代わりに5,120個のバタフライで済みます。約200分の1の仕事です。

2次元のDFTは分離可能です。行を変換し、次に列を変換します。
1024 × 1024の画像を64 × 64のカーネルで直接畳み込むと1画素当たり4,096回の積和が掛かりますが、埋めた画像のFFT、積、逆FFTなら数百回です。小さなカーネルでは直接の畳み込みが勝ちます。
グラフィックスでは、大きなレンズ形のカーネルによるブルーム(執筆時点でUnreal EngineにはFFTの「コンボリューションブルーム」があります)、海面の高さ場(Tessendorf、2001年。毎フレーム波のスペクトルを逆FFTします)、位相相関による画像の位置合わせ、そして逆畳み込みに使われます。

ウィーナーデコンボリューション

16.5節で見た様に、ぼけを元に戻すのは脆い作業です。周波数特性に0を持つぼけkは其の周波数を消し去り、(殆ど)0で割るとノイズが限り無く増幅されます。
周波数で見ると、ぼけた画像はB=K⋅S+N(Nはノイズ)で、素朴な逆フィルタはKで割ります。
ウィーナーフィルタ(Norbert Wiener、1940年代)は二乗誤差の期待値を最小にする線型フィルタで、ノイズと信号の電力比をκとすると

S^=K∗|K|2+κB

です(K∗は複素共役)。
ぼけが信号を強く残す所(|K|2≫κ)ではKで割り、そうでない所では潔く諦めます。

計算例:κ=0.01での実数のKに対する倍率。

K 1 0.5 0.1 0.01 0
逆フィルタ1/K 1 2 10 100 ∞
ウィーナーK/(K2+κ) 0.99 1.92 5.00 0.99 0

最大の倍率はK=κ=0.1での1/(2κ)=5です。
16.5節の3画素の動きぼけでは、K(ω)=1+2cosω3がω=2π/3で丁度0になります。逆フィルタは其処で発散しますが、ウィーナーフィルタは其の周波数で何も出力せず、誤差は有限に留まります。
リチャードソン・ルーシー法や学習による方法も同じ原則に従います:ぼけが壊した周波数は信用しない事です。

27.7 エッジを保つフィルタとノイズ除去

15.5節のバイラテラルフィルタはエッジを越えずに平滑化しますが、遅く(重みが画素毎に変わるので畳み込みではなく、FFTの近道もありません)、アーティファクトもあります。より良い道具を見ていきます。

ジョイント(クロス)バイラテラルフィルタ

ジョイントバイラテラルフィルタ(クロスバイラテラルとも)は、範囲の重みを2枚目のガイド画像Iから取り、入力pを平滑化します:

q(𝐱)=∑𝐲Gσs(‖𝐱−𝐲‖)Gσr(I(𝐱)−I(𝐲))p(𝐲)∑𝐲Gσs(‖𝐱−𝐲‖)Gσr(I(𝐱)−I(𝐲))

フラッシュ/ノーフラッシュ撮影(16.6節)はフラッシュ写真をガイドに使います。レンダラーはレイトレースした影やAOを、Gバッファの深度と法線をガイドにしてノイズ除去し(第24章)、ジョイントバイラテラルアップサンプリング(Kopf等、2007年)は半分の解像度のエフェクトのエッジをフル解像度の深度から取ります。

バイラテラルグリッド

Jiawen Chen、Sylvain Paris、Frédo Durand(SIGGRAPH 2007)は、バイラテラルフィルタがより高い次元の空間での普通のガウシアンぼかしである事に気付きました。各画素を(x,y,I(x,y))に持ち上げると、エッジの反対側にある画素同士は3番目の次元で遠く離れます。
バイラテラルグリッドは(x,y,I)の上の粗い3次元配列で、σs毎及びσr毎に大体1回標本化します:

  1. スプラット:各画素の値(と個数1)を其のグリッドのセルに足し込みます。
  2. ブラー:グリッドを3つの次元全てで小さなガウシアンでぼかします。
  3. スライス:各画素について、グリッドを(x,y,I(x,y))で3線型補間し、値を個数で割ります。

計算例:大きさ。
1920 × 1080のフレームでσs=16画素、σr=0.1(0〜1の強度に対して)なら、グリッドは120×68×11=89760セルで、2,073,600画素の23分の1です。
ぼかしは此の小さなグリッドの上で行うので、コストはσsに殆ど依存せず(大きなシグマは大きなカーネルではなく、粗いグリッドを意味します)、全ての段階が局所的でGPU向きです。2007年にHD動画のエッジを保つフィルタリングをリアルタイムにし、色変換のグリッドを予測する学習済みのカメラパイプラインも此れを土台にしています。

ガイデッドフィルタ

Kaiming He、Jian Sun、Xiaoou Tang(「Guided Image Filtering」、ECCV 2010)は別の考えから出発します。小さな窓ωkの中では、出力はガイドの線型関数だとします:

qi=akIi+bk(i∈ωk)

akを小さく保つ罰則ϵak2を付けて、最小二乗法でak,bkを入力pに合わせると

ak=covk(I,p)vark(I)+ϵ,bk=p¯k−akI¯k

が得られ、各画素は多くの窓に含まれるので、最終的な出力は其れらのaとbを平均します:qi=a¯iIi+b¯i。
平均、分散、共分散は全てボックスフィルタで、累積和(積分画像)を使えば窓の大きさに関係無く1画素当たりO(1)です。ガイデッドフィルタは線型時間で厳密で、近似ではありません。

計算例:自己ガイド(I=p)、ϵ=0.01、3画素の窓。
エッジではp=(0.2,0.2,0.8):平均0.4、分散0.04+0.04+0.163=0.08なので、a=0.08/0.09=0.889、b=0.4(1−0.889)=0.044。出力は(0.222,0.222,0.756)で、エッジは殆ど変わりません。
平らでノイズのある領域ではp=(0.40,0.42,0.38):分散0.000267、a=0.026、b=0.390。出力は(0.400,0.401,0.399)で、ノイズは殆ど消えます。
つまりϵはσr2の役割を果たします。ϵ=0.1よりずっと小さな変化は平滑化され、大きな変化は保たれます。
出力が局所的にガイドの線型関数なので、バイラテラルフィルタが強いエッジで起こし得る勾配の反転のアーティファクトを避けられます。霞の除去(同じ著者達)、マットの修正、ジョイントアップサンプリング、リアルタイムのデノイザーに使われます。

非局所平均

Antoni Buades、Bartomeu Coll、Jean-Michel Morel(「A Non-Local Algorithm for Image Denoising」、CVPR 2005)は問いました:何故近くの画素とだけ平均するのか?
画像は自分自身を繰り返します(レンガ、葉、文字)。ある画素を平均する一番良い相手は、何処にあっても、近傍が同じに見える画素です。
非局所平均は、探索窓の中の各候補画素𝐲に、𝐱と𝐲の周りのパッチの違いから重みを与えます:

w(𝐱,𝐲)=exp(−d2(P𝐱,P𝐲)h2),f^(𝐱)=∑𝐲wf(𝐲)∑𝐲w

d2はパッチの差の二乗の平均、hはノイズのレベルに結び付いたフィルタの強さです。

計算例(1次元、3画素のパッチ、h=3)。
ノイズを除きたい画素は51で、パッチは(10,51,11)です。
候補Aのパッチは(9,49,10):差は1,2,1、d2=6/3=2、重みe−2/9=0.801。
候補B、(12,53,9):d2=(4+4+4)/3=4、重みe−4/9=0.641。
候補Cは此の画素の隣、(51,11,10):d2=(1681+1600+1)/3=1094、重みe−121.6≈0。
画素自身を重み1として:f^=51+0.801⋅49+0.641⋅531+0.801+0.641=124.222.442=50.87。
ガウシアンぼかしなら隣の11を混ぜて山を潰していました。非局所平均は他の山とだけ平均しました。

代償は時間です。21 × 21の探索窓と7 × 7のパッチでは、1画素当たり約441⋅49≈21600回の演算が掛かります。
BM3D(Dabov等、2007年)が此の考えを洗練させ、今は学習によるデノイザーが主流です(第23章と第29章)。

異方性拡散

Pietro PeronaとJitendra Malik(IEEE PAMI、1990年)はガウシアンぼかしを熱拡散∂I/∂t=∇2Iと見なし、強い勾配を越える熱の流れを減らしました。此れが異方性拡散です:

∂I∂t=div(g(‖∇I‖)∇I),g(s)=11+(s/K)2

K=5、ステップ0.25で行(0,0,10,10)を考えると、画素1は差10を見て、伝導率はg(10)=0.2、変化は2.5ではなく0.25⋅0.2⋅10=0.5です。一方、ノイズの差1はg(1)=0.96のままです。何回も繰り返すと、エッジは生き残り、ノイズは溶けます。

円、暗い正方形、グラデーション、縞を持つノイズのある合成テスト画像を、ガウシアンフィルタ、バイラテラルフィルタ、ガイデッドフィルタ、非局所平均でノイズ除去した物。PSNRはノイズのある画像の23.1 dBから26.0、31.9、28.3、36.2 dBに上がる

此のテスト画像(ノイズσ=0.07)では、ガウシアンはエッジをぼかし、バイラテラルとガイデッドはエッジを保ち(此の設定ではガイデッドの方がノイズを多く残します)、非局所平均がはっきり勝ちます。縞と平らな領域が同じパッチを沢山提供するからです。
PSNR(ピーク信号対雑音比)は10log10(peak2/MSE) dBで、+6 dBでRMS誤差が半分になります。

27.8 ピラミッド・動き・解像度

ラプラシアンピラミッドによる合成、再び

ラプラシアンピラミッドによる合成(BurtとAdelson、1983年)は、マスクのガウシアンピラミッドを使って、2つの画像を帯域毎にLkout=Gk(M)Lk(A)+(1−Gk(M))Lk(B)と混ぜます。レベルkでは切り替わりの幅が約2k画素なので、色は徐々に切り替わり、エッジは1〜2画素で切り替わり、継ぎ目もゴーストも出ません。
露出融合やパノラマの合成も此れを使います。

オプティカルフロー

動画の処理(手ぶれ補正、フレーム補間、時間方向のノイズ除去)には、各画素が何処へ動くかが必要です。画素毎のベクトル(u,v)、オプティカルフローです。
基本的な仮定は明るさの不変性です。点は動いても明るさを保ちます:I(x+u,y+v,t+1)=I(x,y,t)。
1次のテイラー展開で、此れは画素毎に1つの線型方程式になります:

Ixu+Iyv+It=0

Ix、Iyは画像の勾配(15.5節)、Itはフレーム間の変化です。
方程式1つに未知数2つ。1つの画素からは勾配に沿った動きしか分かりません。
此れが開口問題です。小さな穴から覗くと、動く真っ直ぐなエッジは、エッジに沿った動きが何であっても同じに見えます。

ルーカス・カナデ法(Bruce LucasとTakeo Kanade、「An Iterative Image Registration Technique with an Application to Stereo Vision」、IJCAI 1981)は、小さな窓の中の全ての画素が一緒に動くと仮定し、窓の方程式を最小二乗法で解きます。すると2 × 2の連立方程式になります:

[∑Ix2∑IxIy∑IxIy∑Iy2][uv]=−[∑IxIt∑IyIt]

計算例:4画素の窓。

画素 Ix Iy It
1 1 0 −1
2 0 1 −0.5
3 1 1 −1.5
4 2 1 −2.5

和:∑Ix2=1+0+1+4=6、∑IxIy=0+0+1+2=3、∑Iy2=0+1+1+1=3、∑IxIt=−1+0−1.5−5=−7.5、∑IyIt=0−0.5−1.5−2.5=−4.5。
連立方程式は[6333][uv]=[7.54.5]で、行列式は18−9=9なので(線型代数の第10章)、u=7.5⋅3−3⋅4.59=1、v=6⋅4.5−3⋅7.59=0.5。
窓は右に1画素、下に半画素動きました。全ての画素の方程式が正確に成り立ちます(画素4:2⋅1+1⋅0.5−2.5=0)。

窓の中の勾配が全て平行(真っ直ぐなエッジ)なら、行列は特異になります。再び開口問題です。
其の固有値(此処では7.85と1.15)は窓がどれだけ良く拘束されているかを表し、小さい方が大きい窓は追跡に向いています(ShiとTomasiの「good features to track」)。
線型化は1画素程度の動きでしか成り立たないので、実装は反復し、ガウシアンピラミッドの上で粗から密へ処理します。元の大きさで16画素の動きは、8分の1の大きさでは2画素です。
レンダラーは全ての画素の本当の動きを知っていて、時間方向のアンチエイリアシングの為にモーションベクトルのバッファに書き込みます(第20章)。モーションベクトルが捉えない物(影、反射、パーティクル)には、例えばフレーム生成で、今もオプティカルフローが役立ちます(第29章)。

超解像

リアルタイムのレンダラーは、16.6節の複数フレームの超解像を意図的に使います。毎フレームカメラをサブピクセルずらし、低い解像度で描画し、モーションベクトルに沿ってフレームを蓄積します。時間方向のアップスケーラーは、履歴を信用出来るかを決める経験則又はニューラルネットワークを加えた物です(第29章)。

検定のポイント

27.6〜27.8節は、第15章のシラバス項目領域に基づく画像変換(空間フィルタリング)を深めます:

  • 空間での畳み込みは周波数での掛け算で、平滑化はローパスです。FFTはDFTをO(NlogN)にし、大きなカーネルで得をします。DFTは循環的なので、埋めて広げます。
  • 逆畳み込みはぼけの周波数特性で割ります。ウィーナーフィルタは信号がノイズより弱い所で倍率を抑えます。
  • エッジを保つ平滑化:バイラテラル(空間と範囲の重み)、ジョイント/クロスバイラテラル(範囲の重みをガイドから)、ガイデッドフィルタ(局所的な線型モデル)、非局所平均(パッチの類似度)、異方性拡散(強い勾配で伝導率が下がる)。第15章のメディアンフィルタもエッジを保つフィルタです。
  • ピラミッド:ガウシアン(小さな複製)、ラプラシアン(帯域、厳密に再構成可能)。帯域毎に合成すると継ぎ目が出ません。

27.9 画像圧縮の詳細

DCT

2.10節でJPEGの手順を挙げました:YCbCr、クロマサブサンプリング、8 × 8ブロックのDCT(離散コサイン変換)、量子化、可逆符号化です。
DCTはコサインだけを使うDFTの親戚で、ブロックを繰り返すのではなく両端で鏡に映すと仮定するので、ブロックの境界に段差が無く、滑らかなブロックのエネルギーがごく少数の係数に集まります。
此れが其の8個の1次元の基底パターン(x=0,…,7に対するcos(2x+1)kπ16)です。2.10節の64個のパターンは、横と縦のパターンの積です:

長さ8のDCTの8個の基底ベクトル、kは0から7。k = 0は一定、k = 1はコサインの半周期、k = 7は殆ど標本毎に符号が変わる

8ビットの値から128を引いた(「レベルシフト」)後のブロックf(x,y)に対するJPEGの順変換は

F(u,v)=14C(u)C(v)∑x=07∑y=07f(x,y)cos(2x+1)uπ16cos(2y+1)vπ16

で、C(0)=1/2、其れ以外はC(k)=1です。
手で計算する為に、8行が全て同じ行f(x)であるブロックを考えます。
するとv=0のコサイン(1です)のyについての和は8になり、v≠0の係数は全て消え、一番上の行の係数だけが残ります:

F(u,0)=2C(u)∑x=07f(x)cos(2x+1)uπ16

計算例:滑らかな行。
行60、64、72、86、104、122、134、140(緩やかな傾斜)をレベルシフトすると:−68、−64、−56、−42、−24、−6、6、12。
F(0,0)=2⋅12⋅(−242)=−242、単なる和です。
u=1のコサインは±0.981,±0.831,±0.556,±0.195(最初の4つの標本で正、最後の4つで負)で、重み付きの和は−167.95なので、F(1,0)=2⋅(−167.95)=−237.5。
8個の係数全て:−242、−237.5、15.2、8.7、−2.0、−0.7、0.2、−0.9。
殆ど全てのエネルギー(99.7%)が最初の2つにあります。

量子化

JPEGは各係数を量子化テーブルの段差で割って丸めます。情報が失われるのは此の丸めです。
JPEG規格のAnnex Kは、見え方の実験から導いた輝度の表の例を挙げています。其の一番上の行(F(u,0)に使います)は16、11、10、16、24、40、51、61で、高い周波数ほど粗い段差になります。
エンコーダーは「品質」の設定に応じて表全体を拡大縮小します。

u 0 1 2 3 4 5 6 7
F(u,0) −242.0 −237.5 15.2 8.7 −2.0 −0.7 0.2 −0.9
段差 16 11 10 16 24 40 51 61
レベル −15 −22 2 1 0 0 0 0
復号したF −240 −242 20 16 0 0 0 0

0でないレベルは4つだけで、末尾の0は可逆の段階(ジグザグ順、ランレングス、ハフマン符号化)の後には殆ど何のコストにもなりません。
デコーダーは掛け戻して逆DCTを適用します:62、63、70、85、105、123、135、141。
全ての画素が元の値から2以内で、MSEは1.75、PSNRは10log10(2552/1.75)=45.7 dBで、目には見えません。

リンギングとブロックノイズ

鋭いエッジには高い周波数が必要です。
行40、40、40、40、200、200、200、200はu=7まで大きな奇数番目の係数を持ちます(−64、−579.9、0、203.6、0、−136.1、0、115.4)。
表を4倍にする(低い品質の設定)と、u=7のレベルは0に丸められ、他も粗くなり、復号した行は33、57、25、58、182、215、183、207になります。エッジの両側の波打ちで、215まで行き過ぎ、25まで沈みます。
此れがリンギング、2.10節の「モスキートノイズ」で、15.5節の理想的なローパスフィルタと同じギブズ現象です。

左:滑らかな行と、Annex Kの表で量子化した後の復号結果。殆ど同じ。右:表を4倍にして量子化した後に復号したエッジの行。エッジの両側に波打ちがある

もう1つの典型的なアーティファクトはブロック其の物から来ます。
8 × 8ブロックは其々独立に量子化されるので、低い品質では滑らかなグラデーションが殆ど平らなブロックの階段になり、境界でレベルが合いません。此れがブロックノイズです。

滑らかなグラデーション、明るい円、暗い棒を持つ合成のグレー画像を、8 × 8のDCTとAnnex Kの表の3つの倍率で圧縮した物:41.9 dBでは殆ど変化が見えず、32.9 dBではブロックとリンギングが見え、28.8 dBでは強いブロックが出る

動画のコーデック(H.264、HEVC、AV1)は符号化ループの中でデブロッキングフィルタを実行し、アーティファクトと見なせる程度に段差が小さい所だけでブロックの境界を跨いで平滑化します。此れもエッジを保つフィルタです。更にブロックを隣や他のフレームから予測します。WebP、AVIF、JPEG XLは同じ材料を洗練させた物で、執筆時点ではAVIFとJPEG XLはPQやHLGのHDR画像も保存出来ます。

検定のポイント

此れは第2章の画像ファイル形式と圧縮を広げます:

  • JPEG:YCbCrとクロマサブサンプリング、8 × 8のDCT、量子化(非可逆の段階。高い周波数ほど粗い)、ジグザグ順、ランレングスとハフマン符号化(可逆)。
  • 滑らかなブロックはエネルギーが低い周波数に集中します。量子化はブロックノイズ(ブロックを独立に量子化する為)と、エッジ付近のリンギング(高い周波数の除去)を起こします。
  • 8ビットの画像ではPSNR =10log10(2552/MSE) dB。

27.10 テクスチャ圧縮

異なる要件

テクスチャはランダムな順に、一度に数テクセルずつ読まれ(第11章)、帯域を節約する為にメモリ上でも圧縮されたままでなければなりません。
其の為テクスチャ圧縮は固定レートのブロック形式を使います。(普通は)4 × 4の各ブロックが同じビット数を受け取るので、どのテクセルのアドレスも簡単な計算で求まり、テクスチャユニットがハードウェアでブロックを復号します。符号化は遅くても構いませんが、復号は簡単でなければなりません。

BC1の計算例

BC1(DXT1又はS3TCとも。1990年代後半のS3 Graphics発)は4 × 4ブロックのRGBのテクセルを64ビットに保存します:

  • 2つの端点の色c0とc1、其々RGB565(赤5ビット、緑6ビット、青5ビット)の16ビット、合わせて32ビット。
  • テクセル毎に2ビットのインデックスが16個(32ビット)。4色のパレットc0、c1、23c0+13c1、13c0+23c1から選びます。

24ビットの代わりに1テクセル当たり4ビット、RGB8に対して6:1、RGBA8に対して8:1です。
16ビットの数としてc0≤c1なら、ブロックは2つ目のモードに切り替わります。3色(c0、c1と其の平均)と透明な黒で、切り抜きのテクスチャ用です。
パレットはRGB空間の直線上にあるので、BC1はブロックの色が1本の直線の近くにある時に上手く働きます。此れは意外に良くある事です。

計算例。
ブロックは琥珀色(240,180,60)から青(40,80,160)への正確な4段のグラデーションで、右上の角に赤みがかったオレンジの外れ値(220,90,40)が1つあります。

  1. 端点:一番違う2色は琥珀色と青です。RGB565では240⋅31/255=29.18→29、180⋅63/255=44.47→44、60⋅31/255=7.29→7なので、c0=(29,44,7)、16ビットの値は29⋅2048+44⋅32+7=60807 = 0xED87。青はc1=(5,20,19) = 0x2A93 = 10,899。c0>c1なので4色モードです。
  2. 端点を8ビットに復号(ビットの複製、R8=8R5+⌊R5/4⌋、G8=4G6+⌊G6/16⌋):c0=(239,178,57)、c1=(41,81,156)で、元から数レベルずれます。
  3. パレット:23c0+13c1=(173,145.7,90)と13c0+23c1=(107,113.3,123)。
  4. インデックス:各テクセルが一番近いパレットの色を選びます。グラデーションのテクセルは約4レベル以内で一致します(RGB565の丸め)。外れ値の距離は91.6、213.5、88.4、142.1で、インデックス2の黄褐色になり、誤差は88です。

4 × 4ブロックのBC1符号化:赤みがかったオレンジの外れ値を持つ元のブロック、端点c0 = 29, 44, 7とc1 = 5, 20, 19から作った4色のパレット、2ビットのインデックスの格子、外れ値が黄褐色になった復号結果

ブロックは48バイトから8バイトに縮み、15個のテクセルは殆ど完璧ですが、直線から外れた色は失われます。実際のエンコーダーがどれだけ端点を探しても、関係の無い2色を含むブロックでは此れがBC1の典型的なアーティファクトです。
デコーダーは此れだけです:

#include <cstdint>
#include <cstdio>

struct Rgb { int r, g, b; };

// Expand a 5:6:5 color to 8 bits per channel by bit replication.
Rgb from565(uint16_t c) {
  int r = c >> 11, g = (c >> 5) & 63, b = c & 31;
  return {(r << 3) | (r >> 2), (g << 2) | (g >> 4), (b << 3) | (b >> 2)};
}

// Decode one BC1 block (8 bytes, little endian) into 16 colors, row by row.
void decodeBc1(const uint8_t blk[8], Rgb out[16]) {
  uint16_t c0 = blk[0] | blk[1] << 8, c1 = blk[2] | blk[3] << 8;
  uint32_t idx = blk[4] | blk[5] << 8 | blk[6] << 16 | uint32_t(blk[7]) << 24;
  Rgb p[4] = {from565(c0), from565(c1)};
  if (c0 > c1) {  // four-color mode
    p[2] = {(2 * p[0].r + p[1].r) / 3, (2 * p[0].g + p[1].g) / 3, (2 * p[0].b + p[1].b) / 3};
    p[3] = {(p[0].r + 2 * p[1].r) / 3, (p[0].g + 2 * p[1].g) / 3, (p[0].b + 2 * p[1].b) / 3};
  } else {  // three colors + transparent black
    p[2] = {(p[0].r + p[1].r) / 2, (p[0].g + p[1].g) / 2, (p[0].b + p[1].b) / 2};
    p[3] = {0, 0, 0};
  }
  for (int i = 0; i < 16; ++i) out[i] = p[(idx >> (2 * i)) & 3];
}

int main() {
  const uint8_t blk[8] = {0x87, 0xED, 0x93, 0x2A, 0xA8, 0xFA, 0x7E, 0x5F};
  Rgb px[16];
  decodeBc1(blk, px);
  for (int y = 0; y < 4; ++y) {
    for (int x = 0; x < 4; ++x)
      printf(" (%3d,%3d,%3d)", px[4 * y + x].r, px[4 * y + x].g, px[4 * y + x].b);
    printf("\n");
  }
  return 0;
}

 (239,178, 57) (173,145, 90) (173,145, 90) (173,145, 90)
 (173,145, 90) (173,145, 90) (107,113,123) (107,113,123)
 (173,145, 90) (107,113,123) (107,113,123) ( 41, 81,156)
 (107,113,123) (107,113,123) ( 41, 81,156) ( 41, 81,156)

インデックスのワード0x5F7EFAA8は、テクセル0を一番下の2ビットに持ちます。整数の割り算は145.7を145に切り捨てます(ハードウェアのデコーダーは此の様な丸めで1レベル程違う事があります)。
アドレス計算:幅256テクセルのBC1テクスチャで、テクセル(37,22)はブロック(⌊37/4⌋,⌊22/4⌋)=(9,5)にあり、此れはバイトオフセット329⋅8=2632のブロック番号5⋅64+9=329です。其の中ではテクセル(1,2)で、インデックスはインデックスのワードの2⋅(2⋅4+1)=18ビット目と19ビット目にあります。

BCnファミリー、ASTC、ETC2

デスクトップの形式(Direct3Dの名前。VulkanとOpenGLも同じ形式を持ちます):

形式 内容 ブロック当たりのビット数 テクセル当たりのビット数 典型的な用途
BC1 RGB(+ 1ビットのアルファ) 64 4 不透明な色
BC3 RGBA:BC1の色 + BC4式のアルファ 128 8 滑らかなアルファ付きの色
BC4 1チャンネル、8ビットの端点2つ、3ビットのインデックス 64 4 ラフネス、高さ、マスク
BC5 BC4のチャンネル2つ 128 8 法線マップ(x, y)
BC6H 半精度浮動小数点のRGB 128 8 HDR(環境マップ、ライトマップ)
BC7 RGB(A)、複数のモードと分割 128 8 高品質な色

BC7はブロックを2つ又は3つの分割に分け、其々に端点を持たせる事で、BC1の「直線1本」の制限を解きます。BC5は法線マップのxとyを保存し、シェーダーがz=1−x2−y2を作り直します(x=0.6、y=0からz=0.8)。

ASTC(Adaptive Scalable Texture Compression。ARM発で、Nystad等がHigh Performance Graphics 2012で発表し、同じ年にKhronosが採用)は常にブロック当たり128ビットを使いますが、ブロックの大きさを4 × 4から12 × 12テクセルまで変えられるので、ビットレートは128/(w⋅h)です:4 × 4で1テクセル当たり8ビット、6 × 6で3.56、8 × 8で2、12 × 12で0.89。
LDRとHDR、1〜4チャンネルを扱え、執筆時点ではモバイルのGPUで一般的な選択です。ETC2はOpenGL ES 3.0の必須の形式で、超圧縮のファイル(KTX 2.0の中のBasis Universal)は読み込み時にGPUが対応する形式に変換されます。

計算例:4096 × 4096のテクスチャのメモリ。

形式 テクセル当たりのビット数 最上位レベル ミップマップ込み(× 4/3)
RGBA8 32 64 MiB 85.3 MiB
BC7 8 16 MiB 21.3 MiB
BC1 4 8 MiB 10.7 MiB
ASTC 6 × 6 3.56 7.1 MiB 9.5 MiB
ASTC 8 × 8 2 4 MiB 5.3 MiB

最上位レベルは40962⋅bits/8バイトで、ミップマップの連鎖が3分の1を加えます(11.7節)。
ブロック圧縮は帯域も同じ割合で減らし、メモリより其方の方が重要な事も良くあります。

27.11 まとめ

  • シーン参照のリニアな値は露出され(x=L/Lmax、Lmax=1.2⋅2EV100)、トーンマッピングでディスプレイ参照の値になり、符号化されます(sRGB、PQ、HLG)。自動露出は指数的に順応します。
  • 大域的な演算子はトーンカーブです:Reinhard、Hable(0.18 → 0.128)、ACESのフィット(0.18 → 0.267)。チャンネル毎なら明るい色の彩度が下がり、輝度に掛ければ比は保たれますが溢れます。
  • 局所的なトーンマッピングはベース層を圧縮してディテールを保ちます。バイラテラルのベースはガウシアンのベースのハローを避けます。露出融合はLDRの露出をラプラシアンピラミッドで混ぜます。
  • PQは絶対的(100 cd/m² → 0.508、1,000 → 0.752)、HLGは相対的でSDRと互換。基準白は203 cd/m²、HDR10 = PQ + BT.2020 + 10ビット。ICC、OCIO、ACESが色空間を運び、ガマットマッピングはクリップの代わりに圧縮します。
  • FFTはDFTをO(NlogN)で計算し、畳み込みは掛け算になります。ウィーナーフィルタはノイズを爆発させずに逆畳み込みします。
  • エッジを保つフィルタ:ジョイントバイラテラル、バイラテラルグリッド、ガイデッドフィルタ、非局所平均、異方性拡散。ルーカス・カナデ法は窓毎に2 × 2の連立方程式を解きます。
  • JPEGのDCTは滑らかなブロックを集中させ、量子化はリンギングとブロックノイズを起こします。BC1はRGB565の端点2つと2ビットのインデックスを保存し(1テクセル当たり4ビット)、BC7、BC5、BC6H、ASTC(128ビット、4 × 4〜12 × 12)が残りを受け持ちます。

27.12 用語

English 日本語
scene-referred / display-referred シーン参照/ディスプレイ参照
exposure value (EV, EV100) 露出値(EV、EV100)
automatic exposure, adaptation 自動露出、順応
tone mapping operator トーンマッピング演算子
global / local tone mapping 大域的/局所的なトーンマッピング
toe / shoulder トウ/ショルダー
filmic tone curve フィルミックなトーンカーブ
ACES (RRT, ODT, output transform) ACES(RRT、ODT、出力変換)
base layer / detail layer ベース層/ディテール層
halo ハロー
exposure fusion 露出融合
reference white (paper white) 基準白(ペーパーホワイト)
PQ (perceptual quantizer) PQ(知覚量子化)
HLG (hybrid log-gamma) HLG(ハイブリッドログガンマ)
EOTF / OETF / OOTF 電光変換関数/光電変換関数/光光変換関数
color management カラーマネジメント
ICC profile / profile connection space ICCプロファイル/プロファイル接続空間
rendering intent レンダリングインテント
gamut mapping ガマットマッピング(色域マッピング)
discrete Fourier transform (DFT) 離散フーリエ変換(DFT)
fast Fourier transform (FFT) 高速フーリエ変換(FFT)
convolution theorem 畳み込み定理
Wiener filter ウィーナーフィルタ
joint (cross) bilateral filter ジョイント(クロス)バイラテラルフィルタ
bilateral grid バイラテラルグリッド
guided filter ガイデッドフィルタ
non-local means 非局所平均(ノンローカルミーンズ)
anisotropic diffusion 異方性拡散
PSNR ピーク信号対雑音比(PSNR)
Laplacian pyramid blending ラプラシアンピラミッドによる合成
optical flow オプティカルフロー
aperture problem 開口問題
Lucas–Kanade method ルーカス・カナデ法
super-resolution 超解像
discrete cosine transform (DCT) 離散コサイン変換(DCT)
quantization table 量子化テーブル
ringing / block artifacts リンギング/ブロックノイズ
texture compression テクスチャ圧縮
block compression (BC1 to BC7) ブロック圧縮(BC1〜BC7)
ASTC / ETC2 ASTC/ETC2

27.13 課題

  1. ★ 400 cd/m²のPQの信号E′と10ビットのフルレンジの符号、E=0.02とE=0.5のHLGの信号を求めて下さい。PQの値が203と1,000 cd/m²の値の間にある事を確かめて下さい。
  2. ★ x=0.09とx=3で、Reinhard、Hable(バイアス2、W=11.2)、ACESのフィットを求めて下さい。一番影が暗いのはどのカーブで、0.09と3の間のコントラストを一番保つのはどのカーブですか?
  3. ★★ 走査線がlog10L=(−2.0,−1.8,−2.0,−1.8,1.0,1.2,1.0,1.2)です。各側の平均をベースとし、目標のコントラストを10として、c、8つの出力全て、模様の比を求めて下さい。次に、同じ強さの大域的な圧縮で残る模様の比を求めて下さい。
  4. ★★ x=(2,0,2,4)の4点DFTを求め(今回は複素数になります)、H=(1,0.5,0,0.5)を掛けて逆変換して下さい。結果を、h=14(2,1,0,1)との循環畳み込みを直接計算した物と比べて下さい。
  5. ★★★ 純粋な赤8テクセルと純粋な緑8テクセルのBC1ブロックを符号化し、次に赤、緑、青のブロックも符号化して下さい。2つ目が失敗する理由、BC7の分割が其れを解決する方法、そしてミップマップ込みの2048 × 2048の法線マップがBC5とRGBA8で其々どれだけのメモリを使うかを説明して下さい。

選択問題

問1 ★ 次のうち、トーンマッピングについて正しい物はどれか。

あ Reinhardの演算子L/(1+L)は、シーンの値1を画面の白に写す。
い 大域的なトーンマッピング演算子は、同じシーンの輝度を持つ2つの画素が画像の違う領域にあれば、違う表示値を与え得る。
う トーンカーブをRGBのチャンネル毎に掛けると非常に明るい色は白に近付き、輝度に掛けて色を拡大縮小するとチャンネルの間の比が保たれる。
え 露出はトーンマッピングの後に、表示値に2EVを掛けて適用する。
お ガウシアンでぼかしたベース層を使う局所的なトーンマッピングは、ガウシアンフィルタがエッジを保つのでハローが出ない。

問2 ★ 次のうち、HDRの映像信号について正しい物はどれか。

あ PQは10,000 cd/m²までの絶対的な輝度を符号化するので、PQの符号値は其の輝度を出せる全てのディスプレイで同じ輝度を意味する。
い HLGはPQと同じくディスプレイ参照で、HLGの信号1.0は常に1,000 cd/m²を意味する。
う BT.2020の原色はsRGBの三角形の内側にあるので、sRGBの色をBT.2020に変換すると負の値が出る。
え PQでは、10ビットの符号の1段はカーブの何処でも同じcd/m²だけ輝度を変える。
お 基準白(ペーパーホワイト)は、ユーザーインターフェースが出来るだけ明るく見える様に、ディスプレイのピーク輝度に設定すべきである。

問3 ★★ 次のうち、フィルタリングと圧縮について正しい物はどれか。

あ JPEGでは量子化は可逆で、情報はハフマン符号化で失われる。
い BC1は4 × 4ブロック毎に独立した16ビットの色を16個保存するので、1ブロックは32バイトになる。
う バイラテラルグリッドのコストは、グリッドが細かくなるので空間のσsの二乗に比例して増える。
え ASTCは常に4 × 4のブロックを使い、品質の設定で変わるのはブロック当たりのビット数だけである。
お 畳み込み定理により、畳み込みはゼロで埋めて広げた画像とカーネルのDFTを掛けて計算出来る。FFTを使うと、此れは大きなカーネルで得になる。

27.14 実践課題

1本の走査線を、シーンから画面へ、そしてファイルへ、手で追います。
電卓(log、exp、累乗付き)、方眼紙、鉛筆を用意して下さい。

走査線は薄暗い部屋から日の当たる窓へ横切ります。シーンの輝度はcd/m²で2、3、2、3、2000、3000、2000、3000です。

  • 手順1:露出。カメラはF2.8、1/60秒、ISO 800です。EV100、Lmax、8つの露出後の値x=L/Lmaxを求めます。
  • 手順2:大域的なカーブ。8つの値にReinhard x/(1+x)とHableのカーブ(バイアス2、W=11.2、1でクランプ)を掛け、8ビットのsRGBに符号化し(3.7節)、両方の行を方眼紙に描きます。
  • 手順3:局所的なトーンマッピング。シーンの値のlog10Lを取り、各側の平均をベース、目標のコントラストを5として、c、8つの出力Lout、各側の模様の比を求めます。同じ強さの大域的な圧縮と比べます。
  • 手順4:HDR出力。PQのディスプレイで、部屋の平均を25 cd/m²、窓のテクセルを600と900 cd/m²で表示します。25、203(ペーパーホワイト)、600、900 cd/m²のPQの信号と10ビットのフルレンジの符号を求めます。
  • 手順5:DCT。部屋の模様の1行が50、70、50、70、50、70、50、70です。此の行を8つ並べたブロックのF(0,0)とF(7,0)を求め(此の行にはu=1,3,5にもっと小さな係数もありますが、此処では無視します。u=7のコサインは0.1951、−0.5556、0.8315、−0.9808、0.9808、−0.8315、0.5556、−0.1951)、Annex Kの段差16と61で量子化し、次にF(7,0)を4倍の段差で量子化し直します。両方の場合の復号した模様を説明します。
  • 手順6:BC1。グレーの4 × 4ブロックの行が40 60 80 100 / 60 80 100 120 / 80 100 120 140 / 100 120 140 160です。端点を選び、パレットを作り(先ずは端点が正確に保存されると仮定)、16個のインデックスを書き、最大の誤差を求めます。次に160と40の本当のRGB565の端点と、其の8ビットの復号値を求めます。

確かめ方:

  • 手順1:EV100は6を少し下回り、Lmaxは70を少し上回ります。部屋の値は百分の数程度、窓の値は数十です。
  • 手順2:Reinhardでは窓の4画素が8ビットで1レベルしか違わず(窓の模様は実質的に消えます)、Hableでは全て255です。部屋ではHableの方がReinhardより暗くなります(トウ)。
  • 手順3:ベースの範囲は丁度3、cは0.233、模様の比は両側とも1.5のままで、大域的な版では約1.1しか残りません。
  • 手順4:203 cd/m²は表の通り594になり、600と900 cd/m²(0.6段の差)は40符号以上離れます。
  • 手順5:F(0,0)=−544は丁度量子化されます(レベル−34)。F(7,0)は段差61ではレベル−1として残り(コントラストが少しずつ違う縞が戻ります)、段差244では0になるので、行は60の平らな灰色になり、模様は消えます。
  • 手順6:パレットは丁度40、80、120、160で、テクセル60、100、140は2つの色の丁度中間にあり(最大の誤差20)、RGB565の端点はもう純粋な灰色ではありません。160は(156, 162, 156)に復号され、少し緑がかります。


コメント(0)

まだコメントはありません。