2009-08-13

ローパスフィルタ

結論

最近の画素が多いデジカメの場合、輪郭強調をしない設定にします。ブログなどのために縮小するならRalphaを用いて設定(0)リサイズ設定(R)で縮小アルゴリズムをLanczosに設定し半径を2に、「リニアな色空間で処理する」にチェックをつけて縮小します。


網戸に蝉がとまっていました。85mm, 1/15sでも手ぶれ補正のおかげで手ぶれがありませんし、DxOのノイズリダクション処理によりISO3200でもノイズが少ないです。

本文

最近のデジカメは何百万画素もあり、パソコンの画面にせよ葉書サイズへの印刷にせよ縮小することになります。そこで縮小に使われる関数について調べてみました。
殆どの縮小方法は、近くのいくつかの点の明るさの平均を計算します。その時に、より近い点は重点的に、少し離れた点は軽く評価して平均します。距離に対してどれくらいの重みで評価するかを決める関数によって、縮小後の画質も変わってきます。
そのために使われる関数は色々ありますが、シンプルな平均画素法と、理論的には性能が良いLanczos法について調べました。Excel VBAのプログラムも書いていますが、すごく手抜きで概略を掴むためです。積分の計算も単純に長方形を足しているだけです。

画素平均法は、面積平均法とも呼ばれ、元の画像の各画素を同じ大きさの正方形で描いたとして、それを縮小後の画素数に合わせて等分して枠線を引き、枠線の中の画素の平均を計算します。
縦横2画素計4画素を1画素に縮小する場合は、その4画素の平均になります。
他の例として5分の2に縮小する場合は
12345
□□□□□
  |  
 1 2 
縮小後の左の画素は、元の画像の1番目と2番目、そして3番目の半分ということで
1番目の2/5倍、2番目の2/5倍、3番目の1/5倍を足したものになります。

Lanczos法について
縮小すると細かい部分が表現できなくなります。横に1000画素あれば、白黒白黒…の繰り返しで500本の縦線を表現できますが、これを500画素に縮小すると250本の縦線までしか表現できなくなります。この場合、元の画像に300本の縦線があると表現できなくて、250を超えた分が折り返して200本の縦線のモアレになります。ですから縮小する時には250本より細かい模様は消しておく必要があります。
この目的で、250本までは全く影響を与えず、それ以上は完全に消してしまうフィルターがsinc関数であり、それに窓関数を組み合わせて途中で切るのがLanczos関数です。

これらをVBAで書くと
Function sinc(x) As Double
If x = 0 Then
  sinc = 1
Else
  sinc = Sin(x * Application.Pi()) / (x * Application.Pi())
End If
End Function

Function lowpass(x, k) As Double
If k <= 0 Then
  If Abs(x) < 0.5 Then
    lowpass = 1
  Else
    lowpass = 0
  End If
Else
  If Abs(x) < k Then
    lowpass = sinc(x) * sinc(x / k)
  Else
    lowpass = 0
  End If
End If
End Function
このようになります。kが自然数の場合は幅がkのLanczos関数に、関数を分けるのが面倒なのでkを0以下にすると画素平均法の関数になります。
関数の形状は
Sub shape()
For k = 0 To 4
  For i = 1 To 801
    x = (i - 401) / 100
    Cells(i, 1) = x
    Cells(i, 2 + k) = lowpass(x, k)
  Next i
Next k
End Sub
このプログラムで表示できて

このようになります。Lanczosの2以上だと、少し離れたところがマイナスになります。これによって輪郭が強調されます。画素平均法は0と1の境界の部分で、面積に応じた中間の比率を使うことが特徴なのですが、このプログラムには反映されていません。

これらによって明るさが急激に変わる画像
Function original(x)
If x > 0 Then
  original = 1
Else
  original = 0
End If
End Function
を変換すると

このようになります。使ったプログラムは
Sub highcut()
Dim normal(4) As Double

For j = 0 To 4
  wa = 0
  For i = -3 To 3 Step 0.01
    wa = wa + lowpass(i, j) / 100
  Next i
  normal(j) = wa
Next j

For k = 0 To 4
  For i = 1 To 601
    wa = 0
    x = (i - 301) / 100
    For j = -3 To 3 Step 0.01
      wa = wa + original(x + j) * lowpass(j, k) / 100
    Next j
    Cells(i, 1) = x
    Cells(i, 2 + k) = wa / normal(k)
  Next i
Next k
End Sub
です。Lanczos2以上だと上がるときに一旦上がりすぎてから下がります。これによって輪郭強調効果がありますが、Lanczos3以上だと今度は上がった後もう一度下がり、境界が波打ちます。これはリンギングと呼ばれます。Lanczos2による輪郭強調効果は、人間の錯視の一つマッハバンドと同じ傾向ですので不自然さを感じません。Lanczos3以上だとちょっと違和感を感じます。

これらによって、消すべき細かい線がちゃんと消えているか、残すべき大きな線がちゃんと残っているかを調べてみました。使ったプログラムは Sub tokusei()
Dim wa As Double, wa2 As Double, wa3 As Double, normal(4) As Double

For j = 0 To 4
  wa = 0
  For i = -3 To 3 Step 0.01
    wa = wa + lowpass(i, j) / 100
  Next i
  normal(j) = wa ^ 2
Next j

For i = 1 To 501
  j = 2 ^ ((i - 1) / 100)
  
  wa3 = 0
  For x = -4 To 4 Step 0.01
    wa3 = wa3 + Sin(j * x) * Sin(j * x) * 0.01
  Next x
  
  Cells(i, 1) = j
  For k = 0 To 4
  wa2 = 0
  For x = -4 To 4 Step 0.01
    wa = 0
    For y = -4 To 4 Step 0.01
      wa = wa + Sin(j * (x + y)) * lowpass(y, k) / 100
    Next y
    wa2 = wa2 + wa * wa * 0.01
  Next x
  Cells(i, 2 + k) = Log(wa2 / wa3 / normal(k))
  Next k
Next i
End Sub
です。

Lanczosの大きなものほど理想的な減衰率に近づきますが、リンギングが出るので一長一短です。

最後に画素混合によりノイズがどのくらい平滑化されるか調べます。
Sub sn()
For j = 0 To 4
  wa = 0
  wa2 = 0
  For i = -4 To 4 Step 0.01
    wa = wa + lowpass(i, j) / 100
    wa2 = wa2 + lowpass(i, j) ^ 2 / 100
  Next i
  Cells(1, 1 + j) = Sqr(wa2) / wa
Next j
値が小さいほどノイズが減ります。

僅かな差でLanczos1が良く、次がLanczos2です。画素平均法は参照範囲が少ないのでノイズ減少が少ないです。

画像縮小アルゴリズム比較がなされています。

0 件のコメント: