ぼかしを応用した主要被写体輝度推定の改良

ID: 159
creation date: 2013/04/11 21:14
modification date: 2013/04/11 21:14
owner: fallabs

前回前々回の記事では、liquid rescaleを応用して、画面内の情報量が多い部分を主要被写体とみなしてその輝度を推定する方法について考察した。今回はそれを画面中心に重み付けして適用する方法について述べる。

主要被写体の抽出

おさらいだが、主要被写体に多くの階調を割り当てる作業を自動化するのがこの自動補正手法の目的である。すなわち、主要被写体の輝度の上限と下限に合わせて出力画像の上限と下限を設定し、さらに主要被写体の平均輝度を一定の範囲に収めるようにガンマ補正とシグモイドを施す。その前提として、主要被写体がどこかを機械的に推定する必要がある。そのために、liquid rescaleを用いて情報量の多い領域を残してそうでない領域を削る手法をすでに実装している。そうすると主要被写体が多くを占める画像が得られるので、その輝度の統計値を取れば所望の操作が可能になる。

ところで、我々素人が写真を撮る場合、主要被写体は画面の真ん中の方に置かれることが多い。もちろん主要被写体が常にど真ん中にあるとは限らないが、三分割とか黄金分割とかに立脚したとしても、画面の端っこに主要被写体が来る確率はかなり低いと考えられる。カメラのAFセンサーも画面端には反応しないし。というかあんまり凝ったことをする人達は自動補正ツールの対象ユーザではない。したがって、画面中央付近を主要被写体とみなすのも合理的な方法だろう。

liquid rescaleの方法は、主要被写体のコントラストが低かったり、主要被写体以外にコントラストの高い被写体が写っていたりすると、うまくいかない。画面中央の方法は、主要被写体が画面中央に占める割合が低い場合にうまくいかない。そこで、両者を併用する方法について検討してみる。単純に画面端を切り落としてからliquid rescaleを適用すればよいし、実際従来はそうしていたのだが、もう少しシームレスに統合したい。

画面端に近くても情報量が多いのであれば生き残り、画面中央にあっても情報量が少なければ削られるようにしたい。すなわち、領域内の情報量が高いほど主要被写体を構成する確率が上がり、画面端から離れた領域ほど主要被写体を構成する確率が上がるというようなモデルにしたい。

前処理によるliquid rescaleへの干渉

liquid rescaleが画像を横方向に縮める際には、画面の上端から下端に向かって引かれる任意の曲線の中で、通り道の画素間の差異の総和である「energy function」の値が最も小さいものを削る処理を繰り返す。縦方向に縮める差異には右端から左端までの曲線で同様のことを行う。分かち書きなどと同じくいかにも動的計画法な感じのアルゴリズムである。てことは、隣接する画素間の差異を減らした領域はliquid rescaleの削除対象として選択されやすくなるということである。そのためには、二つの方法がある。近隣の画素を混ぜる「ぼかし」と、各画素を色空間上の一定の座標に近づける「コントラスト低減」である。

「ぼかし」と「コントラスト低減」を、画面端に近づくほど強くかけた上で、liquid rescaleをかけると、目的の効果が得られるはずである。都合のいいことに、ぼかしもコントラスト低減も、異常に強くかけるのでなければ、対象領域の輝度の統計値に対しては重大な影響を及ぼさないので、前処理として追加しても支障がなさそうだ。

フォトレタッチソフトを使うのであれば、画像全体の選択、選択領域の縮小、選択領域のぼかし、選択領域の反転をした上で、ガウシアンぼかしフィルタと明度コントラスト操作を適用するという流れになるだろう。これをプログラム(Image Magick)でやるとなかなか面倒なのだが、無理矢理Rubyスクリプトで実装してみた。

C_id_cmd = 'identify'
C_cnv_cmd = 'convert'
C_cmps_cmd = 'composite'

def blur_sample(sample_path, test_path)
  fields = `#{C_id_cmd} -format \"%w,%h\" \"#{sample_path}\"`.chomp.split(',')
  width = fields[0].to_i
  height = fields[1].to_i
  ratio = 1.0
  3.times {
    ratio *= 0.9
    cw = (width * ratio).to_i
    ch = (height * ratio).to_i
    ow = (width - cw) / 2
    oh = (height - ch) / 2
    cmd = "#{C_cnv_cmd}"
    cmd += " -crop \"#{cw}x#{ch}+#{ow}+#{oh}\""
    cmd += " -depth \"16\""
    cmd += " \"#{sample_path}\""
    cmd += " \"#{test_path}\""
    system(cmd)
    cmd = "#{C_cnv_cmd}"
    cmd += " -brightness-contrast \"0x-1\""
    cmd += " -blur \"0x2\""
    cmd += " -depth \"16\""
    cmd += " \"#{sample_path}\""
    cmd += " \"#{sample_path}\""
    system(cmd)
    cmd = "#{C_cmps_cmd}"
    cmd += " -compose \"src-over\""
    cmd += " -geometry +#{ow}+#{oh}"
    cmd += " -depth \"16\""
    cmd += " \"#{test_path}\""
    cmd += " \"#{sample_path}\""
    cmd += " \"#{sample_path}\""
    system(cmd)
  }
end

画像の中央90%の領域を切り出して別画像として保存してから、元画像にぼかしとコントラスト低減をほどこして、そして切り出した画像を元の位置に戻す。そうすると画面端にだけ前処理が適用された画像が得られる。このように、前処理から逃れる領域を中央方向に縮小しながら、合計3回の処理を行う。アホっぽいが、確実な方法だ。もちろん3回と か90%とかいうパラメータは適当に変えてOK。

実際の画像に対しての適用例を見てみよう。左が元画像で右が前処理適用後である。画面端がほんの少しだけぼけているのがわかるだろうか。

image:1:1365682072-big.jpg
image:2:1365682123-up-sample.jpg

両者に対して、各辺を50%に縮小するliquid rescaleをかけてみるとこうなる。ぱっと見ではわかりにくいかもしれないが、前処理を行った場合には、画面端ほど優先して削らていることがわかるだろう。

image:3:1365682198-up-orig-liquid.jpg
image:4:1365682214-up-sample-liquid.jpg

これで得られた画像をもって「主要被写体です」というのは憚られる感じもするが、輝度の統計値を算出する材料としては問題ない。実際には、画面端に対する前処理の後にさらに全体に0x3のぼかしをかけてから、liquid rescaleを適用するのがいいっぽい。最終的にはこんな画像を主要被写体として扱うことになる。

image:5:1365682239-up-sample-final.jpg

元画像では牛は画面のど真ん中には居なかったのだが、抽出してみるとちゃんとど真ん中に鎮座している。

まとめ

ぼかしとコントラスト低減の前処理を画面端ほど強くかけてから、liquid rescaleを適用すると、中央付近にある情報量が多そうな領域を優先して残した画像を得ることができる。これを利用して自動補正をかけると、自分で試してみた限りでは、そこそこ思い通りの結果になるような気がする。画面の方に白トビなどがあっても主要被写体に着目した輝度補正がきちんとできることが多いし、主要被写体が画面中央から外れていてもきちんと補正できることが多い。馬鹿な機械がやっているわりにはかなりまともな出力が得られて嬉しい。

0 comments
riddle for guest comment authorization:
Where is the capital city of Japan? ...