15. 画像処理

此処まで、此のシリーズの殆ど全てはモデルから画像を作る話でした。
此の章は其の次の段階、つまり画像を受け取って別の画像を作る画像処理の話です。
第2章(2.11節)では、何が入って何が出るかで分野を分け、画像処理は「画像 → 画像」でした。

何故CGをやる人に画像処理が必要なのでしょうか?
レンダリングしたフレームが其のまま画面に届く事は無いからです。ゲームは1を遥かに超える光の値を計算し、トーンマッピングで其れを画面の範囲に押し込み、更に毎フレームぼかし、シャープにし、色を調整します。
コンポジターは画像を変形し、合成し、レタッチします。テクスチャは、真っ直ぐに直して綺麗にした写真から作られます。
検定対策としては、此の章は分野6の6つのシラバス項目を全て扱います:ディジタル画像の表現、2次元画像の生成と描画、画素ごとの濃淡変換と色変換、領域に基づく画像変換(空間フィルタリング)、画像の幾何学的変換、そして画像の編集です。

第2章、第3章の色空間・ガンマ・輝度、第4章の2次元の変換、第6章のラスタライズ、第8章のベジェ曲線、第10章のover演算、第11章のテクスチャフィルタリングの上に成り立っています。
此の章のテスト画像は全てプログラムで生成した物で、処理前後の画像は全て本文の式で計算しています。

15.1 ディジタル画像の表現

濃淡のディジタル画像は、各画素の位置に値を与える関数f(x,y)です。xは列、yは行で、左上の角から始まり、yは下向きです(2.5節)。
カラー画像は、チャンネル毎に1つずつ、3つの関数を持ちます。
此の章の全ての処理は古い画像fから新しい画像gを計算しますが、各出力画素が入力のどれだけを見るかで分類すると便利です:

  • 画素ごと(点)の処理:g(x,y)はf(x,y)だけで決まります(トーンカーブ、色変換。15.3節と15.4節)。
  • 局所(近傍)の処理:g(x,y)は(x,y)の周りの小さな窓で決まります(フィルタ。15.5節)。
  • 幾何学的な処理:g(x,y)はfの別の場所から値を取ります(回転、拡大縮小、変形。15.6節)。
  • 大域的な処理:全ての出力が画像全体に依存します(ヒストグラム平坦化、フーリエ変換)。

ヒストグラム

画像のヒストグラムは、各値を持つ画素の数を数えた物です:h(k) = レベルkの画素数。
画素が何処にあるかは忘れ、どれだけ明るいかだけを残します。暗い画像のヒストグラムは左に寄り、コントラストの低い画像では幅が狭く、白飛びがあると右端に鋭い山が出来ます。

15.1節と15.3節では、ずっと1つの小さな画像を使います。4 × 4画素、8階調(0〜7)です。
画素は行毎に 1 2 2 5 / 1 2 3 5 / 2 2 4 6 / 1 2 5 5 です。
数えるとヒストグラムが得られます:

レベル k 0 1 2 3 4 5 6 7
h(k) 0 3 6 1 1 4 1 0
累積 H(k) 0 3 9 10 11 15 16 16

4 × 4の例の画像と其のヒストグラム、レベル3と4の間の大津のしきい値、そしてヒストグラム平坦化の後の画像とヒストグラム

ヒストグラムには2つの山があります。レベル2付近の暗い山と、レベル5付近の明るい山です。背景の上に物体がある時に典型的な、双峰性のヒストグラムです。
最後の行は累積ヒストグラム H(k)=h(0)+h(1)+⋯+h(k) で、レベルk以下の画素数です。最後は必ず画素数N=16になります。
Nで割るとどちらも割合になり、15.3節で其れを使います。

画像の統計量

ヒストグラムを2つの数でまとめます。
平均は平均の明るさ、分散は其の周りの広がりで、コントラストの簡単な目安です:

μ=1N∑kkh(k),σ2=1N∑k(k−μ)2h(k)

小さな画像では、∑kh(k)=1⋅3+2⋅6+3+4+5⋅4+6=48なので、μ=48/16=3です。
距離の2乗を画素数で重み付けすると3⋅4+6⋅1+1⋅0+1⋅1+4⋅4+1⋅9=44なので、σ2=44/16=2.75、標準偏差はσ=2.75≈1.658です。

二値画像と近傍

二値画像は0と1(背景と物体)の2つの値だけを持ちます。
二値画像は二値化(15.3節)で生まれ、マスクや形の解析に使われます。
二値画像を扱うには、どの画素が近傍かを決める必要があります。4近傍は左右上下の画素、8近傍は其れに斜めの4つを加えた物です。
近傍を辿って繋がる物体画素の集まりが連結成分で、連結成分に番号を付けるのがラベリングです。プログラムは此れで物体の数を数えます。
此の選択は結果を変えます。角だけで接する2つの物体画素は、8連結では1つの成分、4連結では2つの成分です。

処理に使う色空間

第3章の色空間を何に使うか:

  • sRGB(ガンマで符号化された値):ファイルが保存し、殆どの編集ソフトが直接扱う値。其の和や平均は物理的には正しくありません(3.7節)。
  • リニアRGB:光を扱う全ての処理に。ぼかし、リサイズ、露出、トーンマッピング。
  • YCbCr(3.6節):明るさYだけにフィルタを掛けられます(鮮鋭化、ノイズ除去)。
  • HSV/HSL(3.5節):色相や彩度の変更、色による選択。
  • CIELAB:おおよそ知覚的に均等で、「2つの色がどれだけ違って見えるか」に。

HDR画像

2.10節のHDRの予告で述べた様に、実際のシーンは「画面で出せる一番明るい白」より遥かに明るくなり得ます。
ダイナミックレンジは、意味のある一番明るい値と一番暗い値の比で、写真では2倍毎の段(ストップ)で数えます:段数=log2(Lmax/Lmin)。
15.4節で使う部屋のシーンの輝度は、0.0166(影の中の床)から282(窓の中の太陽)まで。比は約17,000、つまりlog217000≈14段です。
8ビットの画像では、影と窓の両方を保持出来ません。

ハイダイナミックレンジ画像(HDR画像)は、リニアな光の値を浮動小数点数で保存します。其の為0.0166も282も同じ相対精度で残ります。
押さえておきたい形式は2つです:

  • OpenEXR(.exr):Industrial Light & Magicで開発され、今では映画業界の標準です。チャンネル毎に32ビット浮動小数点数か、16ビットのhalf(符号1ビット、指数5ビット、仮数10ビット)。halfは65,504までの値を相対的な刻み2−10≈0.001、つまり何処でも約3桁の精度で表し、チャンネル数も自由です。
  • Radiance HDR(.hdr、Greg WardのレンダラーRadianceから)と其のRGBE符号化:1画素4バイトで、色チャンネル毎に8ビットの仮数と、共通の指数E 1つ。チャンネルの値はm⋅2E−136です。

計算例:RGBE。
リニアな色(0.3,1.5,6.0)を符号化します。
一番大きいチャンネルは6.0=0.75⋅23なので指数は3、保存するバイトはE=3+128=131です。
一番大きいチャンネルが8ビットの仮数一杯になる様に全チャンネルを拡大します。係数は0.75⋅256/6.0=32で、0.3⋅32=9.6→9、1.5⋅32=48、6.0⋅32=192です。
復号:9⋅2131−136=9/32=0.28125、48/32=1.5、192/32=6.0。
明るいチャンネルは正確に戻りますが、暗いチャンネルは6%失いました。共通の指数は安上がり(12バイトではなく4バイト)ですが、色の強い画素の暗いチャンネルの精度は下がります。

HDR画像はレンダラーや、露出を変えて撮った写真の合成(第16章)から生まれ、第12章(12.13節)のライトプローブとして使われます。画面に表示するにはトーンマッピング(15.4節)が必要です。

画像ピラミッド

多くのアルゴリズムは、同じ画像を幾つかの縮尺で欲しがります。
画像と、段々小さくなる其のコピーを積み重ねた多重解像度の表現が画像ピラミッドです。
1つは既に知っています。11.7節のミップマップは2 × 2のブロックを平均して作るピラミッドで、余分なメモリは3分の1だけでした。

  • ガウシアンピラミッドは、半分にする前に毎回小さなガウシアン(15.5節)でぼかします。2 × 2のボックスより良い前置フィルタです。
  • ラプラシアンピラミッド(Burt と Adelson、1983年)は、各レベルに其のレベルと、1つ粗いレベルを拡大した物との差を保存し、最後に一番小さいレベル其のものを保存します。足し戻すと元の画像が正確に再構成されます。

テスト画像の4レベルのガウシアンピラミッドと、エッジと細部だけが写った対応するラプラシアンピラミッド

ラプラシアンの各レベルは殆どがゼロ(灰色)で、エッジに線があるだけです。1つの周波数帯で、画像を帯域毎に合成する(パノラマ、第16章)のや、粗い所から細かい所へ進むマッチングに便利です。

15.2 2次元画像の生成と描画

2つ目のシラバス項目は、形から画像を作る事です。ペイントソフト、ベクターイラスト、ユーザーインターフェース、PDFビューア、そしてフォントです。

ラスタ画像への図形の描画

線を引いたり多角形を塗ったりするのは第6章のラスタライズ(ブレゼンハム、6.6節。円、6.7節。スキャンラインによる塗りつぶし、6.8節)で、画素毎に「内か外か」を決める為、ジャギーが出ます。

アンチエイリアス描画

アンチエイリアス描画は、エッジの各画素に被覆率、つまり画素の面積のうち図形の内側にある割合を与えます(エリアサンプリング、2.7節)。
被覆率をアルファ値として使い、図形の色と既にある色を混ぜます。
計算例。白い背景の上で、黒い図形が画素の30%を覆っています。
リニアな光では、此の画素は白の0.3⋅0+0.7⋅1=0.7を出すべきで、3.7節のsRGB符号化で0.854、コード218になります。
コードを直接混ぜると0.7⋅255≈178で目に見えて暗く、素朴に合成した文字が太く見えるのは其の為です。
線については、Xiaolin Wuのアルゴリズム(1991年)がブレゼンハムのアンチエイリアス版です。各列で、正確な線に一番近い2つの画素に距離に応じて明るさを分けます。線がy=2.25を通る所では、行2と行3の画素が75%と25%を受け取ります。

