写真の物理学 ㉞ RAW現像の信号処理

Share
📐
写真の物理学シリーズ ㉞
このシリーズでは、写真にまつわる現象を物理学の言葉で記述する。「なんとなくそうなる」を「なぜそうなるか」に変換することが目的である。

デジタルカメラのシャッターを切った瞬間にセンサーが捉えるのは、色も階調もコントラストもないリニアな整数値の二次元配列にすぎない。この配列が「写真」になるまでには、ブラックレベル補正からデモザイキング、カラーマトリクス変換、トーンカーブ適用、JPEG圧縮に至る十を超える信号処理工程が介在する。本稿ではRAW現像パイプラインの各工程が画像のどの物理量をどう変換しているのかを数式で記述する。

RAWデータの正体:リニアな光子カウント

イメージセンサーの各画素(フォトダイオード)は、露光時間中に入射した光子を電荷に変換し、その電荷量をアナログ-デジタル変換器(ADC)で整数値に量子化する。この整数値を ADU(Analog-to-Digital Unit)と呼ぶ。RAWファイルに記録されているのは、このADU値の二次元配列である。

画素 $(i, j)$ におけるADU値 $S_{i,j}$ は、入射光子数 $N_{i,j}$、量子効率 $\eta$、システムゲイン $g$(電子/ADU)、暗電流由来のオフセット $D_{i,j}$ を用いて次のように書ける。

$$ S_{i,j} = \frac{\eta \, N_{i,j}}{g} + D_{i,j} + n_{i,j} $$

ここで $n_{i,j}$ は読み出しノイズなどの確率的雑音項である。量子効率やフォトダイオードの物理については光電効果とフォトダイオードで詳述した。重要なのは、光子数 $N_{i,j}$ とADU値 $S_{i,j}$ の間にはオフセットと雑音を除けば線形関係が成り立つという点だ。光が2倍になればADU値もおよそ2倍になる。この線形性こそがRAWデータの最大の特徴であり、後段の処理が数学的に整合する根拠でもある。

典型的な14ビットADCでは $S_{i,j}$ は0から16383の範囲をとる。センサーのフルウェルキャパシティ(飽和電荷量)に達すると値はクリップされ、飽和画素となる。ビット深度とダイナミックレンジの定量的な関係はダイナミックレンジとビット深度で論じた。写真の物理学 ① 光の直進と薄肉レンズの結像で光が像面に到達するまでの過程を扱ったが、ここではその光がセンサー上で電気信号に変わった直後の状態から出発する。

ブラックレベル補正:暗電流オフセットの減算

センサーには光が一切入射しなくても、熱励起による暗電流が電荷を生む。これに加え、ADCの設計上のオフセット(ペデスタル)が意図的に加算されている場合もある。これらを総称してブラックレベルと呼ぶ。

ブラックレベル補正は、RAW現像パイプラインの最初の演算であり、単純な減算で表される。

$$ S'_{i,j} = S_{i,j} - B_{i,j} $$

$B_{i,j}$ はブラックレベル値であり、多くのカメラではRAWファイルのメタデータ(Exifタグ)に記録されている。画素ごとに異なる場合もあるが、通常はセンサーの光学的に遮蔽された領域(オプティカルブラック領域)の平均値、あるいはメーカーが出荷時に校正した固定値が用いられる。

この補正を怠ると、暗部に浮いたオフセットが残り、後段のホワイトバランスやトーンカーブで暗部の階調が破綻する。まぶたの裏に残る灰色の光で触れた暗電流の物理的起源(シリコン中の熱励起キャリア)がここに直接現れている。

ホワイトバランス:チャネルゲイン乗算

ブラックレベル補正後のデータは依然としてベイヤー配列上のモザイク画像であり、各画素はR・G・Bいずれか一色のフィルタを通した値しか持たない。ホワイトバランスは、光源の色温度に起因するチャネル間の強度差を補正する処理であり、デモザイキングの前に適用される。

