RAW現像パイプラインを自分で書いてみたわけだが、その中で気づいたことや躓いたことについてここにメモしておこう。おそらく同じことを考えている人の役に立つだろう。

ChatGPTに画像処理を教えてもらうシリーズも、今度こそ終盤であり、RAW現像の基本と色ノイズの処理については前回の記事で解説した。今回は、現像処理の基本であるデモザイクと色ノイズ除去と自動露出補正について、実装というより考え方に絞って説明したい。
mikio.hatenablog.com
デモザイク
デジカメの基礎の話になる。現在市場にあるほとんどのデジカメは、CMOS(相補型金属酸化膜半導体)センサーで光子の量を測定している。CMOSセンサー単体では光の波長は区別できないので、カラーフィルタを併用する。センサーの前に赤緑青のフィルターを格子状に配置して、赤フィルターの後ろの画素の光子の量が多ければ赤っぽいと判断し、緑と青でも同じことをする。このカラーフィルタの配列は色々考えられるのだが、現在市場にあるほとんどのデジカメは、以下のように緑2個と赤1個と青1個からなるパターンを敷き詰める方式である。これをベイヤー配列と言い、ベイヤー配列のカラーフィルタをベイヤーフィルタと呼ぶ。
| R | G | R | G | R | G |
| G | B | G | B | G | B |
| R | G | R | G | R | G |
| G | B | G | B | G | B |
液晶のRGB配列と混同しないように注意されたい。液晶画面は各画素に赤青緑のサブピクセルが配置されているので、各画素で完結してRGBの色情報を出力できる。一方で、ベイヤーフィルタは、各画素で1色の色情報しか持っていないわけで、液晶画面を基準にして考えるなら、画素数を3倍に水増しして計上しているとも言える。各画素は、自分が知らない色の情報を近隣の画素から借りてくることで、RGBの色情報を復元する。モザイク状のパターンから復元処理を行うので、この処理はデモザイクと呼ばれる。
RAW画像と呼ばれるデータは、このデモザイク処理をする前のデータである。RAWと言いながら、生(無加工)ではない。センサーからの信号はISO感度設定に基づいて増幅され、その後にA/D(アナログ-デジタル)変換が行われ、ノイズ処理などの各社の秘密のレシピが適用された後に、記録されたデータである。RAW画像のフォーマットは各社で様々で、キャノンならCR3、ニコンならNEF、ソニーならARW、オリンパスならORF、パナソニックならRW2、富士フイルムならRAF、ライカならDNGといった拡張子で保存される。
デジカメの現像処理の中核は、デモザイク処理である。RAW現像の代表的なライブラリであるDCRawおよびそのラッパーかつ後継であるLibRawは、AHD(Adaptive Homogeneity Directed=適応均質性指向)というアルゴリズムを実装している。詳細は省くが、かなり保守的なアルゴリズムであり、偽色がほぼ発生しない代わりに、解像度が最善とは言い難い結果になる。オープンソース製品でもDarktableやRawtherapeeはAMaZEやらのより高度なアルゴリズムを実装していて、ピクセル単位の解像度がより高い出力を生成できる。Lightroomがどんなアルゴリズムなのかは非公開だが、少なくともAHDよりは解像度が高く、またデモザイクとノイズ処理をうまく融合させているっぽく、高感度でも解像感の高い出力が得られる。
私のツールitb_stak.pyはLibRawのラッパーであるrawpyを使っているので、必然的にデモザイクアルゴリズムはAHD一択となる。この時点でピクセル単位の解像力は他の実装に負けている。等倍で見ないと分からない違いだとも言えるが、等倍で見れば確実にDarktableやRawtherapeeやLightroomの出力の方が解像力が高いことが分かる。Rawtherapeeが解像力は最高だが、若干偽色が出ている。Lightroomは解像力がそれに匹敵していて、偽色がまったくない。さすがだ。




