## ConditionalFilter

`ConditionalFilter` (clip testclip, clip source1, clip source2, string expression1, string operator, string expression2, bool "show")

`ConditionalFilter` – условный фильтр – проверяет для каждого кадра условие, образуемое "expression1+operator+expression2" (выражение1+оператор+выражение2), и возвращает кадры клипа source1, когда условие выполняется, в противном случае возвращаются кадры клипа source2. Если в вызове некоторой функции в выражениях expression1 или expression1 явным образом не задан клип, то она применяется к клипу testclip. Звук берётся из клипа source1.

Пример: выбираем кадры из клипа vid_blur, если средняя яркость кадра меньше 20, иначе берём кадры клипа vid.

    vid = AviSource("file")
    vid_blur = vid.Blur(1.5)
    ConditionalFilter(vid, vid_blur, vid, "AverageLuma()", "lessthan", "20")

Параметр show="true" включает вывод на экран текущих значений выражений для каждого кадра.

Строковые (неименованные) параметры expression1 и expression2 могут быть любыми численными или логическими выражениями и могут включать внутренние или пользовательские функции, а также некоторые предопределённые функции (функции времени исполнения (Runtime)) и специальную переменную времени исполнения current_frame (номер запрашиваемого кадра).
Строка operator может принимать значения "equals" (равно), "greaterthan" (больше) или "lessthan" (меньше). Или соответственно "=", ">" и "<".

## ScriptClip

`ScriptClip` (clip, string filter, bool "show", bool "after_frame")

`ScriptClip` выдаёт клип, возвращаемый фильтром filter, применяемым для каждого кадра. Строковый параметр filter может быть любым выражением, возвращающим клип, включая встроенные или пользовательские клиповые функции, и может включать переносы строк (позволяющие выполнение последовательности команд). Также, некоторые предопределённые функции (функции времени исполнения (Runtime)) и специальная переменная времени исполнения current_frame (номер запрашиваемого кадра) могут быть использованы в выражении для filter.

Параметр show="true" включает вывод значения параметра на каждом кадре.

Примеры:

    # печатает разницу между этим и предыдущим кадром поверх текущего кадра
    clip = AviSource("c:\file.avi")
    ScriptClip(clip, "Subtitle(String(YDifferenceFromPrevious))")

    # Применяет размытие для каждого кадра в зависимости от разницы с предыдущим кадром
    # Также показывает, как на некоторые кадры накладывается сообщение об ошибке
    clip = AviSource("c:\file.avi")
    ScriptClip(clip, "Blur(YDifferenceFromPrevious/20.0)")

    # Применяет temporalsoften на достаточно статичных сценах, и меняющееся размытие на динамичных.
    # Также производится присваивание переменной – поэтому вставлен символ разрыва строки
    function fmin(float f1, float f2) {
      return (f1<f2) ? f1 : f2
    }
    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 (те же ширина, высота и цветовой формат). Возвращаемый клип может иметь другую длину, но будет использоваться длина исходного клипа. Звук из исходного клипа возвращается без изменений. Для двух сильно отличающихся источников (MPEG2DEC3 and AviSource) может получиться несовпадение цветовых пространств – это известная "фишка".

## FrameEvaluate

`FrameEvaluate` (clip clip, script filter, bool "after_frame")

Действует аналогично `ScriptClip`, но результат работы filter игнорируется – возвращаются кадры из исходного клипа. Это полезно, например, для присваивания переменных в теле функции.
В версии v2.53 добавлен параметр after_frame=`true/false`. Он определяет, должен ли скрипт вычисляться до (по умолчанию) или после того, как кадр будет получен от вышерасположенных фильтров.

## ConditionalReader

Этот фильтр позволяет считывать произвольную информацию из файла в некоторую переменную.

Описание находится на отдельной странице.

## Функции времени исполнения (Runtime)

Это встроенные функции, вычисляемые на каждом конкретном кадре.