補正は各チャネルに独立したゲインを乗じる形で行われる。

$$ S''_R = k_R \cdot S'_R, \quad S''_G = k_G \cdot S'_G, \quad S''_B = k_B \cdot S'_B $$

ゲイン $k_R, k_G, k_B$ は光源のスペクトル分布に依存する。実用上は次の手順で決定される。

1. グレーワールド仮定: シーン全体の平均反射率が無彩色であると仮定し、各チャネルの平均値が等しくなるようにゲインを算出する。

$$ k_c = \frac{\bar{S}'_G}{\bar{S}'_c} \quad (c \in \{R, G, B\}) $$

2. 既知の白色点: 撮影者が「この領域は白(または灰色)である」と指定し、その領域の平均値からゲインを逆算する。

3. 色温度モデル: 色温度と黒体放射で扱った黒体放射のスペクトル分布とセンサーの分光感度特性から理論的にゲインを計算する。カメラ内蔵のAWB(オートホワイトバランス)はこの手法とグレーワールド仮定を組み合わせた推定アルゴリズムを用いる。

ホワイトバランスはリニア空間上でのスケーリングであるから、ガンマ補正やトーンカーブを適用した後に行うと非線形歪みが混入する。RAWデータがリニアであることの利点はここに端的に表れる。ストロボ撮影で色がずれる理由と対策で扱った混合光源下での色ずれも、根本的にはこのチャネルゲインの設定問題に帰着する。

デモザイキング:ベイヤーからフルカラーへ

ベイヤー配列のセンサーでは、各画素は一色分の情報しか持たない。フルカラー画像を得るには、欠落した二色を周囲の画素から補間する必要がある。この処理がデモザイキング(demosaicing)である。

最も単純な双線形補間では、画素 $(i, j)$ の欠落チャネル値を周囲の同色画素の加重平均で推定する。

$$ \hat{C}_{i,j} = \sum_{(m,n) \in \mathcal{N}} w_{m,n} \cdot C_{m,n} $$

$\mathcal{N}$ は $(i, j)$ の近傍にある同色画素の集合、$w_{m,n}$ は距離に基づく重みである。双線形補間は計算が軽いが、エッジ部分で偽色やジッパーアーティファクトを生む。

高品質なデモザイキングアルゴリズム(AHD、DCB、AMaZEなど)は、エッジの方向を検出し、エッジに沿った方向にのみ補間を行う。たとえば水平方向に走るエッジが検出された画素では、エッジに沿った水平方向の画素のみから補間し、エッジを横切る垂直方向の補間を抑制する。

デモザイキングはRAW現像パイプラインの中で最も情報を「創出」する工程である。存在しないデータを推定しているのだから、原理的に推定誤差が伴う。この点についてはベイヤー配列とデモザイキングの数学で詳しく議論した。

カラーマトリクス変換:カメラRGBからsRGBへ

デモザイキングによって得られたRGB値は、あくまでカメラ固有の色空間における値である。カメラのカラーフィルタの分光透過特性はメーカーやセンサーモデルごとに異なるため、同じ被写体を撮影しても異なるRGB値が記録される。これを標準的な色空間(色とは何かで導入したCIE XYZやsRGBなど)に変換するのがカラーマトリクス変換である。

変換は3×3の行列で記述される。

$$ \begin{pmatrix} R_{\text{sRGB}} \\ G_{\text{sRGB}} \\ B_{\text{sRGB}} \end{pmatrix} = \mathbf{M} \begin{pmatrix} R_{\text{cam}} \\ G_{\text{cam}} \\ B_{\text{cam}} \end{pmatrix} $$

行列 $\mathbf{M}$ はカメラプロファイル(DCP、ICCプロファイルなど)に格納されている。Adobe DNG仕様では、二つの光源(標準イルミナントA:約2856K、D65:約6504K)に対する行列 $\mathbf{M}_1, \mathbf{M}_2$ が記録され、撮影時の色温度に応じて線形補間される。

