VapourSynth

VapourSynth позволяет строить воспроизводимые цепочки обработки видео на Python: покадрово читать исходники, обрезать и переставлять кадры, менять размер и цветовое пространство, работать с масками и метаданными, подключать фильтры, просматривать результат и передавать готовый поток внешнему кодировщику через VSPipe.

В VapourSynth работа строится не вокруг проекта с дорожками и кнопкой экспорта, а вокруг скрипта .vpy. Скрипт описывает граф вычислений: какой источник открыть, какие преобразования применить, что считать выходом и в каком порядке запрашивать кадры. Такой подход особенно удобен там, где важны повторяемость, точные параметры фильтрации, пакетная обработка и возможность сохранить весь процесс в обычном текстовом файле.

Сам движок предоставляет базовые операции и Python API, а сложные задачи обычно решаются плагинами и библиотеками сценариев. При этом VapourSynth не навязывает конкретный кодировщик или контейнер: он может подготовить кадры, а дальнейшее сжатие выполнить FFmpeg, x264, x265, FLAC или другая программа, умеющая принимать поток из канала или именованного канала.

Скачать VapourSynth

Оценка 9.7Рекомендуем
  • Конвертация видео
  • Сжатие файлов
  • Просто для новичков
Скачать бесплатно на Windows
Лучшая альтернатива
VapourSynth
Оценка 8.5
  • Нужен Python и скрипты
  • Нет единого GUI
  • Плагины требуют настройки
Скачать VapourSynth
Загрузка начнётся после нажатия

Что именно делает VapourSynth

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

Основной пользовательский объект — клип. Для видео это VideoNode, для аудио — AudioNode. Клип не обязан содержать заранее просчитанные кадры; он описывает, как эти кадры получить. Поэтому выражение, которое в коде выглядит как последовательность присваиваний clip = ..., обычно строит граф обработки. Реальное вычисление начинается во время предпросмотра, вызова get_frame(), перебора кадров или запуска VSPipe.

Главная сильная сторона такого устройства — точное управление обработкой. Можно взять один и тот же исходник и собрать несколько независимых ветвей: например, одну оставить для сравнения, вторую пропустить через ресайз и шумоподавление, третью использовать как источник маски. Затем ветви объединяются в нужном месте. При этом промежуточные результаты не требуется сохранять на диск, если они нужны только как часть фильтрационной схемы.

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

VapourSynth Editor со скриптом генерации тестовой сетки и журналом успешного выполнения

Как устроен скрипт .vpy

Сценарий VapourSynth является Python-кодом. Это означает, что в нём доступны обычные переменные, функции, условия, циклы, словари, импорты и собственные модули. Но кадры не следует путать с обычными Python-объектами, которые сразу вычисляются при присваивании. Большая часть фильтров возвращает новый узел графа, а фактическая работа откладывается до момента запроса выходного кадра.

Минимальная структура обычно состоит из импорта ядра, получения исходного клипа через source-плагин, одной или нескольких операций и вызова set_output(). В современных сценариях используется объект core, через который доступны пространства имён загруженных модулей. Встроенные операции находятся, например, в core.std, масштабирование и преобразования цвета — в core.resize, текстовые диагностические функции — в core.text. Сторонний плагин добавляет своё пространство имён после загрузки.

from vapoursynth import core

clip = core.bs.VideoSource(source="input.mkv")
clip = core.std.Crop(clip, left=8, right=8)
clip = core.resize.Bicubic(clip, width=1920, height=1080)
clip.set_output()

В этом примере bs.VideoSource относится не к базовому ядру, а к BestSource. Это принципиальный момент: VapourSynth сам по себе предоставляет инфраструктуру и набор базовых фильтров, но открытие произвольных контейнеров обычно поручается source-плагину. В другом окружении вместо BestSource может использоваться FFMS2, L-SMASH-Works, d2vsource или специализированный декодер.

Выходов может быть несколько. Метод set_output(index=0) регистрирует клип под заданным индексом, после чего предпросмотрщик или VSPipe может выбрать нужный выход. Удобно оставлять, например, индекс 0 для обработанной версии, индекс 1 для исходника, индекс 2 для маски. В редакторах и просмотрщиках с поддержкой нескольких выходов это ускоряет визуальное сравнение и отладку.

Core, пространства имён и функции плагинов

core — центральный объект, через который VapourSynth предоставляет загруженные плагины. У каждого плагина есть namespace, а внутри него — функции. Поэтому вызов core.std.Trim() читается как функция Trim из стандартного пространства имён. Такая схема снижает риск конфликтов между одноимёнными функциями разных модулей и делает происхождение операции заметным прямо в скрипте.

Список доступных плагинов зависит от окружения. Наличие примера в чужом скрипте не означает, что соответствующая функция доступна после чистой установки. Если Python сообщает, что у core нет нужного пространства имён, первым делом проверяют, установлен ли плагин, попал ли он в каталог автозагрузки и подходит ли его сборка к текущей архитектуре и API. Для нативных модулей критична совместимость двоичных файлов; для Python-пакетов — ещё и зависимости конкретного окружения.

Нативный плагин можно подключить вручную через std.LoadPlugin, если автозагрузка не подходит. Это полезно при тестировании разных сборок одного фильтра или при хранении проекта в самодостаточном каталоге. Однако ручная загрузка требует аккуратного управления путями, иначе скрипт будет работать только на одном компьютере. Для воспроизводимой рабочей схемы удобнее либо стандартизировать окружение, либо явно документировать все внешние зависимости рядом со скриптом.

Встроенная функция получения списка плагинов помогает понять, что реально видит текущий Core. Это лучше, чем ориентироваться на содержимое каталога: файл может лежать на диске, но не загрузиться из-за отсутствующей DLL, несовместимого API или другой ошибки. Автозагрузка нативных плагинов специально не всегда останавливает запуск при проблемном модуле, поэтому отсутствие namespace в Core бывает первым заметным симптомом.

Отложенное вычисление и запрос кадров

Ленивое вычисление — ключ к пониманию производительности VapourSynth. Фильтр не обязан обрабатывать весь ролик в момент вызова. Он объявляет зависимости: например, для выходного кадра 100 ему нужны кадры 98–102 входа, а другой фильтр может потребовать только кадр 100. Планировщик отправляет запросы по графу и старается распараллелить работу там, где это безопасно.

Такой подход позволяет мгновенно открыть предпросмотр в середине длинного файла, если source-плагин и фильтры поддерживают эффективный произвольный доступ. Одновременно он объясняет, почему некоторые цепочки тормозят при перемотке: временные фильтры, сложные источники и операции с длинной историей могут вынуждать вычислять дополнительные соседние кадры. Чем больше temporal radius фильтра, тем больше работы скрывается за одним кадром предпросмотра.

Для программной проверки можно запросить кадр методом get_frame(n). Объект VideoFrame содержит формат, размеры, плоскости и свойства кадра. Если требуется перебрать весь клип из Python, доступен генератор frames(), который умеет рендерить несколько кадров параллельно. Однако типичный пользовательский сценарий всё равно заключается в том, чтобы передать клип VSPipe или предпросмотрщику, а не писать собственный цикл выдачи кадров.

Содержимое кадра считается неизменяемым, пока не создана записываемая копия. Это особенно важно при ModifyFrame: чтобы поменять свойства кадра, нужно сначала получить копию, затем изменить её props и вернуть результат. Такой контракт предотвращает случайное изменение кадра, который одновременно используется несколькими ветвями графа.

Открытие исходников и индексирование

У VapourSynth нет универсальной встроенной команды OpenVideo для всех контейнеров и кодеков. Источник выбирают под материал и требования к точности. BestSource работает как современный универсальный source-плагин на базе библиотек FFmpeg и предоставляет видео- и аудиодоступ. FFMS2 часто используется для индексируемого доступа к широкому кругу форматов. L-SMASH-Works популярен для MP4, MOV и других контейнеров, а d2vsource применяется в сценариях с заранее созданными индексами.

Source-фильтр решает более сложную задачу, чем обычный последовательный декодер. Фреймсервер может запросить кадр 5000, затем 120, затем 5010, поэтому плагину нужен корректный seek и понятное соответствие между отображаемыми кадрами и структурой потока. Для длинных GOP, VFR, repeat field flags и некоторых контейнеров индексирование помогает сделать случайный доступ предсказуемым.

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

Отдельно следует проверять свойства цвета, которые source-фильтр прикрепляет к кадрам. Декодер может корректно получить пиксели, но не знать матрицу, primaries, transfer или диапазон. Для дальнейшего конвертирования цвета это критично: resize-модуль ориентируется на frame properties, а если они не заданы или помечены как unspecified, часть параметров приходится указывать вручную.

VideoNode: длина, частота кадров и формат

VideoNode предоставляет информацию о клипе: число кадров, ширину, высоту, частоту кадров и формат. Если формат или размеры меняются от кадра к кадру, статическое свойство может быть неопределённым, потому что один набор параметров не описывает весь клип. VapourSynth допускает variable-format и variable-resolution графы, но не каждый фильтр способен с ними работать.