今どきの高画素デジカメの出力を等倍で鑑賞することもそうそうないので、ピクセル単位の解像度が問題になることはほとんどない。しかし、若干悔しい気持ちもあるので、AMaZEを実装している他のライブラリを使うことも考えた。Pythonから使うことを考えると、「LibRaw Demosaic Pack GPL3」をソースから入れるのが王道らしい。なので、諦めた。pip3だけでインストールできないと面倒すぎて誰も使わないだろうし、自分自身が面倒くさい。
以上の経緯により、現状ではデモザイクに妥協をしている。現像処理の中核で妥協しているというのは癪だが、仕方がない。これを出発点とすると、ピクセル単位の解像力を追求するアプローチは、それ以後の層では意味がない、という割り切りができる。失った情報は戻らないわけで、無駄な努力はすべきでない。解像力に関しては、悪化させないならばそれで良い。それよりは、実際の見栄えにより需要となるトーンや色味の最適化に力を注ごう。
ノイズ除去
これも前回述べたことだが、ノイズ除去は自作RAW現像パイプラインにとって鬼門だ。デモザイク処理では各画素で足りない色情報を隣接画素から取ってくるわけだが、自分にもノイズが乗っているかもしれないし、隣接する画素にもノイズが乗っているかもしれない。そんな中で、解像力とノイズ耐性を両立させるのはかなり難しい。Lightroomはデモザイクにノイズ除去アルゴリズムが組み込まれているっぽいが、その他の実装だとデモザイクの後にノイズ除去をするのが普通だ。そうなると、どこから来たか良くわからないノイズを何とかして処理しなきゃならない。下記のISO12800の現像例を見ると、各実装でノイズの残り方がかなり違うことがわかるだろう。Lightroomはさすがの仕上がりだ。それには負けるが、itb_stack.pyもかなり検討しているだろう。




itb_stack.pyでのノイズ除去アルゴリズムについては前回の記事で詳しく述べた。輝度ノイズが多少乗っていてもあまり気にならず、「粒状感が出て良い」とかいう謎の観点を持つ人達もいるくらいだ。一方、色ノイズはかなり不快だ。赤青緑の高周波のちらつきが出る自然物は存在しないので、生理的に受け付けないのだ。よって、Lightroomではデフォルト設定でも徹底的に色ノイズを潰してくる。輝度ノイズは残ったままで黒ゴマ的な斑が目立つにしても、色ノイズは全く視認できない。私もその観点には同意で、だからかなり強力に色ノイズを落とした。具体的には、色ノイズに絞って画素間の勾配を検出して、色ノイズっぽい画素の色相を徹底的にぼかした。人間は輝度の変化には敏感だが色相の変化には鈍感なので、ほとんど解像感を損なわずにノイズ除去することに成功している。ISO12800の例でもかなり見られる結果になっているので、大抵の写真でノイズが気になることはないだろう。
ここで各実装の哲学の違いが明らかになる。既に述べたように、Lightroomはデモザイクとノイズ除去を統合して解像力とノイズ耐性の両立をしている。一方で、DarktableとRawtherapeeは、解像力については本気を出しているが、ノイズ除去にはあまり力を入れていないっぽい。私が数日で実装したノイズ除去に負けているくらいだ。おそらくだが、解像力フェチである多くの開発者は、ノイズ除去をすると多少なりとも解像力が落ちるので、そんなことに力を入れないのだろう。一方私は、デモザイクでは勝てないと割り切っているし、そもそもデモザイクエンジンを自分で書いていないので、せめてノイズ除去だけは頑張ろうと思った。実際のところ、ピクセル単位の解像度が高いよりは、色ノイズが少ない方が幸せになれるだろう。
自動露出補正
12ビットRAWとか14ビットRAWとかいう名前を聞いたことがあるだろう。12ビットRAWは各画素で12ビットの情報を記録するので、2^12 = 4096階調になる。14ビットRAWは各画素で14ビットの情報を記録するので、2^14 = 16384階調になる。すなわち、RAW画像の各画素は[0,4095]もしくは[0,16383]の変域を持つ。さらに、ブラックポイントおよびホワイトポイントというメタデータもついてくる。ブラックポイント以下の値は0とみなし、ホワイトポイント以上の値は1とみなした上で、間の階調をビット深度に合わせて線形変換するのだ、つまり8ビット深度なら [0,255] に変換され、16ビット深度なら [0,63335] に変換される。わざわざRAW現像をする場合、普通は16ビットを使うし、当然LibRawもそれをサポートしている。私のパイプラインでは、16ビットで生成された [0,65535] の情報は、すぐに [0,1] の32ビット浮動小数点数に変換している。そうすることで後段の処理での量子化誤差を最小化するのだ。
一般的に、現像ライブラリにRAW画像を読み込ませると、上述の線形変換をして [0,65535] の変域のデータを各画素に割り当てた上で、デモザイク処理を行って、各画素のRGBの各々のチャンネルに [0,65535] のデータを持たせた [x, y, 3]の形の3次元配列を生成してくれる。しかし、多くの場合、適正露出で撮った写真のデータは変域の25%くらいしか使っていないのが普通だ。元をたどると、RAW画像データの中で[0,4095]ないし[0,16383]の変域の更に一部分であるブラックポイントからホワイトポイントまでの区間の25%くらいしか使っていないということだ。記録形式が12ビットRAWとか14ビットRAWとか言うのと、センサーのダイナミックレンジが12ビットないし14ビットあるかどうかは別問題であり、基本的にはセンサーのダイナミックレンジはRAWファイルのビット深度よりも低い。センサーの潜在力を活かすためにはRAWファイルの能力に余裕を持たせる必要があるので、この挙動は設計通りなのだろう。
しかし、野良RAW現像パイプライン作者としては、実際の変域が分からないのは悩ましい。デモザイク後の画像をそのまま後工程に流すと、やたら暗い画像が生成されてしまう。未加工で保存するとこんな感じだ。