$$ \mathbf{M}(T) = (1 - \alpha) \mathbf{M}_1 + \alpha \mathbf{M}_2 $$

$\alpha$ は色温度 $T$ に依存する補間係数である。

この変換は本質的には基底変換、すなわちカメラRGB空間の三つの基底ベクトルを色空間の数学で整理したsRGB空間の基底ベクトルに写す線形写像である。変換行列が正方かつ正則であれば逆変換も可能だが、カメラの分光感度がルーサー・アイヴス条件(CIE等色関数の線形結合で表せる条件)を満たさない限り、変換には系統的な誤差(演色性とメタメリズムで論じたメタメリズム・ミスマッチ)が残る。現像ソフトを選び直すで触れたLightroomとCapture Oneの色の違いは、このカラーマトリクスとそれに続くトーンカーブの設計思想の差に起因する。

ノイズリダクション:信号と雑音の分離

センサーが捉えた信号には、ノイズの物理学で分類した光子ショットノイズ、暗電流ショットノイズ、読み出しノイズなど、複数の雑音源が重畳している。ノイズリダクション(NR)はこれらの雑音成分を抑制する処理である。

空間フィルタリング

最も基本的な手法はカーネルベースの空間フィルタリングである。ガウシアンぼかしを例にとると、画素 $(i,j)$ の出力値は

$$ \hat{I}_{i,j} = \sum_{(m,n) \in \mathcal{K}} G(m,n;\sigma) \cdot I_{i+m,j+n} $$

$G(m,n;\sigma)$ はガウスカーネル、$\sigma$ はぼかしの強さを制御する標準偏差である。ガウシアンぼかしはノイズを平滑化するが、エッジも同時にぼかしてしまう。

バイラテラルフィルタ

バイラテラルフィルタは空間的な距離だけでなく、画素値の差(レンジ)にも重みを与える。

$$ \hat{I}_{i,j} = \frac{1}{W} \sum_{(m,n) \in \mathcal{K}} G_s(m,n;\sigma_s) \cdot G_r(I_{i+m,j+n} - I_{i,j};\sigma_r) \cdot I_{i+m,j+n} $$

$G_s$ は空間ガウス、$G_r$ はレンジガウスであり、$W$ は正規化定数、$\mathcal{K}$ は中心画素の近傍である。画素値が大きく異なる近傍画素(すなわちエッジの反対側)の重みは小さくなるため、エッジを保存しながらノイズを除去できる。

周波数領域フィルタリング

ウェーブレット変換を用いた手法では、画像を空間周波数の異なる複数のサブバンドに分解し、ノイズが支配的な高周波サブバンドの係数を選択的にしきい値処理(ソフトスレッショルディング)する。

$$ \hat{w}_{k} = \text{sign}(w_k) \cdot \max(|w_k| - \lambda, 0) $$

$w_k$ はウェーブレット係数、$\lambda$ はしきい値であり、$|w_k| < \lambda$ の係数はゼロに縮退する。ノイズの分散 $\sigma_n^2$ が既知であれば、Donoho-Johnstoneのユニバーサルしきい値 $\lambda = \sigma_n \sqrt{2 \ln N}$($N$ は画素数)が理論的な最適値を与える。

いずれの手法においても、ノイズ除去とディテール保持はトレードオフの関係にある。過度なNRは画像の微細構造を破壊し、いわゆる「水彩画」のような不自然な平滑化を招く。

シャープニング:アンシャープマスクの畳み込み

デモザイキングやノイズリダクションで失われた見かけ上の先鋭度を回復するために、シャープニングが適用される。最も広く使われる手法はアンシャープマスク(USM: Unsharp Mask)である。

USMの原理は単純で、元画像からぼかした画像を差し引いてエッジ成分を抽出し、それを元画像に加算する。

