PNLM と PNLM2 の処理について

気が向いたらまた手を入れるとは思いますが、もしかするともう2度と触らないかもしれないので、一応内部で何をしているかを書いておきます。

PNLM の処理概要

指定された範囲で、重み付けを行う

  1. 輝度の差を求める
  2. 輝度差と 1. で求めた値の積を取る
  3. 2. で求めた値から、重み付け係数を取得する
    2. で得られた値を i 、任意の係数 H および S とすして、重み付けは以下の関数で求める
    exp ( - 10 ^ ( - strength / 22 ) * i * i )

上記範囲内で、

  1. 3. でもとめた値の和を計算する
  2. 3. で求めた値と輝度の積をとり、その和を計算する
  3. 5. で求めた値を 4. で求めた値で割る

この結果を処理結果としています。

PNLM2 の処理概要

前処理で濃度勾配を計算する。

  1. 3x3 の輝度の平均をとる
  2. 輝度の平均値から、上下方向と左右方向の濃度勾配を計算する

以下、指定された範囲で、重み付けを行う

  1. 輝度の差、上下方向の濃度勾配の差の絶対値、左右方向の濃度勾配の差の絶対値を求める
  2. 3. でもとめた勾配差の絶対値の和をとる
  3. 輝度差と 4. で求めた値の積を取る
  4. 5. で求めた値から、重み付け係数を取得する
    2. で得られた値を i 、任意の係数 H および S とすして、重み付けは以下の関数で求める
    exp ( - 10 ^ ( - strength / 22 ) * i * i )

上記範囲内で、

  1. 6. でもとめた値の和を計算する
  2. 6. で求めた値と輝度の積をとり、その和を計算する
  3. 8. で求めた値を 7. で求めた値で割る

この結果を処理結果としています。

計算量および PMD との比較

PNLM は前処理がなく、輝度の差のみで処理を行っています。勾配差の計算をしていませんし、輝度と勾配差の積を取っていません。輝度差だけなので、あふれないようにチェックしたりする処理も必要ないので、PNLM2 より処理が単純で、処理量も少ないです。
PMD は平均した輝度の差で重みを計算し、(平均化していない)輝度の差を重みで割ってから、元の値に加算します。処理範囲の中で周りにより似たピクセルがあってもなくても、輝度の差が同じなら同じ重みになります。処理範囲は、上下左右の4ピクセルです。PNLM では最低 3x3 なので、1ピクセルを処理するための対象範囲も倍以上になっているため PNLM は PMD より遅いです(PNLM のデフォルトは 3x3x3 なので単純計算だと6倍以上の計算量)。
PNLM および PNLM2 では重みと輝度の積を範囲内で総和を求めてから、範囲内の重みの総和で割るので、重みが大きいものが多いほど、重みの小さいものの影響がより小さくなります。なので、PMD よりも PNLM のほうがぼけにくいです。 これらの処理は特徴が似ていることを条件に重み付け平均していますから、ある程度広い範囲を処理した方が処理結果は良くなります。しかし、広い範囲を処理すると、その分計算量も増えてしまい遅くなります。
重み付け係数の計算は、PNLM の場合、輝度差から表引きしちゃっているので、表引きして、和をとるという非常に単純な計算しかしていません。PMD は生の輝度差じゃないので、平均輝度差から表引きした後に、積を取ってます。

PNLM2 の処理についてもう少し

PNLM は単純なので(実装は少し変ですがw)、上に書いた範囲で分ると思います。なので、PNLM2 の重み付けの処理の説明をもう少ししておきます。
PNLM2 で輝度平均を取るのは 3x3 の単純平均です。上下の濃度勾配は、対象ピクセルの上のピクセルの平均値から、対象ピクセルの下のピクセルの平均値を引いたものです。
つまり、上下方向の勾配を得るマトリクスは、

 1  1  1
 1  1  1
 0  0  0
-1 -1 -1
-1 -1 -1

となります。同様に左右方向の濃度勾配は、対象ピクセルの左から右を引いたものです。
つまり、上下方向の勾配を得るマトリクスは、

1  1  0 -1 -1
1  1  0 -1 -1
1  1  0 -1 -1

となります。
この2つの濃度勾配と輝度の値がピクセルを特徴付ける値になります。ピクセルの重み付けにはこれらの値が近いものには大きな重みを、値が異なるものは小さな重みをつけるようにします。
wSpan と tSpan で処理範囲を指定されるわけですが、この指定された処理範囲内の書くピクセルに対して、中央のピクセルとどの程度にているかを重み付けしていきます。
処理範囲の任意のピクセルに対して、中央のピクセルと輝度差、上下濃度差、左右濃度差を計算します。2つの濃度差の和と輝度差の積を計算する前に、輝度差を右にビットシフトして1を足すという処理を行っています。輝度差を何ビットシフトするかというのが angw パラメータで指定できる値です。angw に小さい値を指定すると右シフトするビット数が増え、係数が1になるものが多くなります。
処理範囲の任意のピクセルに対して、重みと輝度差と重みの積を計算して行き、その和をとるのですが、中心ピクセルの重みは最大になります。周囲にたくさん似ているピクセルがあればこれでもいいのですが、ノイジーなものだと上手くいかないかもしれません。自分自身の重みを最大重みからどれだけずらすかを指定するのが selfw パラメータになります。selfw を大きくすると周りの影響が弱くなり、小さくすると周りの影響が強くなります。

それと、Readme に書き忘れたんだけど、SSE2 で処理できる幅じゃないと右端処理されなかったりする仕様。横幅が YUY2 なら8の倍数、YV12 なら16の倍数じゃないと駄目。