2次元ベクターグラフィックスと塗りつぶし規則

SVGやPDFの様なベクタ形式(2.9節)は、絵をパスで表します。「移動」「直線」「2次又は3次のベジェ曲線」(8.4節のベジェ曲線)「閉じる」と言った命令の列です。
パスは線として描く(太さ、端点、角の形を持つ)事も、塗る事も出来ます。塗る時は曲線を短い線分に分割し(ド・カステリョの分割)、多角形を塗ります。

部分が重なるパスでは「内側」に規則が必要で、6.8節で2つを紹介しました。偶奇規則と非ゼロ回転数規則です。
どちらも点から右へ半直線を伸ばします。偶奇規則は交差の数を数え、非ゼロ規則は上向きに横切る辺毎に+1、下向きの辺毎に−1を足します。此の和が回転数です。
実際に一番大事なのは、正方形の穴が開いた正方形の場合です:

2つの入れ子の正方形。内側を外側と同じ向きに描いた回転数2の場合と、逆向きに描いた回転数0の場合を、其々偶奇規則と非ゼロ規則で塗った物

計算例。
外側の正方形は反時計回りなので、右の辺は上向きです。中心からの半直線は其れを+1で横切ります。
内側の正方形も反時計回りなら、其の右の辺が更に+1を加え、回転数は2です。
偶奇規則は交差を2つ、つまり偶数と見て中心を空け、非ゼロ規則は2 ≠ 0と見て塗ります。
内側の正方形が時計回りなら、其の右の辺は下向きで−1。回転数は1−1=0となり、両方の規則が穴を空けます。
2つの正方形の間の点は、どちらの場合も回転数1で、両方の規則で塗られます。
フォントやアイコンのデザイナーが穴(「o」の内側)を外形と逆向きに描くのは此の為です。そうすれば、レンダラーがどちらの規則を使っても穴が正しく出ます。

フォントと文字の描画

アウトラインフォントは各グリフを塗りつぶすパスとして保存します。TrueTypeは2次ベジェ曲線、PostScript/CFF(OpenType)は3次ベジェ曲線です(8.4節)。
ビットマップフォントは、決まった大きさの出来上がった画素の画像を保存します。
小さな大きさでは、幅1.4画素の縦線が文字毎に画素の格子に違う様に乗ってしまうので、ヒンティングが輪郭を少しずらし、縦線やベースラインが画素の境界に揃う様にします。
ゲームではグリフを符号付き距離場のテクスチャとして保存する事が多く、どんな倍率でもくっきりしたままです。文字の描画はOpenGLシリーズの第26章で自分で行います。

ペイントツールとブラシ

ペイントソフトのブラシは、殆ど必ずスタンプです。小さなブラシの画像のコピーをストロークに沿って一定の間隔で置き、キャンバスに合成します。
間隔(ブラシの直径に対する割合)はストロークが繋がって見えるかを決め、フロー(各スタンプの不透明度)はスタンプが重なる所で絵の具がどれだけ速く溜まるかを決めます。

スタンプで作った波形のブラシストローク。間隔が直径の100%、50%、そしてフロー0.2で10%の場合

計算例。長さ100画素のストロークを直径20画素のブラシ、間隔25%で描くと、0.25⋅20=5画素毎にスタンプが置かれ、100/5+1=21個のスタンプになります。
フロー0.2で各スタンプをover演算で合成すると、n個のスタンプに覆われた点の不透明度は1−(1−0.2)nです。n=1,2,3,4で0.2、0.36、0.488、0.590。
筆圧は普通、各スタンプの大きさ又はフローを変えます。

アルファによるレイヤーの合成

ドローソフトはレイヤーを下から上へ10.12節のover演算で重ねます。両方のレイヤーが半透明になり得る場合の完全な式は次の通りです:

αo=αs+(1−αs)αd,Co=αsCs+(1−αs)αdCdαo

第14章(14.10節)で其の他の利点と一緒に紹介した乗算済みアルファ(プリマルチプライドアルファ)、C′=αCを使えば割り算は消え、overは色にもアルファにも同じ簡単な式になります:Co′=Cs′+(1−αs)Cd′。
計算例。αs=0.6の赤(1,0,0)を、αd=0.5の青(0,0,1)の上に重ねます。
乗算済みではCs′=(0.6,0,0)、Cd′=(0,0,0.5)。
Co′=(0.6,0,0)+0.4⋅(0,0,0.5)=(0.6,0,0.2)、αo=0.6+0.4⋅0.5=0.8。
αoで割ると、普通の色(0.75,0,0.25)になります。

15.3 画素ごとの濃淡変換

画素ごと(点)の処理は、各出力値を同じ画素の入力値だけから計算します:g(x,y)=T(f(x,y))。
此の方法で濃淡の値を変えるのが濃淡変換、色に対して行うのが色変換(15.4節)です。

トーンカーブ

関数Tを入力から出力へのグラフとして描いた物がトーンカーブです。
対角線T(v)=vは何も変えません。カーブが急な範囲ほど、其の範囲の値のコントラストが上がります。

5つのトーンカーブ:50から180の線型の引き伸ばし、ネガ、γ = 0.5と2のガンマカーブ、4階調のポスタリゼーション、128でのしきい値

0〜255の8ビットの値fに対する代表的なカーブと、其々1つの計算例です:

  • 線型の引き伸ばし(コントラスト伸張):使われている範囲[a,b]を全範囲に写します。g=255f−ab−a、0〜255にクリップ。a=50、b=180で、f=100は255⋅50/130=98.08→98。
  • ネガ(反転):g=255−f。100は155になります。
  • ガンマ変換:g=255(f/255)γ。γ<1は暗い階調を明るくし、γ>1は暗くします。γ=0.5ならf=64は25564/255=127.7→128、γ=2なら16.06→16。(3.7節のディスプレイのガンマと同じ種類のべき乗のカーブを、編集の道具として使う物です。)
  • ポスタリゼーション:n階調だけを残します。g=round(⌊fn/256⌋⋅255n−1)。n=4、f=100で⌊1.5625⌋=1なのでg=85。ポスタリゼーションは2.8節の疑似輪郭をわざと作ります。
  • コントラストと明るさ:g=c(f−128)+128+b。コントラストcは中間の灰色を中心に直線を回し、明るさbは直線をずらします。c=1.5、b=10で、f=100は1.5⋅(−28)+138=96。
  • しきい値:0から255への段差。下の二値化です。

各トーンカーブを適用したテスト画像:元の画像、線型の引き伸ばし、ネガ、ガンマ0.5、4階調のポスタリゼーション、128での二値化

トーンカーブは画像の何処でも同じ入力を同じ出力に写すので、ある灰色の四角形だけを明るくする事は出来ず、同じ灰色の四角形が全て明るくなります。其れにはマスクや局所的な処理が必要です。

ルックアップテーブル

8ビットの画像の入力値は256通りしか無いので、何百万もの画素についてT(べき乗関数かも知れません)を計算する代わりに、プログラムは256回だけ計算し、結果をルックアップテーブル(LUT)に保存します。
すると1画素の変換は配列へのアクセス1回、g = lut[f]です。
色では3D LUTがRGBをRGBに写します。典型的なカラーグレーディングのLUTは333=35937個の項目を持ち、其の間をトライリニア補間します。ゲームエンジンは此の様な「ルック」をポストプロセスで3次元テクスチャとして適用します。

二値化と大津の方法

二値化は、しきい値tで濃淡画像を二値画像にします。tより大きい画素は1、其れ以外は0です。
128の様な固定のしきい値は、明るさが予め分かっている時にしか上手く行きません。
大津の方法(大津展之、1979年。判別分析法とも呼ばれます)は、ヒストグラムだけからtを選びます。
tでレベルを分けると、暗いクラス(k≤t)と明るいクラス(k>t)の2つのクラスが出来、其々画素の割合ω0,ω1と平均レベルμ0,μ1を持ちます。
良いしきい値は2つのクラスを遠く離し、各クラスをまとまらせます。大津の方法はクラス間分散を最大にします:

σB2(t)=ω0ω1(μ0−μ1)2

此れはクラス内の分散を最小にするのと同じです。2つの和は常に全体の分散σ2になるからです。

小さな画像での計算例(N=16):

t ω0 ω1 μ0 μ1 σB2
1 3/16 13/16 1 45/13 ≈ 3.462 0.923
2 9/16 7/16 15/9 ≈ 1.667 33/7 ≈ 4.714 2.286
3 10/16 6/16 1.8 5 2.4
4 11/16 5/16 2 5.2 2.2
5 15/16 1/16 2.8 6 0.6

t=3では、暗いクラスはレベル1、2、3で、画素は3+6+1=10個、合計は3+12+3=18なのでμ0=1.8。明るいクラスはレベル4、5、6で、画素は1+4+1=6個、合計は4+20+6=30なのでμ1=5です。
σB2=1016⋅616⋅(1.8−5)2=0.234375⋅10.24=2.4が最大で、大津のしきい値はt=3、ヒストグラムの図の破線です。
確認:クラス内の分散は0.36(暗い方)と0.333(明るい方)で、重み付けすると1016⋅0.36+616⋅0.333=0.35、そして0.35+2.4=2.75=σ2です。
(しきい値0と6はクラスが1つ空になるので飛ばします。)

灰色の背景の上の暗い円盤のノイズのある画像。固定しきい値128では殆ど全部が黒になり、大津のしきい値では円盤が綺麗に分かれる。大津のしきい値を示した双峰性のヒストグラム付き

此処では背景が約107、円盤が約36なので、固定のしきい値128は全てを暗いと判定しますが、大津の方法は谷の中にt=71を見つけます。
画像の中で照明が変わる場合は、代わりに適応的二値化が領域毎にしきい値を計算します。

ヒストグラム平坦化

コントラストの低い画像は、使えるレベルの一部しか使っていません。
ヒストグラム平坦化(均等化とも)は、累積ヒストグラムをトーンカーブとして使う事で其れを広げます:

g(k)=round((L−1)H(k)N)

Lは階調数、Nは画素数です。
画素の10%が其れより暗いレベルは出力範囲の10%の位置に行くので、ヒストグラムの混んだ部分が引き伸ばされます。

小さな画像での計算例、L=8、N=16:

k 1 2 3 4 5 6
H(k) 3 9 10 11 15 16
7 H(k) / 16 1.3125 3.9375 4.375 4.8125 6.5625 7
g(k) 1 4 4 5 7 7

画像は 1 4 4 7 / 1 4 4 7 / 4 4 5 7 / 1 4 7 7 になり、ヒストグラムはh′=(0,3,0,0,7,1,0,5)です(此の章の最初の図の右半分)。
平均は3から4.4375に、分散は2.75から4.371に上がりました。コントラストが上がり、1から7までの全範囲が使われています。
注目すべき点が3つあり、3つとも検定対策として押さえておきたい物です:

  • 結果は平らではありません。同じ入力レベルの画素は必ず同じ出力レベルになるので、高い棒を分ける事は出来ません。平坦化は棒同士を離すだけです。
  • レベルは合流し得ます。2と3は両方4に、5と6は両方7になりました。異なるレベルの数は決して増えず、減るだけです。
  • カーブは単調(決して減らない)なので、明るさの順序は保たれます。暗い画素が明るい画素より明るくなる事はありません。

コントラストの低いテスト画像のヒストグラム平坦化の前と後。両方のヒストグラムと、橙色の累積ヒストグラム付き

細部が現れますが、滑らかな部分(空)は引き伸ばされて縞が見え、ノイズも強調されます。
最も暗い使用レベルが丁度0になる様にH(k)−HminN−Hminを使う本もあります。CLAHEは小さなタイル毎に傾きを制限して平坦化する方法で、医用画像で良く使われます。

15.4 色変換とトーンマッピング

色変換は、画素毎の関数を3つのチャンネルに同時に適用します。
役に立つ物の多くは3 × 3の行列です:𝐜′=𝐌𝐜、𝐜=(R,G,B)。線型代数シリーズの第6章の行列・ベクトル積です。
以下の計算例は全て、橙色𝐜=(0.8,0.4,0.2)を使います。

カラーのテスト画像の元の画像、グレースケール、セピアの行列、彩度0.5と1.5、色相を120度回した物

グレースケール

灰色の版は、各チャンネルをどれだけ明るく見えるかで重み付けします(3.6節)。
編集ソフトは普通、保存された値にルーマの式を使います。BT.601の重みでY′=0.299⋅0.8+0.587⋅0.4+0.114⋅0.2=0.4968。
物理的に正しい方法は、先ずリニアに復号し(0.6038,0.1329,0.0331)、BT.709の輝度Y=0.2126⋅0.6038+0.7152⋅0.1329+0.0722⋅0.0331=0.2258を取り、再び符号化します:0.5125。
単純な平均(R+G+B)/3=0.4667は一番大雑把な選択で、青を緑と同じ明るさにしてしまいます。

色の行列:彩度、セピア、ホワイトバランス

彩度の変更は、色を其の色自身の灰色から遠ざけたり近付けたりします:チャンネル毎に𝐜′=Y+s(𝐜−Y)。
Y=0.4968で、s=0は灰色(0.4968,0.4968,0.4968)、s=0.5は(0.6484,0.4484,0.3484)、s=1.5は(0.9516,0.3516,0.0516)です。
YはR、G、Bの線型結合なので、此の処理全体も1つの3 × 3の行列です。

セピア調、つまり古い写真の茶色は、全ての行が重みの違う「輝度の様な」和である行列です。良く使われる物は次の通りです:

[R′G′B′]=[0.3930.7690.1890.3490.6860.1680.2720.5340.131][RGB]

例の橙色では:R′=0.3144+0.3076+0.0378=0.6598、G′=0.2792+0.2744+0.0336=0.5872、B′=0.2176+0.2136+0.0262=0.4574。
全ての色が同じ茶色の濃淡になります。白は(1.351,1.203,0.937)になるので、クリップが必要です。

ホワイトバランス(3.8節)は、灰色の物体が無彩色になる様に各チャンネルを拡大縮小する対角行列diag(gR,gG,gB)で、リニアな値に適用すべき物です。

色相の回転

色相を回すと、全ての色が色相環の上を移動します。
綺麗な方法はHSV(3.5節)を通す事です。変換し、Hに角度を足し、戻します。
計算例。(0.8,0.4,0.2)はV=0.8、S=(0.8−0.2)/0.8=0.75、H=60°⋅0.4−0.20.8−0.2=20°です。
120°を足すとH=140°、緑が最大のチャンネルである120°から180°の区間です。
従ってG=V=0.8、R=V(1−S)=0.2、Bは最小値から上がってB=0.2+0.6⋅140−12060=0.4、結果は(0.2,0.8,0.4)です。
丁度120°の回転はチャンネルを入れ替えるだけ(R,G,B)→(B,R,G)です。シェーダーでは、他の角度をRGB空間の灰色の軸周りの3 × 3の回転で近似する事が良くあります。

HDR画像のトーンマッピング

いよいよ第2章、第3章、第10章の約束です。HDR画像を0から1までの画面に表示する方法です。
トーンマッピングは、シーンの輝度L(0から「幾らでも」)から表示値(0から1)へのトーンカーブです。
其の前の段階が露出です。リニアな値に2EVを掛けます。+1 EVは明るさを2倍に、−2 EVは4分の1にします。カメラの絞りを1段開けたり閉じたりするのと全く同じです(2.3節)。

何もしなければクリップ、min(L,1)です。露出0では窓が真っ白になり、−5 EVでは部屋が真っ黒になります。

HDRの部屋のシーンを露出0でクリップした物、−5 EVでクリップした物、キー0.18のReinhardの演算子でトーンマッピングした物、拡張Reinhardの演算子でトーンマッピングした物

Reinhardの演算子(Reinhard等、2002年)は、クリップせずに圧縮します:

Ld=L1+L

小さな値は殆ど其のまま通り(L=0.18→0.153)、L=1は0.5、L=4は0.8になり、L=100でさえ0.990にしかなりません。全てが収まり、何もクリップされず、コントラストは主にハイライトから取られます。
拡張版では、一番明るい値Lwhiteが丁度1になります:

Ld=L(1+L/Lwhite2)1+L

Lwhite=4では、L=4は4⋅(1+4/16)5=1、L=1は1.06252=0.531になります。

クリップ、ReinhardのL割る1足すL、L whiteが4の拡張Reinhardのカーブ。点1, 0.5と4, 0.8と4, 1に印付き

露出を自動で決める為に、Reinhardは画像の対数平均輝度がキー値a(普通は中間の灰色の0.18)になる様に画像を拡大縮小します:

L¯=exp(1N∑ln(δ+L)),Ls=aL¯L

(δはln0を避ける為の小さな数です。幾何平均を使うのは、太陽が支配しない様にする為です。)
計算例。輝度0.01、0.1、1、10の4つの画素(δは無視)。
L¯=exp(ln0.01+ln0.1+ln1+ln104)=exp(−4.605−2.303+0+2.3034)=e−1.151=0.3162。
倍率は0.18/0.3162=0.5692で、輝度は0.00569、0.0569、0.5692、5.692になります。
Reinhard:0.00566、0.0539、0.3627、0.8506。
画面用にsRGBで符号化すると(3.7節):0.067、0.257、0.636、0.931。
1000:1(約10段)の比が画面に収まり、一番暗い画素もまだ黒と区別出来ます。

トーンマッピングは輝度に対して行い、色を其れに合わせて拡大縮小𝐜d=𝐜⋅Ld/Lすべきです。そうすれば色相が変わりません。
ゲームでは主にACESの様なS字型の「フィルミック」なカーブが使われます。此れらは全て大域的(画像全体に1本のカーブ)で、局所的な演算子は目の様に領域毎に適応します(第2部)。

シェーダーで

リアルタイムレンダラーは浮動小数点数のフレームバッファに描き、全画面のパスでトーンマッピングします。其の中心部分は次の様になります:

// Exposure, then Reinhard on luminance; the result is still linear.
float3 toneMapReinhard(float3 hdr, float exposureEv) {
  float3 c = hdr * exp2(exposureEv);
  float L = dot(c, float3(0.2126, 0.7152, 0.0722));
  float Ld = L / (1.0 + L);
  return saturate(c * (Ld / max(L, 1e-6)));
}
// Exposure, then Reinhard on luminance; the result is still linear.
vec3 toneMapReinhard(vec3 hdr, float exposureEv) {
  vec3 c = hdr * exp2(exposureEv);
  float L = dot(c, vec3(0.2126, 0.7152, 0.0722));
  float Ld = L / (1.0 + L);
  return clamp(c * (Ld / max(L, 1e-6)), 0.0, 1.0);
}
// Exposure, then Reinhard on luminance; the result is still linear.
float3 toneMapReinhard(float3 hdr, float exposureEv) {
  float3 c = hdr * exp2(exposureEv);
  float L = dot(c, float3(0.2126, 0.7152, 0.0722));
  float Ld = L / (1.0 + L);
  return saturate(c * (Ld / max(L, 1e-6)));
}

最後のクランプが必要なのは、彩度の高い色をLd/Lで拡大縮小すると、輝度が1未満でも1つのチャンネルが1を超え得るからです。
結果は其の後sRGBで符号化します。手で行う(3.10節)か、sRGBのフレームバッファに書き込みます。
正に此のパスをOpenGLシリーズの第21章で作ります。

検定のポイント

  • 点処理:g=T(f)。トーンカーブは同じ値の2つの画素を区別出来ません。線型の引き伸ばし255(f−a)/(b−a)、ネガ255−f、ガンマ255(f/255)γ(γ<1で明るくなる)、ポスタリゼーション、しきい値。
  • LUTは、全ての入力値(8ビットなら256個)についてTを予め計算しておく物です。
  • 大津の方法は、クラス間分散ω0ω1(μ0−μ1)2を最大にするしきい値を選びます(クラス内分散の最小化と同じ)。
  • ヒストグラム平坦化は正規化した累積ヒストグラムをトーンカーブとして使います。結果は完全には平らにならず、レベルは合流しても分かれる事は無く、明るさの順序は保たれます。
  • グレースケール:単純な平均ではなく重み付きの和(ルーマにはBT.601の0.299/0.587/0.114、リニアな輝度にはBT.709の0.2126/0.7152/0.0722)。
  • HDR画像はリニアな浮動小数点数の値を保存します(OpenEXRのhalf/float、共通の指数を持つRadianceのRGBE)。トーンマッピングは其れを0〜1に圧縮します。例えばキーと対数平均で拡大縮小した後のReinhard L/(1+L)。

15.5 空間フィルタリング

空間フィルタは、各出力画素を入力画素の近傍から計算します。シラバスの「領域に基づく画像変換」、つまり空間フィルタリングです。
殆どのフィルタは線型で、小さな重みの表で書けます。

畳み込み

重みはカーネル(フィルタ係数、マスクとも)を成し、普通は中心を(0,0)とする3 × 3又は5 × 5です。
出力は近傍の重み付き和です:

g(x,y)=∑j=−rr∑i=−rrh(i,j)f(x+i,y+j)

厳密には此れは相関で、数学的な意味での畳み込みはf(x−i,y−j)を使います。カーネルを180°回した相関と同じです。
対称なカーネルでは両者は同じで、下のエッジのカーネルでは反転しても符号が変わるだけです。本では普通、どちらも「畳み込み」と呼びます。

此の節全体で使う計算例の格子は、縦のエッジ(左が10、右が90)と、値250の明るいノイズ画素を1つ持つ5 × 5の画像です:

縦のエッジと250のスパイクを持つ5 × 5の入力の格子、3 × 3の平均値フィルタの結果、3 × 3のメディアンフィルタの結果

計算例:3 × 3の平均値フィルタ。行1・列2の窓(橙色)には 10 10 90 / 250 10 90 / 10 10 90 があります。
合計は570で、570/9=63.3です。
画素は10でしたが63.3になりました。平均化はスパイクを3 × 3の染みに広げ、エッジを2列に渡ってぼかします(下の行は10から90への段差ではなく36.7、63.3)。

画像の端の扱い

画像の端では窓の一部が画像の外に出るので、其処に何かを仮定する必要があります。ゼロ埋め(外側 = 0)、クランプ(端の画素を繰り返す)、ミラー又はラップ(周期的)。11.6節のラップモードと同じです。又は単に出力を小さくします。
格子の右上の画素(行0・列4)は90で、近傍も全て90です。
クランプでは平均は90ですが、ゼロ埋めでは窓の9つの値のうち5つが0になり、平均は4⋅90/9=40。画像の周りに暗い枠が出来ます。
上の図はクランプを使っています。此れが普通の選択です。

平滑化:ボックスフィルタとガウシアンフィルタ

平滑化フィルタは隣の画素を平均してノイズを減らしますが、其の代わりにぼやけます。
平均値フィルタ(ボックスフィルタ、移動平均とも)は、n×nの全画素に重み1/n2を与えます。
ガウシアンフィルタは釣鐘型の曲線G(i,j)=12πσ2e−(i2+j2)/(2σ2)で重み付けするので、近い画素ほど重く数えます。σがぼかしの半径を決め、約±3σのカーネルに重みの殆ど全てが入ります。
一番小さい整数の近似は次の通りです:

116[121242121]

スパイク(行1・列1)では:116(4⋅250+2⋅(10+10+10+10)+(10+10+10+10))=1000+80+4016=70。平均値フィルタの36.7と比べて下さい。
ガウシアンは中心をより多く残し、鋭い切れ目がありません。此れは下の周波数領域で効いてきます。

分離可能性

上のガウシアンのカーネルは14(1,2,1)と其れ自身の外積です。此のフィルタは分離可能です。
行を14[121]でフィルタリングし、次に列を同じカーネルでフィルタリングすると、2次元の結果と正確に同じになります。
スパイクでの確認:横のパスは列1で、行0が10+20+104=10、行1が10+500+104=130、行2が10です。
縦のパス:10+2⋅130+104=2804=70、先程と同じです。
利点は速さです。n×nのカーネルは1画素当たりn2回の乗算、1次元の2つのパスは2n回。15 × 15のガウシアンなら225回ではなく30回です。

メディアンフィルタ

メディアンフィルタはn×nの値を並べ替えて真ん中の物を取ります。
行1・列2の窓では、並べ替えた値は 10 10 10 10 10 90 90 90 250 で、中央値は10、正しい値です。
結果全体(図の右の格子)はスパイクの無い元の画像で、エッジは10から90への完全な段差のままです。
1つの極端な値が中央値を引っ張る事は決して無く、真っ直ぐなエッジでは窓の大部分が其の画素と同じ側にあります。メディアンは線型ではないので、カーネルを持ちません。

どのノイズにどのフィルタかは、検定対策として確実にしておきたい点です:

  • ガウスノイズ:全ての画素が少しずつランダムにずれる(センサーのノイズ)。平均化が効きます。ガウシアン、ボックス、又は下のバイラテラルフィルタ。
  • ごま塩ノイズ:少数の画素が完全に間違っている(黒か白)。平均化は其れを広げてしまい、メディアンは取り除きます。

上の段:ガウスノイズを5 × 5のボックスフィルタ、ガウシアンフィルタ、バイラテラルフィルタで除去した物。下の段:ごま塩ノイズに3 × 3のボックス、ガウシアン、3 × 3のメディアンフィルタを掛けた物

メディアンは全ての点を取り除きましたが、細い縞も壊しました。幅1画素の縞は、ノイズと同じ様に窓の中で少数派だからです。

エッジ検出:差分、プリューウィット、ソーベル

エッジは明るさが急に変わる所で、エッジ検出は微分の大きな値を探します。
画素の上では微分は差分になります。前進差分f(x+1)−f(x)又は中心差分f(x+1)−f(x−1)2です。
2次元では2つの偏微分が勾配∇f=(fx,fy)を成します。其の長さがエッジの強さで、其の向きはエッジを横切って明るい側を指します。

単純な差分はノイズに強く反応するので、実用的な微分フィルタは一方向に差分を取り、もう一方向に平滑化します。
プリューウィットフィルタ(プレウィットとも)は等しい重みで、ソーベルフィルタは1 2 1で平滑化します:

Prewitt: [−101−101−101],Sobel: hx=[−101−202−101],hy=[−1−2−1000121]

縦方向のカーネルは転置です。yが下向きなので、hyは画像が下に行くほど明るくなる時に正の値を返します。

|∇f|=fx2+fy2(近似として |fx|+|fy| も良く使われる),θ=arctanfyfx

計算例。左上が暗い斜めのエッジの上の3 × 3の部分:10 10 40 / 10 40 90 / 40 90 90。
ソーベルx:各行の右の値から左の値を引き、1、2、1で重み付け:(40−10)+2(90−10)+(90−40)=30+160+50=240。
ソーベルy:下の行から上の行を引き、1、2、1で重み付け:(40+180+90)−(10+20+40)=310−70=240。
大きさは2402+2402=339.4(|fx|+|fy|の近似では480)、向きはθ=arctan(240/240)=45°。右下を指し、斜めのエッジを横切って明るい角に向かいます。
同じ部分にプリューウィットを掛けるとfx=fy=160で、向きは同じ、大きさの尺度が違います。

テスト画像、ソーベルの勾配の大きさ、色相で表した勾配の方向、LoGのゼロ交差

5 × 5の格子にソーベルを掛けると、微分の弱点が分かります。スパイクは周り中に最大480の大きさを作り、本物のエッジ(320)より大きいのです。エッジ検出器が先ず平滑化するのは其の為です。
キャニー法(1986年)は正に其れを行います。ガウシアンによる平滑化、ソーベルの勾配、非極大値抑制(エッジを1画素の細さにする)、そしてヒステリシスしきい値処理(エッジの開始には強いしきい値、継続には弱いしきい値)です。

ラプラシアンとゼロ交差

2次微分は、エッジを別の角度から見せてくれます。
ラプラシアン∇2f=fxx+fyyは、両方向の2階差分f(x−1)−2f(x)+f(x+1)でラプラシアンフィルタになります:

4近傍: [0101−41010],8近傍: [1111−81111]

等方的(1つの数で、向きは無し)で、1次微分よりも更にノイズに敏感です。
上の斜めの部分では:4近傍は10+10+90+90−4⋅40=40、8近傍は10+10+40+10+90+40+90+90−8⋅40=60。

エッジは正確には何処でしょうか?
1次元での計算例:断面 10 10 10 30 70 90 90 90(画素0〜7)は柔らかいエッジです。
画素1〜6の中心差分は0、10、30、30、10、0で、画素3と4で最大です。
画素1〜6の2階差分は0、20、20、−20、−20、0で、エッジの前では正、後では負です。
エッジは2次微分の符号が変わる所、ゼロ交差で、此処では画素3と4の間、丁度30と70の中間です。
先に平滑化するとLoGフィルタ(ラプラシアン・オブ・ガウシアン、Marr と Hildreth、1980年)になります。上の図の4枚目です。

鮮鋭化とアンシャープマスキング

鮮鋭化は細部を強調します。
暗室の古典的な技法であるアンシャープマスキングは、ぼかしたコピーを引いて細部を取り出し、其れを強さkで足し戻します:

g=f+k(f−blur(f))

断面 10 10 10 30 70 90 90 90 に、ぼかし14[121](端はクランプ)とk=1を使った計算例:

画素 0 1 2 3 4 5 6 7
f 10 10 10 30 70 90 90 90
blur(f) 10 10 15 35 65 85 90 90
f − blur(f) 0 0 −5 −5 5 5 0 0
g 10 10 5 25 75 95 90 90

例えば画素3では10+2⋅30+704=35なので、30+(30−35)=25です。

計算例の1次元の断面と其のぼかした版、アンダーシュート5とオーバーシュート95を持つ鮮鋭化の結果、棒で表したマスクf − ぼかし

エッジは急になり(30〜70ではなく25〜75)、両側に暗いアンダーシュート(5)と明るいオーバーシュート(95)が付きました。
此れでエッジがくっきり見えますが、kが大きいと目に見えるハローになります:

ぼけたテスト画像を強さ1と強さ3のアンシャープマスキングで鮮鋭化した物。強さ3ではエッジに沿って明るいハローと暗いハローが出る

鮮鋭化とラプラシアンは近い関係にあります。此のぼかしでは1次元でf−blur(f)=−14∇2fなので、アンシャープマスキングは「ラプラシアンを少し引く」事、g=f−c∇2fです(課題5)。

バイラテラルフィルタ

バイラテラルフィルタ(Tomasi と Manduchi、1998年)は、重みに更に値の差の2つ目のガウシアンを掛けたガウシアンです:

w(i,j)=exp(−i2+j22σs2)exp(−(f(x+i,y+j)−f(x,y))22σr2),g=∑wf∑w

計算例。σs=1、σr=20で、90へのエッジの隣にある値10の画素では:同じ側の直接の隣はe−1/2⋅e0=0.607、エッジの向こう側の直接の隣は0.607⋅e−802/800=0.607⋅e−8=0.000204を得ます。
エッジの向こう側の画素は事実上無視されます。
10を中心とする3 × 3の窓 10 10 90 / 10 10 90 / 10 10 90 では、普通のガウシアンは31.9、バイラテラルは10.01を返します。
エッジをぼかさずに平滑化します。非線型で遅いですが、多くのノイズ除去や肌を滑らかにするツールの基になっています。第16章では其の変形をフラッシュ有り・無しの写真の合成に使います。

二値画像のモルフォロジー

モルフォロジーは、小さな構造要素(此処では3 × 3の正方形)で二値画像の形を処理します。

  • 膨張:要素の下の画素のどれかが1なら、画素は1になります。形は周り中1画素ずつ太り、穴や隙間は縮みます。
  • 収縮:要素の下の画素が全て1の時だけ、画素は1のままです。形は細り、細い部分や点は消えます。
  • オープニング:収縮してから膨張。要素より小さな点や細い突起を取り除き、残りを(殆ど)元の形に戻します。
  • クロージング:膨張してから収縮。小さな穴や隙間を埋め、輪郭を元に戻します。

2つの小さな二値画像のモルフォロジー。突起と点を持つ画像Aと其の収縮とオープニング、穴と切れ込みを持つ画像Bと其の膨張とクロージング

計算例。画像Aは4 × 4のブロック(16画素)に3画素の突起と1つの孤立した点が付いた、全部で20画素の画像です。
収縮は3 × 3の近傍が全て内側にある画素だけを残します。ブロックの内側の2 × 2、4画素です。突起(太さ1画素)と点は消えます。
其の4画素を膨張させると4 × 4のブロックが戻ります。オープニングは16画素、突起と点の無い元の形です。
画像Bは1画素の穴と上の辺の切れ込みを持つ5 × 6のブロックで、28画素です。
膨張は両方を埋め、ブロックを7 × 8 = 56画素に太らせます。収縮で元に縮め、クロージングは完全な5 × 6のブロック、30画素になります。
オープニングとクロージングは二値化した画像やマスクの掃除に使います。濃淡画像では、局所的な最小値フィルタと最大値フィルタになります。

周波数領域

フーリエ変換は画像を、色々な周波数と向きを持つ正弦波(縞)の和として書き直します。2.10節のJPEGのDCTが8 × 8のブロックを書き直したのと同じです。
低い周波数は滑らかで大きな部分、高い周波数はエッジ、細かい模様、ノイズです。
畳み込み定理は、画像でカーネルと畳み込む事が、スペクトルにカーネル自身のスペクトル、つまり周波数特性を掛ける事と同じだと言います。
従って平滑化フィルタはローパスフィルタ(低い周波数を通す)、鮮鋭化やエッジのフィルタはハイパスフィルタです。

テスト画像、其のフーリエスペクトルの振幅の対数、ローパスフィルタを掛けた物、ハイパスフィルタを掛けた物

スペクトルでは中心が周波数0で、中心からの距離が周波数です。明るい線は真っ直ぐなエッジから来ています。
計算例。一番細かい模様は1次元の市松模様 +1, −1, +1, −1, … で、2.6節のナイキスト周波数にあります。
3タップのボックスフィルタ13[111]は、+1の画素で−1+1−13=−13を返します。模様は3分の1の強さで、反転して残ります。
カーネル14[121]は−1+2−14=0を返し、完全に取り除きます。
一般に、両者の周波数特性はωが0からπ(1画素当たりのラジアン)で1+2cosω3と2+2cosω4で、ボックスの特性は負になります。
鋭い縁のカーネルは困った振る舞いをします。ボックスは一部の高い周波数を符号を反転して通し、スペクトルを鋭く切る「理想的な」ローパスは全てのエッジの横でリンギングを起こします。一方ガウシアンのスペクトルは再び滑らかなガウシアンです。
縮小の前の前置フィルタ(第2章)は正にローパスフィルタで、ぼけの除去(デコンボリューション、第16章)は周波数領域で働きます。

C++:3 × 3のフィルタ

端をクランプする汎用の3 × 3フィルタを、ボックスのカーネルとソーベルで5 × 5の格子に適用します:

#include <cmath>
#include <cstdio>

// A gray image of w x h pixels, row 0 first.
struct Image {
  int w, h;
  float *px;
  float at(int x, int y) const { // clamp to the border ("replicate")
    x = x < 0 ? 0 : (x >= w ? w - 1 : x);
    y = y < 0 ? 0 : (y >= h ? h - 1 : y);
    return px[y * w + x];
  }
};

// out(x, y) = sum of k(i, j) * in(x + i, y + j) for i, j in -1..1
// (correlation: the kernel is not flipped).
void filter3x3(const Image &in, const float (&k)[3][3], Image &out) {
  for (int y = 0; y < in.h; ++y) {
    for (int x = 0; x < in.w; ++x) {
      float s = 0;
      for (int j = -1; j <= 1; ++j)
        for (int i = -1; i <= 1; ++i) s += k[j + 1][i + 1] * in.at(x + i, y + j);
      out.px[y * out.w + x] = s;
    }
  }
}

int main() {
  float a[25] = {10, 10, 10, 90, 90, 10, 250, 10, 90, 90, 10, 10, 10, 90, 90,
                 10, 10, 10, 90, 90, 10, 10, 10, 90, 90};
  float b[25], gx[25], gy[25];
  Image in{5, 5, a}, box{5, 5, b}, sx{5, 5, gx}, sy{5, 5, gy};
  const float t = 1 / 9.f;
  const float boxK[3][3] = {{t, t, t}, {t, t, t}, {t, t, t}};
  const float sobelX[3][3] = {{-1, 0, 1}, {-2, 0, 2}, {-1, 0, 1}};
  const float sobelY[3][3] = {{-1, -2, -1}, {0, 0, 0}, {1, 2, 1}};
  filter3x3(in, boxK, box);
  filter3x3(in, sobelX, sx);
  filter3x3(in, sobelY, sy);
  for (int y = 0; y < 5; ++y) {
    printf("box:");
    for (int x = 0; x < 5; ++x) printf(" %6.2f", b[y * 5 + x]);
    printf("   |G|:");
    for (int x = 0; x < 5; ++x) printf(" %4.0f", std::hypot(gx[y * 5 + x], gy[y * 5 + x]));
    printf("\n");
  }
  return 0;
}

box:  36.67  36.67  63.33  63.33  90.00   |G|:  339  480  253  320    0
box:  36.67  36.67  63.33  63.33  90.00   |G|:  480    0  160  320    0
box:  36.67  36.67  63.33  63.33  90.00   |G|:  339  480  253  320    0
box:  10.00  10.00  36.67  63.33  90.00   |G|:    0    0  320  320    0
box:  10.00  10.00  36.67  63.33  90.00   |G|:    0    0  320  320    0

下の行は綺麗なエッジ(320)を、上の行は1つのノイズ画素が「エッジ」の輪を光らせ、スパイク自身は0になる様子を示しています。
GPUでは同じ物がポストプロセスのパスとして動きます。此の様なカーネルはOpenGLシリーズの第17章で自分で書きます。

検定のポイント

  • 線型フィルタはカーネルとの畳み込み(相関)です。分離可能なカーネル(ボックス、ガウシアン)は1次元の2つのパスで適用出来ます(n2回ではなく2n回の演算)。
  • 端の扱い:ゼロ埋めは端を暗くします。クランプ、ミラー、ラップはそうなりません。
  • ガウスノイズ → 平均化(ガウシアン、ボックス、バイラテラル)、ごま塩ノイズ → メディアン。メディアンは非線型でエッジを保ちます。
  • 1次微分:プリューウィット、ソーベル(一方向に差分、もう一方向に平滑化。勾配の大きさと向き)。2次微分:ラプラシアン(等方的、向きは無し、エッジはゼロ交差、ノイズに非常に敏感)。
  • アンシャープマスキング:f+k(f−blurf)、エッジが急になりオーバーシュートが付く。強過ぎるとハロー。
  • 膨張は太らせ、収縮は細らせます。オープニング(収縮 → 膨張)は小さな明るい部分を取り除き、クロージング(膨張 → 収縮)は小さな穴を埋めます。
  • 平滑化 = ローパス、エッジ検出と鮮鋭化 = ハイパス。空間での畳み込み = 周波数での掛け算。

15.6 画像の幾何学的変換

画像を回転、拡大縮小、変形するのは、画素の値を変えるのではなく画素を動かす事です。幾何学的変換です。
変換自体は第4章の数学で、新しい問題は画素の値が何処から来るかです。

順方向マッピングと逆方向マッピング

すぐ思い付く方法は順方向マッピングです。入力の画素を順に回り、各位置を変換し、着地した出力画素に値を書き込みます。
拡大する時は一度も当たらない出力画素、つまり穴が出来、縮小する時は幾つもの入力画素が互いに上書きします。
計算例。1.2倍に拡大すると出力画素は1.22=1.44倍になるので、少なくとも1−1/1.44≈30.6%は空のままです。下の左の図(30°回転、1.2倍拡大)では、34,848個の出力画素のうち10,648個、丁度其の30.6%が値を得られませんでした。

