12  高度強調

ここまで我々が画像に対して行ってきたことの大半は「修復」である:ノイズの除去(チャプター 6)、照明の平坦化(チャプター 4)、グレースケール値の適切な範囲への伸張。これらの操作の共通点は復元——画像が本来あるべき姿に修復すること——である。本章では方向を変え、強化について述べる。画像内に確かに存在する情報ではあるものの、後続のアルゴリズムが直接利用できないほど微弱な情報が存在する。レンズと焦点ぼけによってエッジが鈍って緩やかになり、暗部の細部は読み出しノイズに埋もれ、明部の細部はセンサーの飽和容量と量子化の上限によって白く飛んでしまう。本章の2つのツールはまさにこの問題のために存在する:シャープ化(sharpening)は鈍ったエッジを再び急峻にし、結像系のMTF損失を補償する;高ダイナミックレンジ(high dynamic range, HDR)撮像は複数回の露光を用いて、単一フレームのダイナミックレンジを超える明暗両端の情報を併せて取り込む。図 12.1 は最初のツールの直感的な効果を示している:焦点ぼけで鈍ったエッジが、アンシャープマスク強調によって再び急峻になる。なお、照明むらによって生じる影の問題については、チャプター 4 でシェーディング補正を詳しく論じたので、本章では繰り返さない。

