豪鬼メモ

一瞬千撃

レンズプロファイルによる倍率色収差と歪曲収差の自動補正

RAW現像時にレンズプロファイルを参照して倍率色収差と歪曲収差を補正できる仕組みを実装してみた。そういったデジタル補正をやるのとやらないのとでは出来上がった画像のスッキリ感が全く違う。
f:id:fridaynight:20181125132850j:plain


まず、デモサイトで遊んでみてほしい。TCA CorrectionとDistortion Correctionの設定がデフォルトでProfileになっていて、補正プロファイルに載っているレンズであれば自動的に倍率色収差と歪曲収差が補正される。それぞれの設定をNoneで現像すると、補正なしの現像例も見ることができる。プロファイルが登録されていないレンズではManualにして自分で補正値を入力することができる。
f:id:fridaynight:20181207151220p:plain


倍率色収差とは、光の屈折率が波長によって異なることによって、像面において赤と緑と青の画像の大きさが微妙に変わってしまう収差である。倍率色収差が残っていると画像の周辺のハイコントラストな部分にカラーフリンジが発生してしまい、まるでガラス越しに水槽をのぞいているような気分にさせられる。安いレンズやズームレンズはもちろん、高い単焦点レンズでさえも倍率色収差の除去が完全(視認できないレベル)であることは稀なので、それを後処理で除去する機能は重要だ。幸いなことに、倍率色収差の出方はレンズによってだいたい決まっているので、それを知っていれば十分な補正を行うことができる。現像プログラムDcrawには、現像時にRGBの各チャンネルの画像に倍率を指定することで、レンズの倍率色収差を補正する機能がある。

個々の作例を現像する度にそれを撮影したレンズを調べて倍率色収差の補正値を入力するのは面倒だし、そもそも適切な補正値を自分で調べるのは素人には難しい。嬉しいことに、LensFunというプロジェクトが有志によって報告された各種レンズの補正値をデータベース化していて、さらにそれを検索するためのライブラリも提供してくれている。様々なツールが協力して事を成し遂げるというのはいかにもUNIX的で素晴らしい。しかし、LensFunのCライブラリをPythonから呼べるようにするのはちょっと面倒だ。なので、LensFunをインストールするとついてくるのXMLのデータベースを予め走査して、倍率色収差の補正値のみをTSVファイルとして抜き出しておくことにした。こんな簡単なスクリプトでOK。ここでは、倍率色収差の出方は3次の多項式として記録されているので、それを線形モデルに変換してやる必要がある。

Rd = b * Ru^3 + c * Ru^2 + v * Ru

上記の式で、Ruは本来の点像のイメージサークルの中心から端までの中での半径を示し、Rdは収差によってその点像がずれた先の半径を示している。半径の値は画像の短辺の半分で正規化されるので、アスペクトが4:3の場合、画面上下端の中央は1、画面左右端の中央は1.333、画面四隅は1.666ということになる。で、各項の係数であるbとcとvがプロファイルに記録されることになる。倍率色収差のプロファイルはtca_correctというツールで作成することができる。LensFunのデータベースでは、Gチャンネル(緑)を基準として考えるので、プロファイルにはRチャンネル(赤)とBチャンネル(青)の情報のみが載っている。こんな感じで。

<lens>
<model>Olympus M.Zuiko Digital 45mm f/1.8</model>
<calibration>
...
<tca model="poly3" focal="45" br="-0.0000129" vr="1.0001904" bb="0.0000108" vb="0.9999203"/>
...
</calibration>
</lens>

多項式モデルではどの半径に着目するかで倍率が変わってくることになるのだが、ここでは半径1の位置に着目することにする。倍率色収差が目立つのは画面端の方だが、四隅だと測定値が暴れていそうだし、四隅には視線がそんなに行かないだろう。短辺くらいの場所が綺麗になると最も気持ちが良さそうだ。てわけで、上記のMZ45の倍率色収差の代表値は、赤が ((1 ** 3) * -0.0000129 + 1 * 1.000190) / 1 = 1.0001771 で、青が ((1 ** 1) * 0.0000108 + 1 * 0.9999203) / 1 = 0.9999311 ということになる。例えば画面サイズが4640x3472のカメラで撮影した画像での収差の出方を考えてみよう。半径1の位置すなわち中央から1736ピクセルの位置に着目する。そこでは赤は 1736 * 1.0001771 = 1736.3074456 で、緑より0.3ピクセルくらい大きく描画される。青は 1736 * 0.9999203 = で、0.13ピクセルくらい小さく描画される。つまり、補正しない場合、白い線の内側(画面中心側)には赤の補色であるシアンとその内側に青が視認され、外側(画面端側 )には赤の補色である黄色とその外側に赤が視認されることが予想される。MZ45は倍率色収差に関してかなりマシなほうだ。MZ17はR=1.000267、B=0.999875ということなので、半径1の位置だと赤は0.77外側に、青は0.37ピクセル内側に描画される。赤と青の両方が正に振れているレンズも結構あって、その場合は内側に緑の縁取りが視認され、外側にマゼンタの縁取りが視認されるだろう。赤と青の双方が負に触れている場合はその逆になるはずだ。