Частота кадров представлена рациональным числом — числителем и знаменателем. Это важно для значений вроде 24000/1001 и 30000/1001: замена их на приближённые десятичные 23.976 или 29.97 постепенно создаёт ошибку тайминга. Операции AssumeFPS, SelectEvery, SeparateFields и другие функции могут изменять длительность кадра или объявленную частоту, поэтому после сложных перестановок стоит проверять итоговый timing.

Само число кадров не говорит, соответствует ли поток исходным временным меткам. Для VFR применяются свойства длительности кадров и, при необходимости, отдельные timecodes. VSPipe умеет записывать timecodes v2, чтобы внешний процесс сохранил переменную длительность. Если кодировщик или контейнер ожидает CFR, нужно заранее решить, как преобразовать временную структуру, а не просто выставить новое значение fps.

Формат описывает семейство цвета, sample type, глубину, субдискретизацию и количество плоскостей. Например, YUV 4:2:0 10 bit и RGB float — принципиально разные представления. Фильтр может принимать только определённую группу форматов, поэтому значительная часть реальных скриптов включает подготовительное преобразование перед сложным плагином и обратное преобразование после него.

Python-срезы вместо части стандартных фильтров

VapourSynth специально связывает типичные операции с синтаксисом Python. Выражение clip[5] создаёт однокадровый клип из кадра 5, clip[5:11] берёт кадры 5–10, clip[::2] оставляет чётные кадры, а clip[::-1] разворачивает последовательность. Сложение клипов соответствует склейке, умножение — повторению.

Срезы особенно удобны при локальном ремонте. Например, участок можно вырезать, обработать отдельно и вернуть на место: before + fixed + after. При этом важно помнить, что индексация начинается с нуля, а правая граница Python-среза не включается. Ошибка на один кадр в такой схеме легко приводит либо к дублю, либо к пропуску.

Когда сценарий становится сложнее, явные std.Trim, std.Splice и std.SelectEvery иногда читаются лучше, чем компактные срезы. Особенно это относится к функциям, где нужно точно контролировать изменение duration. Хороший стиль скрипта — не минимальное число символов, а ясное отражение временной логики.

Для сравнения двух обработок полезен Interleave: кадры ветвей чередуются, и при покадровом просмотре можно переключаться между вариантами простым шагом вперёд. Но Interleave меняет эффективную частоту кадров, если включено автоматическое изменение duration. Такой сравнительный клип обычно служебный и не предназначен для финального кодирования.

Trim, Splice, SelectEvery и операции над временной осью

std.Trim выделяет диапазон, причём параметр last включительный. std.Splice последовательно соединяет несколько клипов. SelectEvery выбирает заданные смещения внутри повторяющегося цикла. Последняя функция полезна не только для простого отбора чётных/нечётных кадров, но и для детерминированной децимации, когда известен регулярный шаблон.

DuplicateFrames и DeleteFrames позволяют точечно дублировать или удалять кадры по номерам. Это не замена полноценному восстановлению временной структуры, но удобный инструмент для единичных дефектов. Номера относятся к входному клипу конкретной операции, поэтому при нескольких последовательных вставках и удалениях список индексов следует рассчитывать относительно правильного этапа графа.

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

AssumeFPS меняет заявленную скорость воспроизведения без интерполяции новых кадров. Поэтому он подходит для осознанного изменения тайминга, но не превращает 24 кадров в секунду в настоящие 60 fps. Если нужна интерполяция движения, её выполняет отдельный алгоритм, а затем timing приводится к целевому значению.

Геометрия кадра: Crop, AddBorders, Flip и Transpose

std.Crop удаляет заданное число пикселей по краям, CropAbs задаёт итоговое окно через размер и смещение. Для субдискретизированного YUV действуют ограничения выравнивания: например, в 4:2:0 нельзя произвольно обрезать одну хрома-координату так, чтобы сетка цветности стала некорректной. Ошибка о несоответствии subsampling обычно означает, что величину crop нужно привести к допустимому шагу или временно перейти в другой формат.

AddBorders добавляет поля заданного цвета. Это полезно для восстановления исходного размера после обрезки, создания canvas нужных размеров или подготовки изображения к фильтру, который плохо работает на краях. Цвет границы задаётся в координатах текущего формата, поэтому числовые значения для RGB и YUV интерпретируются по-разному.

Для отражений доступны FlipHorizontal и FlipVertical, а Transpose меняет оси изображения. Эти операции просты, но могут быть частью более сложной геометрической процедуры, например при повороте, обработке только одной ориентации или построении собственных тестовых шаблонов.

Важно разделять обрезку и масштабирование. Crop не интерполирует пиксели: он меняет окно кадра. Resize пересчитывает сетку и может одновременно конвертировать цвет. Если требуется точное кадрирование с дробным смещением относительно исходной сетки, удобнее использовать параметры source window у resizer, а не пытаться имитировать дробный crop целыми координатами.

Resize: масштабирование и преобразование цвета

Модуль resize делает значительно больше, чем изменение ширины и высоты. Он умеет преобразовывать семейства цвета, глубину, субдискретизацию, матрицу, transfer, primaries, range и chroma location. Поэтому один вызов часто заменяет отдельные шаги перевести в RGB, изменить размер и вернуть в YUV, хотя для сложной цепочки всё равно важно понимать, в каком пространстве выполняется каждая операция.

Основные resizer-функции включают Point, Bilinear, Bicubic, Lanczos, Spline16, Spline36 и Spline64. Универсально лучшего фильтра нет. Point сохраняет ближайший образец и полезен для масок или целочисленного пиксельного представления; Bilinear мягкий; Bicubic даёт управляемый компромисс; Lanczos и spline-варианты отличаются характером резкости и ringing. Для нейтральной отправной точки документация предлагает Bicubic.

Для Bicubic доступны параметры B и C, для Lanczos — число taps. Отдельно можно выбрать resampling для хромы. Это важно, когда яркостная и цветовые плоскости имеют разную структуру. Параметры src_left, src_top, src_width и src_height описывают исходное окно и позволяют делать субпиксельное смещение или совмещать crop с resize.

При конвертации между YUV и RGB матрица должна быть однозначно определена. Если frame property отсутствует или помечено как unspecified, появляются ошибки вроде невозможности найти путь между цветовыми пространствами. Тогда задают matrix_in или строковую форму matrix_in_s, а для выхода — matrix/matrix_s. Аналогично контролируются transfer, primaries, range и chroma location.

Параметр dither нужен при уменьшении разрядности. Просто отбрасывать младшие биты нежелательно: на плавных градиентах это усиливает полосы. Конкретная схема dithering зависит от версии модуля и задачи, но сам принцип постоянен — снижение глубины должно учитывать квантование. Если последующий кодировщик принимает 10- или 16-битный поток, часто разумнее сохранять повышенную точность до последнего этапа.

Цветовые свойства кадра и почему они важны

Frame properties — одна из фундаментальных особенностей VapourSynth. Помимо пользовательских ключей, есть зарезервированные свойства, описывающие цвет и временную структуру. Среди них — матрица, transfer characteristics, primaries, диапазон, chroma location и field structure. Фильтр может читать эти данные и принимать решение без дополнительного параметра в каждом вызове.

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

std.SetFrameProps добавляет или заменяет свойства на каждом кадре. CopyFrameProps переносит их с другого клипа, RemoveFrameProps удаляет выбранные ключи. Это полезно при фильтрах, которые потеряли метаданные, либо при построении ветви-маски, свойства которой не должны влиять на итоговый цвет.

Для полевой структуры существует удобный SetFieldBased. Значение 0 означает frame based, 1 — bottom field first, 2 — top field first. Если прогрессивный материал ошибочно помечен как interlaced, resizer может выполнять обработку по полям и давать нежелательный результат. В таком случае исправление свойства перед масштабированием — не косметика, а обязательная часть корректной обработки.

Плоскости YUV, RGB и GRAY

Многие фильтры позволяют указывать список плоскостей. В YUV плоскость 0 обычно является яркостью Y, 1 и 2 — U и V. В RGB индексы соответствуют цветовым каналам в порядке, определяемом форматом. Одноканальный GRAY удобен для масок, анализа яркости и промежуточных вычислений.

std.ShufflePlanes умеет извлекать плоскость, переставлять U/V и собирать новый многоканальный клип из нескольких источников. Например, можно получить Y как GRAY, обработать только яркость и затем вернуть её к неизменённой хроме. Это распространённый приём, когда фильтр предназначен для структуры изображения и не должен трогать цветовые каналы.

Но механическое обрабатывать только luma не всегда правильно. Некоторые артефакты присутствуют в хроме, а часть алгоритмов предполагает RGB. Кроме того, размеры U/V в 4:2:0 меньше яркостной плоскости, что влияет на допустимые операции. При сборке плоскостей нужно сохранять согласованную геометрию и понимать, какой клип задаёт свойства результата.

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

Merge, MaskedMerge и работа с масками

std.Merge смешивает два клипа с постоянным весом. Вес 0 оставляет первый клип, 1 выбирает второй, промежуточное значение даёт линейную смесь. Вес можно задавать по плоскостям, поэтому возможны сценарии вроде взять яркость из B, хрому из A. Размеры и формат входов должны совпадать.