暗い画像を適切に明るくするには、各社の各機種のセンサーの実際の変域を知っておく必要がある。Lightroomは、メーカーから提供されているのかリバースエンジニアリングをしているのか分からないが、そのデータベースを持っている。よって、実際の変域に合わせて上限を決めて線形変換をしてやれば、撮影者の意図に合った露出の出力を生成できる。個々の画像データで最大値や99パーセンタイルなどを見て線形変換することも可能だが、そうすると、撮影者が敢えて暗めに撮った画像と、明るめに撮った画像が、区別できなくなってしまう。Lightroomの画像一覧を見た時に、撮影時の露出に応じて画像が明るかったり暗かったりするのは、実際の変域を知っているからこそなのだ。
そんなデータベースを持っていない私は、ここで途方に暮れた。個々の写真を適正露出に補正することよりも、撮影者の意図を組んだ露出を表現することの方が難しいのだ。各機種毎にマジックナンバーをひたすら書くわけにもいかない。そもそも全自動バッチ処理をするためのパイプラインなんだから、撮影者の意図なんて気にしないという割り切りも可能ではある。しかし、itb_stack.pyはHDR合成をサポートしているので、勝手に露出を合わせてしまうのはご法度だ。HDRの素材は露出ブラケットで撮るから、入力画像には必ず明るい画像と暗い画像が混ざってくる。後段のDebevecなどの合成処理にデータを渡す際には、明るく撮った画像は明るいまま、適正露出の画像は適正なまま、暗く撮った画像は暗いままにしておく必要がある。
機種ごとのデータベースを持っていないので、入力データのみから変域を判断せざるを得ない。ただし入力データの間の露出の差異は維持せねばならない。そこで、EXIFデータに頼ることにした。EXIFにはF値(Fv)と露光時間(Tv)とISO感度(Sv)が記録されている。カメラの設定がどのくらいの明るさに適するかという指標としてLV(Light Value)があるが、それは Lv = log2(1 / (Tv * Fv^2)) + log2(Sv / 100) で表される。式を変形すれば、輝度が露光時間とレンズ開口部の面積と感度に比例するというだけの話で、物理的に当然そうなるとだろうと納得がいく。この話はHDRを実装した際に書いた。
mikio.hatenablog.com
LVが高いほど、明るいシーンに適するように、光量に対する撮像の輝度を低めようとしていることになる。例えば、Aモードで露出ブラケットで5枚の写真を撮ったとしよう。この例では、絞りはF4で固定、ISO感度は200で固定されて、露出の変化は露光時間を変化させて実現された。露出補正は基準画像において-0.3段であった。その際のLVやその他の値の変化を見てみよう。これは実際にExifから取得された値である。
| 露出補正 | LV | TV | FV | SV |
| -2.30 | 8.64 | 0.000313 | 4.00 | 200.00 |
| -1.30 | 7.64 | 0.000625 | 4.00 | 200.00 |
| -0.30 | 6.64 | 0.001250 | 4.00 | 200.00 |
| 0.70 | 5.64 | 0.002500 | 4.00 | 200.00 |
| 1.70 | 4.64 | 0.005000 | 4.00 | 200.00 |
見て分かるように、露出補正とLVは逆の動きをしている。同じシーンを撮った場合、LVが高いほどに暗い画像が生成されるからだ。そこで、ブラケット撮影における真の露出意図を表すBracket Compensation Valueという尺度を導入した。それは二つの算出方法がある。LVの平均から各画像のLVを引くか、各画像の露出補正値から露出補正値の平均を引くかだ。また、EXIFがない場合には、撮像の平均輝度から推定することも可能だ。輝度の平均のlog2を各画像の露出補正とみなせば良い。上述の例でその計算をすると、以下のようになる。
| LvからのBCv | 露出補正からのBCv | 撮像輝度からのBCv |
| 6.64 - 8.64 = -2.00 | -2.30 - -0.30 = -2.00 | -3.98 - -6.23 = -2.25 |
| 6.64 - 7.64 = -1.00 | -1.30 - -0.30 = -1.00 | -3.98 - -5.01 = -1.03 |
| 6.64 - 6.64 = -0.00 | -0.30 - -0.30 = -0.00 | -3.98 - -3.82 = 0.16 |
| 6.64 - 5.64 = 1.00 | 0.70 - -0.30 = 1.00 | -3.98 - -2.71 = 1.27 |
| 6.64 - 4.64 = 2.00 | 1.70 - -0.30 = 2.00 | -3.98 - -2.13 = 1.85 |
LVからと露出補正からのどちらの式でも同じ値が取得できることが分かった。それらと撮像輝度からの推定がずれていることも分かった。理想的なセンサーならずれがないはずだが、当然ながらそんなものは存在しない。意図を汲み取るという意味では、明らかにEXIFを使うべきで、何らかの理由でEXIFデータが取得できない場合だけ撮像から推定すべきだ。EXIFデータが利用できる場合、Lvが利用できる時はLvを使うのが上策だろう。露出補正は1/3段数でしか指定できず、またF値やISOの刻みが離散的であることから、露出補正とLvが確実に連動するとは限らない。また、カメラの自動露出を使わずにMモードの手動でブラケット撮影をした場合にもLvは算出できる。
以上を踏まえると、設計判断は次の通りになる。入力ファイルが一つの場合は、複数であってもLVがほぼ同じ場合は、おそらく全てを適正露出にしたいだろうから、そうする。つまり、99パーセンタイルの輝度の画素が0.9になるように線形変換する。入力ファイルが複数であり、かつLVが異なる場合は、おそらく露出ブラケットの撮影データなので、個々の画像の仕上がりにLVに応じた差をつける。全ての入力のLVの平均を取り、平均であればそのまま99パーセンタイルの輝度の画素が0.9になるように線形変換する。平均以下であれば、平均との比率を0.9に掛けた値を目標輝度にする。
ロールオフ
理想的には、線形RGBとして画像を表現する限りにおいては、各画素の輝度は、被写体から来る光量とLV値に比例すべきだ。しかし、変域が[0,1]に限られている状況で、LV値に合わせて掛け算をしてしまうと、普通に1を超える値が生成されてしまう。それは白飛びになってしまう。露出ブラケットは通常+-1EV以上でなされるので、少なくとも2倍の倍率には耐えなければならない。たとえ基準を0.5にしても、それは99パーセンタイルの話なので、2倍して目標輝度が1.0になった画像は1%が確実に白飛びしてしまう。安全係数として基準を0.4にする手もあるが、+-1.5EVのブラケットには耐えられないわけで、根本的な解決になっていない。
そこで、ロールオフという手法を導入する必要がある。これも前回述べたが、1を超えた画素がある場合、ハイライトの階調を圧縮して無理やり[0,1]の変域に押し込めるのだ。Lightroom等の現像ソフトを使った人は、露出を2倍とかにしても、ヒストグラムが右端に張り付かないことに気づいているだろう。これはロールオフのおかげだ。物理的な光量を正確にシミュレートするには本来は線形変換すべきところを、画層フォーマットと再生装置の都合で非線形変換せねばならないというジレンマがある。このロールオフ問題をどう扱うかは、実は各社の哲学に基づく秘密のレシピなのだ。