$$ I_{\text{sharp}} = I + \alpha \cdot (I - I_{\text{blur}}) $$

$I_{\text{blur}}$ はガウシアンぼかしを施した画像、$\alpha$ は強度(Amount)を制御するパラメータである。$I - I_{\text{blur}}$ は高周波成分の近似であり、これを加算することで空間周波数応答(MTFで読むレンズの解像力参照)の高周波側が増幅される。

畳み込みの観点からは、USMカーネル $K_{\text{USM}}$ は

$$ K_{\text{USM}} = (1 + \alpha)\delta - \alpha G_\sigma $$

$\delta$ はディラックのデルタ(単位インパルス)、$G_\sigma$ はガウスカーネルである。このカーネルの周波数応答は、低周波ではほぼ1(変化なし)、高周波では $1 + \alpha$(増幅)となる。

USMの制御パラメータは三つある。

Amount($\alpha$): 高周波増幅の強さ。値が大きいほどシャープに見えるが、過剰にするとエッジにハロー(白い縁取り)が発生する。

Radius($\sigma$): ガウスカーネルの標準偏差。小さい値は微細なディテールに、大きい値は粗い構造に作用する。

Threshold: 画素値の差がこの値以下の場合はシャープニングを適用しない。ノイズの増幅を防ぐために設定される。

シャープニングは情報を創出しているわけではなく、既存の信号の特定周波数帯を増幅しているに過ぎない。したがって、本来存在しないディテールを「でっち上げる」ことはできないし、やりすぎればアーティファクトとして可視化される。

レンズ補正:光学収差のデジタル逆変換

レンズは理想的な結像装置ではなく、収差の物理学で体系的に扱った歪曲収差、周辺光量落ち(ヴィネッティング)、色収差などの光学的欠陥を持つ。RAW現像ソフトはレンズプロファイルに基づき、これらを逆変換する。

歪曲収差補正

歪曲収差は、像の幾何学的な変形であり、像高 $r$ に対して次の多項式モデルで記述される。

$$ r_{\text{distorted}} = r(1 + k_1 r^2 + k_2 r^4 + k_3 r^6 + \cdots) $$

$k_1, k_2, k_3$ は歪曲係数であり、$k_1 > 0$ で糸巻き型歪曲、$k_1 < 0$ で樽型歪曲となる。補正は歪曲のない理想座標から歪曲座標への逆写像を計算し、サブピクセル補間で画素値を再配置する。

周辺光量落ち補正

ヴィネッティングは像面照度の $\cos^4 \theta$ 則および機械的ケラレに起因する。補正は像高に依存するゲインマップの乗算で行われる。

$$ I_{\text{corrected}}(r) = \frac{I(r)}{V(r)} $$

$V(r)$ は正規化されたヴィネッティングプロファイルであり、$V(0) = 1$、$V(r) < 1$($r > 0$)である。この補正は本質的にはフラットフィールド補正と同じ操作(既知の不均一性で割る)である。収差の物理的起源については絞りと有効口径の物理的意味も参照されたい。

倍率色収差補正

色収差のうち倍率色収差(横色収差)は、波長ごとに結像倍率が異なることで生じる。R・G・Bチャネルで像のスケールが異なるため、高コントラストなエッジでR/Bの色ずれが現れる。

補正は、Gチャネルを基準としてR・Bチャネルに像高依存のスケーリングを適用する。

$$ r'_c = r \cdot (1 + \beta_c r^2 + \gamma_c r^4) \quad (c \in \{R, B\}) $$

$\beta_c, \gamma_c$ はチャネルごとの倍率色収差係数である。この補正はサブピクセル精度の座標変換と補間を伴う。

トーンカーブ:リニアから知覚空間への変換

ここまでの工程はすべてリニア空間(物理的な光量に比例する空間)で行われてきた。しかし人間の視覚系は光量に対して非線形な応答特性を持つ(視覚の知覚心理物理学参照)。ガンマとトーンカーブの知覚心理物理学で詳述したスティーヴンスのべき法則によれば、知覚される明るさ $\psi$ は光量 $L$ に対して