std.MaskedMerge делает вес переменным для каждого пикселя. Маска определяет, где оставить clipa, а где подмешать clipb. Это базовый инструмент локальной обработки: один фильтр применяется ко всему кадру, затем по маске возвращаются нужные области. Такой подход проще контролировать, чем пытаться заставить сложный фильтр анализировать область интереса самостоятельно.

Маска может быть GRAY и использоваться для всех плоскостей. При несоответствии размеров она может быть автоматически приведена, но для точной работы лучше заранее понимать её геометрию. Особенно осторожно надо обращаться с premultiplied-режимом и диапазоном: смешивание full и limited без согласованной математики способно создать заметные сдвиги.

Классическая схема локальной фильтрации выглядит так: filtered = filter(src), затем result = MaskedMerge(src, filtered, mask). Для отладки полезно вывести mask отдельным индексом и визуально проверить, действительно ли белые области совпадают с зонами, которые должны измениться.

VapourSynth Multi-Viewer с вкладками source, mask и вариантами фильтрации для покадрового сравнения

Expr: пиксельная математика без отдельного плагина

std.Expr вычисляет выражение для каждого пикселя и может использовать до 26 входных клипов. Синтаксис построен на обратной польской записи. Это компактный, но требующий привычки способ описать арифметику, сравнения и условные операции. Expr применяют для масок, простых коррекций, комбинации каналов и тестовых преобразований.

Важная особенность: целочисленные значения не нормализуются автоматически. В 8 bit диапазон кодов обычно 0–255, в 10 bit — 0–1023. Поэтому выражение, написанное под фиксированную глубину, может вести себя иначе после перехода на другой формат. Для переносимых скриптов пороги рассчитывают относительно bit depth или предварительно приводят данные к ожидаемому типу.

Пустое выражение для плоскости может означать быстрое копирование соответствующей плоскости первого клипа. Это удобно, когда меняется только Y, а U/V должны пройти без вычислений. Но если формат выхода несовместим с быстрым копированием, нельзя рассчитывать на неопределённые данные — формат и выражения нужно выбирать осознанно.

Expr не заменяет специализированные фильтры, если нужна сложная окрестность, motion estimation или адаптивный анализ, но он хорошо подходит для небольших операций, ради которых не хочется вводить дополнительную зависимость. При этом длинные RPN-формулы быстро становятся нечитаемыми, поэтому их полезно строить из именованных фрагментов строк и снабжать комментариями.

Convolution, BoxBlur, Minimum и Maximum

Стандартный набор включает базовые пространственные операции. Convolution применяет ядро свёртки и позволяет реализовать простое размытие, резкость, выделение границ и другие линейные фильтры. Документация показывает типичный пример unsharp-подхода в связке Convolution, MakeDiff и MergeDiff.

BoxBlur даёт управляемое прямоугольное размытие. Морфологические функции вроде Minimum/Maximum, Inflate/Deflate полезны прежде всего при подготовке масок: ими расширяют или сужают области, убирают одиночные точки, подготавливают края к MaskedMerge. Для полноценного шумоподавления эти простые операции обычно недостаточны, но как вспомогательные узлы графа они незаменимы.

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

Если базовой свёртки мало, подключают сторонние плагины с SIMD, OpenCL, Vulkan или собственными оптимизированными ядрами. VapourSynth при этом остаётся диспетчером графа: он соединяет результаты фильтров независимо от того, реализованы они в стандартном модуле, нативной DLL/SO или Python-обёртке.

MakeDiff и MergeDiff для остатка и локальной резкости

std.MakeDiff вычисляет разность между двумя клипами и кодирует её в пригодной для дальнейшей обработки форме. MergeDiff добавляет этот остаток обратно. Связка полезна, когда требуется отдельно работать с высокочастотной составляющей или переносить разницу между этапами фильтрации.

Простой пример — нерезкая маска: создаётся размытую копия, вычисляется разница исходника и blur, затем эта разница возвращается. На практике качественная резкость требует защиты от halo и шума, поэтому такой пример скорее демонстрирует принцип. Сила подхода в том, что деталь становится отдельным клипом, который можно ограничить по плоскости или маске.

При смешивании diff нужно учитывать глубину и диапазон. Не стоит интерпретировать среднее значение diff как обычную серую картинку без понимания кодирования разности. Для визуального анализа удобнее использовать специализированное преобразование или текстовые/статистические инструменты.

PlaneStats и измерение различий

std.PlaneStats вычисляет минимум, максимум и среднее значение выбранной плоскости и записывает результаты в frame properties. Если передан второй клип, дополнительно рассчитывается нормализованная абсолютная разница. Полученные значения лежат в диапазоне 0–1 независимо от исходной глубины, что удобно для сравнительных условий.

PlaneStats не является полноценной метрикой качества: средняя абсолютная разница не описывает визуальную значимость изменения. Но как технический сигнал она полезна. Например, можно обнаруживать почти полностью чёрные кадры, выбирать ветвь обработки по яркости или проверять, действительно ли два этапа дают одинаковый результат.

Статистика особенно сильна в связке с FrameEval: один узел рассчитывает свойства кадра, а другой на их основе выбирает обработанный или исходный клип. Так строятся адаптивные сценарии без ручного перечисления номеров кадров.

FrameEval и условная обработка по кадрам

std.FrameEval вызывает Python-функцию для каждого кадра и ожидает, что функция вернёт клип, из которого будет взят текущий результат. Номер кадра передаётся как n. Через prop_src можно получить свойства текущих кадров из дополнительных узлов и на их основе принимать решение.

Это прямой способ построить условие на кадрах с признаком X использовать ветвь A, иначе B. Например, анализатор сцены или combing-фильтр может записать property, после чего FrameEval выберет отдельную обработку только для отмеченных кадров. Важно не выполнять внутри callback тяжёлую инициализацию, которую можно создать один раз заранее: функция вызывается многократно.

Аргумент clip_src позволяет указать клипы, на которые callback будет ссылаться. Это помогает кэшу и построению графа, потому что зависимости становятся явнее. Скрипт может работать и без такого указания, но для сложных динамических схем прозрачный граф легче оптимизировать и диагностировать.

Если требуется не выбрать другой клип, а изменить properties текущего кадра, предпочтительнее ModifyFrame. Такое разделение обязанностей делает сценарий понятнее: FrameEval — выбор результата, ModifyFrame — изменение метаданных или содержимого возвращаемого кадра при соблюдении ограничений формата.

ModifyFrame и пользовательские свойства

std.ModifyFrame передаёт callback номер кадра и один или несколько входных VideoFrame. Чтобы изменить frame properties, входной кадр копируют, затем меняют словарь props. Возвращаемый кадр должен соответствовать формату, который ожидает результирующий клип.

Пользовательские properties удобны для переноса результатов анализа через граф. Вместо отдельного массива Python можно пометить каждый кадр, например, флагом сцены, оценкой шума или служебным идентификатором. Затем другой фильтр прочитает это свойство. Для собственных ключей лучше использовать понятный префикс, чтобы не столкнуться с именами сторонних фильтров.

Зарезервированные свойства с подчёркиванием имеют определённую семантику и не должны заполняться произвольными значениями. Ошибочное значение colorimetry или field flag способно изменить работу downstream-фильтра. Пользовательские данные безопаснее хранить под собственными именами без ведущего подчёркивания.

Текстовые диагностические фильтры

Пространство core.text содержит средства вывода информации поверх кадра. Text печатает строку встроенным bitmap-шрифтом, ClipInfo показывает характеристики клипа, FrameNum — номер кадра, FrameProps — properties. Эти функции предназначены в первую очередь для диагностики, а не для художественного титрования.

FrameProps особенно полезен при цветовых проблемах и автоматических анализаторах. Вместо печати словаря в терминал можно видеть свойства прямо на соответствующем кадре предпросмотра. Так проще заметить, что _FieldBased, matrix или scene flag меняются на конкретном участке.

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

VapourSynth Editor в Linux со скриптом интерполяции и журналом с параметрами выходного клипа

Многопоточность и планировщик

VapourSynth изначально рассчитан на многопоточную обработку на уровне кадров. Core распределяет независимые запросы по рабочим потокам, а фильтры объявляют режим взаимодействия с параллельными вызовами. Пользователю обычно не нужно вручную разбивать ролик на части, чтобы загрузить процессор.

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

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

Нельзя делать вывод о производительности только по скорости одного кадра в предпросмотре. Preview часто запрашивает кадры нерегулярно, а полный encode идёт последовательнее и лучше использует pipeline. Для оценки именно фильтрационной цепочки полезнее прогон без записи выходных кадров или режим измерения filter time в VSPipe.

Кэш: зачем он нужен и когда мешает

У каждого узла может быть кэш. Он помогает, если один и тот же кадр нужен нескольким downstream-фильтрам или если временной алгоритм повторно запрашивает соседние кадры. Однако кэш потребляет память, а 4K 16-bit кадры и сложные временные структуры быстро превращают несколько десятков кадров в гигабайты.

