27. HDRと高度な画像処理
第15章では、検定のシラバスの画像処理を学びました。ヒストグラム、トーンカーブ、畳み込み、バイラテラルフィルタ、ピラミッド、そして大域的なトーンマッピング演算子を1つ、Reinhardのです。
其の章は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段の装置でどう見せるかを決める、パイプラインの部分です。
段階を順に並べます:
- リニアなシーンの光:レンダラー(浮動小数点のフレームバッファ)又はカメラ(RAW、又は16.2節の様なHDR合成)から。
- 露出:全ての画素に同じ掛け算を1回。シーンの範囲のどの部分を画面の真ん中に置くかを選びます。
- グレーディング:色補正と芸術的な「ルック」。まだシーンの値の上で行います。
- トーンマッピング:範囲を圧縮するカーブ又は演算子。此処でシーン参照がディスプレイ参照になります。
- 符号化:SDRには3.7節のsRGBのカーブ、HDRにはPQ又はHLG(27.4節)。ディスプレイが其れを光に戻します。
露出は何も圧縮しません。シーンをカーブに沿って滑らせるだけです。情報を手放すのはトーンマッピングです。
段数で考える露出
2.3節ではF値とシャッター時間から露出値を定義し、第15章では露出をの掛け算として使いました。
物理ベースのレンダラーは此の2つを繋げ、アーティストが本物の単位(ルクスやカンデラ)でシーンを照らし、本物のカメラ設定で「撮影」出来る様にします。
先ず感度(ISO値)を組み込み、ISO 100での露出値を求めます:
次に、センサーを丁度満たす輝度が必要です。
ISO 12232の飽和に基づく感度によれば、センサーは焦点面の露光量ルクス秒で飽和し、無限遠に合わせたレンズはシーンの輝度を焦点面の露光量()に変えます。
此の2つを等しいと置くと、カメラが記録出来る最大の輝度が得られます:
だからです。
レンダラーの露出の段階は単純にで、は「丁度飽和」を意味します。
此れはLagardeとde Rousiersの物理ベースのFrostbiteのコースノート(SIGGRAPH 2014)の約束事で、幾つかのエンジンが此れに従っています。
計算例:夕方の部屋。
F2.8、1/60秒、ISO 800:、なので、です。
cd/m²。
20 cd/m²の壁は、300 cd/m²のランプシェードはで、1を遥かに超えますが、其れで構いません。其れはトーンカーブの仕事です。
屋外のF16、1/100秒、ISO 100(写真家の「サニー16」)では、 cd/m²で、9段感度が低くなります。
自動露出と順応
ゲームでは普通、カメラの露出計や目の様に、露出を自動で選びます。
方法は15.4節で見ました。フレームの対数平均輝度を測り(普通はヒストグラムから、両端の数パーセントを無視して)、其れを0.18の様なキー値に置きます。
新しい露出に1フレームで飛ぶのは不自然に見えます。目は徐々に順応するからです。
其の為、EVで測った露出は目標に向かって指数的に動きます:
計算例。
プレイヤーが暗い部屋に入り、目標が−1から+3 EVに跳ねます。
60フレーム毎秒(秒)、時定数秒では、各フレームで差のを詰めます。最初のフレームの動きは EVです。
1秒後に残る差は EVで、露出はにあります。
フレームレートに関係無く同じ様に振る舞い、多くのゲームは明るくなる方向に短いを使います。目は暗さより光の方に速く順応するからです。
27.2 大域的なトーンマッピング演算子
良いカーブの条件
トーンマッピング演算子は、露出後のシーンの値を表示値に写します。
大域的な物は画像全体に1本のカーブなので、15.3節のトーンカーブと全く同じ画素ごとの濃淡変換です。只、入力の範囲に上限がありません。
写真家や下の図の様にに対して描くと、良いカーブには3つの部分があります:
- 暗い端のトウ:傾きが下がり、黒が黒のまま残ります。
- 中間の灰色(0.18)の周りの真っ直ぐな部分:其の傾きがコントラストを決めます。
- ショルダー:ハイライトをクリップせずに、範囲の上端へ徐々に曲げます。
此のS字は写真フィルムの光への応答の形で、其の為此の様なカーブはフィルミックと呼ばれます。
Reinhardのにはショルダーはありますがトウが無く、中間が平らで(画像が少し白っぽく見えます)、決して1に達しません(15.4節の拡張版は、白の値を使って最後の点だけを直します)。
Hableのフィルミックカーブ
ゲーム「Uncharted 2」の為に、John Hable(2010年)は6つのパラメータを持つ1つの有理関数で、トウとショルダーを持つカーブを公開しました:
(ショルダーの強さ)、、(直線部分)、(トウの強さ)、、です。
彼のシェーダーは入力に露出バイアス2を掛け、白点でのカーブの値で割ります。従ってが丁度1になります:
計算例:中間の灰色。
:分子、分母なので、。
では:分子、分母なので、。
:トウの為にReinhardの0.153より少し暗くなります。其の上ではカーブが急で、で0.493、で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に当てはめた有理関数のカーブです:
にクランプします。
事前の露出が組み込まれています(Narkowiczによれば、元のカーブにするには入力に0.6を掛けます)。
計算例: 、。
で1に達します(大きなでの極限はで、クランプが切り取ります)。
Hableのカーブより明るくコントラストが強く、トウも強めです(に対して0.0077)。
| 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通りあり、失敗の仕方が違います。
計算例。 リニアな色のオレンジの光:
- チャンネル毎にReinhard:。赤と青の比は16でしたが、4になりました。色は彩度がずっと低くなり、光が明るい程白に近付きます。チャンネル毎のHableはで、同じ効果です。
- 輝度に(15.4節):、で、色を倍して。比は正確に保たれますが、赤が1を超えます。にクランプすると色相が黄色寄りにずれ(赤と緑の比が4から2.6に下がります)、明るさも失われます。
クリップは全てのランプを黄、シアン、マゼンタ、白の平らな塊にします。輝度に掛けた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章のシラバス項目画素ごとの濃淡変換と色変換に繋がります:
- 大域的な演算子はトーンカーブです。同じ入力には同じ出力。露出(× )は其の前に、リニアな空間で行います。
- フィルミックなカーブにはトウ、真っ直ぐな中間部、ショルダーがあります。Reinhardのはショルダーだけで、で0.5になります。
- チャンネル毎に掛けると明るい色の彩度が下がり、輝度に掛けると比は保たれますが、チャンネルが1を超える事があります。
27.3 局所的なトーンマッピング
大域的なカーブが奪う物
17段を10段に押し込むカーブは、何処かでコントラストを下げなければなりません。大域的なカーブは其れを至る所で行います。他の場所に明るい窓があるというだけで、日陰の壁の模様のコントラストが下がります。
目は代わりに局所的に順応します。局所的なトーンマッピングの演算子は其れを真似て、写像を近傍に依存させます。従って同じ輝度の2つの画素が違う結果になり得ます。
数字で仕組みを見ましょう。
シーンのダイナミックレンジの殆どは大きなスケールの明るさの変化(部屋と窓)にあり、見たい模様は其の上に乗った小さな比です(目地より1.6倍明るいレンガ)。
比が差になる輝度ので考え、各画素を大きなスケールの部分であるベース層と、残りのディテール層に分けます:
そしてベースだけを圧縮し、ディテールを其のまま足し戻します。
バイラテラルのベース:DurandとDorsey
Frédo DurandとJulie Dorsey(「Fast Bilateral Filtering for the Display of High-Dynamic-Range Images」、SIGGRAPH 2002)は、15.5節のバイラテラルフィルタを対数輝度に掛けてベースを求めます。模様は滑らかにしますが、大きなエッジでは止まるからです。
ベースは、其の範囲全体が目標のコントラストになる様に選んだ係数で圧縮します:
Durand自身のメモでは目標のコントラストを(従って)にしています。を引く事で、一番明るいベースが、つまり画面の白になります。
色は15.4節と同じ様に運びます。各チャンネルを倍します。
計算例:窓を横切る1本の走査線。
部屋に4画素、窓に4画素、倍で交互に変わる模様があります:
シーンの範囲は、約1,260:1です。
範囲の(模様の0.2よりずっと大きく、段差の2.9よりずっと小さい)のバイラテラルフィルタは各側を平均しますが、エッジを越えては平均しません。ベースは各側の平均から0.02以内に収まるので、部屋で、窓でとします。
するとディテールは至る所でです。
- 圧縮:。
- 部屋:にディテールを足してと、つまりと。
- 窓:にディテールを足してと、つまりと(一番明るいテクセルは1を少し超えるのでクリップするか、結果全体を少し暗くします)。
部屋と窓の比は794:1から丁度5:1に下がりましたが、模様の比は両側とものままです。
同じ強さの大域的な圧縮(全ての対数を0.241倍)なら、模様はに下がっていました。殆ど見えません。
ハロー
何故普通のガウシアンぼかしでベースを求めないのでしょうか?
ぼかしはエッジで止まらないからです。
窓のすぐ隣では、ぼかしたベースが部屋と窓の中間にあるので、其処のディテール層にはエッジ自体の大きな一部が入ります。ディテールは圧縮しないので、其の部分が全力で表示されます。
此の走査線の32画素の長い版で、画素のガウシアンは部屋の最後の画素のベースをではなくにするので、ディテールはではなくになります。圧縮後の値はバイラテラルのベースの場合よりで0.94低く、本来の明るさの約9分の1です。
明るいエッジに沿った此の暗い帯(と反対側の明るい帯)がハローで、局所的なトーンマッピングの典型的なアーティファクトです。
エッジを保つフィルタ(バイラテラル、ガイデッド、27.7節のバイラテラルグリッド)がハローを減らし、圧縮を控えめにすれば残りも目立たなくなります。
露出融合
HDR画像を丸ごと飛ばす方法もあります。
Tom Mertens、Jan Kautz、Frank Van Reethの露出融合(「Exposure Fusion」、Pacific Graphics 2007)は、露出を変えて撮ったLDRの写真其の物を直接混ぜ合わせ、画素毎に其の画素が一番良く見える写真を選びます。
各写真は、3つの尺度の積である重みのマップを持ちます:
- コントラスト:グレー画像に掛けたラプラシアンフィルタ(15.5節)の絶対値。エッジや模様が高い点になります。
- 彩度:其の画素のR、G、Bの標準偏差。色の抜けた画素は低い点になります。
- 露出の良さ:各チャンネルが0.5にどれだけ近いか、を全チャンネルで掛けた物。
重みは各画素で和が1になる様に正規化します。
計算例:露出の良さだけ。
1つの灰色の画素が、2段ずつ離れた3枚の写真で0.05、0.45、0.97に写っています。
重みは、、で、和は1.112なので、正規化した重みは0.072、0.872、0.057です。
画素毎に混ぜるとになります。
画素毎に混ぜると、ある写真から別の写真に切り替わる所に継ぎ目が出来ます。其の為、混合は帯域毎に行います(15.1節)。各写真をラプラシアンピラミッドに、各重みのマップをガウシアンピラミッドにし、各レベルを其のレベルの重みで混ぜ、結果をまとめ直します。
粗いレベルは滑らかな重み(継ぎ目無し)、細かいレベルは鋭い重み(各ディテールは一番露出の良い写真から)を受け取ります。パノラマのマルチバンドブレンディング(16.3節)を、位置ではなく露出に使った物です。
合成の部屋の範囲は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²のピークまでは段のハイライトの余裕があります。
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(ディスプレイの輝度から信号へ。EOTF、電光変換関数はディスプレイ側の向きです)は
で、定数は正確な2進の分数です:
| 定数 | 定義 | 値 |
|---|---|---|
| 2610 / 16384 | 0.1593017578125 | |
| 2523 / 4096 × 128 | 78.84375 | |
| 3424 / 4096 = | 0.8359375 | |
| 2413 / 4096 × 32 | 18.8515625 | |
| 2392 / 4096 × 32 | 18.6875 |
ディスプレイが適用するEOTF其の物は其の逆関数です:
計算例:100 cd/m²。
、。
分子、分母、比は0.991449。
。
つまりSDRの全範囲、0〜100 cd/m²がPQの信号の約半分を使い、残りの半分が100〜10,000の為にあります。
同じ手順で、1,000 cd/m²は、203 cd/m²は0.5807になります。
10ビットの整数としては、PQはフルレンジ()か、映像ではナローレンジ(、符号64〜940、BT.2100)で送ります:
| 輝度(cd/m²) | 0.1 | 80 | 100 | 203 | 1,000 | 10,000 |
|---|---|---|---|---|---|---|
| 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)は逆の考え方を取ります。
相対的でシーン参照です。カメラはシーンの光(カメラが記録する一番明るい値を1に正規化)をOETF(光電変換関数、カメラ側の向き)で符号化します:
、、です。
平方根の下の部分はSDRのカメラが行う事に似ているので、HLGの信号は古いSDRのテレビでも其れなりに見えます。対数の上の部分がハイライトを詰め込み、定数は2つの部分が、で同じ傾きで繋がる様に選ばれています。
計算例。
:、対数の枝はで、同じです。
:。
ディスプレイは其の後、自分のピークに依存するシステムガンマを持つOOTFを適用します。チャンネル毎に(はシーンの輝度)、です。
1,000 cd/m²のディスプレイではです。BT.2408はHLGの基準白を信号0.75に置き、此れはで、 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の行列です:
各行の和は1(白は白のまま)で、sRGBの純粋な赤はBT.2020でになります。大きな三角形の内側なので、値は全て正のままです。
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から」となり、個の変換器の代わりに個のプロファイルで済みます。
プリンターが出せない色は、プロファイルのレンダリングインテントが決めます。知覚的は色域全体を滑らかに圧縮し、相対的な測色的は色域内の色を正確に保って残りをクリップし、彩度は色を鮮やかに保ち(グラフ用)、絶対的な測色的は紙の白まで再現します(色校正用)。
OCIOとACES
映画やゲームのパイプラインはOpenColorIO(OCIO。Sony Pictures Imageworks発で、今はAcademy Software Foundationのプロジェクト)を使います。1つの設定ファイルが制作の全ての色空間と其の間の変換を記述し、全てのアプリケーションが同じファイルを読みます。
一番良く使われる設定がACESです。保管用の空間ACES2065-1は全ての可視の色を囲むAP0の原色を使い、レンダリングと合成はBT.2020より少し広いAP1の原色を持つリニアなACEScgで行い、出力変換が画面毎の画像を作ります。
作業空間はレンダリングにとって重要です。色をチャンネル毎に掛け合わせると、原色が違えば結果も違うからです。
ガマットマッピング
大きな色域から小さな色域へ移ると、移り先に存在しない色が出来る事があります。
計算例。
BT.2020の純粋な緑を上の行列の逆行列でリニアなBT.709に変換するとです。2つの負の成分と1を超える成分を持つ、sRGBの三角形の外の色です。
各チャンネルをにクリップすると、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次元です。
個の標本の離散フーリエ変換(DFT)は
で、逆変換はです。
なので、各は信号と、周期のコサイン及びサインとの2つの内積(線型代数の第4章)です。其の大きさは其の周波数がどれだけ含まれるか、角度(位相)は山が何処にあるかを表し、は単なる和です。
計算例:。
4つの標本ではなので、係数はの累乗に過ぎません:。
とします:
- 、
- 、
- 、
- 。
信号が対称なので値は実数です。課題4では複素数になります。
畳み込み定理の計算例
ではをカーネルでぼかします。此れは第15章ののカーネルを標本0を中心に置いた物で、信号は端で折り返します(循環畳み込み、):
- 直接:、、、。
- 周波数経由:から、従ってで、逆DFTは、、、。
同じ答えで、周波数の側が理由を説明します。ぼかしは平均を保ち()、周波数1を半分にし、ナイキスト周波数を完全に取り除きます()。15.5節で市松模様について見た通りです。
此れが畳み込み定理です:標本毎に。
DFTは信号を周期的と見なすので、普通の畳み込みにするには画像とカーネルを先にゼロで埋めて広げます。そうしないと右端が左端に漏れ込みます。
FFT
定義通りに計算すると、DFTは出力1つにつき回、全部で回の掛け算が必要です。
高速フーリエ変換(FFT。CooleyとTukey、1965年。但しガウスが1805年頃に既に此の考えを持っていました)は和を偶数番目と奇数番目の標本に分けます:
とは半分の長さのDFTで、、です(バタフライ、1回の掛け算から2つの出力)。
分割を回繰り返すと、個のバタフライが残ります。
計算例。 では、偶数番目の2点DFTは、奇数番目はで、として、、、。
では、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を持つぼけは其の周波数を消し去り、(殆ど)0で割るとノイズが限り無く増幅されます。
周波数で見ると、ぼけた画像は(はノイズ)で、素朴な逆フィルタはで割ります。
ウィーナーフィルタ(Norbert Wiener、1940年代)は二乗誤差の期待値を最小にする線型フィルタで、ノイズと信号の電力比をとすると
です(は複素共役)。
ぼけが信号を強く残す所()ではで割り、そうでない所では潔く諦めます。
計算例:での実数のに対する倍率。
| 1 | 0.5 | 0.1 | 0.01 | 0 | |
|---|---|---|---|---|---|
| 逆フィルタ | 1 | 2 | 10 | 100 | ∞ |
| ウィーナー | 0.99 | 1.92 | 5.00 | 0.99 | 0 |
最大の倍率はでのです。
16.5節の3画素の動きぼけでは、がで丁度0になります。逆フィルタは其処で発散しますが、ウィーナーフィルタは其の周波数で何も出力せず、誤差は有限に留まります。
リチャードソン・ルーシー法や学習による方法も同じ原則に従います:ぼけが壊した周波数は信用しない事です。
27.7 エッジを保つフィルタとノイズ除去
15.5節のバイラテラルフィルタはエッジを越えずに平滑化しますが、遅く(重みが画素毎に変わるので畳み込みではなく、FFTの近道もありません)、アーティファクトもあります。より良い道具を見ていきます。
ジョイント(クロス)バイラテラルフィルタ
ジョイントバイラテラルフィルタ(クロスバイラテラルとも)は、範囲の重みを2枚目のガイド画像から取り、入力を平滑化します:
フラッシュ/ノーフラッシュ撮影(16.6節)はフラッシュ写真をガイドに使います。レンダラーはレイトレースした影やAOを、Gバッファの深度と法線をガイドにしてノイズ除去し(第24章)、ジョイントバイラテラルアップサンプリング(Kopf等、2007年)は半分の解像度のエフェクトのエッジをフル解像度の深度から取ります。
バイラテラルグリッド
Jiawen Chen、Sylvain Paris、Frédo Durand(SIGGRAPH 2007)は、バイラテラルフィルタがより高い次元の空間での普通のガウシアンぼかしである事に気付きました。各画素をに持ち上げると、エッジの反対側にある画素同士は3番目の次元で遠く離れます。
バイラテラルグリッドはの上の粗い3次元配列で、毎及び毎に大体1回標本化します:
- スプラット:各画素の値(と個数1)を其のグリッドのセルに足し込みます。
- ブラー:グリッドを3つの次元全てで小さなガウシアンでぼかします。
- スライス:各画素について、グリッドをで3線型補間し、値を個数で割ります。
計算例:大きさ。
1920 × 1080のフレームで画素、(0〜1の強度に対して)なら、グリッドはセルで、2,073,600画素の23分の1です。
ぼかしは此の小さなグリッドの上で行うので、コストはに殆ど依存せず(大きなシグマは大きなカーネルではなく、粗いグリッドを意味します)、全ての段階が局所的でGPU向きです。2007年にHD動画のエッジを保つフィルタリングをリアルタイムにし、色変換のグリッドを予測する学習済みのカメラパイプラインも此れを土台にしています。
ガイデッドフィルタ
Kaiming He、Jian Sun、Xiaoou Tang(「Guided Image Filtering」、ECCV 2010)は別の考えから出発します。小さな窓の中では、出力はガイドの線型関数だとします:
を小さく保つ罰則を付けて、最小二乗法でを入力に合わせると
が得られ、各画素は多くの窓に含まれるので、最終的な出力は其れらのとを平均します:。
平均、分散、共分散は全てボックスフィルタで、累積和(積分画像)を使えば窓の大きさに関係無く1画素当たりです。ガイデッドフィルタは線型時間で厳密で、近似ではありません。
計算例:自己ガイド()、、3画素の窓。
エッジでは:平均0.4、分散なので、、。出力はで、エッジは殆ど変わりません。
平らでノイズのある領域では:分散0.000267、、。出力はで、ノイズは殆ど消えます。
つまりはの役割を果たします。よりずっと小さな変化は平滑化され、大きな変化は保たれます。
出力が局所的にガイドの線型関数なので、バイラテラルフィルタが強いエッジで起こし得る勾配の反転のアーティファクトを避けられます。霞の除去(同じ著者達)、マットの修正、ジョイントアップサンプリング、リアルタイムのデノイザーに使われます。
非局所平均
Antoni Buades、Bartomeu Coll、Jean-Michel Morel(「A Non-Local Algorithm for Image Denoising」、CVPR 2005)は問いました:何故近くの画素とだけ平均するのか?
画像は自分自身を繰り返します(レンガ、葉、文字)。ある画素を平均する一番良い相手は、何処にあっても、近傍が同じに見える画素です。
非局所平均は、探索窓の中の各候補画素に、との周りのパッチの違いから重みを与えます:
はパッチの差の二乗の平均、はノイズのレベルに結び付いたフィルタの強さです。
計算例(1次元、3画素のパッチ、)。
ノイズを除きたい画素は51で、パッチはです。
候補Aのパッチは:差は、、重み。
候補B、:、重み。
候補Cは此の画素の隣、:、重み。
画素自身を重み1として:。
ガウシアンぼかしなら隣の11を混ぜて山を潰していました。非局所平均は他の山とだけ平均しました。
代償は時間です。21 × 21の探索窓と7 × 7のパッチでは、1画素当たり約回の演算が掛かります。
BM3D(Dabov等、2007年)が此の考えを洗練させ、今は学習によるデノイザーが主流です(第23章と第29章)。
異方性拡散
Pietro PeronaとJitendra Malik(IEEE PAMI、1990年)はガウシアンぼかしを熱拡散と見なし、強い勾配を越える熱の流れを減らしました。此れが異方性拡散です:
、ステップ0.25で行を考えると、画素1は差10を見て、伝導率は、変化は2.5ではなくです。一方、ノイズの差1はのままです。何回も繰り返すと、エッジは生き残り、ノイズは溶けます。
此のテスト画像(ノイズ)では、ガウシアンはエッジをぼかし、バイラテラルとガイデッドはエッジを保ち(此の設定ではガイデッドの方がノイズを多く残します)、非局所平均がはっきり勝ちます。縞と平らな領域が同じパッチを沢山提供するからです。
PSNR(ピーク信号対雑音比)は dBで、+6 dBでRMS誤差が半分になります。
27.8 ピラミッド・動き・解像度
ラプラシアンピラミッドによる合成、再び
ラプラシアンピラミッドによる合成(BurtとAdelson、1983年)は、マスクのガウシアンピラミッドを使って、2つの画像を帯域毎にと混ぜます。レベルでは切り替わりの幅が約画素なので、色は徐々に切り替わり、エッジは1〜2画素で切り替わり、継ぎ目もゴーストも出ません。
露出融合やパノラマの合成も此れを使います。
オプティカルフロー
動画の処理(手ぶれ補正、フレーム補間、時間方向のノイズ除去)には、各画素が何処へ動くかが必要です。画素毎のベクトル、オプティカルフローです。
基本的な仮定は明るさの不変性です。点は動いても明るさを保ちます:。
1次のテイラー展開で、此れは画素毎に1つの線型方程式になります:
、は画像の勾配(15.5節)、はフレーム間の変化です。
方程式1つに未知数2つ。1つの画素からは勾配に沿った動きしか分かりません。
此れが開口問題です。小さな穴から覗くと、動く真っ直ぐなエッジは、エッジに沿った動きが何であっても同じに見えます。
ルーカス・カナデ法(Bruce LucasとTakeo Kanade、「An Iterative Image Registration Technique with an Application to Stereo Vision」、IJCAI 1981)は、小さな窓の中の全ての画素が一緒に動くと仮定し、窓の方程式を最小二乗法で解きます。すると2 × 2の連立方程式になります:
計算例:4画素の窓。
| 画素 | |||
|---|---|---|---|
| 1 | 1 | 0 | −1 |
| 2 | 0 | 1 | −0.5 |
| 3 | 1 | 1 | −1.5 |
| 4 | 2 | 1 | −2.5 |
和:、、、、。
連立方程式はで、行列式はなので(線型代数の第10章)、、。
窓は右に1画素、下に半画素動きました。全ての画素の方程式が正確に成り立ちます(画素4:)。
窓の中の勾配が全て平行(真っ直ぐなエッジ)なら、行列は特異になります。再び開口問題です。
其の固有値(此処では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をにし、大きなカーネルで得をします。DFTは循環的なので、埋めて広げます。
- 逆畳み込みはぼけの周波数特性で割ります。ウィーナーフィルタは信号がノイズより弱い所で倍率を抑えます。
- エッジを保つ平滑化:バイラテラル(空間と範囲の重み)、ジョイント/クロスバイラテラル(範囲の重みをガイドから)、ガイデッドフィルタ(局所的な線型モデル)、非局所平均(パッチの類似度)、異方性拡散(強い勾配で伝導率が下がる)。第15章のメディアンフィルタもエッジを保つフィルタです。
- ピラミッド:ガウシアン(小さな複製)、ラプラシアン(帯域、厳密に再構成可能)。帯域毎に合成すると継ぎ目が出ません。
27.9 画像圧縮の詳細
DCT
2.10節でJPEGの手順を挙げました:YCbCr、クロマサブサンプリング、8 × 8ブロックのDCT(離散コサイン変換)、量子化、可逆符号化です。
DCTはコサインだけを使うDFTの親戚で、ブロックを繰り返すのではなく両端で鏡に映すと仮定するので、ブロックの境界に段差が無く、滑らかなブロックのエネルギーがごく少数の係数に集まります。
此れが其の8個の1次元の基底パターン(に対する)です。2.10節の64個のパターンは、横と縦のパターンの積です:
8ビットの値から128を引いた(「レベルシフト」)後のブロックに対するJPEGの順変換は
で、、其れ以外はです。
手で計算する為に、8行が全て同じ行であるブロックを考えます。
するとのコサイン(1です)のについての和は8になり、の係数は全て消え、一番上の行の係数だけが残ります:
計算例:滑らかな行。
行60、64、72、86、104、122、134、140(緩やかな傾斜)をレベルシフトすると:−68、−64、−56、−42、−24、−6、6、12。
、単なる和です。
のコサインは(最初の4つの標本で正、最後の4つで負)で、重み付きの和はなので、。
8個の係数全て:−242、−237.5、15.2、8.7、−2.0、−0.7、0.2、−0.9。
殆ど全てのエネルギー(99.7%)が最初の2つにあります。
量子化
JPEGは各係数を量子化テーブルの段差で割って丸めます。情報が失われるのは此の丸めです。
JPEG規格のAnnex Kは、見え方の実験から導いた輝度の表の例を挙げています。其の一番上の行(に使います)は16、11、10、16、24、40、51、61で、高い周波数ほど粗い段差になります。
エンコーダーは「品質」の設定に応じて表全体を拡大縮小します。
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | |
|---|---|---|---|---|---|---|---|---|
| −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 |
| 復号した | −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は dBで、目には見えません。
リンギングとブロックノイズ
鋭いエッジには高い周波数が必要です。
行40、40、40、40、200、200、200、200はまで大きな奇数番目の係数を持ちます(−64、−579.9、0、203.6、0、−136.1、0、115.4)。
表を4倍にする(低い品質の設定)と、のレベルは0に丸められ、他も粗くなり、復号した行は33、57、25、58、182、215、183、207になります。エッジの両側の波打ちで、215まで行き過ぎ、25まで沈みます。
此れがリンギング、2.10節の「モスキートノイズ」で、15.5節の理想的なローパスフィルタと同じギブズ現象です。
もう1つの典型的なアーティファクトはブロック其の物から来ます。
8 × 8ブロックは其々独立に量子化されるので、低い品質では滑らかなグラデーションが殆ど平らなブロックの階段になり、境界でレベルが合いません。此れがブロックノイズです。
動画のコーデック(H.264、HEVC、AV1)は符号化ループの中でデブロッキングフィルタを実行し、アーティファクトと見なせる程度に段差が小さい所だけでブロックの境界を跨いで平滑化します。此れもエッジを保つフィルタです。更にブロックを隣や他のフレームから予測します。WebP、AVIF、JPEG XLは同じ材料を洗練させた物で、執筆時点ではAVIFとJPEG XLはPQやHLGのHDR画像も保存出来ます。
検定のポイント
此れは第2章の画像ファイル形式と圧縮を広げます:
- JPEG:YCbCrとクロマサブサンプリング、8 × 8のDCT、量子化(非可逆の段階。高い周波数ほど粗い)、ジグザグ順、ランレングスとハフマン符号化(可逆)。
- 滑らかなブロックはエネルギーが低い周波数に集中します。量子化はブロックノイズ(ブロックを独立に量子化する為)と、エッジ付近のリンギング(高い周波数の除去)を起こします。
- 8ビットの画像ではPSNR dB。
27.10 テクスチャ圧縮
異なる要件
テクスチャはランダムな順に、一度に数テクセルずつ読まれ(第11章)、帯域を節約する為にメモリ上でも圧縮されたままでなければなりません。
其の為テクスチャ圧縮は固定レートのブロック形式を使います。(普通は)4 × 4の各ブロックが同じビット数を受け取るので、どのテクセルのアドレスも簡単な計算で求まり、テクスチャユニットがハードウェアでブロックを復号します。符号化は遅くても構いませんが、復号は簡単でなければなりません。
BC1の計算例
BC1(DXT1又はS3TCとも。1990年代後半のS3 Graphics発)は4 × 4ブロックのRGBのテクセルを64ビットに保存します:
- 2つの端点の色と、其々RGB565(赤5ビット、緑6ビット、青5ビット)の16ビット、合わせて32ビット。
- テクセル毎に2ビットのインデックスが16個(32ビット)。4色のパレット、、、から選びます。
24ビットの代わりに1テクセル当たり4ビット、RGB8に対して6:1、RGBA8に対して8:1です。
16ビットの数としてなら、ブロックは2つ目のモードに切り替わります。3色(、と其の平均)と透明な黒で、切り抜きのテクスチャ用です。
パレットはRGB空間の直線上にあるので、BC1はブロックの色が1本の直線の近くにある時に上手く働きます。此れは意外に良くある事です。
計算例。
ブロックは琥珀色から青への正確な4段のグラデーションで、右上の角に赤みがかったオレンジの外れ値が1つあります。
- 端点:一番違う2色は琥珀色と青です。RGB565では、、なので、、16ビットの値は = 0xED87。青は = 0x2A93 = 10,899。なので4色モードです。
- 端点を8ビットに復号(ビットの複製、、):、で、元から数レベルずれます。
- パレット:と。
- インデックス:各テクセルが一番近いパレットの色を選びます。グラデーションのテクセルは約4レベル以内で一致します(RGB565の丸め)。外れ値の距離は91.6、213.5、88.4、142.1で、インデックス2の黄褐色になり、誤差は88です。
ブロックは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テクスチャで、テクセルはブロックにあり、此れはバイトオフセットのブロック番号です。其の中ではテクセルで、インデックスはインデックスのワードのビット目と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は法線マップのとを保存し、シェーダーがを作り直します(、から)。
ASTC(Adaptive Scalable Texture Compression。ARM発で、Nystad等がHigh Performance Graphics 2012で発表し、同じ年にKhronosが採用)は常にブロック当たり128ビットを使いますが、ブロックの大きさを4 × 4から12 × 12テクセルまで変えられるので、ビットレートはです: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 |
最上位レベルはバイトで、ミップマップの連鎖が3分の1を加えます(11.7節)。
ブロック圧縮は帯域も同じ割合で減らし、メモリより其方の方が重要な事も良くあります。
27.11 まとめ
- シーン参照のリニアな値は露出され(、)、トーンマッピングでディスプレイ参照の値になり、符号化されます(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をで計算し、畳み込みは掛け算になります。ウィーナーフィルタはノイズを爆発させずに逆畳み込みします。
- エッジを保つフィルタ:ジョイントバイラテラル、バイラテラルグリッド、ガイデッドフィルタ、非局所平均、異方性拡散。ルーカス・カナデ法は窓毎に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 課題
- ★ 400 cd/m²のPQの信号と10ビットのフルレンジの符号、とのHLGの信号を求めて下さい。PQの値が203と1,000 cd/m²の値の間にある事を確かめて下さい。
- ★ とで、Reinhard、Hable(バイアス2、)、ACESのフィットを求めて下さい。一番影が暗いのはどのカーブで、0.09と3の間のコントラストを一番保つのはどのカーブですか?
- ★★ 走査線がです。各側の平均をベースとし、目標のコントラストを10として、、8つの出力全て、模様の比を求めて下さい。次に、同じ強さの大域的な圧縮で残る模様の比を求めて下さい。
- ★★ の4点DFTを求め(今回は複素数になります)、を掛けて逆変換して下さい。結果を、との循環畳み込みを直接計算した物と比べて下さい。
- ★★★ 純粋な赤8テクセルと純粋な緑8テクセルのBC1ブロックを符号化し、次に赤、緑、青のブロックも符号化して下さい。2つ目が失敗する理由、BC7の分割が其れを解決する方法、そしてミップマップ込みの2048 × 2048の法線マップがBC5とRGBA8で其々どれだけのメモリを使うかを説明して下さい。
選択問題
問1 ★ 次のうち、トーンマッピングについて正しい物はどれか。
| あ | Reinhardの演算子は、シーンの値1を画面の白に写す。 |
| い | 大域的なトーンマッピング演算子は、同じシーンの輝度を持つ2つの画素が画像の違う領域にあれば、違う表示値を与え得る。 |
| う | トーンカーブをRGBのチャンネル毎に掛けると非常に明るい色は白に近付き、輝度に掛けて色を拡大縮小するとチャンネルの間の比が保たれる。 |
| え | 露出はトーンマッピングの後に、表示値にを掛けて適用する。 |
| お | ガウシアンでぼかしたベース層を使う局所的なトーンマッピングは、ガウシアンフィルタがエッジを保つのでハローが出ない。 |
問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バイトになる。 |
| う | バイラテラルグリッドのコストは、グリッドが細かくなるので空間のの二乗に比例して増える。 |
| え | ASTCは常に4 × 4のブロックを使い、品質の設定で変わるのはブロック当たりのビット数だけである。 |
| お | 畳み込み定理により、畳み込みはゼロで埋めて広げた画像とカーネルのDFTを掛けて計算出来る。FFTを使うと、此れは大きなカーネルで得になる。 |
27.14 実践課題
1本の走査線を、シーンから画面へ、そしてファイルへ、手で追います。
電卓(、、累乗付き)、方眼紙、鉛筆を用意して下さい。
走査線は薄暗い部屋から日の当たる窓へ横切ります。シーンの輝度はcd/m²で2、3、2、3、2000、3000、2000、3000です。
- 手順1:露出。カメラはF2.8、1/60秒、ISO 800です。、、8つの露出後の値を求めます。
- 手順2:大域的なカーブ。8つの値にReinhard とHableのカーブ(バイアス2、、1でクランプ)を掛け、8ビットのsRGBに符号化し(3.7節)、両方の行を方眼紙に描きます。
- 手順3:局所的なトーンマッピング。シーンの値のを取り、各側の平均をベース、目標のコントラストを5として、、8つの出力、各側の模様の比を求めます。同じ強さの大域的な圧縮と比べます。
- 手順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つ並べたブロックのとを求め(此の行にはにもっと小さな係数もありますが、此処では無視します。のコサインは0.1951、−0.5556、0.8315、−0.9808、0.9808、−0.8315、0.5556、−0.1951)、Annex Kの段差16と61で量子化し、次にを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:は6を少し下回り、は70を少し上回ります。部屋の値は百分の数程度、窓の値は数十です。
- 手順2:Reinhardでは窓の4画素が8ビットで1レベルしか違わず(窓の模様は実質的に消えます)、Hableでは全て255です。部屋ではHableの方がReinhardより暗くなります(トウ)。
- 手順3:ベースの範囲は丁度3、は0.233、模様の比は両側とも1.5のままで、大域的な版では約1.1しか残りません。
- 手順4:203 cd/m²は表の通り594になり、600と900 cd/m²(0.6段の差)は40符号以上離れます。
- 手順5:は丁度量子化されます(レベル−34)。は段差61ではレベル−1として残り(コントラストが少しずつ違う縞が戻ります)、段差244では0になるので、行は60の平らな灰色になり、模様は消えます。
- 手順6:パレットは丁度40、80、120、160で、テクセル60、100、140は2つの色の丁度中間にあり(最大の誤差20)、RGB565の端点はもう純粋な灰色ではありません。160は(156, 162, 156)に復号され、少し緑がかります。
コメント(0)
まだコメントはありません。
ログインするとコメント出来ます。