$$ \psi \propto L^{0.33} $$

と近似される。リニアなデータをそのまま表示すると暗部が潰れ、明部に階調が偏った不自然な画像になる。

トーンカーブはリニアデータを知覚的に自然な階調分布に変換する。sRGBのガンマ関数は次のように定義される。

$$ C_{\text{sRGB}} = \begin{cases} 12.92 \cdot C_{\text{linear}} & \text{if } C_{\text{linear}} \le 0.0031308 \\ 1.055 \cdot C_{\text{linear}}^{1/2.4} - 0.055 & \text{if } C_{\text{linear}} > 0.0031308 \end{cases} $$

低輝度域は線形、それ以外は $1/2.4 \approx 0.417$ のべき乗で圧縮される。しばしば「ガンマ2.2」と簡略化されるが、リニア値をsRGB値に変換する実際の符号化関数は上記の分割関数であり、単純なべき乗ではない。

RAW現像ソフトが適用するトーンカーブはsRGBガンマよりも複雑であり、コントラストのS字カーブ、ハイライト圧縮、シャドウ持ち上げなどの要素を含む。Lightroomの「Adobe Standard」やCapture Oneのベースカーブは、このトーンカーブの設計によってJPEGの「ルック」を決定している。

リニアデータを直接表示して「RAWは眠い」と言われるのは、このトーンカーブが未適用だからである。RAWデータが本質的に眠いのではなく、人間の視覚特性に合わせた変換がまだ施されていないだけだ。

JPEG圧縮の数学:DCTと量子化

RAW現像の最終段階として、処理済みの画像をJPEGに書き出す工程を概観する。JPEG圧縮は非可逆圧縮であり、人間の知覚特性を利用して視覚的に重要でない情報を削減する。

色空間変換

まずRGBからYCbCrへの変換が行われる。Yは輝度、Cb・Crは色差成分である。

$$ \begin{pmatrix} Y \\ C_b \\ C_r \end{pmatrix} = \begin{pmatrix} 0.299 & 0.587 & 0.114 \\ -0.169 & -0.331 & 0.500 \\ 0.500 & -0.419 & -0.081 \end{pmatrix} \begin{pmatrix} R \\ G \\ B \end{pmatrix} $$

人間の視覚系は輝度の空間分解能が色差よりも高いため、Cb・Crチャネルを4:2:0などでサブサンプリングしてデータ量を削減できる。この知覚的非対称性についてはクロマサブサンプリングの数学的基礎で詳述した。

DCT(離散コサイン変換)

画像は8×8ピクセルのブロックに分割され、各ブロックに二次元DCTが適用される。

$$ F(u,v) = \frac{1}{4} C(u) C(v) \sum_{x=0}^{7} \sum_{y=0}^{7} f(x,y) \cos\!\left[\frac{(2x+1)u\pi}{16}\right] \cos\!\left[\frac{(2y+1)v\pi}{16}\right] $$

$C(k) = 1/\sqrt{2}$($k=0$の場合)、$C(k) = 1$($k \neq 0$の場合)である。$F(0,0)$ はブロック内の平均値(DC成分)、それ以外は空間周波数成分(AC成分)を表す。

DCTは情報を捨てない可逆変換である。圧縮が起きるのは次の量子化の段階である。

量子化

DCT係数は量子化テーブル $Q(u,v)$ で除算され、整数に丸められる。

$$ F_q(u,v) = \text{round}\!\left(\frac{F(u,v)}{Q(u,v)}\right) $$

高周波成分ほど $Q(u,v)$ が大きく設定されており、視覚的に知覚しにくい高周波のDCT係数が優先的にゼロに丸められる。これが非可逆圧縮の核心であり、量子化テーブルの値がJPEGの「品質」を決定する。品質を下げる(量子化テーブルの値を大きくする)と、より多くの係数がゼロに丸められ、ファイルサイズが小さくなる代わりにブロックノイズ(8×8ピクセル単位のアーティファクト)が顕在化する。