Core предоставляет общий предел кэша, а std.SetVideoCache позволяет переопределить автоматическое поведение для конкретного узла. Режим -1 возвращает автоматические настройки, 0 отключает, 1 принудительно включает кэш. Без понимания графа лучше не расставлять принудительные кэши повсюду: это может удерживать в памяти данные, которые планировщик сам бы быстро освободил.

При нехватке памяти полезно искать не только слишком большой max_cache_size. Часть памяти занята рабочими буферами фильтров и не относится к сохранённым кадрам кэша. Временной degrain может держать несколько reference frames на каждый активный поток, поэтому снижение числа одновременно выполняемых запросов иногда эффективнее уменьшения формального лимита кэша.

Для диагностики в API доступны значения текущего использования, а некоторые редакторы отображают статистику Core. Но цифра кэша не равна полному resident set процесса. Если система завершается по OOM, контролируют оба уровня: настройки VapourSynth и фактическое потребление процесса в диспетчере задач или системном мониторе.

Предпросмотр: VapourSynth Editor, vspreview и другие оболочки

У движка нет единственного обязательного GUI. Скрипт можно писать в любом редакторе, а просматривать через программу, которая умеет загрузить VSScript. Официальная документация перечисляет VapourSynth Editor, VapourSynth Editor 2, vspreview, vspreview-rs, VirtualDub2 и другие приложения. Выбор оболочки не меняет смысл самого .vpy, но влияет на удобство навигации, сравнения выходов и просмотра properties.

VapourSynth Editor объединяет редактор кода, log и быстрый preview. В его типичном меню Script есть команды Preview, Check script, Benchmark, Encode video, очередь заданий и окно jobs. Это удобно для обучения и коротких рабочих сценариев: ошибка Python видна в нижней панели, а результат открывается без отдельной командной строки.

vspreview ориентирован именно на визуальную проверку. Он запускается с .vpy, умеет интегрироваться с внешним редактором и расширяться собственными плагинами предпросмотра. Для сложной фильтрации полезны функции сохранения кадров, переходы по сценам, сравнение outputs и просмотр свойств. Конкретный набор зависит от версии previewer, поэтому статья о VapourSynth не должна приписывать эти GUI-возможности самому Core.

Multi-Viewer и похожие инструменты ценны при сравнении нескольких вариантов. Вместо постоянного редактирования одного параметра можно вывести набор ветвей и переключаться между ними на одинаковом кадре. Такой способ особенно полезен для тонкой настройки deband, denoise, sharpen и rescale, где разницу лучше оценивать на нескольких типах сцен.

Проверка скрипта и чтение ошибок

Python-ошибка обычно содержит traceback: путь к скрипту, номер строки и сообщение. Начинать нужно с последней строки, где указан тип проблемы, а затем подниматься к месту вызова. Ошибка AttributeError у namespace часто означает отсутствующий плагин; TypeError — неверный тип или имя аргумента; ошибки resize — недостаточные или конфликтующие параметры цвета.

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

При нестабильной ошибке сначала упрощают граф. Временно назначают выходом source, затем добавляют этапы по одному до появления проблемы. После этого проверяют формат перед фильтром, размеры, properties и зависимости. Такой бинарный поиск эффективнее, чем менять случайные параметры в длинном скрипте.

VSPipe: вывод скрипта в файл или канал

VSPipe оценивает .vpy и выдаёт выбранный output. Базовый синтаксис состоит из пути к скрипту и назначения. Если назначение — дефис, кадры пишутся в стандартный вывод; двойной дефис запускает обработку без вывода самих кадров. Последний вариант удобен для benchmark и проверки, потому что исключает скорость диска и кодировщика.

Опция --info показывает параметры выходного клипа и завершает работу. --start и --end ограничивают диапазон кадров, --outputindex выбирает индекс, --requests задаёт число конкурентных запросов. --progress печатает прогресс в stderr, чтобы не смешивать его с бинарными данными stdout.

--container y4m добавляет Y4M-заголовок к видеопотоку, wav или w64 — соответствующий заголовок к аудио. Y4M удобен для x264, x265, FFmpeg и других программ, понимающих этот простой transport. Но Y4M имеет собственные ограничения по форматам и метаданным, поэтому для экзотических цветов, alpha или специальных форматов может потребоваться другой способ передачи.

Опция записи timecodes нужна для VFR. Graph-режим выводит граф узлов в dot-формате, а filter-time помогает увидеть, какие фильтры занимают время при полном прогоне. Это не синтетический benchmark процессора, а профиль конкретного скрипта, поэтому он полезен при поиске реального узкого места.

Передача видео в FFmpeg, x264 и x265

Классическая схема использует pipe: VSPipe пишет Y4M в stdout, кодировщик читает stdin. Для FFmpeg вход обозначается дефисом, после чего указываются обычные параметры кодека и контейнера. Важно следить за quoting путей и за тем, чтобы никакой Python-модуль не печатал произвольный текст в stdout: лишние байты разрушат поток Y4M.

x264 и x265 могут принимать Y4M непосредственно. В этом случае VapourSynth отвечает только за фильтрацию, а параметры rate control, profile, GOP, B-frames и прочие настройки кодека остаются полностью в кодировщике. Это хорошее архитектурное разделение: скрипт отвечает за пиксели и timing, encoder — за компрессию.

При сложном пайплайне стоит сохранять параметры кодирования отдельно от фильтра. Тогда один .vpy можно тестировать разными кодеками без копирования графа обработки. И наоборот, один preset кодировщика можно применять к нескольким сценариям, если выходные форматы согласованы.

На Windows можно использовать именованный канал, чтобы отделить бинарный поток от stdout и снизить риск вмешательства библиотек, которые печатают служебные сообщения. Это особенно полезно в Python-средах с модулями машинного обучения, где часть зависимостей пишет banners и warnings напрямую в стандартный вывод.

Диалог Encode в VapourSynth Editor с Y4M, FFmpeg, аргументами и индикатором прогресса

Аудио в VapourSynth

Современный API поддерживает AudioNode и аудиокадры. Source-плагин может выдавать аудиотрек отдельным клипом, после чего доступны базовые операции: Trim, Splice, Reverse, Loop, Gain и Mix. Аудио не обязано сопровождать video node — это независимый output, который можно зарегистрировать под своим индексом.

AudioGain изменяет уровень всех каналов одним коэффициентом или принимает отдельные значения. Для integer-форматов слишком большой gain приводит к clipping; опция overflow check позволяет превратить обнаружение клиппинга в ошибку вместо предупреждения. AudioMix принимает матрицу коэффициентов и может выполнять downmix или перестройку каналов.

AudioTrim работает в сэмплах, а не в видеокадрах. Это важно при синхронном монтаже: нельзя механически перенести номер видеокадра в audio trim без пересчёта по sample rate и timing. Если требуется вырезать одинаковый временной участок, удобнее сначала вычислить границы во времени, а затем перевести их в единицы каждого потока.

VSPipe способен выдавать WAV/WAVE64-заголовок. В практической схеме видео и аудио нередко кодируются раздельно, после чего мультиплексируются в один контейнер. Это не недостаток, а следствие того, что фреймсервер оперирует независимыми outputs и не является muxer.

Установка и рабочее Python-окружение

Для актуальной схемы установки нужен Python 3.12 или новее. Рекомендуемый путь — установить пакет VapourSynth через pip и затем выполнить команду конфигурации vapoursynth config. Бинарные wheels рассчитаны на Windows, Linux и macOS, поэтому отдельная ручная сборка ядра требуется не во всех сценариях.

После установки полезно проверить импорт: открыть Python, импортировать core и вывести его строковое представление. Если это работает, Python-модуль видит библиотеку и может создать Core. Затем проверяют vspipe --version. Эти две проверки отделяют проблемы Python import от проблем конкретного editor или source-плагина.

Виртуальные окружения особенно полезны для VapourSynth, потому что позволяют держать отдельный набор Python-зависимостей под проект. При этом приложения, использующие VSScript извне, должны находить библиотеку именно из нужного окружения. Для этого предусмотрена регистрация пути VSScript. Если editor видит не тот Python, появляется характерная ситуация: скрипт запускается из терминала, но не открывается в previewer.

На Windows для некоторых сборок требуется актуальный Microsoft Visual C++ Redistributable. Если нативный плагин не загружается без ясного Python-traceback, проверяют его зависимые DLL. На Linux аналогичная проблема проявляется как отсутствующая shared library или несовместимая версия системной зависимости.

Каталог плагинов и VSRepo

VapourSynth развивается как экосистема: source-фильтры, deinterlace, denoise, deband, rescale, subtitle rendering и другие функции часто поставляются отдельно. Для поиска пакетов используется каталог сообщества, а VSRepo умеет устанавливать известные плагины и скрипты по идентификатору. В новой схеме установки VSRepo ставится отдельно через pip.

Не все плагины одинаково упакованы. Нативный модуль может состоять из DLL/SO и зависимостей, Python-библиотека — из пакета и дополнительных wheels, ML-фильтр — ещё и из моделей. Поэтому команда установки не освобождает от чтения требований конкретного проекта. Особенно это касается GPU-бэкендов, которым нужны драйверы, CUDA, Vulkan или другие runtime-компоненты.