Lightroomのロールオフのアルゴリズムがどうなのかは正確には分からないが、無補正の最大値が1.0を大きく超えるほどに、ハイライト圧縮に使う変域を少しずつ広げているようにみえる。それを最も単純に実現するのが、次のアルゴリズムだ。Mを無補正輝度の最大値として、屈曲点Iを(1.0 + 1.0 / M) / 2.0をとする。[0,I]の区間はそのままにして、[I,M]の区間は[I,1]にマッピングする。例えば、M=0.9から+0.4EVにしてM=1.2になった場合、Iは0.916になるので、[0.916,1.2]を[0.916,1]に線形変換すれば良い。実際には最大値ではなく99.8パーセンタイルとかでやって、外れ値への耐性を高めている。

Mが無限大に近づくと屈曲点は0.5に漸近する。この漸近点Aをパラメトライズするには、I = A + (1.0 - A) / M に式変形すれば良い。この手法の良いところは、Mが大きくなるほどに画像の平均輝度が上がるという条件と、Mがいくら大きくなっても白飛びが起きないという条件の両方を満たしていることだ。Aを0に近づければロールオフが強力になる代わりに線形変換の効果がどんどん弱くなる。Aを1に近づければ線形変換の効果が本来のものに近づく代わりにロールオフの効果はどんどん弱くなる。ロールオフを実装しておくと、本来は白飛びが怖くて気軽にはできない線形変換をバンバン使うことができる。ブラケットの基準画像が0.5でなくて0.9にできるのはそれが理由だ。なお、この式そのままだとMが一定以上になると画像がほとんど変化しなくなってしまうため、Mが4以上の場合には、AをA^(4/M) に上方修正するハックも入れてある。これが発動することは実際には稀だろうが。
元画像、ロールオフなしの+2EV、ロールオフありの+2EVの漸近点0.7、0.5、0.3を並べてみる。ロールオフありだと、全体の明るさの印象を変えずに、ハイライトの視認性が回復できているのが分かるだろう。0.7だとハイライトの色はほとんどなくなるが、ギリギリ模様がわかる。0.5だと色も模様も分かるが、中間トーンの明るさの増加が減っているのが如実に分かる。0.3だとハイライトの視認性は良好だが、中間トーンも上昇がとても意図通りとは言い難い。よって、輝度のロールオフは0.5が最適だと思う。





