postprocess関数の引数のuser_satに関して、値を動かしながらその効果を確認していきます。
postprocessに関しての詳細はこちら。
今回は罠がありました。user_satの説明文、”saturation adjustment” 皆さんどう訳します?自分は彩度調整と訳してしまいましたが、このパラメータは実は飽和させる値を調整するもののでした…。そっちかい!
user_sat
珍しくint指定のパラメータです。値のレンジに関しての記載はありません。説明も一言” saturation adjustment “だけです。
てっきり彩度を調整するパラメータだと思って、値をいじってみましたがどうも鮮やかになりません。また値のレンジも最初意味が分かりませんでした。
なのでひたすら実験です。するとこの値、彩度ではないことがわかりました。飽和の方です。内部データの最大値をいくつに補正するか。のパラメータのようです。
実験
ひとまず値をいじって、処理OFFに相当するパラメータを探ってみました。するとまたとても中途半端な値で処理OFF相当になりました。
探り方としては以下のように、user_satがNoneの時と同じ入出力(グラフがリニア)になるような数値を探りました。画素値を散布図としてプロットしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | raw = rawpy.imread(filename) gam = [ 1.00 , 0.00 ] img = raw.postprocess(half_size = True , demosaic_algorithm = rawpy.DemosaicAlgorithm.LINEAR, no_auto_bright = True , output_color = rawpy.ColorSpace.raw, gamma = gam) img2 = raw.postprocess(half_size = True , demosaic_algorithm = rawpy.DemosaicAlgorithm.LINEAR, no_auto_bright = True , output_color = rawpy.ColorSpace.raw, user_sat = 14500 , gamma = gam) plt.scatter(img[:,:, 0 ], img2[:,:, 0 ],label = 'R' ) plt.scatter(img[:,:, 1 ], img2[:,:, 1 ],label = 'G' ) plt.scatter(img[:,:, 2 ], img2[:,:, 2 ],label = 'B' ) plt.xlim( 0 , 255 ) plt.ylim( 0 , 255 ) plt.legend() plt.show() |
CanonのEOS 5D MkIIのrawデータを使った場合、以下のようなグラフが書け、およそ14500程度のところで入出力が同程度になりました。横軸入力で、縦軸出力。

まずこの段階で”?”でした。彩度を調整しているつもりなのに、RGBがそろって変化しました。鮮やかさ変わらないじゃん…。
値を倍にしてみます。すると

全体が暗く変化しました。しかもRGBそろって。続いて値を半分にしてみます。

今度は倍明るくなりました。128で値が張り付いています。128付近でひげが出ているのは、以前の記事でも考察したように、デモザイク前処理であることが予想されます。
そして、まったく彩度を調整してません。saturation = 彩度ではないですね…。
処理OFF相当を見つけられれば、ただそれにRGBそれぞれに同じゲインをかけているだけであることが予想されます。ただの掛け算に見えます。OFF相当の値に設定値の逆数を掛けたものをゲイン(直線の傾き)としてそうです。
gain = 14500 / user_sat
続いてこの処理OFF相当の値は何を意味しているのかを考えてみます。
olympusのOM-D EM10 MkIIのrawデータを使います。すると処理OFF相当のuser_satの値が大きくずれました。値にして3850付近です。

OFF相当の値が4倍くらい違います。この4倍がみそでした。
続いてそれぞれの製品カタログにあるrawデータのビット数を調べてみたところ、EOSが14bit、OM-Dが12bitとあります。おそらくこれです。14bitの最大値が16383、12bitが4095なので、近いところ行きました。
カメラのモデル | OFF相当の値 | データビット数 |
EOS 5D MkII | user_sat = 14500 | raw bit = 14bit |
OD-M EM10 MkII | user_sat = 3850 | raw bit = 12bit |
ただこれも単純にそのビットの最大値を示しているわけではありません。現像のプロセスとして、画素の演算前にBlack Levelを引いています。このblack levelを確認するとそれぞれ、1023、254でした。12bitの最大値4095 – 254 = 3841とEM10の方はそれっぽい値になりました。EOSはまだ若干ずれます。
カメラのモデル | OFF相当の値 | ビットの最大値 | Black Level | 差 |
EOS 5D MkII | 14500 | 16383 | 1023 | 15360 |
OM-D EM10 MkII | 3850 | 4095 | 254 | 3841 |
というわけで、EOSは若干ずれがあるものの、おそらくこの値は、各種ビット精度の異なる生データの最大値をどこに持っていくかを指定するパラメータだと思われます。データ生を操作しそうです。そうです。飽和させる値を設定するものだと思われます。saturation = 飽和です。
librawのドキュメントを見ると、rawpyではアクセスができない変数に maximum とか data_maximum という値があります。これも使っているのかなと想像されます。maximum – black_level = saturation(飽和値)ということかなと。このsaturationの値を直接指定し、ゲイン調整させるのがこのパラメータ。これは完全に予想。EOSはこのmaximumが14bitといいながらも必ずしも16383ではないのではないかと思います。
(追記)この最大値ですが、exiftoolというツールを使って、exif情報を眺めてみるとそれっぽいものがありました。
Specular White Level : 15461
鏡面反射の白。とあります。これが14bitの最大値より少し小さいです。この値からblack levelを引くとちょうど14500に近くなります。この値を引っ張ってきているのかもしれません。
生値を直接ゲイン調整するのであれば、exp_shift ( exposure shift 露出補正)とまったく同じ動きをするのではないかと予想して、以下の2種類のパラメータでそれぞれ現像してみたところ、
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | gam = [ 1.00 , 0.00 ] img = raw.postprocess(half_size = True , demosaic_algorithm = rawpy.DemosaicAlgorithm.LINEAR, user_black = 254 , no_auto_bright = True , output_color = rawpy.ColorSpace.raw, exp_shift = 2.0 , gamma = gam) img2 = raw.postprocess(half_size = True , demosaic_algorithm = rawpy.DemosaicAlgorithm.LINEAR, user_black = 254 , no_auto_bright = True , output_color = rawpy.ColorSpace.raw, user_sat = ( 4095 - 254 ) / / 2 , gamma = gam) |
画像が完全一致しました。露出を1段上げる(exp_shift = 2.0)のと、飽和値を半分にする(user_sat = (4095-254)//2) は意味合いとしては同じ。ということになりそうです。
まとめ
この値は彩度調整をするパラメータにあらず。生のデータをいくつに飽和させるかを設定するパラメータ。なのでデータのビット幅に応じて値を指定する必要がある。
ただしblack levelとアクセスできないmaximumの値がわからないと調整が難しく、またexp_shiftでほぼ等価な処理ができそうな気配なので、調整の意図がなかなか見いだせないパラメータ。でした。
コメント