ConditionalFilter
ConditionalFilter †
ConditionalFilter (clip testclip, clip source1, clip source2, string filter, string operator, string value, bool "show")
ConditionalFilter は、「filter + operator + value」によって構成される条件が満たされる時には source1 を返し、さもなければ source2 を返します。filter が明示的にいずれかのクリップに適用されていない場合、そのフィルタは testclip に適用されます。音声は、source1 から取られます。
使用例です。このスクリプトは、フレームの輝度の平均値が 20 より低い時に vid_blur のフレームを選択します。さもなければ、vid のフレームが返されます。
vid = AviSource("file") vid_blur = vid.Blur(1.5) ConditionalFilter(vid, vid_blur, vid, "AverageLuma()", "lessthan", "20")
show=true を追加指定すると、画面上に現在の値が表示されます。
filter 文字列には、任意の内蔵フィルタのほか、いくつかの定義済みフィルタ(ランタイム関数)も指定可能です。
operator 文字列は、"equals"*1、"greaterthan"*2、"lessthan"*3 のいずれかを指定可能です。それぞれ "="、">"、 "<" と記述しても同じです。
ScriptClip †
ScriptClip (clip, string function, bool "show", bool "after_frame")
ScriptClip は、フレームごとに評価される function によって返されるクリップを返します。function 文字列には、任意の内蔵フィルタのほか、いくつかの定義済みフィルタ(ランタイム関数)も指定可能です。show=true を追加指定すると、画面上に現在の値が表示されます。
いくつかの使用例:
# 現在のフレーム上に前のフレームとの輝度の差を表示する: clip = AviSource("c:\file.avi") ScriptClip(clip, "Subtitle(String(YDifferenceFromPrevious))") # 前のフレームとの輝度の差に基づいて、各フレームにぼかし(Blur)を適用する。 # いくつかのフレームでは、エラーがどのように報告されるかについても表示する :) clip = AviSource("c:\file.avi") ScriptClip(clip, "Blur(YDifferenceFromPrevious/20.0)") # ほとんど動きのないシーンには TemporalSoften を適用し、動きのあるシーンには「可変」ぼかしを適用する。 # 今回は Blur にちゃんと上限が設定され、変数も割り当てられている。改行(chr(13))が挿入されているのはこのため: function fmin(float f1, float f2) { return (f1 } clip = AviSource("c:\file.avi") ScriptClip(clip, "diff = YDifferenceToNext()"+chr(13)+"diff > 2.5 ? Blur(fmin(diff/20,1.5)) : TemporalSoften(2,7,7,3,2)") # クリップ内にフレーム番号を表示する: ScriptClip("subtitle(string(current_frame))") # クリップ内に「frame = フレーム番号」を表示する: ScriptClip("""subtitle("frame = " + string(current_frame))""")
v2.55 から、after_frame オプション(true または false)が追加されています。これは、スクリプトの評価が、上述のフィルタからフレームが取得される前(デフォルトの動作)に行われるか、後に行われるかを決定します。
「制限」: スクリプトの出力は、ScriptClip に渡されるクリップと完全に同じである必要があります(同じ色空間、同じ幅、同じ高さ)。返却されるクリップは異なる長さを持つことが許可されていますが、つねに「clip」の長さが使用されます。「clip」の音声は、そのまま素通しされます。2 つの非常に異なるソース(MPEG2DEC3 と AviSource)に関しては、色空間の不一致に陥るかもしれません。これは、有名な逃げ口上です。
FrameEvaluate †
FrameEvaluate (clip clip, script function, bool "after_frame")
フィルタの出力が無視されること以外は、ScriptClip に似ています。これは、変数の割り当てなどのために使用することができます。与えられたクリップのフレームが素通しされます。
v2.53 から、after_frame オプション(true または false)が追加されています。これは、スクリプトの評価が、上述のフィルタからフレームが取得される前(デフォルトの動作)に行われるか、後に行われるかを決定します。
ConditionalReader †
このフィルタは、任意の情報を選択可能な変数にインポートすることを可能にします。
ConditionalReader の専用ページを参照してください。
ランタイム関数 †
フレームごとに評価される内部関数です。
平面のピクセル値の平均を返します(要 YV12、ISSE):
AverageLuma (clip) AverageChromaU (clip) AverageChromaV (clip)
2 つの平面間の差の絶対値を 0 から 255 までの浮動小数点値で返します(要 YV12, ISSE):
RGBDifference (clip1, clip2) LumaDifference (clip1, clip2) ChromaUDifference (clip1, clip2) ChromaVDifference (clip1, clip2)
これらの関数を使用するときには、「暗黙の最後の(implicit last)」クリップが存在します(第 1 パラメータを指定する必要がありません)。したがって、第 1 パラメータは testclip に置き換えられます。
シーンチェンジの検出にかなり役立ちます:
RGBDifferenceFromPrevious (clip) YDifferenceFromPrevious (clip) UDifferenceFromPrevious (clip) VDifferenceFromPrevious (clip) RGBDifferenceToNext (clip) YDifferenceToNext (clip) UDifferenceToNext (clip) VDifferenceToNext (clip)
# シーンチェンジ前の最後のフレームを # シーンチェンジ後の最初のフレームに置き換える: ConditionalFilter(last, last, last.trim(1,0), "YDifferenceToNext()", ">", "10", true)
その他の内部関数::
YPlaneMax (clip, float threshold) UPlaneMax (clip, float threshold) VPlaneMax (clip, float threshold) YPlaneMin (clip, float threshold) UPlaneMin (clip, float threshold) VPlaneMin (clip, float threshold) YPlaneMedian (clip) UPlaneMedian (clip) VPlaneMedian (clip) YPlaneMinMaxDifference (clip, float threshold) UPlaneMinMaxDifference (clip, float threshold) VPlaneMinMaxDifference (clip, float threshold)
threshold はパーセンテージで指定し、ピクセルの何パーセントが最小値を上回るまたは下回ることを許容するかに関係しています。threshold はオプションで、デフォルトは 0 です。
上記の内容を理解したら、条件フィルタリングについてもう少し詳しく解説した「高度な条件フィルタリング」に進むことができます。
高度な条件フィルタリング: パート 1 †
この節を理解するには、AviSynth の機能性について、いくつか知らなければならないことがあります:
スクリプトは上から下へパースされますが、フレームがリクエストされるときには、じつは最後のフィルタが最初に呼び出され、フィルタチェインの中で上方向にフレームをリクエストしていきます。例えば:
AviSource("myfile.avi") ColorYUV(analyze=true) Histogram()
このスクリプトを VirtualDub で開くと、以下のことが起こります:
- VirtualDub があるフレームをリクエストするとき、AviSynth は Histogram からフレームをリクエストする。
- Histogram は、ColorYUV からフレームをリクエストする。
- ColorYUV は、フレームを生成する AviSource からリクエストし、それを ColorYUV に渡す。
- ColorYUV はその画像を処理して、Histogram に送る。Histogram は、それを VirtualDub に返す。
このように、フィルタチェインは、基本的に逆方向に動作します。このことは、フィルタに対し、上方のソースから複数のフレームをリクエストする可能性を与えます。
しかし条件フィルタは、どのフィルタを呼び出すべきかを知る必要があるため、上方のフィルタからフレームをリクエストする前にスクリプトを評価する必要があります。もう 1 つの重要な問題は、条件フィルタ「環境」の中で global に定義された変数のみがその外部でも利用可能であるということです(逆もまた同じ)。次のスクリプトを見てください
v = AviSource("E:\Temp\Test3\atomic_kitten.avi").ConvertToYV12 function g(clip c) { global w = c c2 = ScriptClip(c, "subtitle(t)") c3 = FrameEvaluate(c2, "t = String(text)") c4 = FrameEvaluate(c3, "text = YDifferenceFromPrevious(w)") return c4 } g(v)
このフィルタチェインは、次のように動作します:
- VirtualDub がフレームをリクエストすると、AviSynth は g() からフレームをリクエストする。
- g() は、AviSource() からフレームをリクエストする:
- AviSynth は、2 つ目の FrameEvaluate からフレームをリクエストする。
- 2 つ目の FrameEvaluate は、AviSource のフレームをリクエストした後に、YDifferenceFromPrevious(w) を評価し、その値を変数 text に割り当てる。この後、フレームが 1 つ目の FrameEvaluate からリクエストされる。
- 1 つ目の FrameEvaluate は、「String(text) を評価し、その値を変数 t に割り当てた」後に、ScriptClip からフレームをリクエストする。
- ScriptClip は、ConvertToYV12() からフレームをリクエストし、そのフレーム上で Subtitle(t) を評価する。
- ConvertToYV12() は、AviSource() からフレームをリクエストする。
- AviSource() はそのフレームを生成して g() に送る。g() は、それを VirtualDub に返す。
見てわかるように、w はグローバル変数として定義されています。このように、条件環境ではこれをスクリプトの後方で使用することができます。もし変数 t と text を別の関数(条件環境の内側でも外側でも)の中で使用したいなら、それらの変数もグローバル変数として定義されなければなりません。したがって、たとえば次のようにします:
v = AviSource("E:\Temp\Test3\atomic_kitten.avi").ConvertToYV12 function g(clip c) { global w = c c2 = ScriptClip(c, "subtitle(t)") c3 = FrameEvaluate(c2, "me()") c4 = FrameEvaluate(c3, "global text = YDifferenceFromPrevious(w)") return c4 } function me() { global t = String(text) } g(v)
上記のスクリプトの大半は冗長で、省略することが可能です。次の 2 つのスクリプトは同じ出力を与えます。
v = AviSource("c:\clip.avi") scriptclip(v," YDiffPrev = YDifferenceFromPrevious YDiffPrevStr = string(YDiffPrev) subtitle(YDiffPrevStr) ")
v = AviSource("c:\clip.avi") ScriptClip(v, "Subtitle(String(YDifferenceFromPrevious))")
次の節では、フレーム従属情報をテキストファイルに書き出します。
高度な条件フィルタリング: パート 2 †
次の例では、いくつかのフレーム従属情報がテキストファイルに書き出されます。1 つ目の変数「a」は、(ある閾値に対して)そのフレームにコーミング*4があるかどうかを示します。IsCombed は、Decomb*5 プラグインのフィルタです。2 つ目の変数「b」は、そのフレームの中に「たくさんの」動きがあるかどうかを示します。
global sep="." global combedthreshold=25 function IsMoving() { global b = (diff < 1.0) ? false : true } function CombingInfo(clip c) { file = "F:\interlace.log" global clip = c c = WriteFile(c, file, "a", "sep", "b") c = FrameEvaluate(c, "global a = IsCombed(clip, combedthreshold)") c = FrameEvaluate(c, "IsMoving") c = FrameEvaluate(c,"global diff = 0.50*YDifferenceFromPrevious(clip) + 0.25*UDifferenceFromPrevious(clip) + 0.25*VDifferenceFromPrevious(clip)") return c } v = mpeg2source("F:\From_hell\from_hell.d2v").trim(100,124) CombingInfo(v)
次の節では、「適応型モーション/リサイズフィルタ」の例について考えます。
高度な条件フィルタリング: パート 3 †
適応型のモーションフィルタやリサイズフィルタが Doom9's Forum に登場しました。これらのフィルタは、クリップにおける動きの少ないシーン、動きの激しいシーン、そしてその中間のシーンを(フレーム単位で)識別します。これにより、クリップにおけるモーションの種類に応じて異なるフィルタを使用することができます。一般的には、動きの少ないシーンでは時間軸の平滑化を、動きの激しいシーンでは空間軸の平滑化を、その中間のシーンでは空間軸+時間軸の平滑化を使用します。
下にあるのは、HomiE FR 作 QUANTIFIED MOTION FILTER (QMF) v1.5 b1 (10/07/2003)の簡易版です:
# QUANTIFIED MOTION FILTER v1.3 # AviSynth プラグインの読み込み LoadPlugin("C:\PROGRA~1\GORDIA~1\mpeg2dec3.dll") LoadPlugin("C:\PROGRA~1\GORDIA~1\TemporalCleaner.dll") LoadPlugin("C:\PROGRA~1\GORDIA~1\FluxSmooth.dll") LoadPlugin("C:\PROGRA~1\GORDIA~1\UnFilter.dll") # QUANTIFIED MOTION FILTER スクリプト(qmf.avs)の読み込み Import("E:\temp\QMF\qmf.avs") # ローモーションフィルタ関数 # -> シャープなリサイズ + 時間軸のみ function Low_Motion_Filter(clip c) { c = TemporalCleaner(c, 5, 10) c = LanczosResize(c, 512, 272) return c } # ミディアムモーションフィルタ関数 # -> ニュートラルなバイキュービックリサイズ + 時間軸および空間軸 function Medium_Motion_Filter(clip c) { c = FluxSmooth(c, 7, 7) c = BicubicResize(c, 512, 272, 0.00, 0.50) return c } # ハイモーションフィルタ関数 # -> ソフトなリサイズ + 空間軸のみ function High_Motion_Filter(clip c) { c = FluxSmooth(c, -1, 14) c = UnFilter(c, -30, -30) c = BilinearResize(c, 512, 272) return c } # ビデオソースを開く AviSource("E:\temp\QMF\britney-I_love_rock_'n_roll.avi") ConvertToYV12(interlaced=true) Telecide(0) # 適応型のリサイズフィルタを適用(QMF を使用) QMF()
以下のスクリプトを qmf.avs という名前の別のファイルに保存します:
# HomiE FR (homie.fr@wanadoo.fr)作 QUANTIFIED MOTION FILTER (17/08/2003) # 動き評価(ME)関数 function ME() { # 平均の差によって動きのレベルを設定 [1] global motion_level = (diff < threshold_lm) ? 0 : motion_level global motion_level = (diff >= threshold_lm && diff <= threshold_hm) ? 1 : motion_level global motion_level = (diff > threshold_hm) ? 2 : motion_level } # QUANTIFIED MOTION FILTER (QMF)関数 function QMF(clip c, float "threshold_lm", float "threshold_hm", bool "debug") { # 動きのレベルの閾値を設定 [2] threshold_lm = default(threshold_lm, 4.0) threshold_hm = default(threshold_hm, 12.0) global threshold_lm = threshold_lm global threshold_hm = threshold_hm # デバッグ情報を有効化または無効化する [3] debug = default(debug, false) # 動きのレベルを初期化 global motion_level = 0 # 現在のクリップを設定 [4] global clip = c # 出力解像度を取得 [5] width = Width(Low_Motion_Filter(c)) height = Height(Low_Motion_Filter(c)) global c_resized = PointResize(c, width, height) # 動きのレベルに応じてモーションフィルタを適用 [6] c = ConditionalFilter(c, Low_Motion_Filter(c), c_resized, "motion_level", "=", "0") # [6a] c = ConditionalFilter(c, Medium_Motion_Filter(c), c, "motion_level", "=", "1") # [6b] c = ConditionalFilter(c, High_Motion_Filter(c), c, "motion_level", "=", "2") # [6c] # デバッグ情報を表示 [7] c = (debug == true) ? ScriptClip(c, "Debug()") : c # 動き評価関数を通して、動きのレベルを取得 [8] c = FrameEvaluate(c, "ME()") # 過去/現在のフレームの間の差を取得 [9] c = FrameEvaluate(c, "global diff = 0.50*YDifferenceFromPrevious(clip) + 0.25*UDifferenceFromPrevious(clip) + 0.25*VDifferenceFromPrevious(clip)") return c } # デバッグ情報関数 function Debug(clip c) { # バージョン情報を表示 [10] c = Subtitle(c, "Quantified Motion Filter", x=20, y=30, font="lucida console", size=18, text_color=$FFFFFF) c = Subtitle(c, "by HomiE FR (homie.fr@wanadoo.fr)", x=20, y=45, font="lucida console", size=14, text_color=$FFFFFF) # 動き評価の情報を表示 [11] c = Subtitle(c, "motion estimation", x=20, y=85, font="lucida console", size=18, text_color=$FFFFFF) c = Subtitle(c, "diff = "+string(diff), x=20,y=110, font="lucida console", size=16, text_color=$FFCCCC) # QMF の情報を表示 [12] c = Subtitle(c, "quantified motion filter", x=20, y=135, font="lucida console", size=18, text_color=$FFFFFF) c = (motion_level == 0) ? Subtitle(c, "scene type = low motion", x=20, y=160, font="lucida console", size=16, text_color=$66FF66) : c c = (motion_level == 1) ? Subtitle(c, "scene type = medium motion", x=20, y=160, font="lucida console", size=16, text_color=$66FF66) : c c = (motion_level == 2) ? Subtitle(c, "scene type = high motion", x=20, y=160, font="lucida console", size=16, text_color=$66FF66) : c return c }
このフィルタチェインは、次のように動作します:
- VirtualDub がフレームをリクエストすると、AviSynth は QMF からフレームをリクエストする。
- QMF は FrameEvaluate [9] からフレームをリクエストする。
- この後 [9] のスクリプトが評価され、AviSource からフレームをリクエストした後にグローバル変数 diff が割り当てられる。FrameEvaluate [9] は、FrameEvaluate [8] からフレームをリクエストする。
- もう一度 [8] のスクリプトが評価される:
- ME() を評価するとき、グローバル変数 motion_level がそのフレームに対して割り当てられる [1]
- debug=true なら、フレームは ScriptClip [7] すなわち Debug() からリクエストされる。
- その後(もしくは debug=false のとき)、フレームは直前の ConditionalFilter [6c] からリクエストされる。[6c] は [6b] からフレームをリクエストし、さらに [6b] は [6a] からフレームをリクエストする。
- 最後に、High_Motion_filter、Medium_Motion_filter または Low_Motion_filter のフレームは、motion_level の値にしたがってリクエストされる。
- QMF は Telecide から、Telecide は ConvertToYV12 から、そして最後に ConvertToYV12 は AviSource から、それぞれフレームをリクエストする。
- AviSource はそのフレームを生成して ConvertToYV12 に送る、など。
細かい部分はいくつか省略しましたが、このスクリプトは基本的にこのように動作します。
註: このページは、AviSynth 2.5.8 RC3 に同梱されている英語版ヘルプの日本語訳です。原文は、AviSynth をインストールしたフォルダ内の Docs/english/corefilters/conditionalfilter.htm にあります。なお、このページのテキストおよび画像のライセンスは、オリジナルのそれに準じます。詳しくは、AboutLicense を参照してください。
*1 訳者註: 日本語で「等しい」。
*2 訳者註: 日本語で「より大きい」。
*3 訳者註: 日本語で「より小さい」。
*4 訳者註: 櫛(comb)状のノイズ。参考: インターレースとは(2)、PC用語集 か 〜ののキューブ 〜
*5 訳者註: Filters for Avisynth and VirtualDub -- My Filters and Tools を参照。