このロールオフ処理は、彩度の補正にも使われる。元画像、ロールオフなしの彩度3倍、ロールオフありの彩度3倍の漸近点0.7、0.5、0.3を並べた。まず、ロールオフなしで完全に飽和するとひどいことになる事がわかる。一方で、色飽和は輝度飽和よりも目立ちにくいので、完全な飽和さえしていなければ破綻した感じにはならない。また、輝度を3倍にすることはあっても、彩度を3倍にすることは滅多にない。よって、彩度のロールオフは0.7が最適だと思う。





線形の露出補正や彩度補正を実装するうえではロールオフ機能は必須なのに、意外にOpenCVでもImageMagickでも実装されていない。よって、自分で書く羽目になった。簡単な式を書くだけで線形補正をより積極的に使うことができる。とはいえ、ロールオフは変域が足りないことによる張り付きを回避するための最終手段でもあるので、ロールオフに頼りすぎるのも良くない。--slogによる輝度スケール対数変換や--vibranceによる彩度スケール対数変換で充足する場合にはそれらを使う方が良い。
RAW画像からのHDR合成
ここまでの議論を合わせて、ブラケット撮影に対応した自動露出補正付きの現像パイプラインが出来上がった。あとは、以前から備えていたHDR合成機能と組み合わせれば、RAW画像によるHDR合成が可能となる。まずは、AモードかMモードで露出ブラケット撮影を行う。--align siftで画像のアラインメントをすると、手持ち撮影の結果もHDR合成に使える。以下は、説明のために、合成ではなく素材データをフレームにした動画を作っている。きちんと撮影時の露出が反映された画像が現像されているのがわかるだろう。
$ itb_stack.py *.ORF --o stf-movie.mp4 --align sift
実際には、出力に画像ファイルを指定して、--merge debevec --tone reinhard オプションを付ける。HDRの定番の合成手法Debevecとトーンマッピング手法Reinhardの組み合わせだ。スケール対数で若干明るく調整すると良い。結果はこんな風になる。暗い屋内と明るい屋外を同時に視認できる絵を作るにはこの方法しかない。
$ itb_stack.py *.ORF --o stf-movie.mp4 --align sift --merge debevec --tone reinhard --slog 2

ついでに、擬似STF合成もRAWでできるようになっているので、作例を紹介したい。まずは、SモードかつISO固定で露出ブラケット撮影を行い、絞り値可変のブラケット撮影を行う。結果として露出が異なる現像結果が生成されるが、それを後段で自動露出合わせを行う。以下は、説明のために、合成ではなく素材データをフレームにした動画を作っている。絞りが変わるのに応じてぼけ半径が変わっているのが分かるだろう。
$ itb_stack.py *.ORF --o stf-movie.mp4 --align sift -ax
実際には、出力に画像ファイルを指定して、--merge weighted オプションを付ける。絞り値に応じて重み付けをして平均合成するのだ。そうすると、ボケの縁がボケる疑似STF画像が完成する。
$ itb_stack.py *.ORF --o stf-image.jpg --align sift -ax --merge weighted