Возвращают среднее по всем пискелям кадра значение выбранной составляющей (яркости или цветности). Требуется цветовой формат YV12, наличие поддержки ISSE в процессоре):
`AverageLuma` (clip)
`AverageChromaU` (clip)
`AverageChromaV` (clip)

Возвращают действительное число от 0 до 255, являющееся модулем разности между цветовыми (или яркостными) плоскостями длух клипов (требуется YV12, ISSE):
`RGBDifference` (clip1, clip2)
`LumaDifference` (clip1, clip2)
`ChromaUDifference` (clip1, clip2)
`ChromaVDifference` (clip1, clip2)

При использовании этих функций работает соглашение "implicit last", т.е. если первый параметр пропущен, то подразумевается последний упомянутый клип; в данном случае это входной testclip, передаваемый фильтрам ConditionalFilter, ScriptClip, FrameEvaluate.

Нижеследующие фильтры полезны для определения смены сцены (или смены плана, в общем, когда кадр существенно отличается от предыдущего или следующего):
`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 (в процентах) определяет, какая доля пикселов может быть меньше (больше) минимума (необязателен; по умолчанию 0).

Если предшествующий раздел понятен, можно переходить к разделу углублённого изучения, который поведает о дополнительных возможностях условного фильтрования.

## Углублённое изучение условного фильтрования: часть I

Для понимания этого раздела требуется небольшое пояснение принципа работы AviSynth:
Скрипты обрабатываются сверху вниз, но когда приложение-клиент запрашивает кадр, его вычисление начинается с последнего кадра и продолжается вверх по цепочке фильтров. Пример:

    AviSource("myfile.avi")
    ColorYUV(analyze=true)
    Histogram()

При открытии скрипта, положим, в VirtualDub'е, происходит следующее:

- Когда Vdub запрашивает кадр, AviSynth запрашивает кадр у фильтра Histogram.
- Histogram запрашивает кадр у ColorYUV,
- ColorYUV – у AviSource, который создаёт кадр (читая его из файла) и отдаёт его ColorYUV.
- ColorYUV обрабатывает кадр и отдаёт его фильтру Histogram, который, в свою очередь, возвращает результат работы в Vdub.

Т.о. цепочка фильтров работает, фактически, снизу вверх (кадры вытягиваются из нижнего фильтра, а не выталкиваются из верхнего), что даёт возможность каждому фильтру использовать несколько кадров из вышележащих фильтров для получения запрашиваемого кадра.

Условные фильтры, однако, должны вычислять скрипт до того, как они запрашивают кадры из вышележащих фильтров, т.к. им требуется знать, какой фильтр нужно вызвать. Другая важная особенность состоит в том, что только глобальные переменные (определённые ключевым словом `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)

Эта цепочка фильтров работает следующим образом:

- Когда Vdub запрашивает кадр, AviSynth запрашивает его у функции g().
- g() запрашивает кадр у AviSource():

- AviSynth запрашивает кадр у второго оператора FrameEvaluate.
- Второй FrameEvaluate вычисляет YDifferenceFromPrevious(w) и присваивает это значение переменной text, после получения кадра из AviSource. После этого запрашивается кадр у первого оператора FrameEvaluate.
- Первый FrameEvaluate запрашивает кадр у ScriptClip после вычисления String(text) и присваивания этого значения переменной t.
- ScriptClip запрашивает кадр у ConvertToYV12(), потом вызывает Subtitle(t) для этого кадра.
- ConvertToYV12() запрашивает кадр у AviSource().

- AviSource() создаёт кадр и отдаёт его функции g(), которая возвращает его в Vdub.

Как видно, 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)