Автозагрузка ищет нативные плагины в каталоге пакета VapourSynth. Дополнительный путь можно задать переменной окружения. При конфликте нескольких сборок одного namespace лучше временно отключить лишние копии: порядок автозагрузки не должен становиться скрытым механизмом выбора версии.

Для воспроизводимого проекта полезно хранить список Python-пакетов и заметку о нативных модулях рядом с .vpy. Тогда через несколько месяцев понятно, откуда берётся core.knlm, core.fmtc или другой namespace и какие внешние компоненты нужны. Один скрипт без перечня зависимостей часто недостаточен для повторения результата на другом ПК.

Типичные source-плагины

BestSource удобен как универсальный современный источник с видео- и аудиофункциями. Его часто выбирают для контейнеров, которые поддерживает FFmpeg, когда нужен случайный доступ и единый интерфейс. FFMS2 также строит индекс и предоставляет доступ к видео и аудио; он давно используется в сценариях с большим количеством форматов.

L-SMASH-Works включает источники для контейнеров на базе libavformat/L-SMASH и широко применяется с MP4/MOV/MKV и другими файлами. d2vsource работает с индексами, созданными D2V Witch, и востребован в workflow, где необходимо воспроизводимо разобрать транспортный поток, телесин или дисковый источник до фильтрации.

При одинаковом файле source-плагины могут различаться обработкой VFR, repeat flags, damaged timestamps и выбора декодера. Поэтому какой источник лучший не имеет универсального ответа. Для ответственной обработки проверяют именно тот материал, с которым предстоит работать, и фиксируют выбранный способ в шаблоне проекта.

Деинтерлейсинг и field order

Перед деинтерлейсингом нужно установить, действительно ли материал interlaced и какой у него порядок полей. Неверный TFF/BFF приводит к характерным рывкам: поля восстанавливаются в неправильной временной последовательности. Frame property _FieldBased служит для передачи этой информации между source, resize и фильтрами.

Встроенный resize.Bob может выполнять простой bob и полезен как диагностический вариант, но для качественного результата чаще используются специализированные плагины и скрипты. QTGMC, Bwdif, yadif-подобные реализации и motion-compensated решения имеют разные требования к формату и скорости. Нельзя приписывать их ядру VapourSynth: это внешние компоненты.

Для телесина задача другая: исходник может быть закодирован как 29.97 interlaced, но содержать повторяющийся каденс прогрессивных 23.976 кадров. В таком случае обычный bob не восстанавливает исходную структуру. Применяют field matching, decimation и анализ каденса, а такие инструменты, как Wobbly, помогают вручную разобрать проблемные участки.

После обработки проверяют не только отсутствие combing, но и временную плавность. Статичный кадр может выглядеть идеально даже при неправильном field order. Для контроля выбирают панорамы, быстрое движение и участки с тонкими диагональными деталями.

Шумоподавление, дебандинг и работа с зерном

Шумоподавление в VapourSynth обычно строится из сторонних фильтров. Есть пространственные, временные и motion-compensated алгоритмы, а также нейросетевые решения. Выбор зависит от типа материала: плёночное зерно, хрома-шум, блоки компрессии и цифровой сенсорный шум требуют разных стратегий.

Хорошая цепочка не сводится к максимально убрать шум. Слишком сильный temporal denoise уничтожает мелкое движение и текстуры, после чего кодировщик действительно получает более простой сигнал, но визуальная ценность падает. Поэтому часто создают маску деталей, сравнивают несколько уровней силы и при необходимости возвращают контролируемое зерно после основной очистки.

Deband работает с другим дефектом — дискретными ступенями на градиентах. Он может требовать маски краёв, чтобы не размягчать реальные контуры. После deband нередко добавляют слабый dither или grain: это маскирует квантование и помогает сохранить визуально гладкий градиент после повторного сжатия.

В VapourSynth удобно разделять эти стадии на именованные ветви. Например, denoised, debanded, grained. Тогда каждую можно поставить отдельным output и проверить в одном и том же кадре. Это быстрее и надёжнее, чем менять скрипт и пытаться запомнить, как выглядел предыдущий вариант.

Масштабирование, descale и восстановление линий

Обычный resize меняет размер текущего растрового кадра. Descale решает более специфическую задачу: пытается обратить известное или предполагаемое предыдущее масштабирование и восстановить меньшую сетку, с которой материал был увеличен. Такой подход особенно востребован для анимации и цифровых источников, которые прошли апскейл до конечного master-размера.

Descale не является встроенной функцией стандартного resizer. Его реализуют отдельные плагины и вспомогательные библиотеки. Ключевой риск — выбрать неверный исходный размер или kernel. Тогда результат может показывать ringing, потерю линий и остаточную ошибку. Поэтому типичная схема включает анализ нескольких кандидатов и маскирование областей, которые нельзя уверенно descale.

После восстановления меньшей сетки выполняется upscale. Для этого применяют классические resizer, NNEDI-подобные фильтры, edge-directed методы или ML-модели. VapourSynth позволяет комбинировать их в одном графе: например, линии восстановить одним методом, текстуры — другим, затем смешать по маске.

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

Интерполяция кадров и изменение частоты

VapourSynth часто используется как хост для MVTools, SVP-подобных фильтров, RIFE и других алгоритмов интерполяции. Общая схема одинакова: source приводят к формату, который принимает фильтр, алгоритм создаёт промежуточные кадры, затем clip переводят в формат кодировщика и выставляют корректный timing.

Оптический поток и motion vectors ошибаются на окклюзиях, вспышках, резких сменах сцен, мелких периодических текстурах и анимационных smear-кадрах. Поэтому параметр 60 fps не гарантирует качественный результат. Хороший скрипт включает scene-change detection и на границе сцены не пытается строить кадр между несвязанными изображениями.

Нейросетевые интерполяторы часто требуют RGB float и GPU-бэкенда. Это повышает стоимость цветовых преобразований и памяти. При batch-кодировании имеет смысл заранее оценить, где узкое место: декодирование, преобразование YUV→RGB, сама модель, обратный resize или кодировщик.

Панель VapourSynth в Hybrid с настройками интерполяции кадров и параметрами SVP

Субтитры и текстовые оверлеи

Для burn-in субтитров используют отдельные рендереры ASS/SSA или библиотеки сценариев. Они принимают clip, файл субтитров и, при необходимости, каталог шрифтов. Важно понимать порядок: если после рендера снова сильно масштабировать или менять chroma subsampling, контуры текста могут стать мягче. Обычно субтитры накладывают ближе к финальному размеру.

При HDR и нестандартном цвете overlay требует особой осторожности. Рендерер может ожидать определённый диапазон и transfer. Если на вход подать linear или PQ без учёта требований, белый текст и его alpha будут выглядеть неправильно. Для таких сценариев лучше явно контролировать рабочее пространство до и после рендеринга.

Диагностический core.text.Text не заменяет subtitle renderer: его шрифт и набор символов ограничены, нет сложной разметки, позиционирования ASS, обводок и style engine. Он предназначен для служебных подписей и проверки.

Сравнение исходника и результата

Для фильтрации критично сравнивать одинаковый кадр. Самый простой способ — зарегистрировать source и filtered как разные outputs. Если previewer умеет переключать output index без переоценки всего скрипта, можно быстро проверять детали. Другой вариант — Interleave, когда A0 и B0 следуют друг за другом.

Полезно также делать difference-ветвь. Она показывает, где именно фильтр изменил изображение. Но яркая diff-карта не всегда означает плохое изменение: любая корректная смена яркости или grain даст большую разницу. Difference применяется для локализации эффекта, а окончательное решение принимается по исходнику и результату.

Для масок удобно выводить их рядом с итогом. Если фильтр применяется по MaskedMerge, смотрят как минимум три outputs: source, processed, mask. При сложной схеме добавляют финальный merge. Такой набор быстро показывает, ошибка ли в самом фильтре или в маске, которая пропускает не те области.

Практический сценарий: обрезка, resize и кодирование

Самый понятный pipeline начинается с source и минимального числа преобразований. Сначала открывают файл и проверяют длину, размеры, fps, формат и color properties. Затем делают crop только там, где он действительно нужен. После этого resize приводит изображение к целевому размеру и, при необходимости, к формату YUV420P10 или другому формату кодировщика.

Перед финальным выводом полезно временно добавить ClipInfo или открыть info в VSPipe. Если ожидалось 1920×1080 24000/1001, а выход показывает другой размер или fps, проблема обнаруживается до многочасового кодирования. Затем служебный текст удаляют и оставляют чистый clip как output.

Для FFmpeg VSPipe запускается с Y4M-контейнером, а FFmpeg читает stdin. Аудио можно взять непосредственно из исходного файла в FFmpeg или подготовить отдельным output VapourSynth. Выбор зависит от того, требовалась ли аудиообработка. Если звук не менялся, повторное декодирование через отдельную цепочку часто не даёт преимуществ.

Практический сценарий: локальный ремонт нескольких кадров