現像レシピ
itb_stack.pyは、「入力」「現像」「現像後プリセット補正」「合成」「合成後補正」「出力」という工程を持つパイプラインである。現像は全自動で勝手になされ、パラメータの指定はない。現像後プリセット補正は、プリセット名を指定することで、挙動を変更できる。プリセットには以下のものがある。
- raw-muted : 色ノイズ除去、輝度目標0.90、輝度シグモイド2.7、輝度対数0.4、彩度線形1.1、彩度対数0.1
- raw-std : 色ノイズ除去、輝度目標0.92、輝度シグモイド3.0、輝度対数0.5、彩度線形1.2、彩度対数0.2
- raw-vivid : 色ノイズ除去、輝度目標0.94、輝度シグモイド3.3、輝度対数0.6、彩度線形1.3、彩度対数0.3
- light : 輝度線形1.4、輝度対数0.5
- dark : 輝度線形0.75、輝度対数-0.5
- lift : 輝度ガンマ1.2、輝度対数0.5
- drop : 輝度ガンマ0.84、輝度対数-0.5
- hard : 輝度シグモイド2.0
- soft : 輝度シグモイド-2.0
- muted : 輝度シグモイド-1、彩度線形0.84、彩度対数-0.3
- vivid : 輝度シグモイド1、彩度線形1.2、彩度対数0.3
RAW現像のデフォルトのプリセットはraw-stdだ。プリセットはカンマで区切って複数個指定でき、記述した順序で適用される。また、+0.3 とか -0.3 とか書けば、EV段数を指定して露出補正ができる。wb-auto、wb-auto-temp、wb-auto-faceと書けばホワイトバランスの自動補正もできるし、5500Kとか書いて色温度でホワイトポイントを指定することもできる。
# デフォルト現像よりちょっと明るく $ itb_stack.py input.dng -o output.jpg --raw-preset=raw-std,light # デフォルト現像よりも0.3EV(2^0.3=1.23倍)だけ明るくし、色温度5500Kを白にする $ itb_stack.py input.dng -o output.jpg --raw-preset=raw-std,+0.3,5500K # デフォルト現像よりも暗部持ち上げ $ itb_stack.py input.dng -o output.jpg --raw-preset=raw-std,lift # 撮影者の意図はどうでもいいから、どんな画像も自動的に視認性を最大化 $ itb_stack.py input.dng -o output.jpg --raw-preset=raw-std -ox 1
プリセットを切り替える方法は、--linear, --gamma, --slog, --sigmoidなどの数式を使う方法よりも直感的だろう。もちろんそれらを使った方が微調整はしやすいのだが、そもそも微調整が必要ならGUIでやりたくなる。よって、自動化ツールにはプリセットの方が使いやすいと思っている。なお、RAW現像しない場合は --raw-presetではなく--presetでプリセットの処理を適用することができる。じゃあなぜその二つが分かれているかというと、--raw-presetはHDR等の合成処理が行われる前に発動し、--presetは合成処理の後に発動するという役割の違いがあるからだ。
RAW現像とは、変域が[0,1]であることは分かっているが中身が謎の分布を持つデータを何倍かにして[0,1]に収めるというタスクだ。掛け算しているのに結果の変域を変えられないというのは、算数的には無理ゲーだが、HDRIでない画像フォーマットは変域が限られているので仕方ないのだ。そこで、パラメータの自動推定やロールオフによる保護を行う必要が出てくる。そのうえで、撮影時の意図の反映やユーザが求める味付けができるように「余裕」を持たせることが望ましい。0と1の狭間には奴らの哲学では思いもよらない事があるのだ。
まとめ
RAW現像パイプラインを実装するにあたって考慮すべき点をいくつか解析した。デモザイクの実装の選定、ノイズ除去の実装、自動露出補正の実装の各々に対して、きちんと設計を固めないといけない。試行錯誤で適当に既存技術を組み合わせればできてしまうこともあるし、時に新手法を編み出す必要に駆られることもある。私の試行錯誤が誰かの既存手法として役立てば嬉しい。