量子化後の係数はジグザグスキャンで一次元に並べ替えられ、ランレングス符号化とハフマン符号化(またはアリスメティック符号化)で最終的なビットストリームに変換される。

まとめ

RAW現像パイプラインは、以下の信号処理の連鎖として記述できる。

  1. ブラックレベル補正: 暗電流とペデスタルの減算(線形演算)
  2. ホワイトバランス: チャネル独立のゲイン乗算(線形演算)
  3. デモザイキング: ベイヤーモザイクからフルカラーへの補間(情報推定)
  4. カラーマトリクス変換: カメラ色空間から標準色空間への基底変換(線形変換)
  5. ノイズリダクション: 信号と雑音の分離(線形/非線形フィルタリング)
  6. シャープニング: 高周波増幅による見かけの先鋭度回復(畳み込み)
  7. レンズ補正: 歪曲・ヴィネッティング・色収差の逆変換(幾何変換+ゲイン補正)
  8. トーンカーブ: リニアから知覚空間への非線形変換
  9. JPEG圧縮: DCT+量子化による知覚的非可逆圧縮

工程1から4(場合によっては5まで)はリニア空間で行われ、演算の順序交換が比較的自由である。トーンカーブ以降は非線形空間となり、演算順序が結果に影響を与える。

RAW現像ソフトが提供するスライダーやパラメータは、これらの数学的操作のフロントエンドに過ぎない。「露出補正+1EV」は全画素にゲイン2を乗じる線形スケーリングであり、「ハイライト回復」はトーンカーブの上端を圧縮する非線形写像であり、「ノイズ除去」は空間フィルタのパラメータを調整する操作である。数学的に何が起きているかを理解することは、感覚に頼らない再現性のある現像判断を可能にする。本稿で扱った信号処理の連鎖が写真システム全体のなかでどう位置づけられるかは、すべてを統合するで俯瞰する。

Read more

Dependabotの対応が面倒なのでRenovateを導入する

Dependabotくんはある程度の依存関係の更新を通知してくれるが、山のようにprojectを抱えているとなんとも痒いところに手が届かない。 ということで、Renovateを使ってみる。 Build software better, togetherGitHub is where people build software. More than 150 million people use GitHub to discover, fork, and contribute to over 420 million projects.GitHub ここからGitHub Appを入れる。 あとは、指示に従ってポチポチするだけで、超かんたんだった。 感想 自動化最高。

By Sakashita Yasunobu

nodejsのバグを見つけたのでPRを投げてみた

概要 要するに、node --testで特定のバイナリを標準出力に書くと、テストランナーが固まったり、異常終了したりするバグです。 バグ自体は既知のものだったようで、修正がされていました。 test_runner: infinite loop in FileTest#drainRawBuffer when child stdout contains FF 0F followed by large size bytes · Issue #62693 · nodejs/nodeVersion v25.9.0 (also reproduced on v24.14.1 LTS, v22.21.0 LTS, v23.4.0) Platform Darwin 25.

By Sakashita Yasunobu
青空文庫のParser

青空文庫のParser

青空文庫のParserを作ってます。 GitHub - P4suta/aozora: Pure-functional Rust parser for 青空文庫記法 (Aozora Bunko notation): ルビ, 傍点, 縦中横, 外字, 返り点, indent containers, page breaks.Pure-functional Rust parser for 青空文庫記法 (Aozora Bunko notation): ルビ, 傍点, 縦中横, 外字, 返り点, indent containers, page breaks. - P4suta/aozoraGitHubP4suta Rustで作ってますが、配布物はcargoのほかにnpm/pypiでも入手できます Documentation * Playground — try it in

By Sakashita Yasunobu