Большая часть написанного для иллюстрации скрипта избыточна: следующие два скрипта выдают тот же самый результат.

    v = AviSource("c:\clip.avi")
    # ScriptClip принимает и многострочные скрипты:
    Scriptclip(v,"
    	text = YDifferenceFromPrevious()
    	t = string(text)
    	subtitle(t)
    ")

    v = AviSource("c:\clip.avi")
    ScriptClip(v, "Subtitle(String(YDifferenceFromPrevious))")

В следующем разделе мы научимся записывать в текстовый файл информацию, зависящую от кадра.

## Углублённое изучение условного фильтрования: часть II

В следующем примере в текстовый файл записывается некоторая информация, зависящая от кадра. Первая переменная "a" говорит о том, имеется ли в кадре "гребёнка" (combing), для её определения используется фильтр `IsCombed` из пакета `Decomb`. Вторая переменная "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)

Мы можем и упростить эти две функции, и удалить глобальные переменные, записав их следующим образом:

    function IsMoving(float diff)
    {
     return (diff >= 1.0)
    }

    function CombingInfo(clip c)
    {
     file = "F:\interlace.log"

     c = WriteFile(c, file, "a", "sep", "b")
     c = FrameEvaluate(c,"
           diff = 0.50*YDifferenceFromPrevious() + 0.25*UDifferenceFromPrevious() + 0.25*VDifferenceFromPrevious()
           b = IsMoving(diff)
           a = IsCombed(combedthreshold)
         ")

     return c
    }

В следующем разделе мы рассмотрим пример адаптивной фильтрации/масштабирования.

## Углублённое изучение условного фильтрования: часть III

На форумах (имеется в виду doom9 - прим.перев.) было придумано некоторое количество адаптивных по движению фильтров масштабирования и сглаживания. Эти фильтры различают малое, среднее и большое количество движения в клипе (на основе покадрового сравнения) и применяют различные группы фильтров в зависимости от этого. Как правило, имеет смысл использовать временнУю фильтрацию в статичных сценах, пространственную фильтрацию в динамичных сценах, и их комбинацию в промежуточном случае.
Ниже нажодится упрощённая версия фильтра QUANTIFIED MOTION FILTER v1.5 b1 (10/07/2003) от HomiE FR:

    ----------------------------------------------------
    # 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 (текст файла ниже)

    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
    ----------------------------------------------------

    # QUANTIFIED MOTION FILTER (17/08/2003) by HomiE FR (homie.fr@wanadoo.fr)
    # Функция определения движения
    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
    }

    # функция адаптивной фильтрации
    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)

      # информация о режиме работы Quantified Motion Filter [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
    }
    ----------------------------------------------------

Эта цепочка фильтров работает следующим образом:

- Когда Vdub запрашивает кадр, AviSynth запрашивает его у QMF.

- QMF запрашивает кадр у FrameEvaluate [9].
- После этого вычисляется скрипт [9], и глобальная переменная diff получает значение после запрашивания кадра у AviSource. FrameEvaluate [9] запрашивает кадр у FrameEvaluate [8].
- Вычисляется скрипт [8]:

- при вычислении me() получает значение глобальная переменная motion_level для этого кадра [1]

- Если debug=true, запрашивается кадр у ScriptClip [7] и, соответственно, у Debug().
- После этого (либо если debug=false) запрашивается кадр у последнего оператора ConditionalFilter [6c], который запрашивает его у [6b], а тот, в свою очередь, у [6a].

- В конечном итоге, запрашивается кадр либо у High_Motion_filter, либо у Medium_Motion_filter, либо у Low_Motion_filter, в зависимости от значения motion_level.

- QMF запрашивает кадр у Telecide, Telecide – у ConvertToYV12, и, наконец, ConvertToYV12 – у AviSource.
- AviSource создаёт кадр, отдаёт его ConvertToYV12, и цепочка развёртывается обратно.

Некоторые детали были опущены, но общий смысл работы этого скрипта именно такой.

`$English Date: 2008/12/07 16:36:28 $
Русский перевод 28.05.2005 Eugene Vasiliev (eugvas@mccme.ru)
Обновление 11.09.2008-10.12.2008 Fizick`