Предположим, на коротком участке нужен более сильный denoise или другой deinterlace. Сначала создаётся общая ветвь base. Затем выделяется диапазон с помощью среза, к нему применяется альтернативный фильтр, и клип собирается как base[:start] + fixed + base[end:]. Так остальная часть ролика проходит через прежний pipeline без изменений.

Если участок должен совпадать по длине, format, resolution и timing, это проверяют до splice. Ошибка формата часто возникает, когда локальный фильтр возвращает RGB или другой bit depth. Тогда его приводят обратно к формату base. Для нескольких несвязанных участков лучше описать функцию repair и список диапазонов, чем вручную создавать десятки переменных.

При большом числе исправлений важен порядок индексов. Если сначала удалить кадры, последующие номера смещаются. Поэтому temporal repairs обычно делают на оригинальной нумерации либо заранее строят карту преобразований. Комментарий с причиной каждого патча полезнее загадочного списка чисел.

Практический сценарий: две версии фильтра для сравнения

Одна из сильных сторон скриптового подхода — возможность вычислять несколько вариантов от общего source. Например, a = denoise(src, strength=1) и b = denoise(src, strength=2). Оба клипа регистрируются как разные outputs, а маска и source — дополнительные индексы.

Главное преимущество такого метода в том, что остальная часть графа идентична. Сравнение действительно относится к одному параметру, а не к двум скриптам, где случайно отличаются crop, matrix или grain. Для сложного теста можно вынести фильтрацию в функцию и передавать только набор параметров.

Если просмотрщик поддерживает сохранение кадров, выбирают репрезентативные сцены: тёмный градиент, лицо с мелкими деталями, быстрое движение, grain и графику. Один красивый кадр не показывает temporal artifacts, поэтому для временных фильтров обязательно просматривают движение, а не только стоп-кадр.

Практический сценарий: обработка только яркости

Когда алгоритм должен воздействовать только на структуру яркости, Y отделяют через ShufflePlanes в GRAY, фильтруют и собирают обратно с исходными U/V. Это экономит вычисления и не трогает цветность. Подход уместен для некоторых sharpen, edge mask и detail processing.

Но перед такой оптимизацией нужно проверить требования фильтра. Если плагин рассчитан на RGB или сам использует хрома для оценки движения, отделение Y меняет алгоритм. Также следует учитывать, что Y в нелинейном YUV не является физической luminance; это luma-сигнал конкретной матрицы.

При сборке плоскостей важно сохранить frame properties исходника. После служебных GRAY-ветвей можно использовать подходящий prop_src или копирование properties, чтобы финальный clip не потерял colorimetry.

Практический сценарий: адаптивная ветвь по статистике

Предположим, на очень тёмных кадрах агрессивный sharpen создаёт шум. Сначала PlaneStats вычисляет среднюю яркость. Затем FrameEval читает property и выбирает исходную ветвь ниже порога, а sharpened — выше него. В результате решение принимается отдельно для каждого кадра и не требует ручного списка сцен.

Такую автоматику нельзя строить на случайном пороге без проверки. Среднее значение кадра не различает ночную сцену с яркими деталями и ровную серую заливку одинакового average. Если условие критично, комбинируют несколько признаков или используют специализированный анализатор.

Для плавности перехода между режимами можно не переключать ветви жёстко, а вычислять вес и применять Merge. Тогда небольшое изменение статистики не вызывает резкого temporal jump. Эта техника особенно полезна для адаптивной силы grain или detail enhancement.

Как читать сообщения Resize

Ошибка no path between colorspaces почти всегда указывает, что для преобразования не хватает данных о цвете. Например, YUV-клип имеет unspecified matrix, а пользователь просит RGB. Решение — определить фактическую матрицу источника и указать matrix_in_s или корректно установить frame property до resize.

Ошибка о subsampling возникает, когда ширина, высота, crop или source window несовместимы с форматом. Для YUV420 размеры хрома делятся по обеим осям, поэтому нечётная итоговая геометрия может быть невозможна. Варианты решения: изменить crop до допустимого, временно перейти в 4:4:4/GRAY или выбрать другой итоговый формат.

Слишком тёмный или вымытый результат после RGB↔YUV часто связан с range: full и limited интерпретированы не так, как ожидалось. Это не исправляется случайным повышением contrast. Сначала проверяют range_in/range и исходные properties.

Неожиданный цветовой оттенок после ресайза может указывать на неверную matrix или primaries, а не на сам kernel. Поэтому диагностика цвета начинается с metadata, а не с замены Bicubic на Lanczos.

Ошибки загрузки плагинов

Если namespace отсутствует, проверяют фактический каталог автозагрузки через Python-функцию получения plugin dir. Это особенно важно после перехода на pip-ориентированную структуру: старый каталог может содержать DLL, но Core его больше не просматривает. Копирование ещё одной DLL в случайное место только усложняет проблему.

Если LoadPlugin сообщает, что модуль не может быть загружен, причины обычно три: неправильная архитектура, отсутствующая зависимость или несовместимый VapourSynth API. На Windows зависимость можно проверить системными средствами анализа DLL, на Linux — динамическим linker. Путь к самому plugin-файлу может быть правильным, но ошибка фактически относится к библиотеке, которую тот импортирует.

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

Почему скрипт работает в терминале, но не в редакторе

Чаще всего терминал и редактор используют разные Python/VSScript окружения. В одном установлен VapourSynth и плагины, в другом — нет. Проверка начинается с пути Python, затем с vapoursynth.get_vsscript() и plugin directory. В редакторах, умеющих выбирать VSScript, указывают библиотеку из нужного окружения.

Вторая причина — рабочий каталог. Относительный путь input.mkv разрешается относительно current working directory процесса, а он у editor может отличаться от каталога скрипта. Надёжнее строить пути от __file__ или использовать абсолютный путь для диагностики. После того как проблема понятна, можно вернуть переносимую схему.

Третья причина — переменные окружения. PATH, дополнительные plugin paths, CUDA/Vulkan runtime и пользовательские переменные могут быть заданы только в shell profile. GUI-приложение, запущенное из меню, их не наследует. В таком случае лучше настроить системное окружение или запускать editor из подготовленного environment.

Почему предпросмотр медленный, а encode быстрый — или наоборот

Preview делает случайные запросы и часто отменяет их при перемотке. Temporal filter, который хорошо работает на последовательном потоке, может постоянно перестраивать контекст на произвольных кадрах. Поэтому медленный seek не обязательно означает низкую среднюю скорость полного encode.

Обратная ситуация возникает, когда preview показывает один кадр и кажется быстрым, а полный encode упирается в тяжёлый фильтр на каждом кадре. Для оценки длинного задания используют benchmark диапазона, близкого по содержимому к реальному ролику, и смотрят устойчивую скорость после прогрева кэша.

Некоторые ML-фильтры имеют дорогую инициализацию модели. Первый кадр обрабатывается долго, последующие — быстрее. Если каждый preview-процесс заново загружает модель, интерактивность будет хуже, чем throughput длительного encode. Это свойство конкретного plugin backend, а не VapourSynth как такового.

Память, 4K и тяжёлые временные фильтры

Размер необжатого кадра намного больше его размера в H.264/H.265. 4K RGB float может занимать десятки мегабайт, а temporal filter держит несколько таких кадров. Если одновременно выполняются 16 запросов, рабочий набор быстро становится огромным. Поэтому параметры threads и cache нужно оценивать вместе с форматом между фильтрами.

Один из лучших способов снизить память — не переходить в тяжёлый формат раньше времени. Если фильтр работает в YUV420P10, нет смысла держать всю цепочку в RGBS только потому, что один поздний ML-плагин его требует. Конвертацию ставят непосредственно перед таким этапом и по возможности возвращаются к более компактному формату после.

Вторая мера — уменьшить параллелизм. Третья — не кэшировать узлы принудительно без причины. Четвёртая — проверить, не создаёт ли скрипт несколько почти одинаковых тяжёлых ветвей ради предпросмотра. Для финального encode ненужные outputs можно убрать, чтобы они не удерживали зависимости в графе.

VFR, timecodes и синхронизация

Переменная частота кадров требует различать номер кадра и время. Кадр 1000 не обязан находиться в моменте 1000 / fps, если длительности кадров различаются. VapourSynth хранит временную информацию через свойства и рациональные значения duration, а VSPipe умеет сохранить timecodes в отдельный файл.

Фильтры, меняющие порядок или количество кадров, должны корректно менять duration. В стандартных функциях для этого есть modify_duration. Если его отключить без последующего ручного исправления, визуальная последовательность может быть верной, а timing — нет.

При передаче VFR в кодировщик нужно убедиться, что весь downstream-путь умеет сохранить временные метки. Простой Y4M-поток сам по себе ориентирован на регулярную последовательность; timecodes передаются отдельно. Неподдерживающий VFR этап может неявно превратить поток в CFR, что приведёт к рассинхронизации с аудио.

Работа с переменным форматом и размером

VapourSynth способен представить clip, у которого формат или размер меняется между кадрами. Это полезно для некоторых необычных источников и аналитических задач. Однако большинство фильтров ожидает постоянную геометрию, поэтому variable clip часто приводят к fixed format через resize.