(a) 鈍ったシーン(\(\sigma=1.5\) のガウシアンで焦点ぼけを模擬)
(b) アンシャープマスク強調(\(\lambda=1.0\)
図 12.1: アンシャープマスクによるシャープ化。(a) 鮮鋭なシーンを \(\sigma=1.5\) のガウシアンで鈍らせたもの。ステップエッジが遷移帯として引き伸ばされている。(b) \(\lambda=1.0\) の適度な強化により、可視なアーティファクトを導入することなくエッジが再び急峻になっている。

12.1 シャープ化とアンシャープマスク

シャープ化の最も古典的で、かつ最も広く用いられる形式はアンシャープマスク(unsharp masking)である。この一見矛盾した名前は暗室時代に由来する:写真家がネガを意図的にぼかして(「鈍らせて」)マスクとし、原画と重ねて露光すると、逆に写真が鮮鋭になったことによる。デジタル版の手順はわずか3ステップである:画像を低域フィルタで1回ぼかし、原画からぼかし版を減算して細部レイヤーを得、細部レイヤーを比例して原画に加え戻す:

\[ g = f + \lambda\,\big(f - G_{\sigma} * f\big), \]

ここで \(G_{\sigma} * f\)\(f\) のガウシアンぼかし(チャプター 6 ではノイズ抑圧に用いたが、ここでは周波数成分の分離に用いる)であり、\(\lambda\) は強化の強度である。この操作の論理は非常に単純である:ガウシアンぼかしは低周波数を保持し高周波数を除去するため、\(f - G_{\sigma}*f\) はまさに除去された高周波数部分——エッジ、細線、テクスチャ——に等しい。これに \(\lambda\) を乗じて原画に加え戻すことは、画像の高周波数成分を \(1+\lambda\) 倍に増幅することと等価である。\(\lambda\) は「高周波数ゲインのつまみ」である:\(\lambda=0\) では原画がそのまま出力され、\(\lambda\) が大きいほどエッジのコントラストが強くなる。ぼかしカーネルの \(\sigma\) は「どれほど細かいものを細部とみなすか」を決定し——補償対象のぼかしのスケールと同等にすべきである。

シャープ化はレンズ収差、軽度の焦点ぼけなどによるMTF減衰を補償する(チャプター 3)が、情報を創り出すことはできない:残存する高周波数の残差のみを、その中のノイズと共に増幅できるに過ぎない。ある空間周波数のMTFがすでにゼロまで減衰している場合、細部レイヤーにはその痕跡は何も含まれておらず、どれほど大きな \(\lambda\) を乗じても再生することはできない。シャープ化は「増幅器」であり、「復元器」ではない。

制御された実験でこれを実演する。チャプター 6 と同じ合成シーン(明るい背景180、暗い矩形60、幅2 pxの細い明線250)を用い、まず SciVision のガウシアンフィルタ(\(9\times 9\)\(\sigma=1.5\))で鈍らせて、軽度に焦点ぼけしたレンズを模擬する:これにより 図 12.1 (a) が得られ、エッジはすでに明らかにぼやけている。次に同じガウシアン(\(9\times 9\)\(\sigma=1.5\))をアンシャープマスクのぼかし項として用い、\(\lambda=1.0\) で強化して 図 12.1 (b) を得る:ステップエッジは再び鮮鋭になり、細い明線のコントラストもかなり回復している。

12.2 オーバーシュートとリンギング

\(\lambda\) がゲインのつまみであるなら、大きくするほど鮮鋭になるのだろうか?図 12.2 (a)\(\lambda=4.0\) の結果を示している:エッジは確かに「より鮮鋭」になっているが、暗い矩形の周囲に目に痛い明るいハロー(halo)が浮かび、矩形の内側には過度に暗い縁が縁取っている。これがオーバーシュート(overshoot)アンダーシュート(undershoot)である——シャープ化がステップの両側でそれぞれ上向き、下向きに「力を入れすぎ」、グレースケール値が本来のプラトー値を超えて押し上げられる。強化カーネルが急峻な周波数遮断を持つ場合、オーバーシュートは減衰振動の形で繰り返されることもあり、これをリンギング(ringing)と呼ぶ。

数値が最も説得力を持つ。行180に沿って列90–150のグレースケールプロファイルを抽出する(暗い矩形の左縁をちょうど横切っており、公称グレースケール値は明側180、暗側60である)。3本の曲線を 図 12.2 (b) にプロットし、統計は以下の通りである:

表 12.1: 行180のプロファイル(列90–150)のオーバーシュート統計。公称グレースケール値は180/60
プロファイル min max オーバーシュート / アンダーシュート
鈍らせた原画 58 178 +0 / −2
\(\lambda=1.0\) 56 185 +5 / −4
\(\lambda=4.0\) 36 213 +33 / −24

\(\lambda=1.0\) ではオーバーシュートはわずか +5/−4 グレースケールレベルであり、画像ノイズと同程度で肉眼では認識できない;\(\lambda=4.0\) では +33/−24 まで急上昇し、エッジの両側にそれぞれ約30グレースケールレベルの「擬似プラトー」を押し出している。

(a) 過度な強化(\(\lambda=4.0\)
(b) 行180のグレースケールプロファイル比較
図 12.2: オーバーシュート実験。(a) \(\lambda=4.0\) は暗い矩形の外側に明らかな明るいハローを生じさせている。(b) 列90–150のプロファイル(薄い灰色=鈍らせた原画、中間の灰色=\(\lambda=1.0\)、黒=\(\lambda=4.0\))。エッジは列120付近にあり、\(\lambda=4.0\) の曲線はステップの両側でそれぞれ213と36まで達しており、180/60の公称プラトーをはるかに超えている。

オーバーシュートは単なる審美上の問題ではなく、計測と検査の両方に現実の被害を与える。計測においてチャプター 20 のキャリパーツールはグレースケールプロファイルの微分の極値でエッジを位置決めする;オーバーシュートはエッジの両側でプロファイルの形状を変化させ、微分曲線もそれに応じて歪むため、検出されるエッジ位置は系統的にずれる——シャープ化でエッジが「見つけやすくなった」と思っても、実際にはエッジの位置が「移動して」しまっているのである。検査において:暗いワークの周囲の明るいハローは、ブロブ解析や閾値処理から見れば実在する高輝度領域の輪であり、擬似欠陥のアラームを引き起こすのに十分である;逆に、明るい領域の内側の暗い縁も亀裂と誤判定される可能性がある。これから導かれる \(\lambda\) の選択法則は以下の通りである:代表的なサンプルでエッジプロファイルを抽出し、オーバーシュートの振幅が画像ノイズの振幅に迫るまで \(\lambda\) を大きくし、それ以上は大きくしない。オーバーシュートがノイズに埋もれれば、後続のアルゴリズムに系統的な影響を与えることはない;本例では \(\lambda=1.0\)(オーバーシュート +5、ノイズと同程度)は合格であり、\(\lambda=4.0\) ははるかに限界を超えている。また飽和にも注意する必要がある:オーバーシュートが0または255でクリップされると、元のグレースケール情報は永久に失われる。

12.3 高ダイナミックレンジと露光融合

第2の「微弱情報」の問題はダイナミックレンジ(dynamic range)に生じる:シーンの最も明るい部分と最も暗い部分の放射照度の比が、単一フレームで記録できる範囲を超えることである。上限はセンサーの飽和容量と8ビット量子化の天井に由来し(チャプター 3チャプター 1)、下限は読み出しノイズの床に由来する。結果としてジレンマが生じる:明部に合わせて露光すると暗部の細部がノイズに沈み、暗部に合わせて露光すると明部が真っ白に飛んでしまう。溶接池の脇にあるワークの輪郭、研磨金属に刻印された文字などが典型的な被害者である。

多重露光融合(exposure fusion)の考え方は:ダイナミックレンジが足りなければ、複数フレーム撮影して補うというものである。同じ静止シーンを複数の露光レベルで撮影し——短露光で明部を保持し、長露光で暗部を拾い上げ——画素ごとに各フレームの情報を統合して1枚の放射照度(irradiance)推定マップにする。線形応答の仮定のもと(画素値 \(v_i \approx E \cdot t_i\)\(t_i\) は相対露光量)、各フレームは放射照度の1つの観測値 \(v_i/t_i\) を与え、重み付き平均によって推定値が得られる:

\[ \hat E(p) = \frac{\sum_i w(v_i)\, v_i / t_i}{\sum_i w(v_i)}, \qquad w(v) = \min(v,\, 255-v) + \varepsilon . \]

これは Debevec 式放射照度復元の簡易版であり、\(w\)三角重みである:画素値が中央にあるとき重みが最大で、0または255に近づくと重みはゼロに近づく。

三角重みの直感は「投票の審査」である:各露光フレームはその画素の放射照度に1票を投じるが、暗すぎる票は無効(信号がノイズに埋もれており、\(v_i/t_i\) は極めて信頼できない)、明るすぎる票も無効(すでに飽和してクリップされており、\(v_i\)\(E\cdot t_i\) とは全く等しくない)。露光が適度なフレームだけに発言権がある——そしてシーン内のどの点に対しても、いずれかの露光レベルがちょうど適度になるはずであり、これこそが多重露光の全体的な意義である。

復元された \(\hat E\) の範囲は8ビットを超えるため、最後にトーンマッピング(tone mapping)によって表示可能な範囲に圧縮する必要がある。本章では最も単純な大域的 \(\gamma\) マッピングを用いる:\(\text{out} = 255\,(\hat E/\hat E_{\max})^{1/2.2}\)\(\gamma=1/2.2\) は暗部を持ち上げ明部を圧縮し、人間の視覚的な感覚におおよそ一致する。

実験シーンの放射照度場は以下のように構成する:中間灰色の背景110、左上の明部には252/272の縦縞の細部が隠されており(コントラスト20だがピークは255の8ビットの天井を超える)、右下の暗部には14/24の縦縞が隠されている(コントラスト10でノイズの床に接している)。×0.25 / ×1.0 / ×4.0 の3段階のゲインで露光し、それぞれ [0,255] にクリップして \(\sigma=2\) の読み出しノイズを重畳する(再現性のため固定シード11/22/33を用いる)と、図 12.3 の3フレームが得られる;融合してトーンマッピングすると 図 12.4 が得られる。

(a) 短露光(×0.25)
(b) 中露光(×1.0)
(c) 長露光(×4.0)
図 12.3: 3段階の露光。(a) ×0.25:明部の縞は残存するが暗部はノイズに沈んでいる。(b) ×1.0:明部の縞は255でほぼ完全にクリップされ、暗部の細部はかろうじて識別できる。(c) ×4.0:暗部の縞は鮮明だが明部は完全に飽和して真っ白になっている。
図 12.4: 3フレームを融合し \(\gamma=1/2.2\) でトーンマッピングしたHDR結果:明部の縞と暗部の縞が同時に鮮明に見えている。

肉眼による判断に加え、2つの縞の細部パッチについて平均値と標準偏差を統計する(標準偏差は縞のコントラストの可視性を測る):

表 12.2: 細部パッチの平均値と標準偏差の比較
画像 暗部パッチ平均 / 標準偏差 明部パッチ平均 / 標準偏差
短露光(×0.25) 4.8 / 2.35 65.5 / 3.22
中露光(×1.0) 19.0 / 5.40 253.5 / 2.03
長露光(×4.0) 76.0 / 20.13 255.0 / 0.00
HDR融合 72.5 / 8.76 239.8 / 5.35

表を行ごとに読む:短露光の暗部パッチの標準偏差2.35は読み出しノイズ \(\sigma=2\) とほぼ等しく——縞は完全に埋もれている;中露光の明部パッチは平均253.5、標準偏差2.03であり、クリップ後の残痕である;長露光の明部パッチの標準偏差は正確に0.00で——完全に飽和しており情報はゼロになっている。4枚の画像のうち、融合画像だけが2つの細部パッチの両方で明確な縞のコントラストを同時に保持している(標準偏差8.76と5.35はいずれもノイズの数倍である)。これこそがHDRの価値である。

この実験は追加の教育的ポイントももたらしている:プログラムが出力する復元された放射照度のピーク \(\hat E_{\max}=300.0\) に対し、シーンの真値は272である。誤差は短露光フレームに由来する——その \(\sigma=2\) の読み出しノイズは \(v/t\) で還元される際に \(1/0.25=4\) 倍に増幅される;中露光と長露光がいずれも飽和している最も明るい画素では、短露光フレームだけが唯一の「有効な票」として残り、4倍に増幅されたノイズが直接推定値に反映されるのである。これが我々に与える注意喚起は:短露光フレームが放射照度推定に寄与するものは増幅されたノイズであり、より精緻な実装では三角重みに加えて露光時間に依存するSNR重みを乗じるべきである。

12.4 SciVision 実装

本章のSDKの使用状況は、それ自体が工学の授業となるため、正直に記述する必要がある。我々は当初 SciSvEdgeEnhance::EnhanceEdges をシャープ化に、SciSvHDR シリーズを露光融合に用いる予定であったが、本章の例を開発する際の系統的なテスト(v3.1)により以下の結論に達した:

  • SciSvEdgeEnhance::EnhanceEdgestype/offset/factor/brightness の全パラメータを走査したところ、全ての組み合わせで0/255の二値エッジマップが出力され、「原画 + エッジ強化」からなるシャープ化画像ではなかった——実際にはエッジマップ生成器であり、名前が誤解を招くものであった;
  • SciSvHDR::FusedPicture はグレースケール入力に対してリターンコード0を返すが、暗部全体を平均約0.1の真っ黒に押しつぶす(細部は全て失われる);3チャンネル入力では直接OpenCVの cv::Mat アサーションクラッシュを引き起こす;
  • SciSvHDR::Compose は480×360のシングルチャンネル入力に対し、449×340にクロップされた3チャンネル画像を返す;
  • SciSvHDR::ToneMapping(method=1) はOpenCVの cv::scaleAdd アサーションを引き起こし、プロセスを直接終了させる(終了コード0xC0000409)。

そのため本章のシャープ化とHDR融合はいずれも手書きで実装し、SDKは確実に動作する2つの処理——ガウシアンフィルタと画像入出力——のみを担当している。ガウシアン部分は チャプター 6 と完全に同一である:

SCIMV::SciSvFilter flt;
SciImage soft, blur;
flt.Gaussian(imgSharp, roi, &soft, 9, 9, 1.5, 1.5);  // 軽度の焦点ぼけを模擬
flt.Gaussian(soft, roi, &blur, 9, 9, 1.5, 1.5);      // アンシャープマスクのぼかし項

アンシャープマスクのコアはわずか1行の算術であり、画素ごとに \(\text{out} = \text{in} + \lambda(\text{in} - \text{blur})\) を実行して [0,255] にクランプする:

double v = in[i] + lambda * (in[i] - blur[i]);
out[i] = (unsigned char)(v < 0 ? 0 : (v > 255 ? 255 : (int)(v + 0.5)));

実装では画像境界から5 px以内の画素は処理しない:SDKのガウシアンが境界で用いる外挿値を原画から減算すると擬似的な差分が生じ、直接強化すると画像の周囲に白黒の枠状のアーティファクトが生じるためである。露光融合のコアも同様にコンパクトである——画素ごとに3フレームの三角重み付き投票を累積する:

for (int k = 0; k < 3; ++k) {
    double v = (double)exps[k][i];
    double w = (v < 255.0 - v ? v : 255.0 - v) + 1e-3;  // 三角重み
    sw += w;
    se += w * v / t[k];     // t[] = {0.25, 1.0, 4.0}
}
E[i] = se / sw;             // その後 gamma=1/2.2 でトーンマッピング

完全なプロジェクトは code/advanced_enhancement/ にあり、ファイルヘッダには上述のSDKの不具合の完全なテスト記録が残されている。この経験から得られる工学的な教訓は太字で記述するに値する:商用ビジョンライブラリの各APIは、生産ラインに導入する前に、真値が既知のゴールデン実験で実際の動作を検証しなければならない——手計算で結果がわかる合成入力を構成し、APIに入力して出力を照合するのである。これは チャプター 5 で既知のターゲットを用いてキャリブレーション結果を検査する方法論と同じ系統である:ドキュメントは約束であり、実験で検証されるものこそが実際の動作なのである。

産業事例:電池ケースレーザー刻印のHDR読み取り

ある電池ケース生産ラインでは、研磨されたアルミケース表面にレーザーでロットコードを刻印しており、読み取りカメラは高反射の金属面を正対して撮影していた。単一露光では文字のストロークが飽和して真っ白になるか、背景が真っ黒に押しつぶれるかのいずれかで、文字と背景のコントラストが揃うことはなく、読み取り率は70%前後で推移していた。改造案では、同じステーションで2段階の露光を連続撮影し(短露光で文字を保持、長露光で背景を保持)、本章の重み付き融合方式で1フレームに合成してから読み取り器に送ることで、読み取り率は99%以上に上昇した。代償も明確である:製品ごとに1フレーム余分に撮影するため、撮影タクトが倍になり、ラインは減速を余儀なくされた。後に評価された代替案は12ビットの高ダイナミックレンジカメラに換装し、1フレームで全グレースケール範囲をカバーするものであった——ハードウェアはより高価になるが、タクトは変わらない。この事例から得られる一般的な結論は:ダイナミックレンジの問題はまずハードウェア(より深いビット深度、より大きな飽和容量)を考え、次にアルゴリズム(多重露光融合)を考える——アルゴリズムによる手法は費用を抑えられるが、時間で代償を払うことになる。

12.5 まとめ

  • 強化は増幅であり、復元ではない。アンシャープマスク \(g = f + \lambda(f - G_\sigma * f)\) は高周波数成分を \(1+\lambda\) 倍に増幅し、レンズと焦点ぼけによるMTF減衰を補償するが、MTFがすでにゼロになった細部は誰も救うことができず、ノイズも共に増幅される。
  • \(\lambda\) はオーバーシュートで限界を定める。代表的なエッジでプロファイルを抽出し、オーバーシュートの振幅がノイズの振幅に達したら停止する(本章ではオーバーシュート +5 の \(\lambda=1.0\) は合格であり、オーバーシュート +33 の \(\lambda=4.0\) は限界を超えている);オーバーシュートはキャリパーによるエッジ検出を系統的にずらし、ハローは擬似欠陥を生み出す。
  • ダイナミックレンジが足りなければ、多重露光で補う。三角重みを用いた放射照度復元 \(\hat E = \sum w(v_i)v_i/t_i \,/\, \sum w(v_i)\) ——暗すぎる票と明すぎる票は無効にする;実験では融合画像だけが明部と暗部の両方の細部を同時に可視化できた。
  • 短露光フレームのノイズは \(1/t\) 倍に増幅される(本章では真値272に対し \(\hat E_{\max}=300\) となった);精緻な実装ではSNR重みを追加すべきであり、融合の工学的な代償はタクトが倍になることである——ダイナミックレンジの問題はまず高ビット深度カメラによるハードウェア的な解決策を評価すべきである。
  • 商用ライブラリのAPIの動作はゴールデン実験で検証しなければならない。本章のSDKのシャープ化とHDRのインターフェースはいずれも名前と実際の動作が一致しないか、直接クラッシュするものであった;最終的な解決策ではSDKにガウシアンフィルタと入出力だけを担当させ、コアアルゴリズムは手書きとした——ドキュメントは約束であり、実験で検証されるものこそが実際の動作なのである。

アンシャープマスクなどのシャープ化手法の標準的な解説は Gonzalez と Woods の教科書 (Gonzalez と Woods 2018) にある;本章の多重露光合成の方法論的な起源は、SIGGRAPH 1997で Debevec と Malik が発表した、カメラの応答関数を復元し高ダイナミックレンジ放射照度マップを融合する研究 (Debevec と Malik 1997) であり、撮影、表示からトーンマッピングまでの高ダイナミックレンジ撮像の完全な体系については Reinhard らの専門書 (Reinhard ほか 2010) を参照されたい。シャープ化フィルタの周波数領域解析とより系統的なグレースケール変換手法については、Steger らの著作 (Steger, Ulrich, と Wiedemann 2018) をさらに読み進めることができる。