テスト画像を30度回転し1.2倍に拡大した物。順方向マッピングでは規則的な穴の模様が出来、逆方向マッピングでは最近傍補間と双線型補間を使う

逆方向マッピングはループを逆にします。出力の画素を順に回り、逆変換で其々が入力の何処から来るかを求め、其処の入力を読みます。
同じ順方向・逆方向という言葉は、レイトレーシングで光線を辿る向きにも使われます(12.8節)。考え方は同じで、埋めたい物(出力の画素、目)から出発して、其れが何処から来るかを逆に辿ります。
全ての出力画素が丁度1つの値を得ます。入力での位置が画素の中心に来る事は滅多に無いので、値は補間します。此れが再標本化(リサンプリング)です。
GPUのテクスチャマッピング(第11章)も同じ仕組みです。

画像のアフィン変換

アフィン変換(4.4節)の逆変換は又アフィン変換なので(4.3節)、逆方向マッピングの手間は順方向と同じです。
計算例。画像を中心𝐜=(50,50)(画素座標)の周りにθ=30°回転します。
順方向の写像は𝐩′=𝐑(θ)(𝐩−𝐜)+𝐜なので、逆は𝐩=𝐑(θ)⊤(𝐩′−𝐜)+𝐜です(回転の逆行列は転置、線型代数シリーズの第10章)。
出力画素𝐩′=(80,50)では𝐩′−𝐜=(30,0)で、

𝐑⊤[300]=[cos30°sin30°−sin30°cos30°][300]=[25.98−15]

従って値は入力の(75.98,35.0)、行35の画素75と76の間から来ます。
(画面ではyが下向きなので、此の正の角度は絵を時計回りに回します。向きが反転した軸については4.1節を参照して下さい。)

再標本化:最近傍、双線型、双三次

補間の方法は11.7節の物を画像に使います:

  • 最近傍補間(ニアレストネイバー):一番近い画素を取ります。速く、画素の硬い縁を保ちますが、拡大するとブロック状になり、回転したエッジはギザギザになります。
  • 双線型補間(バイリニア補間):周りの4画素をxとyで線型に混ぜます。滑らかですがぼやけます。鋭い細部は作れません。
  • 双三次補間(バイキュービック補間):周りの4 × 4画素を3次の重みで使います。双線型よりシャープで、読み込みは4倍です。

双三次補間は分離可能です(4つの行をxで、次に其の結果をyで)。
1次元では、標本f0から距離t(0から1)の値はf−1,f0,f1,f2の重み付き和で、重みは3次畳み込みカーネル(Keys、1981年)から得ます:

w(x)={(a+2)|x|3−(a+3)|x|2+1|x|≤1a|x|3−5a|x|2+8a|x|−4a1<|x|<20其れ以外

xは標本からの距離、aはパラメータです。a=−0.5で滑らかな関数を一番良く近似し(Keysの選択)、−0.75も良く使われます。

計算例:t=0.25での重み、a=−0.5。
4つの標本までの距離は1.25、0.25、0.75、1.75です。

  • w(0.25)=1.5⋅0.015625−2.5⋅0.0625+1=0.0234375−0.15625+1=0.8671875
  • w(0.75)=1.5⋅0.421875−2.5⋅0.5625+1=0.6328125−1.40625+1=0.2265625
  • w(1.25)=−0.5⋅1.953125+2.5⋅1.5625−4⋅1.25+2=−0.0703125
  • w(1.75)=−0.5⋅5.359375+2.5⋅3.0625−4⋅1.75+2=−0.0234375

従ってf−1,f0,f1,f2の重みは−0.0703125、0.8671875、0.2265625、−0.0234375で、和は丁度1です。

最近傍(箱型)、双線型(三角形)、a = −0.5の双三次の補間カーネル。t = 0.25での双三次の4つの重みに印付き

負の重みの効果は2つの例で分かります:

  • エッジ、標本0、0、100、100:双三次は100⋅(0.2265625−0.0234375)=20.3、双線型は25。双三次のエッジは急なので、シャープに見えます。
  • 標本0、100、100、100:双三次は100⋅(0.8671875+0.2265625−0.0234375)=107.0で、どの標本よりも大きくなります。アンシャープマスキングと同じリンギングであるオーバーシュートで、結果は有効な範囲にクランプする必要があります。

22 × 22の小さなテスト画像を最近傍、双線型、双三次の補間で10倍に拡大した物

高品質なリサイズにはLanczosカーネル(窓を掛けたsinc、6 × 6画素)も使われます。
縮小では補間だけでは足りません。先にローパスフィルタを掛けないと、細かい模様がモアレになります。ミップマップの縮小の問題と同じです。

射影変換:遠近の補正

斜めから撮ったポスターや書類の写真では、長方形が一般の四角形に写ります。
其の2つの間の写像が4.4節の射影変換、又はホモグラフィです。同次座標に作用する3 × 3の行列𝐇の後に、wでの割り算が続きます:

[x′wy′ww]=[h11h12h13h21h22h23h31h32h33][xy1],x′=h11x+h12y+h13h31x+h32y+h33,y′=h21x+h22y+h23h31x+h32y+h33

𝐇に0でない数を掛けても同じ写像になるので、h33=1と置けます。未知数は8個です。
各点の組(x,y)→(x′,y′)は、分母を払うと未知数について線型な2つの式を与えます:

h11x+h12y+h13−x′xh31−x′yh32=x′,h21x+h22y+h23−y′xh31−y′yh32=y′

従って4組の点(どの3点も一直線上に無い)が8個の式、つまり8 × 8の連立1次方程式を与え、𝐇が決まります。
「遠近法の切り抜き」ツールが4つの角を求めるのは其の為です。
4.4節の行列𝐏で確認します。𝐏は長方形(−1,0),(1,0),(1,2),(−1,2)を台形(−1,0),(1,0),(13,23),(−13,23)に写します。
此の4組について8個の式を書いて解くと、正確にh11=h22=h32=1、他の未知数は全て0(h33=1)が得られます。此れが𝐏です。

写真を正面に直す(ポスターを長方形に戻す)には逆方向マッピングを使います。真っ直ぐな出力の各画素について、𝐏が写真の何処を読むかを教えてくれます。
出力の点(0.5,1)は𝐏(0.5,1,1)⊤=(0.5,1,2)に行き、割り算の後は写真の(0.25,0.5)です。其処で双線型にサンプリングします。
(順方向マッピングなら𝐏−1を使い、其れは写真の(13,23)を角(1,2)に戻します。)

プログラムで生成した市松模様のポスターを斜めから見た物と其の4つの角の印、そして4つの角から計算したホモグラフィで正面に直したポスター

遠くにあった細部は、写真の画素が少ししか覆っていなかったので、正面に直した後はぼやけます。重なり合う写真の間のホモグラフィは、パノラマの合成の基礎でもあります(第16章)。

レンズの歪曲収差の補正

実際のレンズは真っ直ぐな線を曲げます(2.3節)。樽型の歪曲は四隅を内側へ引き込むので真っ直ぐな線が外へ膨らみ(広角レンズに多い)、糸巻き型は四隅を外へ押し出すので線が内側へ反ります。
良く使われるモデルは放射状で、座標は画像の中心を原点とし、rが中心からの距離になる様に尺度を決めます:

𝐩d=𝐩u(1+k1ru2+k2ru4+⋯)

𝐩uは完全なピンホールなら点が来る位置、𝐩dはレンズが実際に点を置く位置です。k1<0が樽型、k1>0が糸巻き型です。
計算例。k1=−0.1(k2は無し)で、ru=1の点𝐩u=(0.6,0.8)は0.9⋅(0.6,0.8)=(0.54,0.72)に、ru=0.5の点(0.3,0.4)は0.975⋅(0.3,0.4)=(0.2925,0.39)に動きます。外側の点は10%、内側の点は2.5%だけ内へ動きます。
此の不均一な拡大縮小が真っ直ぐな線を曲げるのです。

逆方向マッピングを使えば補正は簡単です。補正後の画像の各画素について𝐩uは分かっているので、式を其のまま使って𝐩dを計算し、其処の写真を読みます。
簡単な閉じた形を持たない多項式の逆関数を求める必要は全くありません。

樽型に歪んだテストの格子、糸巻き型に歪んだ同じ格子、そして逆方向マッピングで真っ直ぐな線に戻した樽型の画像

kの値はチェッカーボードを撮影して求めます(カメラキャリブレーション)。VFXでは実写の素材の歪みを取り、CGを合成し、結果を再び歪ませます(第14章)。

ワーピングとモーフィング

ワーピングは、ユーザーが指定する画像平面の滑らかな写像です。画像に被せた格子の点を動かす方法(メッシュワーピング)や、2つの画像に対応する線を引く方法(特徴ベースのワーピング、Beier と Neely、1992年)があり、後者では各点が線の変換を距離で重み付けして混ぜた物に従います。

モーフィングは画像Aを画像Bに変えます。単純なクロスディゾルブはフェードするだけです:It=(1−t)A+tB。
計算例。Aで200、Bで40の画素は、t=0.25で0.75⋅200+0.25⋅40=160です。
形が違う場所にあると、クロスディゾルブでは両方が半透明に見えてしまう(「ゴースト」)ので、モーフィングは先ず両方の画像を中間の形(各特徴を、Aでの位置からBでの位置へ割合tだけ動かした物)に変形し、其れから変形した画像をクロスディゾルブします:

上の段:左の小さな黄色い花から右の大きなピンクの花へのクロスディゾルブでは、2つのゴーストが見える。下の段:先ず両方を中間の位置と大きさに変形するモーフィングでは、1つの花が動きながら大きくなり、色を変える

此処では特徴は各花の中心と半径だけなので、変形は相似変換です。顔には多くの特徴線が必要です。
モーフィングは、13.5節の3次元の形状補間や視点補間(12.13節)の2次元の親戚です。

検定のポイント

  • 順方向マッピング(入力 → 出力)は拡大すると穴が開き、縮小すると上書きが起きます。逆方向マッピング(出力 → 入力)は全ての出力画素に1つの値を与え、補間が必要です。
  • 最近傍(1画素、ブロック状)、双線型(2 × 2、滑らかだがぼやける)、双三次(4 × 4、よりシャープ、負の重みの為オーバーシュートし得る)。
  • 縮小には先にローパスフィルタが必要で、無いとエイリアシングが起きます。
  • アフィン変換の未知数は6個(3組の点)、ホモグラフィは8個(4組の点、h33=1)。遠近の補正 = ホモグラフィ + 逆方向マッピング。
  • 歪曲収差:放射状のモデル1+k1r2+⋯、樽型(k1<0)と糸巻き型(k1>0)。逆方向マッピングで補正します。
  • モーフィング = 両方の画像を中間の形に変形 + クロスディゾルブ。クロスディゾルブだけではゴーストが出ます。

15.7 画像の編集

最後のシラバス項目は、画像編集ソフトが此れまでの全てを使って何をするかです。

レイヤーと描画モード

レイヤーは描画モード(ブレンドモード)で下のレイヤーと組み合わされます。
下の値(ベース)をa、上のレイヤーをbとし、どちらもチャンネル毎に0から1とします。混ぜた結果は、更にレイヤーの不透明度でover演算によってベースに混ぜられます。

モード 式 効果
通常 b 上のレイヤーが覆う
乗算 ab 必ず暗くなる。白は変化無し、黒は黒のまま(スライドやフィルタを重ねる様に)
スクリーン 1 − (1 − a)(1 − b) 必ず明るくなる。黒は変化無し(2台のプロジェクターを1つのスクリーンに当てる様に)
オーバーレイ a < 0.5なら2ab、其れ以外は1 − 2(1 − a)(1 − b) コントラストが上がる。暗いベースでは乗算、明るいベースではスクリーン
加算 min(a + b, 1) 光の足し算、光る効果
差の絶対値 a − bの絶対値 同じ所は0。画像の比較に使う
比較(暗)/比較(明) min(a, b) / max(a, b) チャンネル毎に暗い方/明るい方を残す

計算例。ベースa=0.6、レイヤーb=0.3:

  • 乗算:0.6⋅0.3=0.18。
  • スクリーン:1−0.4⋅0.7=0.72。
  • オーバーレイ:a≥0.5なのでスクリーンの側、1−2⋅0.4⋅0.7=0.44。
  • 加算:0.9、差の絶対値:0.3、比較(暗):0.3、比較(明):0.6。

暗いベースa=0.2では、オーバーレイは乗算の側を使います:2⋅0.2⋅0.3=0.12。
乗算とスクリーンは互いの鏡像です。スクリーンはネガに乗算を適用した物、1−(1−a)(1−b)です。
ハードライトはaとbの役割を入れ替えたオーバーレイ(条件で上のレイヤーを調べる)で、ソフトライトはより穏やかなオーバーレイですが、正確な式はソフト毎に違います。

ベースレイヤーとしてのカラーのテスト画像、合成レイヤーとしての黒から白への横の灰色のランプ、そして通常50パーセント、乗算、スクリーン、オーバーレイ、加算、差の絶対値、比較(暗)、比較(明)の結果

ランプを使うと、乗算は右端(b = 1)でベースを、左端で黒を見せ、スクリーンは其の逆です。
殆どの編集ソフトは此れらをガンマで符号化された値に適用し、其れが見慣れた見た目の一部になっています。光を扱うコンポジットソフトはリニアな空間で作業します。

マスクと選択範囲

マスクは、処理を何処に適用するかを制御する濃淡画像です。1 = 完全に、0 = 全く無し、其の間 = 一部。
選択範囲は編集中のマスクです。矩形、投げ縄のパス、自動選択ツール(6.8節のシードフィルに色の許容範囲を付けた物)、色域(HSV又はLabが向いています)、キーイング(第14章)。
選択範囲はぼかし(境界をぼかして編集が徐々に消える様にする)を掛けたり、膨張や収縮で広げたり狭めたりします。

コピー、修復、ポアソン合成

コピースタンプは、画像の別の部分から一定のずれで画素を柔らかいブラシ越しにコピーします。
明るさや色の違いが継ぎはぎとして見えてしまうので、修復ブラシは元の部分の質感をコピーし、明るさと色は貼り付け先の周りから取ります。

其の背後にある数学がポアソン画像編集(Pérez、Gangnet、Blake、2003年)で、シームレスクローンとも呼ばれます。
貼り付ける領域の内側では、元の値の代わりに元の画像の勾配(エッジと質感)を保ち、領域の境界では結果が貼り付け先と等しくなる事を要求します。
此れが、領域の内側で∇2g=∇2s、境界でg = 貼り付け先、と言う式です。ポアソン方程式で、大きな疎な連立1次方程式(1画素に1つの未知数)として解きます。

1次元での計算例。
貼り付け先の行は、2つの境界画素で100と140です。元の行 20, 30, 60, 30, 20(其の境界の値は20と20)から内側の3画素を貼り付けます。
直接貼り付けると100, 30, 60, 30, 140となり、2つの急な段差が出来ます。
元の行の内側の3画素での2階差分は20−60+60=20、30−120+30=−60、60−60+20=20です。
同じ2階差分を持ち、g0=100、g4=140となるg1,g2,g3が必要です:

100−2g1+g2=20,g1−2g2+g3=−60,g2−2g3+140=20

解はg=(100,120,160,140,140)です。
確認:100−240+160=20、120−320+140=−60、160−280+140=20。
此れは元の行に1次関数のランプ 80, 90, 100, 110, 120 を足した物です。元の膨らみは残り、段差無しで両方の境界に繋がります(2次元では、補正は境界に合う一番滑らかな曲面になります)。

波打つ橙から紫への貼り付け先の画像、青緑の背景に淡い星を持つ元のパッチ、矩形が見える其のままの貼り付け、そして星だけが残り貼り付け先に合わせて色付いたポアソン合成

パッチの平らな背景は消え(勾配が無かったので)、星は形を保ちながら夕焼けの色に染まります。

インペインティング

インペインティングは、元の画像無しで、周りだけから穴を埋めます。
一番簡単な版は、手本がゼロのポアソン方程式∇2g=0です。穴は境界に合う一番滑らかな曲面で埋められます(1次元の例では、100と140の間を110、120、130)。
傷や埃には効きますが、大きな穴はぼやけます。
事例ベースのインペインティングは、画像の別の場所から合うパッチをコピーし、縁から内側へ埋めていきます(Criminisi等、2004年。PatchMatch、2009年によって「コンテンツに応じた塗りつぶし」として対話的になりました)。今では生成型のニューラルネットワークが大きな穴に内容を作り出します。

実際のレタッチ

典型的なレタッチは此の章の全てを使います。トーンカーブとホワイトバランス。コピー、修復、インペインティング。覆い焼きと焼き込み(マスクとして塗る局所的な明るくする処理と暗くする処理)。周波数分離(ローパスとハイパスの2つのレイヤーを別々にレタッチする、2段階のラプラシアンピラミッド)。そして最後の段階としての鮮鋭化です。
プロは非破壊的に(後から変更出来る調整レイヤーとマスクで)、そしてチャンネル当たり16ビットで作業します。トーンを何度も変えても疑似輪郭が出ない様にする為です。

検定のポイント

  • 乗算abは暗くし(白は変化無し)、スクリーン1−(1−a)(1−b)は明るくし(黒は変化無し)、オーバーレイは0.5未満で乗算、以上でスクリーン(下のレイヤーで判定)。ハードライトは上のレイヤーで判定します。
  • マスクは処理を適用する場所を制御します。境界のぼかし = マスクをぼかす事。
  • コピースタンプは画素をコピーします。修復ブラシとポアソン合成は勾配をコピーして周りに合わせます(境界で貼り付け先と等しい∇2g=∇2s)。
  • インペインティングは周りから穴を埋めます。小さな穴には拡散(∇2g=0)、大きな穴にはパッチベース。

15.8 まとめ

  • ディジタル画像は値の配列で、処理は画素ごと、局所、幾何学的、大域的に分けられます。ヒストグラムはレベル毎の画素数を数え、平均と分散は明るさとコントラストを表します。
  • 二値画像は4近傍又は8近傍を使います。HDR画像はリニアな浮動小数点数の光を保存します(OpenEXR、RadianceのRGBE)。ピラミッドは幾つかの解像度(ガウシアン)又は周波数帯(ラプラシアン)を保存します。
  • 2次元の描画:被覆率によるアンチエイリアスを伴うラスタライズ、偶奇規則又は非ゼロ規則で塗るベジェ曲線のパス、アウトラインフォント、ブラシのスタンプ、(乗算済み)アルファで合成するレイヤー。
  • トーンカーブ(引き伸ばし、ネガ、ガンマ、ポスタリゼーション)はLUTで適用します。大津の方法はクラス間分散を最大にし、平坦化は累積ヒストグラムをカーブとして使います。
  • 色変換は重み付きの和と3 × 3の行列、又はHSVを使います。トーンマッピングはHDRを画面に圧縮します(露出、キー0.18、Reinhard L/(1+L))。
  • フィルタ:ボックスとガウシアンによる平滑化(分離可能)、インパルスノイズにはメディアン、プリューウィット/ソーベルの勾配、ラプラシアンとゼロ交差、アンシャープマスキング、バイラテラルフィルタ、二値のモルフォロジー。平滑化はローパス、エッジ検出はハイパスです。
  • 幾何学的変換は逆方向マッピングと再標本化(最近傍、双線型、双三次)を使います。4組の点からのホモグラフィで遠近を補正し、放射状のモデルで歪曲収差を補正します。モーフィング = ワーピング + クロスディゾルブ。
  • 編集:描画モード(乗算、スクリーン、オーバーレイ等)、マスクと選択範囲、コピーとポアソンに基づく修復、インペインティング。

15.9 用語

English 日本語
image processing 画像処理
histogram ヒストグラム
cumulative histogram 累積ヒストグラム
mean / variance / standard deviation 平均/分散/標準偏差
binary image 二値画像
4-neighborhood / 8-neighborhood 4近傍/8近傍
connected component / labeling 連結成分/ラベリング
dynamic range ダイナミックレンジ
high dynamic range (HDR) image ハイダイナミックレンジ画像(HDR画像)
multi-resolution, image pyramid 多重解像度、画像ピラミッド
Gaussian pyramid / Laplacian pyramid ガウシアンピラミッド/ラプラシアンピラミッド
anti-aliased drawing アンチエイリアス描画
path パス
even-odd rule / nonzero winding rule 偶奇規則/非ゼロ回転数規則
winding number 回転数
outline font / bitmap font アウトラインフォント/ビットマップフォント
hinting ヒンティング
layer レイヤー
premultiplied alpha 乗算済みアルファ(プリマルチプライドアルファ)
tone (gray-level) conversion 濃淡変換
tone curve トーンカーブ
linear stretch 線型の引き伸ばし(コントラスト伸張)
negative ネガ(反転)
gamma conversion ガンマ変換
posterization ポスタリゼーション
lookup table (LUT) ルックアップテーブル(LUT)
binarization / threshold 二値化/しきい値
Otsu's method 大津の方法(判別分析法)
between-class variance クラス間分散
histogram equalization ヒストグラム平坦化(均等化)
color conversion 色変換
saturation / hue 彩度/色相
sepia セピア
tone mapping トーンマッピング
exposure 露出
key value / log-average luminance キー値/対数平均輝度
spatial filtering 空間フィルタリング
kernel カーネル(フィルタ係数)
convolution / correlation 畳み込み/相関
smoothing 平滑化
mean (box) filter 平均値フィルタ(ボックスフィルタ)
Gaussian filter ガウシアンフィルタ
separable filter 分離可能なフィルタ
median filter メディアンフィルタ
Gaussian noise / salt-and-pepper noise ガウスノイズ/ごま塩ノイズ
edge detection エッジ検出
gradient 勾配
differential filter 微分フィルタ
Prewitt filter プリューウィットフィルタ
Sobel filter ソーベルフィルタ
Laplacian filter ラプラシアンフィルタ
zero crossing ゼロ交差
Laplacian of Gaussian (LoG) LoGフィルタ
Canny edge detector キャニー法
sharpening 鮮鋭化
unsharp masking アンシャープマスキング
bilateral filter バイラテラルフィルタ
morphology モルフォロジー
structuring element 構造要素
dilation / erosion 膨張/収縮
opening / closing オープニング/クロージング
Fourier transform フーリエ変換
convolution theorem 畳み込み定理
low-pass filter / high-pass filter ローパスフィルタ/ハイパスフィルタ
geometric transformation 幾何学的変換
forward mapping / inverse mapping 順方向マッピング/逆方向マッピング
resampling 再標本化(リサンプリング)
nearest-neighbor interpolation 最近傍補間(ニアレストネイバー)
bilinear interpolation 双線型補間(バイリニア補間)
bicubic interpolation 双三次補間(バイキュービック補間)
projective transformation, homography 射影変換、ホモグラフィ
lens distortion (barrel, pincushion) 歪曲収差(樽型、糸巻き型)
warping ワーピング
morphing / cross-dissolve モーフィング/クロスディゾルブ
blend mode 描画モード(ブレンドモード)
multiply / screen / overlay 乗算/スクリーン/オーバーレイ
mask / selection マスク/選択範囲
clone stamp / healing brush コピースタンプ/修復ブラシ
Poisson image editing ポアソン画像編集
inpainting インペインティング

15.10 課題

  1. ★ 4階調(0〜3)の4 × 4の画像のヒストグラムがh=(6,6,2,2)です。平均と分散、平坦化の表g(k)=round(3H(k)/16)と新しいヒストグラムを求め、t=0,1,2のクラス間分散から大津のしきい値を見つけて下さい。
  2. ★ 3 × 3の窓が 20 30 40 / 30 200 50 / 40 50 60 です。中心の出力を、3 × 3の平均値フィルタ、15.5節の1 2 1のガウシアン、メディアンフィルタ、4近傍のラプラシアンで求め、ソーベルのfx、fy、勾配の大きさと向きも求めて下さい。どの値を「ノイズの無い」推定値と呼びますか?其れは何故ですか?
  3. ★★ t=0.5での双三次の重み(a=−0.5)を求め、標本0、20、60、100(位置−1、0、1、2)の間を補間して下さい。双線型補間と比べ、此処で双三次の結果が線型の結果より低くなる理由を説明して下さい。
  4. ★★ 4つのHDRの画素の輝度が0.05、0.2、0.8、3.2です。対数平均輝度(切りの良い数になります)、キー0.18で拡大縮小した輝度、Reinhardの結果L/(1+L)を求めて下さい。入力の範囲は何段ですか?
  5. ★★★ ぼかし14[121]とk=2のアンシャープマスキングがg=f−12∇2f(1次元の2階差分)と同じである事を示し、15.5節の断面で確かめて下さい。次に、a+bの中央値が中央値の和にならない3画素の信号a、bを見つけ、其れがメディアンフィルタにカーネルが無い事の証明になる理由を説明して下さい。
  6. ★★ 4画素(位置0〜3)の行を、順方向マッピングx′=⌊1.5x⌋で1.5倍に拡大します。出力画素0〜5のどれが値を受け取りますか?次に逆方向マッピングx=x′/1.5と最近傍の丸めで行い、各出力画素がどの入力画素を読むかを書き出して下さい。

選択問題

問1 ★ 次のうち、空間フィルタリングについて正しい物はどれか。

あ ソーベルフィルタは2次微分のフィルタで、其のゼロ交差がエッジを示す。
い ガウシアンフィルタは、窓の中の全ての画素に同じ重みを与える。
う アンシャープマスキングは、ノイズを減らす為に画像をぼかす。
え ラプラシアンフィルタは全ての方向を等しく扱うので、ノイズの影響を受けにくい。
お メディアンフィルタはごま塩ノイズを良く取り除き、平均値フィルタよりもエッジをシャープに保つ。

問2 ★ 次のうち、濃淡変換について正しい物はどれか。

あ ヒストグラム平坦化は、必ず完全に平らなヒストグラムを作る。
い ヒストグラム平坦化は正規化した累積ヒストグラムをトーンカーブとして使い、異なる濃淡レベルの数を増やす事は無い。
う 大津の方法は、クラス間分散を最小にするしきい値を選ぶ。
え 画像のネガは、元の画像と同じヒストグラムを持つ。
お 固定しきい値による二値化は、画像全体の明るさに自動的に適応する。

問3 ★★ 画像を回転して拡大する。次のうち、正しい物はどれか。

あ 順方向マッピングでは、画像を拡大しても穴は決して開かない。
い 双三次補間は、サンプリング位置の周りの2 × 2画素を使う。
う 射影変換(ホモグラフィ)は、3組の対応点で完全に決まる。
え 逆方向マッピングは、各出力画素について入力画像での位置を求めて其処で補間するので、出力に穴が開かない。
お 双線型補間は、使う4つの画素の最大値より大きな値を作り得る。

15.11 実践課題

小さな画像1枚を、此の章の主な手順で手作業で処理し、各手順を其の結果が満たすべき性質で確かめます。
方眼紙、鉛筆、色鉛筆、電卓を用意して下さい。

画像は8階調(0〜7)の6 × 6画素で、行0が一番上です:

列0 列1 列2 列3 列4 列5
行0 6 6 5 6 6 6
行1 6 5 6 6 5 6
行2 6 6 1 2 1 6
行3 5 6 2 7 2 6
行4 6 6 1 2 1 6
行5 6 0 6 6 6 5

明るい背景の上の暗い3 × 3の物体で、物体の中に明るいノイズ画素が1つ、背景に暗いノイズ画素が1つあります。

  • 手順1:描く。方眼紙に画像を描き(1画素を2 × 2マス)、各画素を鉛筆で塗ります(0 = 一番暗い)。
  • 手順2:ヒストグラム。k=0,…,7についてh(k)を数えて棒グラフにし、累積ヒストグラム、平均、分散を求めます。
  • 手順3:大津の方法。全てのしきい値t=0,…,6についてω0、ω1、μ0、μ1、σB2を表にします。tを選び、二値画像(暗いクラス = 物体)を描きます。どの画素が間違って分類されましたか?
  • 手順4:メディアン。3 × 3のメディアンフィルタ(端はクランプ)を36画素全てに掛けて結果を描きます。同じtで二値化し、手順3と比べます。
  • 手順5:ソーベル。元の画像で、行2・列1(物体の左の辺)と行1・列2(其の上)でのfx、fy、勾配の大きさを求めます。勾配ベクトルを矢印として絵に描き込みます。
  • 手順6:平坦化。表g(k)=round(7H(k)/36)と、平坦化した画像のヒストグラムを求めます。
  • 手順7:拡大。画像を逆方向マッピングx=(x′+0.5)/2−0.5(yも同じ)で2倍に拡大します。出力画素x′=5、y′=4の双線型の値を求めます。

確かめ方:

  • 手順2:ヒストグラムの和は36、累積ヒストグラムは36で終わります。平均は2つの山の間にあります。
  • 手順3:レベル3と4は空なので、幾つかのしきい値が同じσB2と同じ二値画像を与えます。選んだtについて、重み付きのクラス内分散とσB2の和が手順2の分散と等しくならなければいけません。間違うのは丁度2画素(2つのノイズ画素)です。
  • 手順4:両方のノイズ画素は消えますが、物体はもう正方形ではありません。其の4つの角の画素に何が起きたかを説明して下さい(其の窓に暗い画素が幾つ入っているかを見ます)。オープニングとクロージング(15.5節)があるのは此の為です。
  • 手順5:矢印は暗い物体から明るい背景へ、物体の辺に垂直に向きます。一定の領域の中では大きさは0です。
  • 手順6:表は決して減らず、使われている一番明るいレベルは7になり、平坦化したヒストグラムの0でない棒の数は元より多くなりません。
  • 手順7:結果は、使った4つの画素の最小値と最大値の間にあります。


コメント(0)

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