2012年8月21日火曜日

FastGradFunkMirror

一見同じようなavsでも書き方一つでパフォーマンスは結構変わっちゃうかもよ、というお話。

GradFunkMirrorという有名なスクリプトがあります。
function GradFunkMirror(clip c, float "strength")
{
    strength = default(strength, 1.2)
    w = c.width()
    h = c.height()
    vflip = c.FlipVertical()
    hflip = c.FlipHorizontal()
 
    StackHorizontal(hflip.crop(w-16, 0, 16, h).AddBorders(0, 16, 0, 16),
    \ stackvertical(vflip.crop(0, h-16, w, 16), c, vflip.Crop(0, 0, w, 16)),
    \ hflip.Crop( 0, 0, 16, h ).AddBorders(0, 16, 0, 16))
    gradfun2db(strength)
    Crop(16, 16, -16, -16)

    return last
}
これはGradFun2DBは上下左右の端16pixの部分を処理しないようになっているので(画像端の処理って面倒ですからねぇ)、それを補うために用意されているスクリプトです。
原理は非常に簡単で、端16pixを処理できないなら周囲に16pix分の鏡像をくっつけてやることで元画像の端まで処理範囲を拡大し、フィルタ後にくっつけた画像をCropしてしまえというもの。

さて、昨日AviSynthの内蔵フィルタのソースを眺めていてふと気づいたので、こう書き直してみました。
function GradFunkMirror2(clip c, float "strength")
{
    strength = default(strength, 1.2)
    w = c.width()
    h = c.height()
    top = c.Crop(0, 0, w, 16).FlipVertical()
    bottom = c.Crop(0, h-16, w, 16).FlipVertical()
    left = c.Crop(0, 0, 16, h).FlipHorizontal().AddBorders(0, 16, 0, 16)
    right = c.Crop(w-16, 0, 16, h).FlipHorizontal().AddBorders(0, 16, 0, 16)

    StackHorizontal(left,
    \               StackVertical(top, c, bottom),
    \               right)
    gradfun2db(strength)
    Crop(16, 16, -16, -16)
    
    return last
}

違いは「鏡像を作る際に、画像を反転してからCropするか、それともCropしてから反転するか」だけです。
出力されるもの自体は同じですがベンチマークを取ってみると
#sample.avs
AVISource("1440x1080_ULY0.avi")
GradFunkMirror()
#GradFunkMirror2()

$ for i in {1..3}; do avs2pipemod -benchmark sample.avs; done

      GradFunkMirror   GradFunkMirror2
1st     50.600fps        57.665fps
2nd     50.566fps        58.214fps
3rd     50.308fps        57.566fps
---------------------------------------
avg     50.491fps        57.815fps
「大きな画像を反転させるよりも小さな画像を反転させるほうがコストは低い」という、まあ当たり前な話なわけですが、結構な差が出るものですな。

追記:
ちなみにGradfun2DBModの場合はここに書いたようなロスはなく、GradFunkMirrorがAddBordersで済ませている四隅の部分もしっかり鏡像にしている芸の細かさを見せてくれます。

6 件のコメント:

  1. Chikuzen氏が書いたGradFunkMirror2関数を.avsiで保存。これをpluginsフォルダに入れ、avspmod2.3.1を実行すると、

    Traceback (most recent call last):
    File "run.py", line 6, in
    File "AvsP.pyo", line 14914, in main
    File "wx\_core.pyo", line 7981, in __init__
    File "wx\_core.pyo", line 7555, in _BootstrapApp
    File "AvsP.pyo", line 14905, in OnInit
    File "AvsP.pyo", line 4224, in __init__
    File "AvsP.pyo", line 4886, in defineFilterInfo
    File "AvsP.pyo", line 5114, in getFilterInfoFromAvisynth
    WindowsError: exception: access violation reading 0x00000000

    とエラーが出てavspmodを使うことが出来ません。
    どう対処するべきでしょうか?

    返信削除
  2. avisynthはxhmikosr氏がビルドした最新のものをインストールしています。

    返信削除
  3. とりあえず1箇所間違いがあったので修正しました。
    修正前 9行目
    > right = c.Crop(w-16, 0, 16, h).FlipHorizontal().AddBorders(0, 16, 0, 16))
    閉じ括弧が一つ余分ですね。
    その他は特に問題なく、右端の')'を削れば手元では普通に動きます。
    ちなみに自分は公式の2.6a3とSEt氏のMT最新版を使用しています(SetMTModeを使わなければ、こちらのほうが色々最新の修正が入っているので良いことが多い)がxhmikosr氏のは使ったこと無いです。
    ためしにAvsPmodではなくVirtualDubかAviUtlでプレビューできるかどうかを試してみて下さい。
    もしプレビューできればAvsPmodが原因ですし、それでもダメならavisynth.dllを他のビルドに変えたほうがよいでしょう。

    返信削除
  4. 括弧を消したところエラーは表示されなくなり、無事にAvsPmodを起動することが出来ました。
    加えて有益な情報も教えて頂き、本当にありがとうございます。

    返信削除
  5. 初めまして
    GurandFunkMirrorなのですが、AviUtlに渡した所
    CAVIStreamSystem:System exception - Accsess Violation at 0x1f43a0c9,writing to 0x292d1000
    と表示が出てしまいます
    意味が良く分からないのですが、これはエラーなのでしょうか?
    GrandFunkMirrorの他の関数を使っても同じようにメッセージが表示されてしまうのですが、何か足りない物があるのか、それとも何か書き方が間違っているのか…(上記されている関数をコピーしました)

    初心者故に質問もままならないので恐縮ですがお知恵をお貸し頂けたら幸いです

    返信削除
  6. 手元では再現しないのでよくわかりませんが、access violationですから、なんらかの不正処理によりクラッシュしていることは確かです。
    もともとgradfun2dbはYV12以外では使えないのに、色空間のチェックはしていないような結構お行儀の悪いプラグインですし、AviUtlはAviUtlで、入力が制限されている上に下手するとエクスプローラを巻き込んで再起動必至になるような扱いの難しいソフトです。

    avsreader.auiのようなプラグインを書いてる自分が言うのもなんですが、AviSynthとAviUtlの相性は決して良いとはいえません(AviSynthは基本的にVirtualDubと組み合わせて使うように作られています)。
    慣れないうちは安易な併用は避けたほうがよいと思います。

    返信削除