Dcrawでは全画面で固定の倍率を指定する線形モデルしか指定できない。よって、上述のように代表値を適当に設定して算出した倍率を使う。画面全体の色収差の面積を最小化するという問題を解析的に解くのは厄介そうなので、ここでは半径0.8と1.0と1.2のそれぞれの収差倍率を平均して代表値とする。LensFun最新版のデータベースに記載されている全ての色収差情報を抜き出したものがこちらのファイルである。1列目がレンズ名で、2列目以降に補正情報が記載されている。ズームレンズは焦点距離によって倍率色収差の出方が違うので、焦点距離と赤倍率と青倍率の組をコロンで区切った表現が並べられている。現像時にEXIFメタデータからレンズ名を取得して照合すれば、その画像でどんな風に倍率色収差が出ているか推定できる。そして赤と青の各チャンネルの倍率を収差データの逆数にすれば、うまいこと補正ができるはずだ。例えばMZ47の作例に対しては、赤は0.99981倍し、青は1.0001倍する感じになる。こんな風に指定する。

$ dcraw -C 0.99981 1.0001 input.dng


倍率色収差の補正例を見ていこう。補正前、補正後の順で画像を並べる。Leica DG Summilux 15mm f/1.7の例。このレンズは赤倍率1.000234の青倍率1.000052なので、主に赤色成分が大きくなることで色ズレが発生する。
f:id:fridaynight:20181207141300j:plain
f:id:fridaynight:20181207141321j:plain

画面恥を拡大してみると、縁取りが大幅に軽減されているのがわかる。それでもまだ残るマゼンタのフリンジは軸状色収差のものだ。
f:id:fridaynight:20181207141355j:plain
f:id:fridaynight:20181207141414j:plain

Olympus M.Zuiko 25mm F1.8の例。このレンズは赤倍率1.000300の青倍率1.000100なので、先ほどの例よりもさらに赤の色ズレがひどいということになる。
f:id:fridaynight:20181207101048j:plain
f:id:fridaynight:20181207142324j:plain

画面恥を拡大してみる。等倍で見ると色付きの縁取りが大幅に軽減されているのがわかる。それでもまだ残るマゼンタのフリンジは軸状色収差のものだ。
f:id:fridaynight:20181207142356j:plain
f:id:fridaynight:20181207142408j:plain

たとえ等倍で鑑賞しなくても、倍率色収差が大きい画像はなんだかすっきりしない、気持ち悪い感じがする。「抜けが良い」「抜けが悪い」の主観評価を左右する一因として色収差が挙げられるが、それを取り除くだけでかなりシャッキリポンとした清々しい画像になる。色収差を取り除くと解像感も上がるので、良い事ずくめだ。


歪曲収差の話に移る。歪曲収差とは、レンズが光を様々な方向に曲げた結果として、被写体の形と相似形にならない像が像面に描画される収差である。多くの広角レンズでは、真っ直ぐはずの直線が画面外側に向かって膨らんで見える樽型の歪みが生じることがある。逆に望遠レンズでは画面内側に向かって凹んで見える糸巻き型の歪曲収差が多く見られる。歪曲収差の残り方はレンズの設計次第で代わり、ズームレンズでは焦点距離によっても形が変わる。それを補正するには、例によってレンズ毎の歪曲収差の出方をプロファイルとして記録しておいて、現像時に逆方向に歪みをかけてあげればよい。

LensFunのデータベースには歪曲収差の情報も載っているので、それを使う。プロファイルにはPTLensというソフトウェアで使われている4次多項式モデルのパラメータが記述されている。

Rd = a * Ru^4 + b * Ru^3 + c * Ru^2 + (1 - a - b - c) * Ru

上記の式で、Ruが歪まない場合の半径で、Rdは歪んだ先の半径で、双方とも短い辺の半径で正規化された値を扱う。1次項の係数が 1-a-b-cなのは、収差補正前後の画像の比率を一定にするために、a+b+c+d=1にするという制約による。なお、Huginというプログラムを使うと、歪曲収差のプロファイルをこのモデルで出してくれるらしい。LensFunのXMLにはこんな風に書いてある。

<lens>
<model>Olympus M.Zuiko Digital 17mm f/1.8</model>
<calibration>
<distortion model="ptlens" focal="17" a="0.01989" b="-0.09761" c="0.07461"/>
<calibration>
</lens>

一般的に最も歪みが大きく現れる画面四隅の値を見たいなら、1.5とかを代入してみればいい。上記設定で、半径1.5の点像の位置を考えてみよう。Dは1 - (0.01989 - -0.09761 - 0.0746) = 0.9571 だ。よって、(1.5**4) * 0.01989 + (1.5**3) * -0.09761 + (1.5**2) * 0.0746 + 0.9571 * 1.5 = 1.37475937 が描画位置になる。つまり半径が91%にまで短縮されるわけで、このレンズには一目でわかるレベルの歪曲があると言える。

例によって補正データを予め抽出しておくには、こんな簡単なスクリプトを実行すればよい。LensFun最新版のデータベースに記載されている全ての歪曲収差情報を抜き出したものがこちらのファイルである。1列目がレンズ名で、2列目以降に補正情報が記載されている。ズームレンズは焦点距離によって倍率色収差の出方が違うので、焦点距離と係数ABCの組をコロンで区切った表現が並べられている。現像時にEXIFメタデータからレンズ名を取得して照合すれば、その画像でどんな風に歪曲収差が出ているか推定できる。そしてその逆関数を適用すれば、うまいこと補正できるはずだ。

Dcrawには残念ながら歪曲収差を補正する機能はないので、現像直後にImageMagickを呼び出して歪曲を補正する。幸いなことに、ImageMagickには-distortというオペレータがあり、そのbarrel関数は、上述したような半径の多項式の係数をリストを受け取って補正を行ってくれる。こんな風に指定する。

$ convert -distort barrel "0.01989 -0.09761 0.0746" input.tif output.tif

歪曲収差を補正した後の画像サイズは、短辺の補正後の長さを基準として決められる。その矩形より飛び出した領域は切り取られ、足りない領域は最近某のピクセルの情報が複製される。樽型の歪曲を補正した場合には糸巻き型の画像が生成されるので、基準矩形から余った部分が自動的に捨てられるという挙動で問題ない。糸巻き型の歪曲収差を補正した場合、樽型の画像が生成されるので、四隅に補完された画素が表示されてしまって格好悪い。したがって、四隅の補正後の長さを基準としてトリミングするのが望ましい。そのためには、中央から四隅までの半径に対して補正の多項式を適用し、その結果の比率でトリミングをすることになる。

ちなみに、ImageMagickの-distortion barrelでで歪曲収差を補正する方法は、色収差の補正にも使える。その場合は線形変換だけでなく2次以上の多項式を適用できるため、LensFunのpoly3モデルをそのまま表現することができる。しかし、現像処理の後に色収差の除去をする方法は最適ではない。ホワイトバランスの調整によってRGBの各チャンネルの情報が混ざってしまうので、補正が最適ではなくなる。RとGとBを別々に現像して補正してから結合する方法も考えられるが、処理速度を考えるとあまり現実的ではなさそうだ。


歪曲収差の補正例を見ていこう。Leica DG Summilux 15mm f/1.7の例。このレンズは係数がA=0.011530, B=-0.052730, C=-0.001970だから、r=1.5の時の比率は85.1%と、かなり大きい樽型歪曲収差がある。
f:id:fridaynight:20181207143217j:plain

等倍で見るまでもなく、歪曲収差は思いっきり視認できてしまう。それをプロファイルで補正すると、このようにスッキリとまっすぐな線が描かれる。カメラ現像やLightroom等の市販の現像ソフトを使っていると歪曲補正を無効にすることができないためにレンズの素性について知る機会は少ないが、実はここまでの補正が秘密裏に行われていたのだ。
f:id:fridaynight:20181207143756j:plain

現代の小型化優先レンズの多くは歪曲補正を現像エンジンに頼る設計であるため、歪曲収差の補正機能を実装するのは実用的なシステムを作る上で重要なことだ。ここで紹介したような歪曲が大きいレンズの場合は補正しないと見るに耐えない画像になるため、補正をしない手はない。一方で、MZ45やMZ25のような歪曲が穏やかなレンズの場合は、歪曲を補正しないというのもありだと思う。歪曲補正をするとピクセルを引き延ばすのでどうしても解像感が低下するからだ。


自動輝度補正、擬似HDRに加えて倍率色収差補正と歪曲収差補正が加わったので、かなり実用的なツールになってきた。トーンの調整に比べれば収差補正が美的感覚に貢献する度合いは小さいが、収差が目につくと画竜点睛を欠くことになるので、補正機能があるに越したことはない。これでやっと、何も考えることなくraw画像をアップロードすればそれなりに使える画像が出てくるシステムになったと言えるかな。
f:id:fridaynight:20181207144743j:plain
f:id:fridaynight:20181207145125j:plain
f:id:fridaynight:20181207195624j:plain
f:id:fridaynight:20181109103522j:plain


デジタル補正の三兄弟と言えば倍率色収差補正、歪曲収差補正に加えて周辺減光補正があるわけだが、周辺減光補正はちと面倒くさいのでお蔵入り。周辺減光の出方はレンズの種類と焦点距離と絞りと被写体距離で変わるので、設定が煩雑になりすぎる。


まとめ。LensFunが提供するレンズプロファイルを参照して、倍率色収差と歪曲収差を補正できるようになった。どちらもパラメータがいっぱいでてくるから最初は面食らうが、きちんと説明を読むとそれほど難しいことをしているわけではない。それに、ここでの実装は単にDcrawやImageMagickに補正操作を丸投げしているだけだ。でも、これらの機能を知っていると知らないとでは天と地の差がある。倍率色収差や歪曲収差を補正すると、めちゃすっきりする。Dcrawを駆使する人はぜひプロファイルによる補正の導入を検討してみてほしい。