Если clip.format равен None, это не обязательно ошибка: формат просто нельзя описать одним объектом. Но попытка передать такой clip в фильтр, требующий фиксированный формат, завершится ошибкой. Аналогично некоторые previewer и encoders не знают, как представить динамический размер.

Стандартный resize специально умеет принимать variable input и выдавать постоянный формат/размер, если нужные параметры заданы. Это удобная точка нормализации перед остальной цепочкой.

Граф фильтров и поиск узкого места

VSPipe может вывести граф output node в DOT-формате. Для длинного скрипта это помогает увидеть, какие ветви действительно соединены с финальным выходом, где произошло дублирование и какие узлы используются повторно. Граф особенно полезен после рефакторинга функций, когда код выглядит линейно, а фактическая структура значительно сложнее.

Filter timing показывает время, проведённое в каждом фильтре. Интерпретировать его следует осторожно: из-за многопоточности суммы могут не совпадать с wall-clock, а shared nodes обслуживают несколько ветвей. Но при сравнении двух вариантов скрипта он помогает понять, ускорил ли новый source, resizer или denoise нужный участок.

Оптимизация должна сохранять результат. Если более быстрый вариант использует другой алгоритм или снижает bit depth, это уже не чистая оптимизация. Для честного сравнения сначала фиксируют одинаковые outputs и только потом оценивают скорость.

Организация больших скриптов

Как только .vpy выходит за несколько десятков строк, стоит выносить повторяющиеся блоки в функции. Хорошая функция принимает clip и явные параметры, возвращает новый clip и не зависит от глобальных переменных без необходимости. Это делает graph более читаемым и позволяет повторно использовать фильтрацию для нескольких источников.

Пути, crop, параметры source и encode лучше хранить отдельно от логики фильтра. Тогда один и тот же pipeline легко перенести на серию файлов. Для batch-процесса аргументы можно передавать в VSPipe через --arg key=value; они появляются в globals скрипта как строки. Скрипт преобразует их к нужным типам и проверяет допустимость.

Нужно избегать магических чисел без комментария. Порог 18 в маске ничего не говорит через полгода; переменная edge_threshold = 18 с коротким пояснением говорит. То же относится к matrix id: строковое имя вроде matrix_s="709" обычно читается лучше числового кода.

Для воспроизводимости полезно фиксировать версии критичных Python-пакетов, но саму статью о программе не нужно превращать в каталог веток. В рабочем проекте достаточно файла зависимостей и комментариев для тех нативных плагинов, которые не ставятся через обычный package manager.

Использование VapourSynth с mpv

Один вариант — отправить Y4M из VSPipe в mpv и использовать проигрыватель как viewer. Это простой способ проверить полный выход без специального editor. Ограничение канала в том, что плеер не управляет исходным source напрямую: seek и повторные запросы зависят от того, как построен pipeline.

Другой вариант — встроенная поддержка VapourSynth в сборке mpv/FFmpeg и применение .vpy как видеофильтра во время воспроизведения. Такой сценарий подходит для realtime interpolation или обработки, если система успевает рассчитывать каждый кадр быстрее времени показа. Совместимость зависит от сборки mpv и установленных плагинов.

Realtime предъявляет более жёсткие требования, чем offline encode: пропущенный deadline виден как рывок. Поэтому фильтр, который даёт 45 fps на 24p source, может быть достаточен для обработки 24p, но не для вывода 60 fps после интерполяции. Для realtime оценивают скорость всей цепочки в целевом fps.

Интеграция со StaxRip и Hybrid

Графические кодирующие оболочки могут генерировать и выполнять VapourSynth-скрипты под выбранные фильтры. Это снижает порог входа: crop, resize, denoise или deinterlace выбираются в GUI, а программа формирует .vpy и передаёт его кодировщику. При сложной диагностике полезно открыть сгенерированный script и увидеть фактический порядок операций.

Важно не смешивать возможности оболочки и движка. Если Hybrid предлагает конкретную панель interframe или StaxRip умеет автоматически ставить плагин, это функция frontend. Сам VapourSynth не получает такую кнопку в другом редакторе. Поэтому перенос рецепта из GUI в чистый .vpy требует определить, какие плагины и параметры оболочка использовала внутри.

Редактор VapourSynth-скрипта в кодирующей оболочке с вызовами SVP и параметрами интерполяции

Что VapourSynth не делает сам

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

VapourSynth также не заменяет полноценный кодировщик. Он готовит кадры, но решения о битрейте, CRF, VBV, GOP, profile и muxing принадлежат x264/x265/FFmpeg и другим внешним инструментам. Ошибка слишком большой файл обычно решается настройкой encoder, а не фильтрами Core.

Он не содержит все популярные фильтры после базовой установки. Если в примере встречаются QTGMC, BM3D, MVTools, RIFE, placebo, fmtconv или descale, это внешние зависимости. Сила экосистемы именно в расширяемости, но цена — необходимость управлять окружением.

Levels, Limiter и тоновая математика

std.Levels выполняет явное преобразование диапазона и gamma для выбранных плоскостей. Параметры min_in и max_in задают участок входных значений, который переводится в min_outmax_out. Это полезно для технических тестов, простой коррекции и нормализации числового диапазона, но не заменяет полноценное управление цветом через transfer functions.

Особенно осторожно Levels применяют к YUV. Luma и chroma имеют разные номинальные диапазоны в limited-сигнале, а для float-форматов поведение по умолчанию может оказаться не тем, которое ожидается интуитивно. Если цель — корректно преобразовать limited YUV в full RGB, предпочтительнее описать исходный range и matrix в resize, чтобы операция выполнялась как цветовое преобразование, а не как независимое растяжение чисел по плоскостям.

std.Limiter ограничивает значения снизу и сверху. Он полезен после арифметики, которая способна создать выбросы, но его нельзя использовать как универсальное средство починить уровни. Клиппинг уничтожает информацию. Если значения систематически выходят за пределы из-за неверно выбранного range или ошибочной матрицы, сначала исправляют причину, а не зажимают результат.

При float-обработке допустимы значения за обычным отображаемым диапазоном, если следующий этап сознательно умеет с ними работать. Поэтому автоматический limiter в середине линейного HDR-pipeline может быть вреден. В скрипте полезно различать технические границы формата, legal range для конкретного представления и творческую тоновую компрессию — это разные операции.

Alpha, ClipToProp и отдельная прозрачность

VapourSynth не требует хранить alpha как четвёртую плоскость обычного RGB-клипа. Прозрачность может существовать отдельным GRAY-клипом. Метод set_output() позволяет зарегистрировать видео вместе с дополнительным alpha output, а VSPipe учитывает такую связку при поддерживаемом способе вывода.

std.ClipToProp умеет вложить кадр маски или alpha в frame property другого клипа, по умолчанию под именем _Alpha. Обратная функция PropToClip извлекает этот clip обратно. Это удобно, когда геометрическая операция должна перемещать основной кадр и связанную с ним маску как логическую пару.

Для корректной математики полупрозрачных изображений есть PreMultiply. Он умножает цвет на alpha с учётом диапазона и подготавливает данные к операциям, где premultiplied representation уменьшает артефакты на полупрозрачных границах. Особенно это заметно при resize: обычное масштабирование непремультиплированного RGB может подтянуть цвет полностью прозрачных пикселей в видимую кромку.

Если маска используется не как настоящая alpha, лучше не помещать её в зарезервированный _Alpha без причины. Служебные маски можно хранить под пользовательским property или отдельным output. Это снижает риск, что downstream-программа интерпретирует аналитическую маску как прозрачность.

Прямой доступ к данным кадра из Python

Хотя типичный скрипт соединяет готовые фильтры, Python API позволяет читать сами плоскости кадра. После clip.get_frame(n) объект VideoFrame даёт размеры, формат, properties и доступ к данным. Плоскость можно получить как memoryview через индексирование кадра, а низкоуровневые методы предоставляют указатель и stride.

Stride означает реальное расстояние в байтах между соседними строками. Оно не обязано равняться width × bytes_per_sample, потому что строки могут иметь выравнивание. Поэтому код, который последовательно читает память как плотный прямоугольник без учёта stride, потенциально неверен. Для безопасного последовательного экспорта существует readchunks(), который выдаёт гарантированно непрерывные фрагменты.

Для анализа кадра можно преобразовать memoryview в структуру, понятную NumPy или другой библиотеке, но тогда ответственность за форму, dtype, endianness и stride лежит на скрипте. Это мощный путь для прототипирования собственных метрик, однако Python-цикл по каждому пикселю обычно значительно медленнее нативного фильтра. Если алгоритм стабилизировался и используется на всём ролике, его выгоднее перенести в vectorized-код или отдельный plugin.

Записывать непосредственно в readonly frame нельзя. Для изменения данных требуется writable copy или создание нового кадра. Такой контракт важен для многопоточности: один и тот же immutable frame может безопасно использоваться несколькими ветвями графа без скрытого изменения из callback.

Асинхронные запросы и собственные утилиты

get_frame_async() возвращает Future или вызывает callback после готовности кадра. Это позволяет строить собственный preview, анализатор или экспортёр без последовательного ожидания каждого номера. При этом не нужно создавать отдельный Core на каждый worker: один граф уже имеет внутренний scheduler.

Функция frames() также делает конкурентный рендер и ограничивает количество ещё не потреблённых кадров через backlog. Эти параметры существуют прежде всего для специальных и диагностических сценариев. В обычном pipeline ручное увеличение prefetch может только поднять потребление памяти, не ускорив фильтры, которые и так насыщают CPU.

Если утилита долго хранит VideoFrame после обработки, она удерживает связанные буферы. Контекстный менеджер или явное освобождение помогает быстрее вернуть память. Это особенно заметно в Python-анализаторах 4K, где список из сотен сохранённых frames превращается в гигабайты данных.

Низкоуровневый доступ полезен и для тестов: можно запросить несколько фиксированных кадров, прочитать properties, вычислить checksum собственным кодом и проверить, что refactoring скрипта не изменил ожидаемые точки. Это уже задача автоматизации, а не стандартного пользовательского GUI, но именно Python-основа делает её естественной.

VSScript и встраивание VapourSynth в другие программы

VapourSynth используется не только через VSPipe. Библиотека VSScript предназначена для приложений, которые хотят создать script environment, выполнить Python-код и получить зарегистрированные outputs через API. Поэтому редактор, previewer или кодирующая оболочка может загружать .vpy напрямую, не запуская отдельный процесс VSPipe для каждого кадра.

Это объясняет, почему проблемы с VSScript отличаются от ошибок самого скрипта. Приложение сначала должно найти правильную библиотеку, создать окружение и встроенный Python, а уже затем evaluate script. Если frontend привязан к другому VSScript, пользователь видит ошибку ещё до загрузки своих плагинов. Функция get_vsscript() помогает определить путь библиотеки, соответствующий активному Python-окружению.

Для разработчиков фильтров доступен C API и публичные заголовки. Плагин регистрирует функции, создаёт video/audio filters, получает запросы кадров и взаимодействует с frame properties. Многопоточность нужно учитывать на уровне реализации: один экземпляр фильтра может получать конкурентные запросы в соответствии с объявленным режимом.

Пользователю не требуется знать C API для написания .vpy, но понимание архитектуры полезно при диагностике. Namespace в Python — это оболочка над зарегистрированными функциями plugin. Поэтому документация конкретного DLL/SO определяет сигнатуру вызова, допустимые форматы и thread-safety, а не синтаксис Python как таковой.

Пути в Windows и другие Python-ловушки

В Windows обратный слеш внутри обычной Python-строки является escape-символом. Поэтому путь вроде C:\video\new\test.mkv может неожиданно содержать \n или \t, если записан неаккуратно. Надёжные варианты — raw string с префиксом r, двойные обратные слеши или прямые слеши.

Эта ошибка часто маскируется под source не открывает файл. Если путь печатается с переносом строки или табуляцией, plugin не виноват. Для больших проектов лучше использовать pathlib.Path, строить пути программно и преобразовывать их в строку только при вызове функции, которая этого требует.

Ещё одна ловушка — аргумент фильтра, имя которого совпадает с ключевым словом Python. VapourSynth позволяет добавить один завершающий underscore, а binding уберёт его перед вызовом. Альтернативный способ — передать такой параметр через словарь **kwargs. Это нормальная часть Python binding, а не другое имя опции в самом plugin.

Скрипт, выполняемый через VSScript, имеет специальное значение __name__, отличное от обычного запуска Python-файла. Поэтому блоки вида if __name__ == "__main__": могут не сработать в editor или VSPipe. Если требуется общий модуль и отдельный launcher, лучше явно разделить библиотечный код и .vpy, который регистрирует outputs.

Диагностическая таблица типичных проблем

СимптомЧто проверить первымТипичное решение
У core нет нужного namespaceКаталог автозагрузки и наличие плагина в активном окруженииУстановить модуль в правильное окружение или загрузить его явно
Resize сообщает no path between colorspacesMatrix, transfer, primaries и range во frame propertiesУказать корректные input-параметры или исправить properties до конвертации
Crop не принимает величинуСубдискретизацию текущего YUV-форматаИспользовать допустимый шаг crop или временно перейти в формат без такого ограничения
Preview работает, encode падает позжеКадры в месте сбоя и variable format/propertiesПрогнать диапазон, упростить граф и найти первый фильтр, где появляется ошибка
Скрипт запускается в терминале, но не в editorПуть VSScript, Python и рабочий каталогПривязать frontend к тому же окружению и нормализовать пути
Процесс расходует слишком много памятиПромежуточный формат, число потоков и temporal radiusСнизить параллелизм, убрать лишние ветви и тяжёлые RGB float этапы
После RGB↔YUV изменились уровниFull/limited range и matrixСделать цветовое преобразование с явными параметрами вместо ручной компенсации
Поля двигаются назад-вперёдTFF/BFF и _FieldBasedИсправить field order до deinterlace или field processing

Эта таблица полезна именно как порядок проверки. В VapourSynth большинство ошибок локальны: либо неверен вход фильтра, либо недоступна зависимость, либо потеряно свойство кадра. Чем раньше определить класс проблемы, тем меньше соблазн случайно менять параметры нескольких этапов одновременно.

Сравнение VapourSynth с аналогами

ПрограммаЛучше подходит дляГлавное ограничение
VapourSynthСложных воспроизводимых цепочек фильтрации, Python-автоматизации, покадрового анализа и передачи результата внешним кодировщикамТребует понимания Python, форматов кадров и зависимостей плагинов; единого обязательного GUI нет
AviSynth+Скриптового фреймсервинга с огромной исторической библиотекой фильтров и совместимостью со многими Windows-workflowСобственный язык сценариев и более сильная привязка многих плагинов к экосистеме Windows
FFmpeg / libavfilterУниверсального декодирования, фильтрации, кодирования, mux/demux и автоматизации в одной командной цепочкеСложные условные покадровые алгоритмы и Python-логику обычно описывать менее удобно, чем в VapourSynth
OpenCVКомпьютерного зрения, анализа кадров, собственных алгоритмов на Python/C++ и работы с массивамиНе предоставляет специализированную экосистему фреймсерверных видеофильтров и типичный encoder-pipe workflow из коробки
VirtualDub2Интерактивной фильтрации, предпросмотра и работы с классическим GUI для видеообработкиМенее удобен для больших программируемых графов, автоматизации и сложной условной логики Python

Если задача — написать повторяемый pipeline с точным контролем каждого фильтра и легко автоматизировать его Python-кодом, VapourSynth обычно оказывается наиболее естественным выбором. AviSynth+ ближе всего по философии фреймсервинга и может быть предпочтительнее в существующем Windows-проекте с нужными legacy-плагинами. FFmpeg выигрывает, когда декодирование, несколько простых фильтров, кодирование и mux хочется выполнить одной утилитой без отдельной экосистемы. OpenCV сильнее как библиотека компьютерного зрения, а VirtualDub2 — как GUI-инструмент для интерактивной обработки.

Кому подходит VapourSynth

Он особенно полезен энтузиастам кодирования, реставраторам, разработчикам фильтров и пользователям, которым важна воспроизводимость. Скрипт можно сохранить в системе контроля версий, сравнить изменения, вынести параметры в функции и запустить тот же pipeline на серии файлов. Для сложных проектов это значительно надёжнее, чем помнить набор ручных действий в GUI.

Порог входа выше, чем у обычного конвертера. Нужно понимать хотя бы основы Python, различия RGB/YUV, bit depth, subsampling, frame rate и работу external encoder. Но эти знания окупаются там, где готовые пресеты не дают нужной точности.

Если задача сводится к разовой перекодировке MP4 в другой формат, VapourSynth избыточен. Если же требуется контролировать deinterlace, построить маски, сравнить несколько denoise-ветвей, выполнить сложный resize, сохранить метаданные кадров и гарантированно повторить обработку позже, его архитектура подходит значительно лучше.

Итоговая логика работы

Надёжный VapourSynth-проект начинается с корректного source, а не с фильтра. После открытия проверяют длину, fps, формат, color properties и field order. Затем приводят clip к рабочему представлению, выполняют фильтры в осмысленном порядке, отдельно проверяют маски и адаптивные ветви, контролируют timing и лишь в самом конце готовят формат внешнего кодировщика.

При проблеме граф упрощают до минимального воспроизводимого узла. При проблеме цвета проверяют metadata и range. При нехватке памяти уменьшают параллелизм и тяжёлые промежуточные форматы. При ошибке плагина проверяют реальное окружение Core. Такой способ диагностики соответствует архитектуре VapourSynth и обычно быстрее случайной замены параметров.

В результате VapourSynth ценен не набором кнопок, а тем, что превращает обработку видео в явно описанный вычислительный граф. Python делает этот граф программируемым, плагины расширяют его специализированными алгоритмами, а VSPipe связывает результат с кодировщиками и другими программами. Именно сочетание точности, автоматизации и фреймсерверной модели делает его удобным для задач, которые трудно стабильно воспроизводить в обычном конвертере.